0659bd04c273b6c7dab7c7405c6870004c92b20f
[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 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62
63 /* for DigField() */
64 #define DF_NO_PUSH              0
65 #define DF_DIG                  1
66 #define DF_SNAP                 2
67
68 /* for MovePlayer() */
69 #define MP_NO_ACTION            0
70 #define MP_MOVING               1
71 #define MP_ACTION               2
72 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
73
74 /* for ScrollPlayer() */
75 #define SCROLL_INIT             0
76 #define SCROLL_GO_ON            1
77
78 /* for Bang()/Explode() */
79 #define EX_PHASE_START          0
80 #define EX_TYPE_NONE            0
81 #define EX_TYPE_NORMAL          (1 << 0)
82 #define EX_TYPE_CENTER          (1 << 1)
83 #define EX_TYPE_BORDER          (1 << 2)
84 #define EX_TYPE_CROSS           (1 << 3)
85 #define EX_TYPE_DYNA            (1 << 4)
86 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
87
88 #if 1
89 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0)
90 #define PANEL_XPOS(p)           (DX + ALIGNED_MENU_XPOS(p))
91 #define PANEL_YPOS(p)           (DY + ALIGNED_MENU_YPOS(p))
92 #else
93 #define PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
94 #define PANEL_XPOS(p)           (ALIGNED_XPOS((p).x, (p).width, (p).align))
95 #define PANEL_YPOS(p)           ((p).y)
96 #endif
97
98 /* special positions in the game control window (relative to control window) */
99 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
100 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
101 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
102 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
103 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
104 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
105 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
106 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
107 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
108 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
109 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
110 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
111 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
112 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
113 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
114 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
115
116 /* special positions in the game control window (relative to main window) */
117 #define DX_LEVEL1               (DX + XX_LEVEL1)
118 #define DX_LEVEL2               (DX + XX_LEVEL2)
119 #define DX_LEVEL                (DX + XX_LEVEL)
120 #define DY_LEVEL                (DY + YY_LEVEL)
121 #define DX_EMERALDS             (DX + XX_EMERALDS)
122 #define DY_EMERALDS             (DY + YY_EMERALDS)
123 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
124 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
125 #define DX_KEYS                 (DX + XX_KEYS)
126 #define DY_KEYS                 (DY + YY_KEYS)
127 #define DX_SCORE                (DX + XX_SCORE)
128 #define DY_SCORE                (DY + YY_SCORE)
129 #define DX_TIME1                (DX + XX_TIME1)
130 #define DX_TIME2                (DX + XX_TIME2)
131 #define DX_TIME                 (DX + XX_TIME)
132 #define DY_TIME                 (DY + YY_TIME)
133
134 /* values for delayed check of falling and moving elements and for collision */
135 #define CHECK_DELAY_MOVING      3
136 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
137 #define CHECK_DELAY_COLLISION   2
138 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
139
140 /* values for initial player move delay (initial delay counter value) */
141 #define INITIAL_MOVE_DELAY_OFF  -1
142 #define INITIAL_MOVE_DELAY_ON   0
143
144 /* values for player movement speed (which is in fact a delay value) */
145 #define MOVE_DELAY_MIN_SPEED    32
146 #define MOVE_DELAY_NORMAL_SPEED 8
147 #define MOVE_DELAY_HIGH_SPEED   4
148 #define MOVE_DELAY_MAX_SPEED    1
149
150 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
151 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
152
153 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
154 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
155
156 /* values for other actions */
157 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
158 #define MOVE_STEPSIZE_MIN       (1)
159 #define MOVE_STEPSIZE_MAX       (TILEX)
160
161 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
162 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
163
164 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
165
166 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
167                                  RND(element_info[e].push_delay_random))
168 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
169                                  RND(element_info[e].drop_delay_random))
170 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
171                                  RND(element_info[e].move_delay_random))
172 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
173                                     (element_info[e].move_delay_random))
174 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
175                                  RND(element_info[e].ce_value_random_initial))
176 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
177 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
178                                  RND((c)->delay_random * (c)->delay_frames))
179 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
180                                  RND((c)->delay_random))
181
182
183 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
184          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
185
186 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
187         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
188          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
189          (be) + (e) - EL_SELF)
190
191 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
192         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
193          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
194          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
195          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
196          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
197          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
198          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
199          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
200          (e))
201
202 #define CAN_GROW_INTO(e)                                                \
203         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
204
205 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
206                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
207                                         (condition)))
208
209 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
210                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
211                                         (CAN_MOVE_INTO_ACID(e) &&       \
212                                          Feld[x][y] == EL_ACID) ||      \
213                                         (condition)))
214
215 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
216                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
217                                         (CAN_MOVE_INTO_ACID(e) &&       \
218                                          Feld[x][y] == EL_ACID) ||      \
219                                         (condition)))
220
221 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
222                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
223                                         (condition) ||                  \
224                                         (CAN_MOVE_INTO_ACID(e) &&       \
225                                          Feld[x][y] == EL_ACID) ||      \
226                                         (DONT_COLLIDE_WITH(e) &&        \
227                                          IS_PLAYER(x, y) &&             \
228                                          !PLAYER_ENEMY_PROTECTED(x, y))))
229
230 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
231         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
232
233 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
234         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
235
236 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
237         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
238
239 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
240         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
241                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
242
243 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
244         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
245
246 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
247         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
248
249 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
250         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
251
252 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
253         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
254
255 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
256         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
257
258 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
259         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
260                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
261                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
262                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
263                                                  IS_FOOD_PENGUIN(Feld[x][y])))
264 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
265         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
266
267 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
268         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
269
270 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
271         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
272
273 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
274         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
275                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
276
277 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
278
279 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
280                 (!IS_PLAYER(x, y) &&                                    \
281                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
282
283 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
284         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
285
286 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
287 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
288
289 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
290 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
291 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
292 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
293
294 /* game button identifiers */
295 #define GAME_CTRL_ID_STOP               0
296 #define GAME_CTRL_ID_PAUSE              1
297 #define GAME_CTRL_ID_PLAY               2
298 #define SOUND_CTRL_ID_MUSIC             3
299 #define SOUND_CTRL_ID_LOOPS             4
300 #define SOUND_CTRL_ID_SIMPLE            5
301
302 #define NUM_GAME_BUTTONS                6
303
304
305 /* forward declaration for internal use */
306
307 static void CreateField(int, int, int);
308
309 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
310 static void AdvanceFrameAndPlayerCounters(int);
311
312 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
313 static boolean MovePlayer(struct PlayerInfo *, int, int);
314 static void ScrollPlayer(struct PlayerInfo *, int);
315 static void ScrollScreen(struct PlayerInfo *, int);
316
317 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
318
319 static void InitBeltMovement(void);
320 static void CloseAllOpenTimegates(void);
321 static void CheckGravityMovement(struct PlayerInfo *);
322 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
323 static void KillPlayerUnlessEnemyProtected(int, int);
324 static void KillPlayerUnlessExplosionProtected(int, int);
325
326 static void TestIfPlayerTouchesCustomElement(int, int);
327 static void TestIfElementTouchesCustomElement(int, int);
328 static void TestIfElementHitsCustomElement(int, int, int);
329 #if 0
330 static void TestIfElementSmashesCustomElement(int, int, int);
331 #endif
332
333 static void HandleElementChange(int, int, int);
334 static void ExecuteCustomElementAction(int, int, int, int);
335 static boolean ChangeElement(int, int, int, int);
336
337 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
338 #define CheckTriggeredElementChange(x, y, e, ev)                        \
339         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
340 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
341         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
342 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
343         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
344 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
345         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
346
347 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
348 #define CheckElementChange(x, y, e, te, ev)                             \
349         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
350 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
351         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
352 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
353         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
354
355 static void PlayLevelSound(int, int, int);
356 static void PlayLevelSoundNearest(int, int, int);
357 static void PlayLevelSoundAction(int, int, int);
358 static void PlayLevelSoundElementAction(int, int, int, int);
359 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
360 static void PlayLevelSoundActionIfLoop(int, int, int);
361 static void StopLevelSoundActionIfLoop(int, int, int);
362 static void PlayLevelMusic();
363
364 static void MapGameButtons();
365 static void HandleGameButtons(struct GadgetInfo *);
366
367 int AmoebeNachbarNr(int, int);
368 void AmoebeUmwandeln(int, int);
369 void ContinueMoving(int, int);
370 void Bang(int, int);
371 void InitMovDir(int, int);
372 void InitAmoebaNr(int, int);
373 int NewHiScore(void);
374
375 void TestIfGoodThingHitsBadThing(int, int, int);
376 void TestIfBadThingHitsGoodThing(int, int, int);
377 void TestIfPlayerTouchesBadThing(int, int);
378 void TestIfPlayerRunsIntoBadThing(int, int, int);
379 void TestIfBadThingTouchesPlayer(int, int);
380 void TestIfBadThingRunsIntoPlayer(int, int, int);
381 void TestIfFriendTouchesBadThing(int, int);
382 void TestIfBadThingTouchesFriend(int, int);
383 void TestIfBadThingTouchesOtherBadThing(int, int);
384
385 void KillPlayer(struct PlayerInfo *);
386 void BuryPlayer(struct PlayerInfo *);
387 void RemovePlayer(struct PlayerInfo *);
388
389 boolean SnapField(struct PlayerInfo *, int, int);
390 boolean DropElement(struct PlayerInfo *);
391
392 static int getInvisibleActiveFromInvisibleElement(int);
393 static int getInvisibleFromInvisibleActiveElement(int);
394
395 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
396
397 /* for detection of endless loops, caused by custom element programming */
398 /* (using maximal playfield width x 10 is just a rough approximation) */
399 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
400
401 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
402 {                                                                       \
403   if (recursion_loop_detected)                                          \
404     return (rc);                                                        \
405                                                                         \
406   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
407   {                                                                     \
408     recursion_loop_detected = TRUE;                                     \
409     recursion_loop_element = (e);                                       \
410   }                                                                     \
411                                                                         \
412   recursion_loop_depth++;                                               \
413 }
414
415 #define RECURSION_LOOP_DETECTION_END()                                  \
416 {                                                                       \
417   recursion_loop_depth--;                                               \
418 }
419
420 static int recursion_loop_depth;
421 static boolean recursion_loop_detected;
422 static boolean recursion_loop_element;
423
424
425 /* ------------------------------------------------------------------------- */
426 /* definition of elements that automatically change to other elements after  */
427 /* a specified time, eventually calling a function when changing             */
428 /* ------------------------------------------------------------------------- */
429
430 /* forward declaration for changer functions */
431 static void InitBuggyBase(int, int);
432 static void WarnBuggyBase(int, int);
433
434 static void InitTrap(int, int);
435 static void ActivateTrap(int, int);
436 static void ChangeActiveTrap(int, int);
437
438 static void InitRobotWheel(int, int);
439 static void RunRobotWheel(int, int);
440 static void StopRobotWheel(int, int);
441
442 static void InitTimegateWheel(int, int);
443 static void RunTimegateWheel(int, int);
444
445 static void InitMagicBallDelay(int, int);
446 static void ActivateMagicBall(int, int);
447
448 struct ChangingElementInfo
449 {
450   int element;
451   int target_element;
452   int change_delay;
453   void (*pre_change_function)(int x, int y);
454   void (*change_function)(int x, int y);
455   void (*post_change_function)(int x, int y);
456 };
457
458 static struct ChangingElementInfo change_delay_list[] =
459 {
460   {
461     EL_NUT_BREAKING,
462     EL_EMERALD,
463     6,
464     NULL,
465     NULL,
466     NULL
467   },
468   {
469     EL_PEARL_BREAKING,
470     EL_EMPTY,
471     8,
472     NULL,
473     NULL,
474     NULL
475   },
476   {
477     EL_EXIT_OPENING,
478     EL_EXIT_OPEN,
479     29,
480     NULL,
481     NULL,
482     NULL
483   },
484   {
485     EL_EXIT_CLOSING,
486     EL_EXIT_CLOSED,
487     29,
488     NULL,
489     NULL,
490     NULL
491   },
492   {
493     EL_STEEL_EXIT_OPENING,
494     EL_STEEL_EXIT_OPEN,
495     29,
496     NULL,
497     NULL,
498     NULL
499   },
500   {
501     EL_STEEL_EXIT_CLOSING,
502     EL_STEEL_EXIT_CLOSED,
503     29,
504     NULL,
505     NULL,
506     NULL
507   },
508   {
509     EL_EM_EXIT_OPENING,
510     EL_EM_EXIT_OPEN,
511     29,
512     NULL,
513     NULL,
514     NULL
515   },
516   {
517     EL_EM_EXIT_CLOSING,
518 #if 1
519     EL_EMPTY,
520 #else
521     EL_EM_EXIT_CLOSED,
522 #endif
523     29,
524     NULL,
525     NULL,
526     NULL
527   },
528   {
529     EL_EM_STEEL_EXIT_OPENING,
530     EL_EM_STEEL_EXIT_OPEN,
531     29,
532     NULL,
533     NULL,
534     NULL
535   },
536   {
537     EL_EM_STEEL_EXIT_CLOSING,
538 #if 1
539     EL_STEELWALL,
540 #else
541     EL_EM_STEEL_EXIT_CLOSED,
542 #endif
543     29,
544     NULL,
545     NULL,
546     NULL
547   },
548   {
549     EL_SP_EXIT_OPENING,
550     EL_SP_EXIT_OPEN,
551     29,
552     NULL,
553     NULL,
554     NULL
555   },
556   {
557     EL_SP_EXIT_CLOSING,
558     EL_SP_EXIT_CLOSED,
559     29,
560     NULL,
561     NULL,
562     NULL
563   },
564   {
565     EL_SWITCHGATE_OPENING,
566     EL_SWITCHGATE_OPEN,
567     29,
568     NULL,
569     NULL,
570     NULL
571   },
572   {
573     EL_SWITCHGATE_CLOSING,
574     EL_SWITCHGATE_CLOSED,
575     29,
576     NULL,
577     NULL,
578     NULL
579   },
580   {
581     EL_TIMEGATE_OPENING,
582     EL_TIMEGATE_OPEN,
583     29,
584     NULL,
585     NULL,
586     NULL
587   },
588   {
589     EL_TIMEGATE_CLOSING,
590     EL_TIMEGATE_CLOSED,
591     29,
592     NULL,
593     NULL,
594     NULL
595   },
596
597   {
598     EL_ACID_SPLASH_LEFT,
599     EL_EMPTY,
600     8,
601     NULL,
602     NULL,
603     NULL
604   },
605   {
606     EL_ACID_SPLASH_RIGHT,
607     EL_EMPTY,
608     8,
609     NULL,
610     NULL,
611     NULL
612   },
613   {
614     EL_SP_BUGGY_BASE,
615     EL_SP_BUGGY_BASE_ACTIVATING,
616     0,
617     InitBuggyBase,
618     NULL,
619     NULL
620   },
621   {
622     EL_SP_BUGGY_BASE_ACTIVATING,
623     EL_SP_BUGGY_BASE_ACTIVE,
624     0,
625     InitBuggyBase,
626     NULL,
627     NULL
628   },
629   {
630     EL_SP_BUGGY_BASE_ACTIVE,
631     EL_SP_BUGGY_BASE,
632     0,
633     InitBuggyBase,
634     WarnBuggyBase,
635     NULL
636   },
637   {
638     EL_TRAP,
639     EL_TRAP_ACTIVE,
640     0,
641     InitTrap,
642     NULL,
643     ActivateTrap
644   },
645   {
646     EL_TRAP_ACTIVE,
647     EL_TRAP,
648     31,
649     NULL,
650     ChangeActiveTrap,
651     NULL
652   },
653   {
654     EL_ROBOT_WHEEL_ACTIVE,
655     EL_ROBOT_WHEEL,
656     0,
657     InitRobotWheel,
658     RunRobotWheel,
659     StopRobotWheel
660   },
661   {
662     EL_TIMEGATE_SWITCH_ACTIVE,
663     EL_TIMEGATE_SWITCH,
664     0,
665     InitTimegateWheel,
666     RunTimegateWheel,
667     NULL
668   },
669   {
670     EL_DC_TIMEGATE_SWITCH_ACTIVE,
671     EL_DC_TIMEGATE_SWITCH,
672     0,
673     InitTimegateWheel,
674     RunTimegateWheel,
675     NULL
676   },
677   {
678     EL_EMC_MAGIC_BALL_ACTIVE,
679     EL_EMC_MAGIC_BALL_ACTIVE,
680     0,
681     InitMagicBallDelay,
682     NULL,
683     ActivateMagicBall
684   },
685   {
686     EL_EMC_SPRING_BUMPER_ACTIVE,
687     EL_EMC_SPRING_BUMPER,
688     8,
689     NULL,
690     NULL,
691     NULL
692   },
693   {
694     EL_DIAGONAL_SHRINKING,
695     EL_UNDEFINED,
696     0,
697     NULL,
698     NULL,
699     NULL
700   },
701   {
702     EL_DIAGONAL_GROWING,
703     EL_UNDEFINED,
704     0,
705     NULL,
706     NULL,
707     NULL,
708   },
709
710   {
711     EL_UNDEFINED,
712     EL_UNDEFINED,
713     -1,
714     NULL,
715     NULL,
716     NULL
717   }
718 };
719
720 struct
721 {
722   int element;
723   int push_delay_fixed, push_delay_random;
724 }
725 push_delay_list[] =
726 {
727   { EL_SPRING,                  0, 0 },
728   { EL_BALLOON,                 0, 0 },
729
730   { EL_SOKOBAN_OBJECT,          2, 0 },
731   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
732   { EL_SATELLITE,               2, 0 },
733   { EL_SP_DISK_YELLOW,          2, 0 },
734
735   { EL_UNDEFINED,               0, 0 },
736 };
737
738 struct
739 {
740   int element;
741   int move_stepsize;
742 }
743 move_stepsize_list[] =
744 {
745   { EL_AMOEBA_DROP,             2 },
746   { EL_AMOEBA_DROPPING,         2 },
747   { EL_QUICKSAND_FILLING,       1 },
748   { EL_QUICKSAND_EMPTYING,      1 },
749   { EL_QUICKSAND_FAST_FILLING,  2 },
750   { EL_QUICKSAND_FAST_EMPTYING, 2 },
751   { EL_MAGIC_WALL_FILLING,      2 },
752   { EL_MAGIC_WALL_EMPTYING,     2 },
753   { EL_BD_MAGIC_WALL_FILLING,   2 },
754   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
755   { EL_DC_MAGIC_WALL_FILLING,   2 },
756   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
757
758   { EL_UNDEFINED,               0 },
759 };
760
761 struct
762 {
763   int element;
764   int count;
765 }
766 collect_count_list[] =
767 {
768   { EL_EMERALD,                 1 },
769   { EL_BD_DIAMOND,              1 },
770   { EL_EMERALD_YELLOW,          1 },
771   { EL_EMERALD_RED,             1 },
772   { EL_EMERALD_PURPLE,          1 },
773   { EL_DIAMOND,                 3 },
774   { EL_SP_INFOTRON,             1 },
775   { EL_PEARL,                   5 },
776   { EL_CRYSTAL,                 8 },
777
778   { EL_UNDEFINED,               0 },
779 };
780
781 struct
782 {
783   int element;
784   int direction;
785 }
786 access_direction_list[] =
787 {
788   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
789   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
790   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
791   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
792   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
793   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
794   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
795   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
796   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
797   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
798   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
799
800   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
801   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
802   { EL_SP_PORT_UP,                                                   MV_DOWN },
803   { EL_SP_PORT_DOWN,                                         MV_UP           },
804   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
805   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
806   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
807   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
808   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
809   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
810   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
811   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
812   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
813   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
814   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
815   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
816   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
817   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
818   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
819
820   { EL_UNDEFINED,                       MV_NONE                              }
821 };
822
823 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
824
825 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
826 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
827 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
828                                  IS_JUST_CHANGING(x, y))
829
830 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
831
832 /* static variables for playfield scan mode (scanning forward or backward) */
833 static int playfield_scan_start_x = 0;
834 static int playfield_scan_start_y = 0;
835 static int playfield_scan_delta_x = 1;
836 static int playfield_scan_delta_y = 1;
837
838 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
839                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
840                                      (y) += playfield_scan_delta_y)     \
841                                 for ((x) = playfield_scan_start_x;      \
842                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
843                                      (x) += playfield_scan_delta_x)     \
844
845 #ifdef DEBUG
846 void DEBUG_SetMaximumDynamite()
847 {
848   int i;
849
850   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
851     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
852       local_player->inventory_element[local_player->inventory_size++] =
853         EL_DYNAMITE;
854 }
855 #endif
856
857 static void InitPlayfieldScanModeVars()
858 {
859   if (game.use_reverse_scan_direction)
860   {
861     playfield_scan_start_x = lev_fieldx - 1;
862     playfield_scan_start_y = lev_fieldy - 1;
863
864     playfield_scan_delta_x = -1;
865     playfield_scan_delta_y = -1;
866   }
867   else
868   {
869     playfield_scan_start_x = 0;
870     playfield_scan_start_y = 0;
871
872     playfield_scan_delta_x = 1;
873     playfield_scan_delta_y = 1;
874   }
875 }
876
877 static void InitPlayfieldScanMode(int mode)
878 {
879   game.use_reverse_scan_direction =
880     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
881
882   InitPlayfieldScanModeVars();
883 }
884
885 static int get_move_delay_from_stepsize(int move_stepsize)
886 {
887   move_stepsize =
888     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
889
890   /* make sure that stepsize value is always a power of 2 */
891   move_stepsize = (1 << log_2(move_stepsize));
892
893   return TILEX / move_stepsize;
894 }
895
896 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
897                                boolean init_game)
898 {
899   int player_nr = player->index_nr;
900   int move_delay = get_move_delay_from_stepsize(move_stepsize);
901   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
902
903   /* do no immediately change move delay -- the player might just be moving */
904   player->move_delay_value_next = move_delay;
905
906   /* information if player can move must be set separately */
907   player->cannot_move = cannot_move;
908
909   if (init_game)
910   {
911     player->move_delay       = game.initial_move_delay[player_nr];
912     player->move_delay_value = game.initial_move_delay_value[player_nr];
913
914     player->move_delay_value_next = -1;
915
916     player->move_delay_reset_counter = 0;
917   }
918 }
919
920 void GetPlayerConfig()
921 {
922   GameFrameDelay = setup.game_frame_delay;
923
924   if (!audio.sound_available)
925     setup.sound_simple = FALSE;
926
927   if (!audio.loops_available)
928     setup.sound_loops = FALSE;
929
930   if (!audio.music_available)
931     setup.sound_music = FALSE;
932
933   if (!video.fullscreen_available)
934     setup.fullscreen = FALSE;
935
936   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
937
938   SetAudioMode(setup.sound);
939   InitJoysticks();
940 }
941
942 static int getBeltNrFromBeltElement(int element)
943 {
944   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
945           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
946           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
947 }
948
949 static int getBeltNrFromBeltActiveElement(int element)
950 {
951   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
952           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
953           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
954 }
955
956 static int getBeltNrFromBeltSwitchElement(int element)
957 {
958   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
959           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
960           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
961 }
962
963 static int getBeltDirNrFromBeltSwitchElement(int element)
964 {
965   static int belt_base_element[4] =
966   {
967     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
968     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
969     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
970     EL_CONVEYOR_BELT_4_SWITCH_LEFT
971   };
972
973   int belt_nr = getBeltNrFromBeltSwitchElement(element);
974   int belt_dir_nr = element - belt_base_element[belt_nr];
975
976   return (belt_dir_nr % 3);
977 }
978
979 static int getBeltDirFromBeltSwitchElement(int element)
980 {
981   static int belt_move_dir[3] =
982   {
983     MV_LEFT,
984     MV_NONE,
985     MV_RIGHT
986   };
987
988   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
989
990   return belt_move_dir[belt_dir_nr];
991 }
992
993 static int get_element_from_group_element(int element)
994 {
995   if (IS_GROUP_ELEMENT(element))
996   {
997     struct ElementGroupInfo *group = element_info[element].group;
998     int last_anim_random_frame = gfx.anim_random_frame;
999     int element_pos;
1000
1001     if (group->choice_mode == ANIM_RANDOM)
1002       gfx.anim_random_frame = RND(group->num_elements_resolved);
1003
1004     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1005                                     group->choice_mode, 0,
1006                                     group->choice_pos);
1007
1008     if (group->choice_mode == ANIM_RANDOM)
1009       gfx.anim_random_frame = last_anim_random_frame;
1010
1011     group->choice_pos++;
1012
1013     element = group->element_resolved[element_pos];
1014   }
1015
1016   return element;
1017 }
1018
1019 static void InitPlayerField(int x, int y, int element, boolean init_game)
1020 {
1021   if (element == EL_SP_MURPHY)
1022   {
1023     if (init_game)
1024     {
1025       if (stored_player[0].present)
1026       {
1027         Feld[x][y] = EL_SP_MURPHY_CLONE;
1028
1029         return;
1030       }
1031       else
1032       {
1033         stored_player[0].use_murphy = TRUE;
1034
1035         if (!level.use_artwork_element[0])
1036           stored_player[0].artwork_element = EL_SP_MURPHY;
1037       }
1038
1039       Feld[x][y] = EL_PLAYER_1;
1040     }
1041   }
1042
1043   if (init_game)
1044   {
1045     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1046     int jx = player->jx, jy = player->jy;
1047
1048     player->present = TRUE;
1049
1050     player->block_last_field = (element == EL_SP_MURPHY ?
1051                                 level.sp_block_last_field :
1052                                 level.block_last_field);
1053
1054     /* ---------- initialize player's last field block delay --------------- */
1055
1056     /* always start with reliable default value (no adjustment needed) */
1057     player->block_delay_adjustment = 0;
1058
1059     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1060     if (player->block_last_field && element == EL_SP_MURPHY)
1061       player->block_delay_adjustment = 1;
1062
1063     /* special case 2: in game engines before 3.1.1, blocking was different */
1064     if (game.use_block_last_field_bug)
1065       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1066
1067     if (!options.network || player->connected)
1068     {
1069       player->active = TRUE;
1070
1071       /* remove potentially duplicate players */
1072       if (StorePlayer[jx][jy] == Feld[x][y])
1073         StorePlayer[jx][jy] = 0;
1074
1075       StorePlayer[x][y] = Feld[x][y];
1076
1077       if (options.debug)
1078       {
1079         printf("Player %d activated.\n", player->element_nr);
1080         printf("[Local player is %d and currently %s.]\n",
1081                local_player->element_nr,
1082                local_player->active ? "active" : "not active");
1083       }
1084     }
1085
1086     Feld[x][y] = EL_EMPTY;
1087
1088     player->jx = player->last_jx = x;
1089     player->jy = player->last_jy = y;
1090   }
1091 }
1092
1093 static void InitField(int x, int y, boolean init_game)
1094 {
1095   int element = Feld[x][y];
1096
1097   switch (element)
1098   {
1099     case EL_SP_MURPHY:
1100     case EL_PLAYER_1:
1101     case EL_PLAYER_2:
1102     case EL_PLAYER_3:
1103     case EL_PLAYER_4:
1104       InitPlayerField(x, y, element, init_game);
1105       break;
1106
1107     case EL_SOKOBAN_FIELD_PLAYER:
1108       element = Feld[x][y] = EL_PLAYER_1;
1109       InitField(x, y, init_game);
1110
1111       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1112       InitField(x, y, init_game);
1113       break;
1114
1115     case EL_SOKOBAN_FIELD_EMPTY:
1116       local_player->sokobanfields_still_needed++;
1117       break;
1118
1119     case EL_STONEBLOCK:
1120       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1121         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1122       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1123         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1124       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1125         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1126       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1127         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1128       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1129         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1130       break;
1131
1132     case EL_BUG:
1133     case EL_BUG_RIGHT:
1134     case EL_BUG_UP:
1135     case EL_BUG_LEFT:
1136     case EL_BUG_DOWN:
1137     case EL_SPACESHIP:
1138     case EL_SPACESHIP_RIGHT:
1139     case EL_SPACESHIP_UP:
1140     case EL_SPACESHIP_LEFT:
1141     case EL_SPACESHIP_DOWN:
1142     case EL_BD_BUTTERFLY:
1143     case EL_BD_BUTTERFLY_RIGHT:
1144     case EL_BD_BUTTERFLY_UP:
1145     case EL_BD_BUTTERFLY_LEFT:
1146     case EL_BD_BUTTERFLY_DOWN:
1147     case EL_BD_FIREFLY:
1148     case EL_BD_FIREFLY_RIGHT:
1149     case EL_BD_FIREFLY_UP:
1150     case EL_BD_FIREFLY_LEFT:
1151     case EL_BD_FIREFLY_DOWN:
1152     case EL_PACMAN_RIGHT:
1153     case EL_PACMAN_UP:
1154     case EL_PACMAN_LEFT:
1155     case EL_PACMAN_DOWN:
1156     case EL_YAMYAM:
1157     case EL_YAMYAM_LEFT:
1158     case EL_YAMYAM_RIGHT:
1159     case EL_YAMYAM_UP:
1160     case EL_YAMYAM_DOWN:
1161     case EL_DARK_YAMYAM:
1162     case EL_ROBOT:
1163     case EL_PACMAN:
1164     case EL_SP_SNIKSNAK:
1165     case EL_SP_ELECTRON:
1166     case EL_MOLE:
1167     case EL_MOLE_LEFT:
1168     case EL_MOLE_RIGHT:
1169     case EL_MOLE_UP:
1170     case EL_MOLE_DOWN:
1171       InitMovDir(x, y);
1172       break;
1173
1174     case EL_AMOEBA_FULL:
1175     case EL_BD_AMOEBA:
1176       InitAmoebaNr(x, y);
1177       break;
1178
1179     case EL_AMOEBA_DROP:
1180       if (y == lev_fieldy - 1)
1181       {
1182         Feld[x][y] = EL_AMOEBA_GROWING;
1183         Store[x][y] = EL_AMOEBA_WET;
1184       }
1185       break;
1186
1187     case EL_DYNAMITE_ACTIVE:
1188     case EL_SP_DISK_RED_ACTIVE:
1189     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1190     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1191     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1192     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1193       MovDelay[x][y] = 96;
1194       break;
1195
1196     case EL_EM_DYNAMITE_ACTIVE:
1197       MovDelay[x][y] = 32;
1198       break;
1199
1200     case EL_LAMP:
1201       local_player->lights_still_needed++;
1202       break;
1203
1204     case EL_PENGUIN:
1205       local_player->friends_still_needed++;
1206       break;
1207
1208     case EL_PIG:
1209     case EL_DRAGON:
1210       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1211       break;
1212
1213     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1214     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1215     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1216     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1217     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1218     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1219     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1220     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1221     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1222     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1223     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1224     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1225       if (init_game)
1226       {
1227         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1228         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1229         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1230
1231         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1232         {
1233           game.belt_dir[belt_nr] = belt_dir;
1234           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1235         }
1236         else    /* more than one switch -- set it like the first switch */
1237         {
1238           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1239         }
1240       }
1241       break;
1242
1243 #if !USE_BOTH_SWITCHGATE_SWITCHES
1244     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1245       if (init_game)
1246         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1247       break;
1248
1249     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1250       if (init_game)
1251         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1252       break;
1253 #endif
1254
1255     case EL_LIGHT_SWITCH_ACTIVE:
1256       if (init_game)
1257         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1258       break;
1259
1260     case EL_INVISIBLE_STEELWALL:
1261     case EL_INVISIBLE_WALL:
1262     case EL_INVISIBLE_SAND:
1263       if (game.light_time_left > 0 ||
1264           game.lenses_time_left > 0)
1265         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1266       break;
1267
1268     case EL_EMC_MAGIC_BALL:
1269       if (game.ball_state)
1270         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1271       break;
1272
1273     case EL_EMC_MAGIC_BALL_SWITCH:
1274       if (game.ball_state)
1275         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1276       break;
1277
1278     default:
1279       if (IS_CUSTOM_ELEMENT(element))
1280       {
1281         if (CAN_MOVE(element))
1282           InitMovDir(x, y);
1283
1284 #if USE_NEW_CUSTOM_VALUE
1285         if (!element_info[element].use_last_ce_value || init_game)
1286           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1287 #endif
1288       }
1289       else if (IS_GROUP_ELEMENT(element))
1290       {
1291         Feld[x][y] = get_element_from_group_element(element);
1292
1293         InitField(x, y, init_game);
1294       }
1295
1296       break;
1297   }
1298
1299   if (!init_game)
1300     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1301 }
1302
1303 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1304 {
1305   InitField(x, y, init_game);
1306
1307   /* not needed to call InitMovDir() -- already done by InitField()! */
1308   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1309       CAN_MOVE(Feld[x][y]))
1310     InitMovDir(x, y);
1311 }
1312
1313 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1314 {
1315   int old_element = Feld[x][y];
1316
1317   InitField(x, y, init_game);
1318
1319   /* not needed to call InitMovDir() -- already done by InitField()! */
1320   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1321       CAN_MOVE(old_element) &&
1322       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1323     InitMovDir(x, y);
1324
1325   /* this case is in fact a combination of not less than three bugs:
1326      first, it calls InitMovDir() for elements that can move, although this is
1327      already done by InitField(); then, it checks the element that was at this
1328      field _before_ the call to InitField() (which can change it); lastly, it
1329      was not called for "mole with direction" elements, which were treated as
1330      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1331   */
1332 }
1333
1334 #if 1
1335
1336 void DrawGameValue_Emeralds(int value)
1337 {
1338   struct TextPosInfo *pos = &game.panel.gems;
1339   int font_nr = FONT_TEXT_2;
1340   int font_width = getFontWidth(font_nr);
1341   int digits = pos->chars;
1342
1343   if (PANEL_DEACTIVATED(pos))
1344     return;
1345
1346   pos->width = digits * font_width;
1347
1348   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1349 }
1350
1351 void DrawGameValue_Dynamite(int value)
1352 {
1353   struct TextPosInfo *pos = &game.panel.inventory;
1354   int font_nr = FONT_TEXT_2;
1355   int font_width = getFontWidth(font_nr);
1356   int digits = pos->chars;
1357
1358   if (PANEL_DEACTIVATED(pos))
1359     return;
1360
1361   pos->width = digits * font_width;
1362
1363   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1364 }
1365
1366 void DrawGameValue_Score(int value)
1367 {
1368   struct TextPosInfo *pos = &game.panel.score;
1369   int font_nr = FONT_TEXT_2;
1370   int font_width = getFontWidth(font_nr);
1371   int digits = pos->chars;
1372
1373   if (PANEL_DEACTIVATED(pos))
1374     return;
1375
1376   pos->width = digits * font_width;
1377
1378   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1379 }
1380
1381 void DrawGameValue_Time(int value)
1382 {
1383   struct TextPosInfo *pos = &game.panel.time;
1384   static int last_value = -1;
1385   int digits1 = 3;
1386   int digits2 = 4;
1387   int digits = pos->chars;
1388   int font1_nr = FONT_TEXT_2;
1389   int font2_nr = FONT_TEXT_1;
1390   int font_nr = font1_nr;
1391   boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1392
1393   if (PANEL_DEACTIVATED(pos))
1394     return;
1395
1396   if (use_dynamic_digits)               /* use dynamic number of digits */
1397   {
1398     digits  = (value < 1000 ? digits1  : digits2);
1399     font_nr = (value < 1000 ? font1_nr : font2_nr);
1400   }
1401
1402   /* clear background if value just changed its size (dynamic digits only) */
1403   if (use_dynamic_digits && (last_value < 1000) != (value < 1000))
1404   {
1405     int width1 = digits1 * getFontWidth(font1_nr);
1406     int width2 = digits2 * getFontWidth(font2_nr);
1407     int max_width = MAX(width1, width2);
1408     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1409
1410     pos->width = max_width;
1411
1412     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1413                                max_width, max_height);
1414   }
1415
1416   pos->width = digits * getFontWidth(font_nr);
1417
1418   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1419
1420   last_value = value;
1421 }
1422
1423 void DrawGameValue_Level(int value)
1424 {
1425   struct TextPosInfo *pos = &game.panel.level;
1426   int digits1 = 2;
1427   int digits2 = 3;
1428   int digits = pos->chars;
1429   int font1_nr = FONT_TEXT_2;
1430   int font2_nr = FONT_TEXT_1;
1431   int font_nr = font1_nr;
1432   boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1433
1434   if (PANEL_DEACTIVATED(pos))
1435     return;
1436
1437   if (use_dynamic_digits)               /* use dynamic number of digits */
1438   {
1439     digits  = (level_nr < 100 ? digits1  : digits2);
1440     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1441   }
1442
1443   pos->width = digits * getFontWidth(font_nr);
1444
1445   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1446 }
1447
1448 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1449 {
1450   struct TextPosInfo *pos = &game.panel.keys;
1451   int base_key_graphic = EL_KEY_1;
1452   int i;
1453
1454   if (PANEL_DEACTIVATED(pos))
1455     return;
1456
1457   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1458     base_key_graphic = EL_EM_KEY_1;
1459
1460   pos->width = 4 * MINI_TILEX;
1461
1462   /* currently only 4 of 8 possible keys are displayed */
1463   for (i = 0; i < STD_NUM_KEYS; i++)
1464   {
1465     int src_x = DOOR_GFX_PAGEX5 + 18;
1466     int src_y = DOOR_GFX_PAGEY1 + 123;
1467     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1468     int dst_y = PANEL_YPOS(pos);
1469
1470     if (key[i])
1471       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1472     else
1473       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1474                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1475   }
1476 }
1477
1478 #else
1479
1480 void DrawGameValue_Emeralds(int value)
1481 {
1482   int font_nr = FONT_TEXT_2;
1483   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1484
1485   if (PANEL_DEACTIVATED(game.panel.gems))
1486     return;
1487
1488   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1489 }
1490
1491 void DrawGameValue_Dynamite(int value)
1492 {
1493   int font_nr = FONT_TEXT_2;
1494   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1495
1496   if (PANEL_DEACTIVATED(game.panel.inventory))
1497     return;
1498
1499   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1500 }
1501
1502 void DrawGameValue_Score(int value)
1503 {
1504   int font_nr = FONT_TEXT_2;
1505   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1506
1507   if (PANEL_DEACTIVATED(game.panel.score))
1508     return;
1509
1510   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1511 }
1512
1513 void DrawGameValue_Time(int value)
1514 {
1515   int font1_nr = FONT_TEXT_2;
1516 #if 1
1517   int font2_nr = FONT_TEXT_1;
1518 #else
1519   int font2_nr = FONT_LEVEL_NUMBER;
1520 #endif
1521   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1522   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1523
1524   if (PANEL_DEACTIVATED(game.panel.time))
1525     return;
1526
1527   /* clear background if value just changed its size */
1528   if (value == 999 || value == 1000)
1529     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1530
1531   if (value < 1000)
1532     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1533   else
1534     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1535 }
1536
1537 void DrawGameValue_Level(int value)
1538 {
1539   int font1_nr = FONT_TEXT_2;
1540 #if 1
1541   int font2_nr = FONT_TEXT_1;
1542 #else
1543   int font2_nr = FONT_LEVEL_NUMBER;
1544 #endif
1545
1546   if (PANEL_DEACTIVATED(game.panel.level))
1547     return;
1548
1549   if (level_nr < 100)
1550     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1551   else
1552     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1553 }
1554
1555 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1556 {
1557   int base_key_graphic = EL_KEY_1;
1558   int i;
1559
1560   if (PANEL_DEACTIVATED(game.panel.keys))
1561     return;
1562
1563   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1564     base_key_graphic = EL_EM_KEY_1;
1565
1566   /* currently only 4 of 8 possible keys are displayed */
1567   for (i = 0; i < STD_NUM_KEYS; i++)
1568   {
1569     int x = XX_KEYS + i * MINI_TILEX;
1570     int y = YY_KEYS;
1571
1572     if (key[i])
1573       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1574     else
1575       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1576                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1577   }
1578 }
1579
1580 #endif
1581
1582 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1583                        int key_bits)
1584 {
1585   int key[MAX_NUM_KEYS];
1586   int i;
1587
1588   /* prevent EM engine from updating time/score values parallel to GameWon() */
1589   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1590       local_player->LevelSolved)
1591     return;
1592
1593   for (i = 0; i < MAX_NUM_KEYS; i++)
1594     key[i] = key_bits & (1 << i);
1595
1596   DrawGameValue_Level(level_nr);
1597
1598   DrawGameValue_Emeralds(emeralds);
1599   DrawGameValue_Dynamite(dynamite);
1600   DrawGameValue_Score(score);
1601   DrawGameValue_Time(time);
1602
1603   DrawGameValue_Keys(key);
1604 }
1605
1606 void DrawGameDoorValues()
1607 {
1608   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1609   int dynamite_value = 0;
1610   int score_value = (local_player->LevelSolved ? local_player->score_final :
1611                      local_player->score);
1612   int gems_value = local_player->gems_still_needed;
1613   int key_bits = 0;
1614   int i, j;
1615
1616   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1617   {
1618     DrawGameDoorValues_EM();
1619
1620     return;
1621   }
1622
1623   if (game.centered_player_nr == -1)
1624   {
1625     for (i = 0; i < MAX_PLAYERS; i++)
1626     {
1627       for (j = 0; j < MAX_NUM_KEYS; j++)
1628         if (stored_player[i].key[j])
1629           key_bits |= (1 << j);
1630
1631       dynamite_value += stored_player[i].inventory_size;
1632     }
1633   }
1634   else
1635   {
1636     int player_nr = game.centered_player_nr;
1637
1638     for (i = 0; i < MAX_NUM_KEYS; i++)
1639       if (stored_player[player_nr].key[i])
1640         key_bits |= (1 << i);
1641
1642     dynamite_value = stored_player[player_nr].inventory_size;
1643   }
1644
1645   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1646                     key_bits);
1647 }
1648
1649
1650 /*
1651   =============================================================================
1652   InitGameEngine()
1653   -----------------------------------------------------------------------------
1654   initialize game engine due to level / tape version number
1655   =============================================================================
1656 */
1657
1658 static void InitGameEngine()
1659 {
1660   int i, j, k, l, x, y;
1661
1662   /* set game engine from tape file when re-playing, else from level file */
1663   game.engine_version = (tape.playing ? tape.engine_version :
1664                          level.game_version);
1665
1666   /* ---------------------------------------------------------------------- */
1667   /* set flags for bugs and changes according to active game engine version */
1668   /* ---------------------------------------------------------------------- */
1669
1670   /*
1671     Summary of bugfix/change:
1672     Fixed handling for custom elements that change when pushed by the player.
1673
1674     Fixed/changed in version:
1675     3.1.0
1676
1677     Description:
1678     Before 3.1.0, custom elements that "change when pushing" changed directly
1679     after the player started pushing them (until then handled in "DigField()").
1680     Since 3.1.0, these custom elements are not changed until the "pushing"
1681     move of the element is finished (now handled in "ContinueMoving()").
1682
1683     Affected levels/tapes:
1684     The first condition is generally needed for all levels/tapes before version
1685     3.1.0, which might use the old behaviour before it was changed; known tapes
1686     that are affected are some tapes from the level set "Walpurgis Gardens" by
1687     Jamie Cullen.
1688     The second condition is an exception from the above case and is needed for
1689     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1690     above (including some development versions of 3.1.0), but before it was
1691     known that this change would break tapes like the above and was fixed in
1692     3.1.1, so that the changed behaviour was active although the engine version
1693     while recording maybe was before 3.1.0. There is at least one tape that is
1694     affected by this exception, which is the tape for the one-level set "Bug
1695     Machine" by Juergen Bonhagen.
1696   */
1697
1698   game.use_change_when_pushing_bug =
1699     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1700      !(tape.playing &&
1701        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1702        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1703
1704   /*
1705     Summary of bugfix/change:
1706     Fixed handling for blocking the field the player leaves when moving.
1707
1708     Fixed/changed in version:
1709     3.1.1
1710
1711     Description:
1712     Before 3.1.1, when "block last field when moving" was enabled, the field
1713     the player is leaving when moving was blocked for the time of the move,
1714     and was directly unblocked afterwards. This resulted in the last field
1715     being blocked for exactly one less than the number of frames of one player
1716     move. Additionally, even when blocking was disabled, the last field was
1717     blocked for exactly one frame.
1718     Since 3.1.1, due to changes in player movement handling, the last field
1719     is not blocked at all when blocking is disabled. When blocking is enabled,
1720     the last field is blocked for exactly the number of frames of one player
1721     move. Additionally, if the player is Murphy, the hero of Supaplex, the
1722     last field is blocked for exactly one more than the number of frames of
1723     one player move.
1724
1725     Affected levels/tapes:
1726     (!!! yet to be determined -- probably many !!!)
1727   */
1728
1729   game.use_block_last_field_bug =
1730     (game.engine_version < VERSION_IDENT(3,1,1,0));
1731
1732   /*
1733     Summary of bugfix/change:
1734     Changed behaviour of CE changes with multiple changes per single frame.
1735
1736     Fixed/changed in version:
1737     3.2.0-6
1738
1739     Description:
1740     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1741     This resulted in race conditions where CEs seem to behave strange in some
1742     situations (where triggered CE changes were just skipped because there was
1743     already a CE change on that tile in the playfield in that engine frame).
1744     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1745     (The number of changes per frame must be limited in any case, because else
1746     it is easily possible to define CE changes that would result in an infinite
1747     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1748     should be set large enough so that it would only be reached in cases where
1749     the corresponding CE change conditions run into a loop. Therefore, it seems
1750     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1751     maximal number of change pages for custom elements.)
1752
1753     Affected levels/tapes:
1754     Probably many.
1755   */
1756
1757 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1758   game.max_num_changes_per_frame = 1;
1759 #else
1760   game.max_num_changes_per_frame =
1761     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1762 #endif
1763
1764   /* ---------------------------------------------------------------------- */
1765
1766   /* default scan direction: scan playfield from top/left to bottom/right */
1767   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1768
1769   /* dynamically adjust element properties according to game engine version */
1770   InitElementPropertiesEngine(game.engine_version);
1771
1772 #if 0
1773   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1774   printf("          tape version == %06d [%s] [file: %06d]\n",
1775          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1776          tape.file_version);
1777   printf("       => game.engine_version == %06d\n", game.engine_version);
1778 #endif
1779
1780   /* ---------- initialize player's initial move delay --------------------- */
1781
1782   /* dynamically adjust player properties according to level information */
1783   for (i = 0; i < MAX_PLAYERS; i++)
1784     game.initial_move_delay_value[i] =
1785       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1786
1787   /* dynamically adjust player properties according to game engine version */
1788   for (i = 0; i < MAX_PLAYERS; i++)
1789     game.initial_move_delay[i] =
1790       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1791        game.initial_move_delay_value[i] : 0);
1792
1793   /* ---------- initialize player's initial push delay --------------------- */
1794
1795   /* dynamically adjust player properties according to game engine version */
1796   game.initial_push_delay_value =
1797     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1798
1799   /* ---------- initialize changing elements ------------------------------- */
1800
1801   /* initialize changing elements information */
1802   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1803   {
1804     struct ElementInfo *ei = &element_info[i];
1805
1806     /* this pointer might have been changed in the level editor */
1807     ei->change = &ei->change_page[0];
1808
1809     if (!IS_CUSTOM_ELEMENT(i))
1810     {
1811       ei->change->target_element = EL_EMPTY_SPACE;
1812       ei->change->delay_fixed = 0;
1813       ei->change->delay_random = 0;
1814       ei->change->delay_frames = 1;
1815     }
1816
1817     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1818     {
1819       ei->has_change_event[j] = FALSE;
1820
1821       ei->event_page_nr[j] = 0;
1822       ei->event_page[j] = &ei->change_page[0];
1823     }
1824   }
1825
1826   /* add changing elements from pre-defined list */
1827   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1828   {
1829     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1830     struct ElementInfo *ei = &element_info[ch_delay->element];
1831
1832     ei->change->target_element       = ch_delay->target_element;
1833     ei->change->delay_fixed          = ch_delay->change_delay;
1834
1835     ei->change->pre_change_function  = ch_delay->pre_change_function;
1836     ei->change->change_function      = ch_delay->change_function;
1837     ei->change->post_change_function = ch_delay->post_change_function;
1838
1839     ei->change->can_change = TRUE;
1840     ei->change->can_change_or_has_action = TRUE;
1841
1842     ei->has_change_event[CE_DELAY] = TRUE;
1843
1844     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1845     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1846   }
1847
1848   /* ---------- initialize internal run-time variables ------------- */
1849
1850   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1851   {
1852     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1853
1854     for (j = 0; j < ei->num_change_pages; j++)
1855     {
1856       ei->change_page[j].can_change_or_has_action =
1857         (ei->change_page[j].can_change |
1858          ei->change_page[j].has_action);
1859     }
1860   }
1861
1862   /* add change events from custom element configuration */
1863   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1864   {
1865     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1866
1867     for (j = 0; j < ei->num_change_pages; j++)
1868     {
1869       if (!ei->change_page[j].can_change_or_has_action)
1870         continue;
1871
1872       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1873       {
1874         /* only add event page for the first page found with this event */
1875         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1876         {
1877           ei->has_change_event[k] = TRUE;
1878
1879           ei->event_page_nr[k] = j;
1880           ei->event_page[k] = &ei->change_page[j];
1881         }
1882       }
1883     }
1884   }
1885
1886   /* ---------- initialize run-time trigger player and element ------------- */
1887
1888   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1889   {
1890     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1891
1892     for (j = 0; j < ei->num_change_pages; j++)
1893     {
1894       ei->change_page[j].actual_trigger_element = EL_EMPTY;
1895       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1896       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1897       ei->change_page[j].actual_trigger_ce_value = 0;
1898       ei->change_page[j].actual_trigger_ce_score = 0;
1899     }
1900   }
1901
1902   /* ---------- initialize trigger events ---------------------------------- */
1903
1904   /* initialize trigger events information */
1905   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1906     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1907       trigger_events[i][j] = FALSE;
1908
1909   /* add trigger events from element change event properties */
1910   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1911   {
1912     struct ElementInfo *ei = &element_info[i];
1913
1914     for (j = 0; j < ei->num_change_pages; j++)
1915     {
1916       if (!ei->change_page[j].can_change_or_has_action)
1917         continue;
1918
1919       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1920       {
1921         int trigger_element = ei->change_page[j].trigger_element;
1922
1923         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1924         {
1925           if (ei->change_page[j].has_event[k])
1926           {
1927             if (IS_GROUP_ELEMENT(trigger_element))
1928             {
1929               struct ElementGroupInfo *group =
1930                 element_info[trigger_element].group;
1931
1932               for (l = 0; l < group->num_elements_resolved; l++)
1933                 trigger_events[group->element_resolved[l]][k] = TRUE;
1934             }
1935             else if (trigger_element == EL_ANY_ELEMENT)
1936               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1937                 trigger_events[l][k] = TRUE;
1938             else
1939               trigger_events[trigger_element][k] = TRUE;
1940           }
1941         }
1942       }
1943     }
1944   }
1945
1946   /* ---------- initialize push delay -------------------------------------- */
1947
1948   /* initialize push delay values to default */
1949   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1950   {
1951     if (!IS_CUSTOM_ELEMENT(i))
1952     {
1953       /* set default push delay values (corrected since version 3.0.7-1) */
1954       if (game.engine_version < VERSION_IDENT(3,0,7,1))
1955       {
1956         element_info[i].push_delay_fixed = 2;
1957         element_info[i].push_delay_random = 8;
1958       }
1959       else
1960       {
1961         element_info[i].push_delay_fixed = 8;
1962         element_info[i].push_delay_random = 8;
1963       }
1964     }
1965   }
1966
1967   /* set push delay value for certain elements from pre-defined list */
1968   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1969   {
1970     int e = push_delay_list[i].element;
1971
1972     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1973     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1974   }
1975
1976   /* set push delay value for Supaplex elements for newer engine versions */
1977   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1978   {
1979     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1980     {
1981       if (IS_SP_ELEMENT(i))
1982       {
1983         /* set SP push delay to just enough to push under a falling zonk */
1984         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1985
1986         element_info[i].push_delay_fixed  = delay;
1987         element_info[i].push_delay_random = 0;
1988       }
1989     }
1990   }
1991
1992   /* ---------- initialize move stepsize ----------------------------------- */
1993
1994   /* initialize move stepsize values to default */
1995   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1996     if (!IS_CUSTOM_ELEMENT(i))
1997       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1998
1999   /* set move stepsize value for certain elements from pre-defined list */
2000   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2001   {
2002     int e = move_stepsize_list[i].element;
2003
2004     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2005   }
2006
2007   /* ---------- initialize collect score ----------------------------------- */
2008
2009   /* initialize collect score values for custom elements from initial value */
2010   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2011     if (IS_CUSTOM_ELEMENT(i))
2012       element_info[i].collect_score = element_info[i].collect_score_initial;
2013
2014   /* ---------- initialize collect count ----------------------------------- */
2015
2016   /* initialize collect count values for non-custom elements */
2017   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2018     if (!IS_CUSTOM_ELEMENT(i))
2019       element_info[i].collect_count_initial = 0;
2020
2021   /* add collect count values for all elements from pre-defined list */
2022   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2023     element_info[collect_count_list[i].element].collect_count_initial =
2024       collect_count_list[i].count;
2025
2026   /* ---------- initialize access direction -------------------------------- */
2027
2028   /* initialize access direction values to default (access from every side) */
2029   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2030     if (!IS_CUSTOM_ELEMENT(i))
2031       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2032
2033   /* set access direction value for certain elements from pre-defined list */
2034   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2035     element_info[access_direction_list[i].element].access_direction =
2036       access_direction_list[i].direction;
2037
2038   /* ---------- initialize explosion content ------------------------------- */
2039   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2040   {
2041     if (IS_CUSTOM_ELEMENT(i))
2042       continue;
2043
2044     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2045     {
2046       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2047
2048       element_info[i].content.e[x][y] =
2049         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2050          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2051          i == EL_PLAYER_3 ? EL_EMERALD :
2052          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2053          i == EL_MOLE ? EL_EMERALD_RED :
2054          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2055          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2056          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2057          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2058          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2059          i == EL_WALL_EMERALD ? EL_EMERALD :
2060          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2061          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2062          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2063          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2064          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2065          i == EL_WALL_PEARL ? EL_PEARL :
2066          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2067          EL_EMPTY);
2068     }
2069   }
2070
2071   /* ---------- initialize recursion detection ------------------------------ */
2072   recursion_loop_depth = 0;
2073   recursion_loop_detected = FALSE;
2074   recursion_loop_element = EL_UNDEFINED;
2075 }
2076
2077 int get_num_special_action(int element, int action_first, int action_last)
2078 {
2079   int num_special_action = 0;
2080   int i, j;
2081
2082   for (i = action_first; i <= action_last; i++)
2083   {
2084     boolean found = FALSE;
2085
2086     for (j = 0; j < NUM_DIRECTIONS; j++)
2087       if (el_act_dir2img(element, i, j) !=
2088           el_act_dir2img(element, ACTION_DEFAULT, j))
2089         found = TRUE;
2090
2091     if (found)
2092       num_special_action++;
2093     else
2094       break;
2095   }
2096
2097   return num_special_action;
2098 }
2099
2100
2101 /*
2102   =============================================================================
2103   InitGame()
2104   -----------------------------------------------------------------------------
2105   initialize and start new game
2106   =============================================================================
2107 */
2108
2109 void InitGame()
2110 {
2111   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2112   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2113   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2114   boolean do_fading = (game_status == GAME_MODE_MAIN);
2115   int i, j, x, y;
2116
2117   game_status = GAME_MODE_PLAYING;
2118
2119   InitGameEngine();
2120
2121   /* don't play tapes over network */
2122   network_playing = (options.network && !tape.playing);
2123
2124   for (i = 0; i < MAX_PLAYERS; i++)
2125   {
2126     struct PlayerInfo *player = &stored_player[i];
2127
2128     player->index_nr = i;
2129     player->index_bit = (1 << i);
2130     player->element_nr = EL_PLAYER_1 + i;
2131
2132     player->present = FALSE;
2133     player->active = FALSE;
2134     player->killed = FALSE;
2135
2136     player->action = 0;
2137     player->effective_action = 0;
2138     player->programmed_action = 0;
2139
2140     player->score = 0;
2141     player->score_final = 0;
2142
2143     player->gems_still_needed = level.gems_needed;
2144     player->sokobanfields_still_needed = 0;
2145     player->lights_still_needed = 0;
2146     player->friends_still_needed = 0;
2147
2148     for (j = 0; j < MAX_NUM_KEYS; j++)
2149       player->key[j] = FALSE;
2150
2151     player->num_white_keys = 0;
2152
2153     player->dynabomb_count = 0;
2154     player->dynabomb_size = 1;
2155     player->dynabombs_left = 0;
2156     player->dynabomb_xl = FALSE;
2157
2158     player->MovDir = MV_NONE;
2159     player->MovPos = 0;
2160     player->GfxPos = 0;
2161     player->GfxDir = MV_NONE;
2162     player->GfxAction = ACTION_DEFAULT;
2163     player->Frame = 0;
2164     player->StepFrame = 0;
2165
2166     player->use_murphy = FALSE;
2167     player->artwork_element =
2168       (level.use_artwork_element[i] ? level.artwork_element[i] :
2169        player->element_nr);
2170
2171     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2172     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2173
2174     player->gravity = level.initial_player_gravity[i];
2175
2176     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2177
2178     player->actual_frame_counter = 0;
2179
2180     player->step_counter = 0;
2181
2182     player->last_move_dir = MV_NONE;
2183
2184     player->is_active = FALSE;
2185
2186     player->is_waiting = FALSE;
2187     player->is_moving = FALSE;
2188     player->is_auto_moving = FALSE;
2189     player->is_digging = FALSE;
2190     player->is_snapping = FALSE;
2191     player->is_collecting = FALSE;
2192     player->is_pushing = FALSE;
2193     player->is_switching = FALSE;
2194     player->is_dropping = FALSE;
2195     player->is_dropping_pressed = FALSE;
2196
2197     player->is_bored = FALSE;
2198     player->is_sleeping = FALSE;
2199
2200     player->frame_counter_bored = -1;
2201     player->frame_counter_sleeping = -1;
2202
2203     player->anim_delay_counter = 0;
2204     player->post_delay_counter = 0;
2205
2206     player->dir_waiting = MV_NONE;
2207     player->action_waiting = ACTION_DEFAULT;
2208     player->last_action_waiting = ACTION_DEFAULT;
2209     player->special_action_bored = ACTION_DEFAULT;
2210     player->special_action_sleeping = ACTION_DEFAULT;
2211
2212     player->switch_x = -1;
2213     player->switch_y = -1;
2214
2215     player->drop_x = -1;
2216     player->drop_y = -1;
2217
2218     player->show_envelope = 0;
2219
2220     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2221
2222     player->push_delay       = -1;      /* initialized when pushing starts */
2223     player->push_delay_value = game.initial_push_delay_value;
2224
2225     player->drop_delay = 0;
2226     player->drop_pressed_delay = 0;
2227
2228     player->last_jx = -1;
2229     player->last_jy = -1;
2230     player->jx = -1;
2231     player->jy = -1;
2232
2233     player->shield_normal_time_left = 0;
2234     player->shield_deadly_time_left = 0;
2235
2236     player->inventory_infinite_element = EL_UNDEFINED;
2237     player->inventory_size = 0;
2238
2239     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2240     SnapField(player, 0, 0);
2241
2242     player->LevelSolved = FALSE;
2243     player->GameOver = FALSE;
2244
2245     player->LevelSolved_GameEnd = FALSE;
2246     player->LevelSolved_SaveTape = FALSE;
2247     player->LevelSolved_SaveScore = FALSE;
2248   }
2249
2250   network_player_action_received = FALSE;
2251
2252 #if defined(NETWORK_AVALIABLE)
2253   /* initial null action */
2254   if (network_playing)
2255     SendToServer_MovePlayer(MV_NONE);
2256 #endif
2257
2258   ZX = ZY = -1;
2259   ExitX = ExitY = -1;
2260
2261   FrameCounter = 0;
2262   TimeFrames = 0;
2263   TimePlayed = 0;
2264   TimeLeft = level.time;
2265   TapeTime = 0;
2266
2267   ScreenMovDir = MV_NONE;
2268   ScreenMovPos = 0;
2269   ScreenGfxPos = 0;
2270
2271   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2272
2273   AllPlayersGone = FALSE;
2274
2275   game.yamyam_content_nr = 0;
2276   game.magic_wall_active = FALSE;
2277   game.magic_wall_time_left = 0;
2278   game.light_time_left = 0;
2279   game.timegate_time_left = 0;
2280   game.switchgate_pos = 0;
2281   game.wind_direction = level.wind_direction_initial;
2282
2283 #if !USE_PLAYER_GRAVITY
2284   game.gravity = FALSE;
2285   game.explosions_delayed = TRUE;
2286 #endif
2287
2288   game.lenses_time_left = 0;
2289   game.magnify_time_left = 0;
2290
2291   game.ball_state = level.ball_state_initial;
2292   game.ball_content_nr = 0;
2293
2294   game.envelope_active = FALSE;
2295
2296   /* set focus to local player for network games, else to all players */
2297   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2298   game.centered_player_nr_next = game.centered_player_nr;
2299   game.set_centered_player = FALSE;
2300
2301   if (network_playing && tape.recording)
2302   {
2303     /* store client dependent player focus when recording network games */
2304     tape.centered_player_nr_next = game.centered_player_nr_next;
2305     tape.set_centered_player = TRUE;
2306   }
2307
2308   for (i = 0; i < NUM_BELTS; i++)
2309   {
2310     game.belt_dir[i] = MV_NONE;
2311     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2312   }
2313
2314   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2315     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2316
2317   SCAN_PLAYFIELD(x, y)
2318   {
2319     Feld[x][y] = level.field[x][y];
2320     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2321     ChangeDelay[x][y] = 0;
2322     ChangePage[x][y] = -1;
2323 #if USE_NEW_CUSTOM_VALUE
2324     CustomValue[x][y] = 0;              /* initialized in InitField() */
2325 #endif
2326     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2327     AmoebaNr[x][y] = 0;
2328     WasJustMoving[x][y] = 0;
2329     WasJustFalling[x][y] = 0;
2330     CheckCollision[x][y] = 0;
2331     CheckImpact[x][y] = 0;
2332     Stop[x][y] = FALSE;
2333     Pushed[x][y] = FALSE;
2334
2335     ChangeCount[x][y] = 0;
2336     ChangeEvent[x][y] = -1;
2337
2338     ExplodePhase[x][y] = 0;
2339     ExplodeDelay[x][y] = 0;
2340     ExplodeField[x][y] = EX_TYPE_NONE;
2341
2342     RunnerVisit[x][y] = 0;
2343     PlayerVisit[x][y] = 0;
2344
2345     GfxFrame[x][y] = 0;
2346     GfxRandom[x][y] = INIT_GFX_RANDOM();
2347     GfxElement[x][y] = EL_UNDEFINED;
2348     GfxAction[x][y] = ACTION_DEFAULT;
2349     GfxDir[x][y] = MV_NONE;
2350   }
2351
2352   SCAN_PLAYFIELD(x, y)
2353   {
2354     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2355       emulate_bd = FALSE;
2356     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2357       emulate_sb = FALSE;
2358     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2359       emulate_sp = FALSE;
2360
2361     InitField(x, y, TRUE);
2362   }
2363
2364   InitBeltMovement();
2365
2366   for (i = 0; i < MAX_PLAYERS; i++)
2367   {
2368     struct PlayerInfo *player = &stored_player[i];
2369
2370     /* set number of special actions for bored and sleeping animation */
2371     player->num_special_action_bored =
2372       get_num_special_action(player->artwork_element,
2373                              ACTION_BORING_1, ACTION_BORING_LAST);
2374     player->num_special_action_sleeping =
2375       get_num_special_action(player->artwork_element,
2376                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2377   }
2378
2379   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2380                     emulate_sb ? EMU_SOKOBAN :
2381                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2382
2383 #if USE_NEW_ALL_SLIPPERY
2384   /* initialize type of slippery elements */
2385   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2386   {
2387     if (!IS_CUSTOM_ELEMENT(i))
2388     {
2389       /* default: elements slip down either to the left or right randomly */
2390       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2391
2392       /* SP style elements prefer to slip down on the left side */
2393       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2394         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2395
2396       /* BD style elements prefer to slip down on the left side */
2397       if (game.emulation == EMU_BOULDERDASH)
2398         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2399     }
2400   }
2401 #endif
2402
2403   /* initialize explosion and ignition delay */
2404   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2405   {
2406     if (!IS_CUSTOM_ELEMENT(i))
2407     {
2408       int num_phase = 8;
2409       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2410                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2411                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2412       int last_phase = (num_phase + 1) * delay;
2413       int half_phase = (num_phase / 2) * delay;
2414
2415       element_info[i].explosion_delay = last_phase - 1;
2416       element_info[i].ignition_delay = half_phase;
2417
2418       if (i == EL_BLACK_ORB)
2419         element_info[i].ignition_delay = 1;
2420     }
2421
2422 #if 0
2423     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2424       element_info[i].explosion_delay = 1;
2425
2426     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2427       element_info[i].ignition_delay = 1;
2428 #endif
2429   }
2430
2431   /* correct non-moving belts to start moving left */
2432   for (i = 0; i < NUM_BELTS; i++)
2433     if (game.belt_dir[i] == MV_NONE)
2434       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2435
2436   /* check if any connected player was not found in playfield */
2437   for (i = 0; i < MAX_PLAYERS; i++)
2438   {
2439     struct PlayerInfo *player = &stored_player[i];
2440
2441     if (player->connected && !player->present)
2442     {
2443       for (j = 0; j < MAX_PLAYERS; j++)
2444       {
2445         struct PlayerInfo *some_player = &stored_player[j];
2446         int jx = some_player->jx, jy = some_player->jy;
2447
2448         /* assign first free player found that is present in the playfield */
2449         if (some_player->present && !some_player->connected)
2450         {
2451           player->present = TRUE;
2452           player->active = TRUE;
2453
2454           some_player->present = FALSE;
2455           some_player->active = FALSE;
2456
2457           player->artwork_element = some_player->artwork_element;
2458
2459           player->block_last_field       = some_player->block_last_field;
2460           player->block_delay_adjustment = some_player->block_delay_adjustment;
2461
2462           StorePlayer[jx][jy] = player->element_nr;
2463           player->jx = player->last_jx = jx;
2464           player->jy = player->last_jy = jy;
2465
2466           break;
2467         }
2468       }
2469     }
2470   }
2471
2472   if (tape.playing)
2473   {
2474     /* when playing a tape, eliminate all players who do not participate */
2475
2476     for (i = 0; i < MAX_PLAYERS; i++)
2477     {
2478       if (stored_player[i].active && !tape.player_participates[i])
2479       {
2480         struct PlayerInfo *player = &stored_player[i];
2481         int jx = player->jx, jy = player->jy;
2482
2483         player->active = FALSE;
2484         StorePlayer[jx][jy] = 0;
2485         Feld[jx][jy] = EL_EMPTY;
2486       }
2487     }
2488   }
2489   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2490   {
2491     /* when in single player mode, eliminate all but the first active player */
2492
2493     for (i = 0; i < MAX_PLAYERS; i++)
2494     {
2495       if (stored_player[i].active)
2496       {
2497         for (j = i + 1; j < MAX_PLAYERS; j++)
2498         {
2499           if (stored_player[j].active)
2500           {
2501             struct PlayerInfo *player = &stored_player[j];
2502             int jx = player->jx, jy = player->jy;
2503
2504             player->active = FALSE;
2505             player->present = FALSE;
2506
2507             StorePlayer[jx][jy] = 0;
2508             Feld[jx][jy] = EL_EMPTY;
2509           }
2510         }
2511       }
2512     }
2513   }
2514
2515   /* when recording the game, store which players take part in the game */
2516   if (tape.recording)
2517   {
2518     for (i = 0; i < MAX_PLAYERS; i++)
2519       if (stored_player[i].active)
2520         tape.player_participates[i] = TRUE;
2521   }
2522
2523   if (options.debug)
2524   {
2525     for (i = 0; i < MAX_PLAYERS; i++)
2526     {
2527       struct PlayerInfo *player = &stored_player[i];
2528
2529       printf("Player %d: present == %d, connected == %d, active == %d.\n",
2530              i+1,
2531              player->present,
2532              player->connected,
2533              player->active);
2534       if (local_player == player)
2535         printf("Player  %d is local player.\n", i+1);
2536     }
2537   }
2538
2539   if (BorderElement == EL_EMPTY)
2540   {
2541     SBX_Left = 0;
2542     SBX_Right = lev_fieldx - SCR_FIELDX;
2543     SBY_Upper = 0;
2544     SBY_Lower = lev_fieldy - SCR_FIELDY;
2545   }
2546   else
2547   {
2548     SBX_Left = -1;
2549     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2550     SBY_Upper = -1;
2551     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2552   }
2553
2554   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2555     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2556
2557   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2558     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2559
2560   /* if local player not found, look for custom element that might create
2561      the player (make some assumptions about the right custom element) */
2562   if (!local_player->present)
2563   {
2564     int start_x = 0, start_y = 0;
2565     int found_rating = 0;
2566     int found_element = EL_UNDEFINED;
2567     int player_nr = local_player->index_nr;
2568
2569     SCAN_PLAYFIELD(x, y)
2570     {
2571       int element = Feld[x][y];
2572       int content;
2573       int xx, yy;
2574       boolean is_player;
2575
2576       if (level.use_start_element[player_nr] &&
2577           level.start_element[player_nr] == element &&
2578           found_rating < 4)
2579       {
2580         start_x = x;
2581         start_y = y;
2582
2583         found_rating = 4;
2584         found_element = element;
2585       }
2586
2587       if (!IS_CUSTOM_ELEMENT(element))
2588         continue;
2589
2590       if (CAN_CHANGE(element))
2591       {
2592         for (i = 0; i < element_info[element].num_change_pages; i++)
2593         {
2594           /* check for player created from custom element as single target */
2595           content = element_info[element].change_page[i].target_element;
2596           is_player = ELEM_IS_PLAYER(content);
2597
2598           if (is_player && (found_rating < 3 || element < found_element))
2599           {
2600             start_x = x;
2601             start_y = y;
2602
2603             found_rating = 3;
2604             found_element = element;
2605           }
2606         }
2607       }
2608
2609       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2610       {
2611         /* check for player created from custom element as explosion content */
2612         content = element_info[element].content.e[xx][yy];
2613         is_player = ELEM_IS_PLAYER(content);
2614
2615         if (is_player && (found_rating < 2 || element < found_element))
2616         {
2617           start_x = x + xx - 1;
2618           start_y = y + yy - 1;
2619
2620           found_rating = 2;
2621           found_element = element;
2622         }
2623
2624         if (!CAN_CHANGE(element))
2625           continue;
2626
2627         for (i = 0; i < element_info[element].num_change_pages; i++)
2628         {
2629           /* check for player created from custom element as extended target */
2630           content =
2631             element_info[element].change_page[i].target_content.e[xx][yy];
2632
2633           is_player = ELEM_IS_PLAYER(content);
2634
2635           if (is_player && (found_rating < 1 || element < found_element))
2636           {
2637             start_x = x + xx - 1;
2638             start_y = y + yy - 1;
2639
2640             found_rating = 1;
2641             found_element = element;
2642           }
2643         }
2644       }
2645     }
2646
2647     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
2648                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2649                 start_x - MIDPOSX);
2650
2651     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2652                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2653                 start_y - MIDPOSY);
2654   }
2655   else
2656   {
2657     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2658                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2659                 local_player->jx - MIDPOSX);
2660
2661     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2662                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2663                 local_player->jy - MIDPOSY);
2664   }
2665
2666   StopAnimation();
2667
2668   if (!game.restart_level)
2669     CloseDoor(DOOR_CLOSE_1);
2670
2671   if (do_fading)
2672     FadeOut(REDRAW_FIELD);
2673
2674   /* !!! FIX THIS (START) !!! */
2675   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2676   {
2677     InitGameEngine_EM();
2678
2679     /* blit playfield from scroll buffer to normal back buffer for fading in */
2680     BlitScreenToBitmap_EM(backbuffer);
2681   }
2682   else
2683   {
2684     DrawLevel();
2685     DrawAllPlayers();
2686
2687     /* after drawing the level, correct some elements */
2688     if (game.timegate_time_left == 0)
2689       CloseAllOpenTimegates();
2690
2691     /* blit playfield from scroll buffer to normal back buffer for fading in */
2692     if (setup.soft_scrolling)
2693       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2694
2695     redraw_mask |= REDRAW_FROM_BACKBUFFER;
2696   }
2697   /* !!! FIX THIS (END) !!! */
2698
2699   if (do_fading)
2700     FadeIn(REDRAW_FIELD);
2701
2702   BackToFront();
2703
2704   if (!game.restart_level)
2705   {
2706     /* copy default game door content to main double buffer */
2707     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2708                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2709   }
2710
2711   SetPanelBackground();
2712   SetDrawBackgroundMask(REDRAW_DOOR_1);
2713
2714   DrawGameDoorValues();
2715
2716   if (!game.restart_level)
2717   {
2718     UnmapGameButtons();
2719     UnmapTapeButtons();
2720     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2721     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2722     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2723     MapGameButtons();
2724     MapTapeButtons();
2725
2726     /* copy actual game door content to door double buffer for OpenDoor() */
2727     BlitBitmap(drawto, bitmap_db_door,
2728                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2729
2730     OpenDoor(DOOR_OPEN_ALL);
2731
2732     PlaySound(SND_GAME_STARTING);
2733
2734     if (setup.sound_music)
2735       PlayLevelMusic();
2736
2737     KeyboardAutoRepeatOffUnlessAutoplay();
2738
2739     if (options.debug)
2740     {
2741       for (i = 0; i < MAX_PLAYERS; i++)
2742         printf("Player %d %sactive.\n",
2743                i + 1, (stored_player[i].active ? "" : "not "));
2744     }
2745   }
2746
2747 #if 1
2748   UnmapAllGadgets();
2749
2750   MapGameButtons();
2751   MapTapeButtons();
2752 #endif
2753
2754   game.restart_level = FALSE;
2755 }
2756
2757 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2758 {
2759   /* this is used for non-R'n'D game engines to update certain engine values */
2760
2761   /* needed to determine if sounds are played within the visible screen area */
2762   scroll_x = actual_scroll_x;
2763   scroll_y = actual_scroll_y;
2764 }
2765
2766 void InitMovDir(int x, int y)
2767 {
2768   int i, element = Feld[x][y];
2769   static int xy[4][2] =
2770   {
2771     {  0, +1 },
2772     { +1,  0 },
2773     {  0, -1 },
2774     { -1,  0 }
2775   };
2776   static int direction[3][4] =
2777   {
2778     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
2779     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
2780     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
2781   };
2782
2783   switch (element)
2784   {
2785     case EL_BUG_RIGHT:
2786     case EL_BUG_UP:
2787     case EL_BUG_LEFT:
2788     case EL_BUG_DOWN:
2789       Feld[x][y] = EL_BUG;
2790       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2791       break;
2792
2793     case EL_SPACESHIP_RIGHT:
2794     case EL_SPACESHIP_UP:
2795     case EL_SPACESHIP_LEFT:
2796     case EL_SPACESHIP_DOWN:
2797       Feld[x][y] = EL_SPACESHIP;
2798       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2799       break;
2800
2801     case EL_BD_BUTTERFLY_RIGHT:
2802     case EL_BD_BUTTERFLY_UP:
2803     case EL_BD_BUTTERFLY_LEFT:
2804     case EL_BD_BUTTERFLY_DOWN:
2805       Feld[x][y] = EL_BD_BUTTERFLY;
2806       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2807       break;
2808
2809     case EL_BD_FIREFLY_RIGHT:
2810     case EL_BD_FIREFLY_UP:
2811     case EL_BD_FIREFLY_LEFT:
2812     case EL_BD_FIREFLY_DOWN:
2813       Feld[x][y] = EL_BD_FIREFLY;
2814       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2815       break;
2816
2817     case EL_PACMAN_RIGHT:
2818     case EL_PACMAN_UP:
2819     case EL_PACMAN_LEFT:
2820     case EL_PACMAN_DOWN:
2821       Feld[x][y] = EL_PACMAN;
2822       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2823       break;
2824
2825     case EL_YAMYAM_LEFT:
2826     case EL_YAMYAM_RIGHT:
2827     case EL_YAMYAM_UP:
2828     case EL_YAMYAM_DOWN:
2829       Feld[x][y] = EL_YAMYAM;
2830       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2831       break;
2832
2833     case EL_SP_SNIKSNAK:
2834       MovDir[x][y] = MV_UP;
2835       break;
2836
2837     case EL_SP_ELECTRON:
2838       MovDir[x][y] = MV_LEFT;
2839       break;
2840
2841     case EL_MOLE_LEFT:
2842     case EL_MOLE_RIGHT:
2843     case EL_MOLE_UP:
2844     case EL_MOLE_DOWN:
2845       Feld[x][y] = EL_MOLE;
2846       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2847       break;
2848
2849     default:
2850       if (IS_CUSTOM_ELEMENT(element))
2851       {
2852         struct ElementInfo *ei = &element_info[element];
2853         int move_direction_initial = ei->move_direction_initial;
2854         int move_pattern = ei->move_pattern;
2855
2856         if (move_direction_initial == MV_START_PREVIOUS)
2857         {
2858           if (MovDir[x][y] != MV_NONE)
2859             return;
2860
2861           move_direction_initial = MV_START_AUTOMATIC;
2862         }
2863
2864         if (move_direction_initial == MV_START_RANDOM)
2865           MovDir[x][y] = 1 << RND(4);
2866         else if (move_direction_initial & MV_ANY_DIRECTION)
2867           MovDir[x][y] = move_direction_initial;
2868         else if (move_pattern == MV_ALL_DIRECTIONS ||
2869                  move_pattern == MV_TURNING_LEFT ||
2870                  move_pattern == MV_TURNING_RIGHT ||
2871                  move_pattern == MV_TURNING_LEFT_RIGHT ||
2872                  move_pattern == MV_TURNING_RIGHT_LEFT ||
2873                  move_pattern == MV_TURNING_RANDOM)
2874           MovDir[x][y] = 1 << RND(4);
2875         else if (move_pattern == MV_HORIZONTAL)
2876           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2877         else if (move_pattern == MV_VERTICAL)
2878           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2879         else if (move_pattern & MV_ANY_DIRECTION)
2880           MovDir[x][y] = element_info[element].move_pattern;
2881         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2882                  move_pattern == MV_ALONG_RIGHT_SIDE)
2883         {
2884           /* use random direction as default start direction */
2885           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2886             MovDir[x][y] = 1 << RND(4);
2887
2888           for (i = 0; i < NUM_DIRECTIONS; i++)
2889           {
2890             int x1 = x + xy[i][0];
2891             int y1 = y + xy[i][1];
2892
2893             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2894             {
2895               if (move_pattern == MV_ALONG_RIGHT_SIDE)
2896                 MovDir[x][y] = direction[0][i];
2897               else
2898                 MovDir[x][y] = direction[1][i];
2899
2900               break;
2901             }
2902           }
2903         }                
2904       }
2905       else
2906       {
2907         MovDir[x][y] = 1 << RND(4);
2908
2909         if (element != EL_BUG &&
2910             element != EL_SPACESHIP &&
2911             element != EL_BD_BUTTERFLY &&
2912             element != EL_BD_FIREFLY)
2913           break;
2914
2915         for (i = 0; i < NUM_DIRECTIONS; i++)
2916         {
2917           int x1 = x + xy[i][0];
2918           int y1 = y + xy[i][1];
2919
2920           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2921           {
2922             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2923             {
2924               MovDir[x][y] = direction[0][i];
2925               break;
2926             }
2927             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2928                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2929             {
2930               MovDir[x][y] = direction[1][i];
2931               break;
2932             }
2933           }
2934         }
2935       }
2936       break;
2937   }
2938
2939   GfxDir[x][y] = MovDir[x][y];
2940 }
2941
2942 void InitAmoebaNr(int x, int y)
2943 {
2944   int i;
2945   int group_nr = AmoebeNachbarNr(x, y);
2946
2947   if (group_nr == 0)
2948   {
2949     for (i = 1; i < MAX_NUM_AMOEBA; i++)
2950     {
2951       if (AmoebaCnt[i] == 0)
2952       {
2953         group_nr = i;
2954         break;
2955       }
2956     }
2957   }
2958
2959   AmoebaNr[x][y] = group_nr;
2960   AmoebaCnt[group_nr]++;
2961   AmoebaCnt2[group_nr]++;
2962 }
2963
2964 static void PlayerWins(struct PlayerInfo *player)
2965 {
2966   player->LevelSolved = TRUE;
2967   player->GameOver = TRUE;
2968
2969   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2970                          level.native_em_level->lev->score : player->score);
2971 }
2972
2973 void GameWon()
2974 {
2975   static int time, time_final;
2976   static int score, score_final;
2977   static int game_over_delay = 0;
2978   int game_over_delay_value = 50;
2979
2980   if (!local_player->LevelSolved_GameEnd)
2981   {
2982     int i;
2983
2984     /* do not start end game actions before the player stops moving (to exit) */
2985     if (local_player->MovPos)
2986       return;
2987
2988     local_player->LevelSolved_GameEnd = TRUE;
2989     local_player->LevelSolved_SaveTape = tape.recording;
2990     local_player->LevelSolved_SaveScore = !tape.playing;
2991
2992     if (tape.auto_play)         /* tape might already be stopped here */
2993       tape.auto_play_level_solved = TRUE;
2994
2995 #if 1
2996     TapeStop();
2997 #endif
2998
2999     game_over_delay = game_over_delay_value;
3000
3001     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3002     score = score_final = local_player->score_final;
3003
3004     if (TimeLeft > 0)
3005     {
3006       time_final = 0;
3007       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3008     }
3009     else if (level.time == 0 && TimePlayed < 999)
3010     {
3011       time_final = 999;
3012       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3013     }
3014
3015     local_player->score_final = score_final;
3016
3017     if (level_editor_test_game)
3018     {
3019       time = time_final;
3020       score = score_final;
3021
3022       DrawGameValue_Time(time);
3023       DrawGameValue_Score(score);
3024     }
3025
3026     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3027     {
3028       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3029       {
3030         /* close exit door after last player */
3031         if ((AllPlayersGone &&
3032              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3033               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3034               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3035             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3036             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3037         {
3038           int element = Feld[ExitX][ExitY];
3039
3040 #if 0
3041           if (element == EL_EM_EXIT_OPEN ||
3042               element == EL_EM_STEEL_EXIT_OPEN)
3043           {
3044             Bang(ExitX, ExitY);
3045           }
3046           else
3047 #endif
3048           {
3049             Feld[ExitX][ExitY] =
3050               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3051                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3052                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3053                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3054                EL_EM_STEEL_EXIT_CLOSING);
3055
3056             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3057           }
3058         }
3059
3060         /* player disappears */
3061         DrawLevelField(ExitX, ExitY);
3062       }
3063
3064       for (i = 0; i < MAX_PLAYERS; i++)
3065       {
3066         struct PlayerInfo *player = &stored_player[i];
3067
3068         if (player->present)
3069         {
3070           RemovePlayer(player);
3071
3072           /* player disappears */
3073           DrawLevelField(player->jx, player->jy);
3074         }
3075       }
3076     }
3077
3078     PlaySound(SND_GAME_WINNING);
3079   }
3080
3081   if (game_over_delay > 0)
3082   {
3083     game_over_delay--;
3084
3085     return;
3086   }
3087
3088   if (time != time_final)
3089   {
3090     int time_to_go = ABS(time_final - time);
3091     int time_count_dir = (time < time_final ? +1 : -1);
3092     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3093
3094     time  += time_count_steps * time_count_dir;
3095     score += time_count_steps * level.score[SC_TIME_BONUS];
3096
3097     DrawGameValue_Time(time);
3098     DrawGameValue_Score(score);
3099
3100     if (time == time_final)
3101       StopSound(SND_GAME_LEVELTIME_BONUS);
3102     else if (setup.sound_loops)
3103       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3104     else
3105       PlaySound(SND_GAME_LEVELTIME_BONUS);
3106   }
3107 }
3108
3109 void GameEnd()
3110 {
3111   int hi_pos;
3112   boolean raise_level = FALSE;
3113
3114   CloseDoor(DOOR_CLOSE_1);
3115
3116   if (local_player->LevelSolved_SaveTape)
3117   {
3118 #if 0
3119     TapeStop();
3120 #endif
3121
3122 #if 1
3123     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3124 #else
3125     SaveTape(tape.level_nr);            /* ask to save tape */
3126 #endif
3127   }
3128
3129   if (level_editor_test_game)
3130   {
3131     game_status = GAME_MODE_MAIN;
3132
3133     DrawMainMenu();
3134
3135     return;
3136   }
3137
3138   if (!local_player->LevelSolved_SaveScore)
3139   {
3140     FadeOut(REDRAW_FIELD);
3141
3142     game_status = GAME_MODE_MAIN;
3143
3144     DrawAndFadeInMainMenu(REDRAW_FIELD);
3145
3146     return;
3147   }
3148
3149   if (level_nr == leveldir_current->handicap_level)
3150   {
3151     leveldir_current->handicap_level++;
3152     SaveLevelSetup_SeriesInfo();
3153   }
3154
3155   if (level_nr < leveldir_current->last_level)
3156     raise_level = TRUE;                 /* advance to next level */
3157
3158   if ((hi_pos = NewHiScore()) >= 0) 
3159   {
3160     game_status = GAME_MODE_SCORES;
3161
3162     DrawHallOfFame(hi_pos);
3163
3164     if (raise_level)
3165     {
3166       level_nr++;
3167       TapeErase();
3168     }
3169   }
3170   else
3171   {
3172     FadeOut(REDRAW_FIELD);
3173
3174     game_status = GAME_MODE_MAIN;
3175
3176     if (raise_level)
3177     {
3178       level_nr++;
3179       TapeErase();
3180     }
3181
3182     DrawAndFadeInMainMenu(REDRAW_FIELD);
3183   }
3184 }
3185
3186 int NewHiScore()
3187 {
3188   int k, l;
3189   int position = -1;
3190
3191   LoadScore(level_nr);
3192
3193   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3194       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3195     return -1;
3196
3197   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3198   {
3199     if (local_player->score_final > highscore[k].Score)
3200     {
3201       /* player has made it to the hall of fame */
3202
3203       if (k < MAX_SCORE_ENTRIES - 1)
3204       {
3205         int m = MAX_SCORE_ENTRIES - 1;
3206
3207 #ifdef ONE_PER_NAME
3208         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3209           if (strEqual(setup.player_name, highscore[l].Name))
3210             m = l;
3211         if (m == k)     /* player's new highscore overwrites his old one */
3212           goto put_into_list;
3213 #endif
3214
3215         for (l = m; l > k; l--)
3216         {
3217           strcpy(highscore[l].Name, highscore[l - 1].Name);
3218           highscore[l].Score = highscore[l - 1].Score;
3219         }
3220       }
3221
3222 #ifdef ONE_PER_NAME
3223       put_into_list:
3224 #endif
3225       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3226       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3227       highscore[k].Score = local_player->score_final; 
3228       position = k;
3229       break;
3230     }
3231
3232 #ifdef ONE_PER_NAME
3233     else if (!strncmp(setup.player_name, highscore[k].Name,
3234                       MAX_PLAYER_NAME_LEN))
3235       break;    /* player already there with a higher score */
3236 #endif
3237
3238   }
3239
3240   if (position >= 0) 
3241     SaveScore(level_nr);
3242
3243   return position;
3244 }
3245
3246 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3247 {
3248   int element = Feld[x][y];
3249   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3250   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3251   int horiz_move = (dx != 0);
3252   int sign = (horiz_move ? dx : dy);
3253   int step = sign * element_info[element].move_stepsize;
3254
3255   /* special values for move stepsize for spring and things on conveyor belt */
3256   if (horiz_move)
3257   {
3258     if (CAN_FALL(element) &&
3259         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3260       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3261     else if (element == EL_SPRING)
3262       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3263   }
3264
3265   return step;
3266 }
3267
3268 inline static int getElementMoveStepsize(int x, int y)
3269 {
3270   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3271 }
3272
3273 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3274 {
3275   if (player->GfxAction != action || player->GfxDir != dir)
3276   {
3277 #if 0
3278     printf("Player frame reset! (%d => %d, %d => %d)\n",
3279            player->GfxAction, action, player->GfxDir, dir);
3280 #endif
3281
3282     player->GfxAction = action;
3283     player->GfxDir = dir;
3284     player->Frame = 0;
3285     player->StepFrame = 0;
3286   }
3287 }
3288
3289 #if USE_GFX_RESET_GFX_ANIMATION
3290 static void ResetGfxFrame(int x, int y, boolean redraw)
3291 {
3292   int element = Feld[x][y];
3293   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3294   int last_gfx_frame = GfxFrame[x][y];
3295
3296   if (graphic_info[graphic].anim_global_sync)
3297     GfxFrame[x][y] = FrameCounter;
3298   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3299     GfxFrame[x][y] = CustomValue[x][y];
3300   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3301     GfxFrame[x][y] = element_info[element].collect_score;
3302   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3303     GfxFrame[x][y] = ChangeDelay[x][y];
3304
3305   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3306     DrawLevelGraphicAnimation(x, y, graphic);
3307 }
3308 #endif
3309
3310 static void ResetGfxAnimation(int x, int y)
3311 {
3312   GfxAction[x][y] = ACTION_DEFAULT;
3313   GfxDir[x][y] = MovDir[x][y];
3314   GfxFrame[x][y] = 0;
3315
3316 #if USE_GFX_RESET_GFX_ANIMATION
3317   ResetGfxFrame(x, y, FALSE);
3318 #endif
3319 }
3320
3321 static void ResetRandomAnimationValue(int x, int y)
3322 {
3323   GfxRandom[x][y] = INIT_GFX_RANDOM();
3324 }
3325
3326 void InitMovingField(int x, int y, int direction)
3327 {
3328   int element = Feld[x][y];
3329   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3330   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3331   int newx = x + dx;
3332   int newy = y + dy;
3333   boolean is_moving_before, is_moving_after;
3334 #if 0
3335   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3336 #endif
3337
3338   /* check if element was/is moving or being moved before/after mode change */
3339 #if 1
3340   is_moving_before = WasJustMoving[x][y];
3341 #else
3342   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3343 #endif
3344   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
3345
3346   /* reset animation only for moving elements which change direction of moving
3347      or which just started or stopped moving
3348      (else CEs with property "can move" / "not moving" are reset each frame) */
3349 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3350 #if 1
3351   if (is_moving_before != is_moving_after ||
3352       direction != MovDir[x][y])
3353     ResetGfxAnimation(x, y);
3354 #else
3355   if ((is_moving_before || is_moving_after) && !continues_moving)
3356     ResetGfxAnimation(x, y);
3357 #endif
3358 #else
3359   if (!continues_moving)
3360     ResetGfxAnimation(x, y);
3361 #endif
3362
3363   MovDir[x][y] = direction;
3364   GfxDir[x][y] = direction;
3365
3366 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3367   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3368                      direction == MV_DOWN && CAN_FALL(element) ?
3369                      ACTION_FALLING : ACTION_MOVING);
3370 #else
3371   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3372                      ACTION_FALLING : ACTION_MOVING);
3373 #endif
3374
3375   /* this is needed for CEs with property "can move" / "not moving" */
3376
3377   if (is_moving_after)
3378   {
3379     if (Feld[newx][newy] == EL_EMPTY)
3380       Feld[newx][newy] = EL_BLOCKED;
3381
3382     MovDir[newx][newy] = MovDir[x][y];
3383
3384 #if USE_NEW_CUSTOM_VALUE
3385     CustomValue[newx][newy] = CustomValue[x][y];
3386 #endif
3387
3388     GfxFrame[newx][newy] = GfxFrame[x][y];
3389     GfxRandom[newx][newy] = GfxRandom[x][y];
3390     GfxAction[newx][newy] = GfxAction[x][y];
3391     GfxDir[newx][newy] = GfxDir[x][y];
3392   }
3393 }
3394
3395 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3396 {
3397   int direction = MovDir[x][y];
3398   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3399   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3400
3401   *goes_to_x = newx;
3402   *goes_to_y = newy;
3403 }
3404
3405 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3406 {
3407   int oldx = x, oldy = y;
3408   int direction = MovDir[x][y];
3409
3410   if (direction == MV_LEFT)
3411     oldx++;
3412   else if (direction == MV_RIGHT)
3413     oldx--;
3414   else if (direction == MV_UP)
3415     oldy++;
3416   else if (direction == MV_DOWN)
3417     oldy--;
3418
3419   *comes_from_x = oldx;
3420   *comes_from_y = oldy;
3421 }
3422
3423 int MovingOrBlocked2Element(int x, int y)
3424 {
3425   int element = Feld[x][y];
3426
3427   if (element == EL_BLOCKED)
3428   {
3429     int oldx, oldy;
3430
3431     Blocked2Moving(x, y, &oldx, &oldy);
3432     return Feld[oldx][oldy];
3433   }
3434   else
3435     return element;
3436 }
3437
3438 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3439 {
3440   /* like MovingOrBlocked2Element(), but if element is moving
3441      and (x,y) is the field the moving element is just leaving,
3442      return EL_BLOCKED instead of the element value */
3443   int element = Feld[x][y];
3444
3445   if (IS_MOVING(x, y))
3446   {
3447     if (element == EL_BLOCKED)
3448     {
3449       int oldx, oldy;
3450
3451       Blocked2Moving(x, y, &oldx, &oldy);
3452       return Feld[oldx][oldy];
3453     }
3454     else
3455       return EL_BLOCKED;
3456   }
3457   else
3458     return element;
3459 }
3460
3461 static void RemoveField(int x, int y)
3462 {
3463   Feld[x][y] = EL_EMPTY;
3464
3465   MovPos[x][y] = 0;
3466   MovDir[x][y] = 0;
3467   MovDelay[x][y] = 0;
3468
3469 #if USE_NEW_CUSTOM_VALUE
3470   CustomValue[x][y] = 0;
3471 #endif
3472
3473   AmoebaNr[x][y] = 0;
3474   ChangeDelay[x][y] = 0;
3475   ChangePage[x][y] = -1;
3476   Pushed[x][y] = FALSE;
3477
3478 #if 0
3479   ExplodeField[x][y] = EX_TYPE_NONE;
3480 #endif
3481
3482   GfxElement[x][y] = EL_UNDEFINED;
3483   GfxAction[x][y] = ACTION_DEFAULT;
3484   GfxDir[x][y] = MV_NONE;
3485 }
3486
3487 void RemoveMovingField(int x, int y)
3488 {
3489   int oldx = x, oldy = y, newx = x, newy = y;
3490   int element = Feld[x][y];
3491   int next_element = EL_UNDEFINED;
3492
3493   if (element != EL_BLOCKED && !IS_MOVING(x, y))
3494     return;
3495
3496   if (IS_MOVING(x, y))
3497   {
3498     Moving2Blocked(x, y, &newx, &newy);
3499
3500     if (Feld[newx][newy] != EL_BLOCKED)
3501     {
3502       /* element is moving, but target field is not free (blocked), but
3503          already occupied by something different (example: acid pool);
3504          in this case, only remove the moving field, but not the target */
3505
3506       RemoveField(oldx, oldy);
3507
3508       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3509
3510       DrawLevelField(oldx, oldy);
3511
3512       return;
3513     }
3514   }
3515   else if (element == EL_BLOCKED)
3516   {
3517     Blocked2Moving(x, y, &oldx, &oldy);
3518     if (!IS_MOVING(oldx, oldy))
3519       return;
3520   }
3521
3522   if (element == EL_BLOCKED &&
3523       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3524        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3525        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3526        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3527        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3528        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3529     next_element = get_next_element(Feld[oldx][oldy]);
3530
3531   RemoveField(oldx, oldy);
3532   RemoveField(newx, newy);
3533
3534   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3535
3536   if (next_element != EL_UNDEFINED)
3537     Feld[oldx][oldy] = next_element;
3538
3539   DrawLevelField(oldx, oldy);
3540   DrawLevelField(newx, newy);
3541 }
3542
3543 void DrawDynamite(int x, int y)
3544 {
3545   int sx = SCREENX(x), sy = SCREENY(y);
3546   int graphic = el2img(Feld[x][y]);
3547   int frame;
3548
3549   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3550     return;
3551
3552   if (IS_WALKABLE_INSIDE(Back[x][y]))
3553     return;
3554
3555   if (Back[x][y])
3556     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3557   else if (Store[x][y])
3558     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3559
3560   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3561
3562   if (Back[x][y] || Store[x][y])
3563     DrawGraphicThruMask(sx, sy, graphic, frame);
3564   else
3565     DrawGraphic(sx, sy, graphic, frame);
3566 }
3567
3568 void CheckDynamite(int x, int y)
3569 {
3570   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
3571   {
3572     MovDelay[x][y]--;
3573
3574     if (MovDelay[x][y] != 0)
3575     {
3576       DrawDynamite(x, y);
3577       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3578
3579       return;
3580     }
3581   }
3582
3583   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3584
3585   Bang(x, y);
3586 }
3587
3588 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3589 {
3590   boolean num_checked_players = 0;
3591   int i;
3592
3593   for (i = 0; i < MAX_PLAYERS; i++)
3594   {
3595     if (stored_player[i].active)
3596     {
3597       int sx = stored_player[i].jx;
3598       int sy = stored_player[i].jy;
3599
3600       if (num_checked_players == 0)
3601       {
3602         *sx1 = *sx2 = sx;
3603         *sy1 = *sy2 = sy;
3604       }
3605       else
3606       {
3607         *sx1 = MIN(*sx1, sx);
3608         *sy1 = MIN(*sy1, sy);
3609         *sx2 = MAX(*sx2, sx);
3610         *sy2 = MAX(*sy2, sy);
3611       }
3612
3613       num_checked_players++;
3614     }
3615   }
3616 }
3617
3618 static boolean checkIfAllPlayersFitToScreen_RND()
3619 {
3620   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3621
3622   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3623
3624   return (sx2 - sx1 < SCR_FIELDX &&
3625           sy2 - sy1 < SCR_FIELDY);
3626 }
3627
3628 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3629 {
3630   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3631
3632   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3633
3634   *sx = (sx1 + sx2) / 2;
3635   *sy = (sy1 + sy2) / 2;
3636 }
3637
3638 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3639                         boolean center_screen, boolean quick_relocation)
3640 {
3641   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3642   boolean no_delay = (tape.warp_forward);
3643   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3644   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3645
3646   if (quick_relocation)
3647   {
3648     int offset = (setup.scroll_delay ? 3 : 0);
3649
3650     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3651     {
3652       if (center_screen)
3653       {
3654         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3655                     x > SBX_Right + MIDPOSX ? SBX_Right :
3656                     x - MIDPOSX);
3657
3658         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3659                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
3660                     y - MIDPOSY);
3661       }
3662       else
3663       {
3664         /* quick relocation (without scrolling), but do not center screen */
3665
3666         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
3667                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
3668                                old_x - MIDPOSX);
3669
3670         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3671                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3672                                old_y - MIDPOSY);
3673
3674         int offset_x = x + (scroll_x - center_scroll_x);
3675         int offset_y = y + (scroll_y - center_scroll_y);
3676
3677         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
3678                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3679                     offset_x - MIDPOSX);
3680
3681         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3682                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3683                     offset_y - MIDPOSY);
3684       }
3685     }
3686     else
3687     {
3688       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
3689           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3690         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3691
3692       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
3693           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3694         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3695
3696       /* don't scroll over playfield boundaries */
3697       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3698         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3699
3700       /* don't scroll over playfield boundaries */
3701       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3702         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3703     }
3704
3705     RedrawPlayfield(TRUE, 0,0,0,0);
3706   }
3707   else
3708   {
3709     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3710                      x > SBX_Right + MIDPOSX ? SBX_Right :
3711                      x - MIDPOSX);
3712
3713     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3714                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
3715                      y - MIDPOSY);
3716
3717     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3718
3719     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3720     {
3721       int dx = 0, dy = 0;
3722       int fx = FX, fy = FY;
3723
3724       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3725       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3726
3727       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3728         break;
3729
3730       scroll_x -= dx;
3731       scroll_y -= dy;
3732
3733       fx += dx * TILEX / 2;
3734       fy += dy * TILEY / 2;
3735
3736       ScrollLevel(dx, dy);
3737       DrawAllPlayers();
3738
3739       /* scroll in two steps of half tile size to make things smoother */
3740       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3741       FlushDisplay();
3742       Delay(wait_delay_value);
3743
3744       /* scroll second step to align at full tile size */
3745       BackToFront();
3746       Delay(wait_delay_value);
3747     }
3748
3749     DrawAllPlayers();
3750     BackToFront();
3751     Delay(wait_delay_value);
3752   }
3753 }
3754
3755 void RelocatePlayer(int jx, int jy, int el_player_raw)
3756 {
3757   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3758   int player_nr = GET_PLAYER_NR(el_player);
3759   struct PlayerInfo *player = &stored_player[player_nr];
3760   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3761   boolean no_delay = (tape.warp_forward);
3762   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3763   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3764   int old_jx = player->jx;
3765   int old_jy = player->jy;
3766   int old_element = Feld[old_jx][old_jy];
3767   int element = Feld[jx][jy];
3768   boolean player_relocated = (old_jx != jx || old_jy != jy);
3769
3770   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3771   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
3772   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3773   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
3774   int leave_side_horiz = move_dir_horiz;
3775   int leave_side_vert  = move_dir_vert;
3776   int enter_side = enter_side_horiz | enter_side_vert;
3777   int leave_side = leave_side_horiz | leave_side_vert;
3778
3779   if (player->GameOver)         /* do not reanimate dead player */
3780     return;
3781
3782   if (!player_relocated)        /* no need to relocate the player */
3783     return;
3784
3785   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
3786   {
3787     RemoveField(jx, jy);        /* temporarily remove newly placed player */
3788     DrawLevelField(jx, jy);
3789   }
3790
3791   if (player->present)
3792   {
3793     while (player->MovPos)
3794     {
3795       ScrollPlayer(player, SCROLL_GO_ON);
3796       ScrollScreen(NULL, SCROLL_GO_ON);
3797
3798       AdvanceFrameAndPlayerCounters(player->index_nr);
3799
3800       DrawPlayer(player);
3801
3802       BackToFront();
3803       Delay(wait_delay_value);
3804     }
3805
3806     DrawPlayer(player);         /* needed here only to cleanup last field */
3807     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
3808
3809     player->is_moving = FALSE;
3810   }
3811
3812   if (IS_CUSTOM_ELEMENT(old_element))
3813     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3814                                CE_LEFT_BY_PLAYER,
3815                                player->index_bit, leave_side);
3816
3817   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3818                                       CE_PLAYER_LEAVES_X,
3819                                       player->index_bit, leave_side);
3820
3821   Feld[jx][jy] = el_player;
3822   InitPlayerField(jx, jy, el_player, TRUE);
3823
3824   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3825   {
3826     Feld[jx][jy] = element;
3827     InitField(jx, jy, FALSE);
3828   }
3829
3830   /* only visually relocate centered player */
3831   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3832                      FALSE, level.instant_relocation);
3833
3834   TestIfPlayerTouchesBadThing(jx, jy);
3835   TestIfPlayerTouchesCustomElement(jx, jy);
3836
3837   if (IS_CUSTOM_ELEMENT(element))
3838     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3839                                player->index_bit, enter_side);
3840
3841   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3842                                       player->index_bit, enter_side);
3843 }
3844
3845 void Explode(int ex, int ey, int phase, int mode)
3846 {
3847   int x, y;
3848   int last_phase;
3849   int border_element;
3850
3851   /* !!! eliminate this variable !!! */
3852   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3853
3854   if (game.explosions_delayed)
3855   {
3856     ExplodeField[ex][ey] = mode;
3857     return;
3858   }
3859
3860   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
3861   {
3862     int center_element = Feld[ex][ey];
3863     int artwork_element, explosion_element;     /* set these values later */
3864
3865 #if 0
3866     /* --- This is only really needed (and now handled) in "Impact()". --- */
3867     /* do not explode moving elements that left the explode field in time */
3868     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3869         center_element == EL_EMPTY &&
3870         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3871       return;
3872 #endif
3873
3874 #if 0
3875     /* !!! at this place, the center element may be EL_BLOCKED !!! */
3876     if (mode == EX_TYPE_NORMAL ||
3877         mode == EX_TYPE_CENTER ||
3878         mode == EX_TYPE_CROSS)
3879       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3880 #endif
3881
3882     /* remove things displayed in background while burning dynamite */
3883     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3884       Back[ex][ey] = 0;
3885
3886     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3887     {
3888       /* put moving element to center field (and let it explode there) */
3889       center_element = MovingOrBlocked2Element(ex, ey);
3890       RemoveMovingField(ex, ey);
3891       Feld[ex][ey] = center_element;
3892     }
3893
3894     /* now "center_element" is finally determined -- set related values now */
3895     artwork_element = center_element;           /* for custom player artwork */
3896     explosion_element = center_element;         /* for custom player artwork */
3897
3898     if (IS_PLAYER(ex, ey))
3899     {
3900       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3901
3902       artwork_element = stored_player[player_nr].artwork_element;
3903
3904       if (level.use_explosion_element[player_nr])
3905       {
3906         explosion_element = level.explosion_element[player_nr];
3907         artwork_element = explosion_element;
3908       }
3909     }
3910
3911 #if 1
3912     if (mode == EX_TYPE_NORMAL ||
3913         mode == EX_TYPE_CENTER ||
3914         mode == EX_TYPE_CROSS)
3915       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3916 #endif
3917
3918     last_phase = element_info[explosion_element].explosion_delay + 1;
3919
3920     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3921     {
3922       int xx = x - ex + 1;
3923       int yy = y - ey + 1;
3924       int element;
3925
3926       if (!IN_LEV_FIELD(x, y) ||
3927           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3928           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
3929         continue;
3930
3931       element = Feld[x][y];
3932
3933       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3934       {
3935         element = MovingOrBlocked2Element(x, y);
3936
3937         if (!IS_EXPLOSION_PROOF(element))
3938           RemoveMovingField(x, y);
3939       }
3940
3941       /* indestructible elements can only explode in center (but not flames) */
3942       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3943                                            mode == EX_TYPE_BORDER)) ||
3944           element == EL_FLAMES)
3945         continue;
3946
3947       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3948          behaviour, for example when touching a yamyam that explodes to rocks
3949          with active deadly shield, a rock is created under the player !!! */
3950       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3951 #if 0
3952       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3953           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3954            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3955 #else
3956       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3957 #endif
3958       {
3959         if (IS_ACTIVE_BOMB(element))
3960         {
3961           /* re-activate things under the bomb like gate or penguin */
3962           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3963           Back[x][y] = 0;
3964         }
3965
3966         continue;
3967       }
3968
3969       /* save walkable background elements while explosion on same tile */
3970       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3971           (x != ex || y != ey || mode == EX_TYPE_BORDER))
3972         Back[x][y] = element;
3973
3974       /* ignite explodable elements reached by other explosion */
3975       if (element == EL_EXPLOSION)
3976         element = Store2[x][y];
3977
3978       if (AmoebaNr[x][y] &&
3979           (element == EL_AMOEBA_FULL ||
3980            element == EL_BD_AMOEBA ||
3981            element == EL_AMOEBA_GROWING))
3982       {
3983         AmoebaCnt[AmoebaNr[x][y]]--;
3984         AmoebaCnt2[AmoebaNr[x][y]]--;
3985       }
3986
3987       RemoveField(x, y);
3988
3989       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3990       {
3991         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3992
3993         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3994
3995         if (PLAYERINFO(ex, ey)->use_murphy)
3996           Store[x][y] = EL_EMPTY;
3997       }
3998
3999       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4000          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4001       else if (ELEM_IS_PLAYER(center_element))
4002         Store[x][y] = EL_EMPTY;
4003       else if (center_element == EL_YAMYAM)
4004         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4005       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4006         Store[x][y] = element_info[center_element].content.e[xx][yy];
4007 #if 1
4008       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4009          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4010          otherwise) -- FIX THIS !!! */
4011       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4012         Store[x][y] = element_info[element].content.e[1][1];
4013 #else
4014       else if (!CAN_EXPLODE(element))
4015         Store[x][y] = element_info[element].content.e[1][1];
4016 #endif
4017       else
4018         Store[x][y] = EL_EMPTY;
4019
4020       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4021           center_element == EL_AMOEBA_TO_DIAMOND)
4022         Store2[x][y] = element;
4023
4024       Feld[x][y] = EL_EXPLOSION;
4025       GfxElement[x][y] = artwork_element;
4026
4027       ExplodePhase[x][y] = 1;
4028       ExplodeDelay[x][y] = last_phase;
4029
4030       Stop[x][y] = TRUE;
4031     }
4032
4033     if (center_element == EL_YAMYAM)
4034       game.yamyam_content_nr =
4035         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4036
4037     return;
4038   }
4039
4040   if (Stop[ex][ey])
4041     return;
4042
4043   x = ex;
4044   y = ey;
4045
4046   if (phase == 1)
4047     GfxFrame[x][y] = 0;         /* restart explosion animation */
4048
4049   last_phase = ExplodeDelay[x][y];
4050
4051   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4052
4053 #ifdef DEBUG
4054
4055   /* activate this even in non-DEBUG version until cause for crash in
4056      getGraphicAnimationFrame() (see below) is found and eliminated */
4057
4058 #endif
4059 #if 1
4060
4061 #if 1
4062   /* this can happen if the player leaves an explosion just in time */
4063   if (GfxElement[x][y] == EL_UNDEFINED)
4064     GfxElement[x][y] = EL_EMPTY;
4065 #else
4066   if (GfxElement[x][y] == EL_UNDEFINED)
4067   {
4068     printf("\n\n");
4069     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4070     printf("Explode(): This should never happen!\n");
4071     printf("\n\n");
4072
4073     GfxElement[x][y] = EL_EMPTY;
4074   }
4075 #endif
4076
4077 #endif
4078
4079   border_element = Store2[x][y];
4080   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4081     border_element = StorePlayer[x][y];
4082
4083   if (phase == element_info[border_element].ignition_delay ||
4084       phase == last_phase)
4085   {
4086     boolean border_explosion = FALSE;
4087
4088     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4089         !PLAYER_EXPLOSION_PROTECTED(x, y))
4090     {
4091       KillPlayerUnlessExplosionProtected(x, y);
4092       border_explosion = TRUE;
4093     }
4094     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4095     {
4096       Feld[x][y] = Store2[x][y];
4097       Store2[x][y] = 0;
4098       Bang(x, y);
4099       border_explosion = TRUE;
4100     }
4101     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4102     {
4103       AmoebeUmwandeln(x, y);
4104       Store2[x][y] = 0;
4105       border_explosion = TRUE;
4106     }
4107
4108     /* if an element just explodes due to another explosion (chain-reaction),
4109        do not immediately end the new explosion when it was the last frame of
4110        the explosion (as it would be done in the following "if"-statement!) */
4111     if (border_explosion && phase == last_phase)
4112       return;
4113   }
4114
4115   if (phase == last_phase)
4116   {
4117     int element;
4118
4119     element = Feld[x][y] = Store[x][y];
4120     Store[x][y] = Store2[x][y] = 0;
4121     GfxElement[x][y] = EL_UNDEFINED;
4122
4123     /* player can escape from explosions and might therefore be still alive */
4124     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4125         element <= EL_PLAYER_IS_EXPLODING_4)
4126     {
4127       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4128       int explosion_element = EL_PLAYER_1 + player_nr;
4129       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4130       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4131
4132       if (level.use_explosion_element[player_nr])
4133         explosion_element = level.explosion_element[player_nr];
4134
4135       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4136                     element_info[explosion_element].content.e[xx][yy]);
4137     }
4138
4139     /* restore probably existing indestructible background element */
4140     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4141       element = Feld[x][y] = Back[x][y];
4142     Back[x][y] = 0;
4143
4144     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4145     GfxDir[x][y] = MV_NONE;
4146     ChangeDelay[x][y] = 0;
4147     ChangePage[x][y] = -1;
4148
4149 #if USE_NEW_CUSTOM_VALUE
4150     CustomValue[x][y] = 0;
4151 #endif
4152
4153     InitField_WithBug2(x, y, FALSE);
4154
4155     DrawLevelField(x, y);
4156
4157     TestIfElementTouchesCustomElement(x, y);
4158
4159     if (GFX_CRUMBLED(element))
4160       DrawLevelFieldCrumbledSandNeighbours(x, y);
4161
4162     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4163       StorePlayer[x][y] = 0;
4164
4165     if (ELEM_IS_PLAYER(element))
4166       RelocatePlayer(x, y, element);
4167   }
4168   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4169   {
4170     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4171     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4172
4173     if (phase == delay)
4174       DrawLevelFieldCrumbledSand(x, y);
4175
4176     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4177     {
4178       DrawLevelElement(x, y, Back[x][y]);
4179       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4180     }
4181     else if (IS_WALKABLE_UNDER(Back[x][y]))
4182     {
4183       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4184       DrawLevelElementThruMask(x, y, Back[x][y]);
4185     }
4186     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4187       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4188   }
4189 }
4190
4191 void DynaExplode(int ex, int ey)
4192 {
4193   int i, j;
4194   int dynabomb_element = Feld[ex][ey];
4195   int dynabomb_size = 1;
4196   boolean dynabomb_xl = FALSE;
4197   struct PlayerInfo *player;
4198   static int xy[4][2] =
4199   {
4200     { 0, -1 },
4201     { -1, 0 },
4202     { +1, 0 },
4203     { 0, +1 }
4204   };
4205
4206   if (IS_ACTIVE_BOMB(dynabomb_element))
4207   {
4208     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4209     dynabomb_size = player->dynabomb_size;
4210     dynabomb_xl = player->dynabomb_xl;
4211     player->dynabombs_left++;
4212   }
4213
4214   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4215
4216   for (i = 0; i < NUM_DIRECTIONS; i++)
4217   {
4218     for (j = 1; j <= dynabomb_size; j++)
4219     {
4220       int x = ex + j * xy[i][0];
4221       int y = ey + j * xy[i][1];
4222       int element;
4223
4224       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4225         break;
4226
4227       element = Feld[x][y];
4228
4229       /* do not restart explosions of fields with active bombs */
4230       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4231         continue;
4232
4233       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4234
4235       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4236           !IS_DIGGABLE(element) && !dynabomb_xl)
4237         break;
4238     }
4239   }
4240 }
4241
4242 void Bang(int x, int y)
4243 {
4244   int element = MovingOrBlocked2Element(x, y);
4245   int explosion_type = EX_TYPE_NORMAL;
4246
4247   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4248   {
4249     struct PlayerInfo *player = PLAYERINFO(x, y);
4250
4251     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4252                             player->element_nr);
4253
4254     if (level.use_explosion_element[player->index_nr])
4255     {
4256       int explosion_element = level.explosion_element[player->index_nr];
4257
4258       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4259         explosion_type = EX_TYPE_CROSS;
4260       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4261         explosion_type = EX_TYPE_CENTER;
4262     }
4263   }
4264
4265   switch (element)
4266   {
4267     case EL_BUG:
4268     case EL_SPACESHIP:
4269     case EL_BD_BUTTERFLY:
4270     case EL_BD_FIREFLY:
4271     case EL_YAMYAM:
4272     case EL_DARK_YAMYAM:
4273     case EL_ROBOT:
4274     case EL_PACMAN:
4275     case EL_MOLE:
4276       RaiseScoreElement(element);
4277       break;
4278
4279     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4280     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4281     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4282     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4283     case EL_DYNABOMB_INCREASE_NUMBER:
4284     case EL_DYNABOMB_INCREASE_SIZE:
4285     case EL_DYNABOMB_INCREASE_POWER:
4286       explosion_type = EX_TYPE_DYNA;
4287       break;
4288
4289     case EL_DC_LANDMINE:
4290 #if 0
4291     case EL_EM_EXIT_OPEN:
4292     case EL_EM_STEEL_EXIT_OPEN:
4293 #endif
4294       explosion_type = EX_TYPE_CENTER;
4295       break;
4296
4297     case EL_PENGUIN:
4298     case EL_LAMP:
4299     case EL_LAMP_ACTIVE:
4300     case EL_AMOEBA_TO_DIAMOND:
4301       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4302         explosion_type = EX_TYPE_CENTER;
4303       break;
4304
4305     default:
4306       if (element_info[element].explosion_type == EXPLODES_CROSS)
4307         explosion_type = EX_TYPE_CROSS;
4308       else if (element_info[element].explosion_type == EXPLODES_1X1)
4309         explosion_type = EX_TYPE_CENTER;
4310       break;
4311   }
4312
4313   if (explosion_type == EX_TYPE_DYNA)
4314     DynaExplode(x, y);
4315   else
4316     Explode(x, y, EX_PHASE_START, explosion_type);
4317
4318   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4319 }
4320
4321 void SplashAcid(int x, int y)
4322 {
4323   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4324       (!IN_LEV_FIELD(x - 1, y - 2) ||
4325        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4326     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4327
4328   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4329       (!IN_LEV_FIELD(x + 1, y - 2) ||
4330        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4331     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4332
4333   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4334 }
4335
4336 static void InitBeltMovement()
4337 {
4338   static int belt_base_element[4] =
4339   {
4340     EL_CONVEYOR_BELT_1_LEFT,
4341     EL_CONVEYOR_BELT_2_LEFT,
4342     EL_CONVEYOR_BELT_3_LEFT,
4343     EL_CONVEYOR_BELT_4_LEFT
4344   };
4345   static int belt_base_active_element[4] =
4346   {
4347     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4348     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4349     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4350     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4351   };
4352
4353   int x, y, i, j;
4354
4355   /* set frame order for belt animation graphic according to belt direction */
4356   for (i = 0; i < NUM_BELTS; i++)
4357   {
4358     int belt_nr = i;
4359
4360     for (j = 0; j < NUM_BELT_PARTS; j++)
4361     {
4362       int element = belt_base_active_element[belt_nr] + j;
4363       int graphic = el2img(element);
4364
4365       if (game.belt_dir[i] == MV_LEFT)
4366         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4367       else
4368         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4369     }
4370   }
4371
4372   SCAN_PLAYFIELD(x, y)
4373   {
4374     int element = Feld[x][y];
4375
4376     for (i = 0; i < NUM_BELTS; i++)
4377     {
4378       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4379       {
4380         int e_belt_nr = getBeltNrFromBeltElement(element);
4381         int belt_nr = i;
4382
4383         if (e_belt_nr == belt_nr)
4384         {
4385           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4386
4387           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4388         }
4389       }
4390     }
4391   }
4392 }
4393
4394 static void ToggleBeltSwitch(int x, int y)
4395 {
4396   static int belt_base_element[4] =
4397   {
4398     EL_CONVEYOR_BELT_1_LEFT,
4399     EL_CONVEYOR_BELT_2_LEFT,
4400     EL_CONVEYOR_BELT_3_LEFT,
4401     EL_CONVEYOR_BELT_4_LEFT
4402   };
4403   static int belt_base_active_element[4] =
4404   {
4405     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4406     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4407     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4408     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4409   };
4410   static int belt_base_switch_element[4] =
4411   {
4412     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4413     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4414     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4415     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4416   };
4417   static int belt_move_dir[4] =
4418   {
4419     MV_LEFT,
4420     MV_NONE,
4421     MV_RIGHT,
4422     MV_NONE,
4423   };
4424
4425   int element = Feld[x][y];
4426   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4427   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4428   int belt_dir = belt_move_dir[belt_dir_nr];
4429   int xx, yy, i;
4430
4431   if (!IS_BELT_SWITCH(element))
4432     return;
4433
4434   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4435   game.belt_dir[belt_nr] = belt_dir;
4436
4437   if (belt_dir_nr == 3)
4438     belt_dir_nr = 1;
4439
4440   /* set frame order for belt animation graphic according to belt direction */
4441   for (i = 0; i < NUM_BELT_PARTS; i++)
4442   {
4443     int element = belt_base_active_element[belt_nr] + i;
4444     int graphic = el2img(element);
4445
4446     if (belt_dir == MV_LEFT)
4447       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4448     else
4449       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4450   }
4451
4452   SCAN_PLAYFIELD(xx, yy)
4453   {
4454     int element = Feld[xx][yy];
4455
4456     if (IS_BELT_SWITCH(element))
4457     {
4458       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4459
4460       if (e_belt_nr == belt_nr)
4461       {
4462         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4463         DrawLevelField(xx, yy);
4464       }
4465     }
4466     else if (IS_BELT(element) && belt_dir != MV_NONE)
4467     {
4468       int e_belt_nr = getBeltNrFromBeltElement(element);
4469
4470       if (e_belt_nr == belt_nr)
4471       {
4472         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4473
4474         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4475         DrawLevelField(xx, yy);
4476       }
4477     }
4478     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4479     {
4480       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4481
4482       if (e_belt_nr == belt_nr)
4483       {
4484         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4485
4486         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4487         DrawLevelField(xx, yy);
4488       }
4489     }
4490   }
4491 }
4492
4493 static void ToggleSwitchgateSwitch(int x, int y)
4494 {
4495   int xx, yy;
4496
4497   game.switchgate_pos = !game.switchgate_pos;
4498
4499   SCAN_PLAYFIELD(xx, yy)
4500   {
4501     int element = Feld[xx][yy];
4502
4503 #if !USE_BOTH_SWITCHGATE_SWITCHES
4504     if (element == EL_SWITCHGATE_SWITCH_UP ||
4505         element == EL_SWITCHGATE_SWITCH_DOWN)
4506     {
4507       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4508       DrawLevelField(xx, yy);
4509     }
4510     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4511              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4512     {
4513       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4514       DrawLevelField(xx, yy);
4515     }
4516 #else
4517     if (element == EL_SWITCHGATE_SWITCH_UP)
4518     {
4519       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4520       DrawLevelField(xx, yy);
4521     }
4522     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4523     {
4524       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4525       DrawLevelField(xx, yy);
4526     }
4527     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4528     {
4529       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4530       DrawLevelField(xx, yy);
4531     }
4532     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4533     {
4534       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4535       DrawLevelField(xx, yy);
4536     }
4537 #endif
4538     else if (element == EL_SWITCHGATE_OPEN ||
4539              element == EL_SWITCHGATE_OPENING)
4540     {
4541       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4542
4543       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4544     }
4545     else if (element == EL_SWITCHGATE_CLOSED ||
4546              element == EL_SWITCHGATE_CLOSING)
4547     {
4548       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4549
4550       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4551     }
4552   }
4553 }
4554
4555 static int getInvisibleActiveFromInvisibleElement(int element)
4556 {
4557   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4558           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
4559           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
4560           element);
4561 }
4562
4563 static int getInvisibleFromInvisibleActiveElement(int element)
4564 {
4565   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4566           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
4567           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
4568           element);
4569 }
4570
4571 static void RedrawAllLightSwitchesAndInvisibleElements()
4572 {
4573   int x, y;
4574
4575   SCAN_PLAYFIELD(x, y)
4576   {
4577     int element = Feld[x][y];
4578
4579     if (element == EL_LIGHT_SWITCH &&
4580         game.light_time_left > 0)
4581     {
4582       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4583       DrawLevelField(x, y);
4584     }
4585     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4586              game.light_time_left == 0)
4587     {
4588       Feld[x][y] = EL_LIGHT_SWITCH;
4589       DrawLevelField(x, y);
4590     }
4591     else if (element == EL_EMC_DRIPPER &&
4592              game.light_time_left > 0)
4593     {
4594       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4595       DrawLevelField(x, y);
4596     }
4597     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4598              game.light_time_left == 0)
4599     {
4600       Feld[x][y] = EL_EMC_DRIPPER;
4601       DrawLevelField(x, y);
4602     }
4603     else if (element == EL_INVISIBLE_STEELWALL ||
4604              element == EL_INVISIBLE_WALL ||
4605              element == EL_INVISIBLE_SAND)
4606     {
4607       if (game.light_time_left > 0)
4608         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4609
4610       DrawLevelField(x, y);
4611
4612       /* uncrumble neighbour fields, if needed */
4613       if (element == EL_INVISIBLE_SAND)
4614         DrawLevelFieldCrumbledSandNeighbours(x, y);
4615     }
4616     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4617              element == EL_INVISIBLE_WALL_ACTIVE ||
4618              element == EL_INVISIBLE_SAND_ACTIVE)
4619     {
4620       if (game.light_time_left == 0)
4621         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4622
4623       DrawLevelField(x, y);
4624
4625       /* re-crumble neighbour fields, if needed */
4626       if (element == EL_INVISIBLE_SAND)
4627         DrawLevelFieldCrumbledSandNeighbours(x, y);
4628     }
4629   }
4630 }
4631
4632 static void RedrawAllInvisibleElementsForLenses()
4633 {
4634   int x, y;
4635
4636   SCAN_PLAYFIELD(x, y)
4637   {
4638     int element = Feld[x][y];
4639
4640     if (element == EL_EMC_DRIPPER &&
4641         game.lenses_time_left > 0)
4642     {
4643       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4644       DrawLevelField(x, y);
4645     }
4646     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4647              game.lenses_time_left == 0)
4648     {
4649       Feld[x][y] = EL_EMC_DRIPPER;
4650       DrawLevelField(x, y);
4651     }
4652     else if (element == EL_INVISIBLE_STEELWALL ||
4653              element == EL_INVISIBLE_WALL ||
4654              element == EL_INVISIBLE_SAND)
4655     {
4656       if (game.lenses_time_left > 0)
4657         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4658
4659       DrawLevelField(x, y);
4660
4661       /* uncrumble neighbour fields, if needed */
4662       if (element == EL_INVISIBLE_SAND)
4663         DrawLevelFieldCrumbledSandNeighbours(x, y);
4664     }
4665     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4666              element == EL_INVISIBLE_WALL_ACTIVE ||
4667              element == EL_INVISIBLE_SAND_ACTIVE)
4668     {
4669       if (game.lenses_time_left == 0)
4670         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4671
4672       DrawLevelField(x, y);
4673
4674       /* re-crumble neighbour fields, if needed */
4675       if (element == EL_INVISIBLE_SAND)
4676         DrawLevelFieldCrumbledSandNeighbours(x, y);
4677     }
4678   }
4679 }
4680
4681 static void RedrawAllInvisibleElementsForMagnifier()
4682 {
4683   int x, y;
4684
4685   SCAN_PLAYFIELD(x, y)
4686   {
4687     int element = Feld[x][y];
4688
4689     if (element == EL_EMC_FAKE_GRASS &&
4690         game.magnify_time_left > 0)
4691     {
4692       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4693       DrawLevelField(x, y);
4694     }
4695     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4696              game.magnify_time_left == 0)
4697     {
4698       Feld[x][y] = EL_EMC_FAKE_GRASS;
4699       DrawLevelField(x, y);
4700     }
4701     else if (IS_GATE_GRAY(element) &&
4702              game.magnify_time_left > 0)
4703     {
4704       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4705                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4706                     IS_EM_GATE_GRAY(element) ?
4707                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4708                     IS_EMC_GATE_GRAY(element) ?
4709                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4710                     element);
4711       DrawLevelField(x, y);
4712     }
4713     else if (IS_GATE_GRAY_ACTIVE(element) &&
4714              game.magnify_time_left == 0)
4715     {
4716       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4717                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4718                     IS_EM_GATE_GRAY_ACTIVE(element) ?
4719                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4720                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
4721                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4722                     element);
4723       DrawLevelField(x, y);
4724     }
4725   }
4726 }
4727
4728 static void ToggleLightSwitch(int x, int y)
4729 {
4730   int element = Feld[x][y];
4731
4732   game.light_time_left =
4733     (element == EL_LIGHT_SWITCH ?
4734      level.time_light * FRAMES_PER_SECOND : 0);
4735
4736   RedrawAllLightSwitchesAndInvisibleElements();
4737 }
4738
4739 static void ActivateTimegateSwitch(int x, int y)
4740 {
4741   int xx, yy;
4742
4743   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4744
4745   SCAN_PLAYFIELD(xx, yy)
4746   {
4747     int element = Feld[xx][yy];
4748
4749     if (element == EL_TIMEGATE_CLOSED ||
4750         element == EL_TIMEGATE_CLOSING)
4751     {
4752       Feld[xx][yy] = EL_TIMEGATE_OPENING;
4753       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4754     }
4755
4756     /*
4757     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4758     {
4759       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4760       DrawLevelField(xx, yy);
4761     }
4762     */
4763
4764   }
4765
4766 #if 1
4767   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4768                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4769 #else
4770   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4771 #endif
4772 }
4773
4774 void Impact(int x, int y)
4775 {
4776   boolean last_line = (y == lev_fieldy - 1);
4777   boolean object_hit = FALSE;
4778   boolean impact = (last_line || object_hit);
4779   int element = Feld[x][y];
4780   int smashed = EL_STEELWALL;
4781
4782   if (!last_line)       /* check if element below was hit */
4783   {
4784     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4785       return;
4786
4787     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4788                                          MovDir[x][y + 1] != MV_DOWN ||
4789                                          MovPos[x][y + 1] <= TILEY / 2));
4790
4791     /* do not smash moving elements that left the smashed field in time */
4792     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4793         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4794       object_hit = FALSE;
4795
4796 #if USE_QUICKSAND_IMPACT_BUGFIX
4797     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4798     {
4799       RemoveMovingField(x, y + 1);
4800       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4801       Feld[x][y + 2] = EL_ROCK;
4802       DrawLevelField(x, y + 2);
4803
4804       object_hit = TRUE;
4805     }
4806
4807     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
4808     {
4809       RemoveMovingField(x, y + 1);
4810       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
4811       Feld[x][y + 2] = EL_ROCK;
4812       DrawLevelField(x, y + 2);
4813
4814       object_hit = TRUE;
4815     }
4816 #endif
4817
4818     if (object_hit)
4819       smashed = MovingOrBlocked2Element(x, y + 1);
4820
4821     impact = (last_line || object_hit);
4822   }
4823
4824   if (!last_line && smashed == EL_ACID) /* element falls into acid */
4825   {
4826     SplashAcid(x, y + 1);
4827     return;
4828   }
4829
4830   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4831   /* only reset graphic animation if graphic really changes after impact */
4832   if (impact &&
4833       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4834   {
4835     ResetGfxAnimation(x, y);
4836     DrawLevelField(x, y);
4837   }
4838
4839   if (impact && CAN_EXPLODE_IMPACT(element))
4840   {
4841     Bang(x, y);
4842     return;
4843   }
4844   else if (impact && element == EL_PEARL &&
4845            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
4846   {
4847     ResetGfxAnimation(x, y);
4848
4849     Feld[x][y] = EL_PEARL_BREAKING;
4850     PlayLevelSound(x, y, SND_PEARL_BREAKING);
4851     return;
4852   }
4853   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4854   {
4855     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4856
4857     return;
4858   }
4859
4860   if (impact && element == EL_AMOEBA_DROP)
4861   {
4862     if (object_hit && IS_PLAYER(x, y + 1))
4863       KillPlayerUnlessEnemyProtected(x, y + 1);
4864     else if (object_hit && smashed == EL_PENGUIN)
4865       Bang(x, y + 1);
4866     else
4867     {
4868       Feld[x][y] = EL_AMOEBA_GROWING;
4869       Store[x][y] = EL_AMOEBA_WET;
4870
4871       ResetRandomAnimationValue(x, y);
4872     }
4873     return;
4874   }
4875
4876   if (object_hit)               /* check which object was hit */
4877   {
4878     if ((CAN_PASS_MAGIC_WALL(element) && 
4879          (smashed == EL_MAGIC_WALL ||
4880           smashed == EL_BD_MAGIC_WALL)) ||
4881         (CAN_PASS_DC_MAGIC_WALL(element) &&
4882          smashed == EL_DC_MAGIC_WALL))
4883     {
4884       int xx, yy;
4885       int activated_magic_wall =
4886         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4887          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
4888          EL_DC_MAGIC_WALL_ACTIVE);
4889
4890       /* activate magic wall / mill */
4891       SCAN_PLAYFIELD(xx, yy)
4892       {
4893         if (Feld[xx][yy] == smashed)
4894           Feld[xx][yy] = activated_magic_wall;
4895       }
4896
4897       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4898       game.magic_wall_active = TRUE;
4899
4900       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4901                             SND_MAGIC_WALL_ACTIVATING :
4902                             smashed == EL_BD_MAGIC_WALL ?
4903                             SND_BD_MAGIC_WALL_ACTIVATING :
4904                             SND_DC_MAGIC_WALL_ACTIVATING));
4905     }
4906
4907     if (IS_PLAYER(x, y + 1))
4908     {
4909       if (CAN_SMASH_PLAYER(element))
4910       {
4911         KillPlayerUnlessEnemyProtected(x, y + 1);
4912         return;
4913       }
4914     }
4915     else if (smashed == EL_PENGUIN)
4916     {
4917       if (CAN_SMASH_PLAYER(element))
4918       {
4919         Bang(x, y + 1);
4920         return;
4921       }
4922     }
4923     else if (element == EL_BD_DIAMOND)
4924     {
4925       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4926       {
4927         Bang(x, y + 1);
4928         return;
4929       }
4930     }
4931     else if (((element == EL_SP_INFOTRON ||
4932                element == EL_SP_ZONK) &&
4933               (smashed == EL_SP_SNIKSNAK ||
4934                smashed == EL_SP_ELECTRON ||
4935                smashed == EL_SP_DISK_ORANGE)) ||
4936              (element == EL_SP_INFOTRON &&
4937               smashed == EL_SP_DISK_YELLOW))
4938     {
4939       Bang(x, y + 1);
4940       return;
4941     }
4942     else if (CAN_SMASH_EVERYTHING(element))
4943     {
4944       if (IS_CLASSIC_ENEMY(smashed) ||
4945           CAN_EXPLODE_SMASHED(smashed))
4946       {
4947         Bang(x, y + 1);
4948         return;
4949       }
4950       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4951       {
4952         if (smashed == EL_LAMP ||
4953             smashed == EL_LAMP_ACTIVE)
4954         {
4955           Bang(x, y + 1);
4956           return;
4957         }
4958         else if (smashed == EL_NUT)
4959         {
4960           Feld[x][y + 1] = EL_NUT_BREAKING;
4961           PlayLevelSound(x, y, SND_NUT_BREAKING);
4962           RaiseScoreElement(EL_NUT);
4963           return;
4964         }
4965         else if (smashed == EL_PEARL)
4966         {
4967           ResetGfxAnimation(x, y);
4968
4969           Feld[x][y + 1] = EL_PEARL_BREAKING;
4970           PlayLevelSound(x, y, SND_PEARL_BREAKING);
4971           return;
4972         }
4973         else if (smashed == EL_DIAMOND)
4974         {
4975           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4976           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4977           return;
4978         }
4979         else if (IS_BELT_SWITCH(smashed))
4980         {
4981           ToggleBeltSwitch(x, y + 1);
4982         }
4983         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4984                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
4985                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
4986                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
4987         {
4988           ToggleSwitchgateSwitch(x, y + 1);
4989         }
4990         else if (smashed == EL_LIGHT_SWITCH ||
4991                  smashed == EL_LIGHT_SWITCH_ACTIVE)
4992         {
4993           ToggleLightSwitch(x, y + 1);
4994         }
4995         else
4996         {
4997 #if 0
4998           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4999 #endif
5000
5001           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5002
5003           CheckElementChangeBySide(x, y + 1, smashed, element,
5004                                    CE_SWITCHED, CH_SIDE_TOP);
5005           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5006                                             CH_SIDE_TOP);
5007         }
5008       }
5009       else
5010       {
5011         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5012       }
5013     }
5014   }
5015
5016   /* play sound of magic wall / mill */
5017   if (!last_line &&
5018       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5019        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5020        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5021   {
5022     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5023       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5024     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5025       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5026     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5027       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5028
5029     return;
5030   }
5031
5032   /* play sound of object that hits the ground */
5033   if (last_line || object_hit)
5034     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5035 }
5036
5037 inline static void TurnRoundExt(int x, int y)
5038 {
5039   static struct
5040   {
5041     int dx, dy;
5042   } move_xy[] =
5043   {
5044     {  0,  0 },
5045     { -1,  0 },
5046     { +1,  0 },
5047     {  0,  0 },
5048     {  0, -1 },
5049     {  0,  0 }, { 0, 0 }, { 0, 0 },
5050     {  0, +1 }
5051   };
5052   static struct
5053   {
5054     int left, right, back;
5055   } turn[] =
5056   {
5057     { 0,        0,              0        },
5058     { MV_DOWN,  MV_UP,          MV_RIGHT },
5059     { MV_UP,    MV_DOWN,        MV_LEFT  },
5060     { 0,        0,              0        },
5061     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5062     { 0,        0,              0        },
5063     { 0,        0,              0        },
5064     { 0,        0,              0        },
5065     { MV_RIGHT, MV_LEFT,        MV_UP    }
5066   };
5067
5068   int element = Feld[x][y];
5069   int move_pattern = element_info[element].move_pattern;
5070
5071   int old_move_dir = MovDir[x][y];
5072   int left_dir  = turn[old_move_dir].left;
5073   int right_dir = turn[old_move_dir].right;
5074   int back_dir  = turn[old_move_dir].back;
5075
5076   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5077   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5078   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5079   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5080
5081   int left_x  = x + left_dx,  left_y  = y + left_dy;
5082   int right_x = x + right_dx, right_y = y + right_dy;
5083   int move_x  = x + move_dx,  move_y  = y + move_dy;
5084
5085   int xx, yy;
5086
5087   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5088   {
5089     TestIfBadThingTouchesOtherBadThing(x, y);
5090
5091     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5092       MovDir[x][y] = right_dir;
5093     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5094       MovDir[x][y] = left_dir;
5095
5096     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5097       MovDelay[x][y] = 9;
5098     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5099       MovDelay[x][y] = 1;
5100   }
5101   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5102   {
5103     TestIfBadThingTouchesOtherBadThing(x, y);
5104
5105     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5106       MovDir[x][y] = left_dir;
5107     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5108       MovDir[x][y] = right_dir;
5109
5110     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5111       MovDelay[x][y] = 9;
5112     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5113       MovDelay[x][y] = 1;
5114   }
5115   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5116   {
5117     TestIfBadThingTouchesOtherBadThing(x, y);
5118
5119     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5120       MovDir[x][y] = left_dir;
5121     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5122       MovDir[x][y] = right_dir;
5123
5124     if (MovDir[x][y] != old_move_dir)
5125       MovDelay[x][y] = 9;
5126   }
5127   else if (element == EL_YAMYAM)
5128   {
5129     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5130     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5131
5132     if (can_turn_left && can_turn_right)
5133       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5134     else if (can_turn_left)
5135       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5136     else if (can_turn_right)
5137       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5138     else
5139       MovDir[x][y] = back_dir;
5140
5141     MovDelay[x][y] = 16 + 16 * RND(3);
5142   }
5143   else if (element == EL_DARK_YAMYAM)
5144   {
5145     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5146                                                          left_x, left_y);
5147     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5148                                                          right_x, right_y);
5149
5150     if (can_turn_left && can_turn_right)
5151       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5152     else if (can_turn_left)
5153       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5154     else if (can_turn_right)
5155       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5156     else
5157       MovDir[x][y] = back_dir;
5158
5159     MovDelay[x][y] = 16 + 16 * RND(3);
5160   }
5161   else if (element == EL_PACMAN)
5162   {
5163     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5164     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5165
5166     if (can_turn_left && can_turn_right)
5167       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5168     else if (can_turn_left)
5169       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5170     else if (can_turn_right)
5171       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5172     else
5173       MovDir[x][y] = back_dir;
5174
5175     MovDelay[x][y] = 6 + RND(40);
5176   }
5177   else if (element == EL_PIG)
5178   {
5179     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5180     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5181     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5182     boolean should_turn_left, should_turn_right, should_move_on;
5183     int rnd_value = 24;
5184     int rnd = RND(rnd_value);
5185
5186     should_turn_left = (can_turn_left &&
5187                         (!can_move_on ||
5188                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5189                                                    y + back_dy + left_dy)));
5190     should_turn_right = (can_turn_right &&
5191                          (!can_move_on ||
5192                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5193                                                     y + back_dy + right_dy)));
5194     should_move_on = (can_move_on &&
5195                       (!can_turn_left ||
5196                        !can_turn_right ||
5197                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5198                                                  y + move_dy + left_dy) ||
5199                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5200                                                  y + move_dy + right_dy)));
5201
5202     if (should_turn_left || should_turn_right || should_move_on)
5203     {
5204       if (should_turn_left && should_turn_right && should_move_on)
5205         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5206                         rnd < 2 * rnd_value / 3 ? right_dir :
5207                         old_move_dir);
5208       else if (should_turn_left && should_turn_right)
5209         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5210       else if (should_turn_left && should_move_on)
5211         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5212       else if (should_turn_right && should_move_on)
5213         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5214       else if (should_turn_left)
5215         MovDir[x][y] = left_dir;
5216       else if (should_turn_right)
5217         MovDir[x][y] = right_dir;
5218       else if (should_move_on)
5219         MovDir[x][y] = old_move_dir;
5220     }
5221     else if (can_move_on && rnd > rnd_value / 8)
5222       MovDir[x][y] = old_move_dir;
5223     else if (can_turn_left && can_turn_right)
5224       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5225     else if (can_turn_left && rnd > rnd_value / 8)
5226       MovDir[x][y] = left_dir;
5227     else if (can_turn_right && rnd > rnd_value/8)
5228       MovDir[x][y] = right_dir;
5229     else
5230       MovDir[x][y] = back_dir;
5231
5232     xx = x + move_xy[MovDir[x][y]].dx;
5233     yy = y + move_xy[MovDir[x][y]].dy;
5234
5235     if (!IN_LEV_FIELD(xx, yy) ||
5236         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5237       MovDir[x][y] = old_move_dir;
5238
5239     MovDelay[x][y] = 0;
5240   }
5241   else if (element == EL_DRAGON)
5242   {
5243     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5244     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5245     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5246     int rnd_value = 24;
5247     int rnd = RND(rnd_value);
5248
5249     if (can_move_on && rnd > rnd_value / 8)
5250       MovDir[x][y] = old_move_dir;
5251     else if (can_turn_left && can_turn_right)
5252       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5253     else if (can_turn_left && rnd > rnd_value / 8)
5254       MovDir[x][y] = left_dir;
5255     else if (can_turn_right && rnd > rnd_value / 8)
5256       MovDir[x][y] = right_dir;
5257     else
5258       MovDir[x][y] = back_dir;
5259
5260     xx = x + move_xy[MovDir[x][y]].dx;
5261     yy = y + move_xy[MovDir[x][y]].dy;
5262
5263     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5264       MovDir[x][y] = old_move_dir;
5265
5266     MovDelay[x][y] = 0;
5267   }
5268   else if (element == EL_MOLE)
5269   {
5270     boolean can_move_on =
5271       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5272                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5273                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5274     if (!can_move_on)
5275     {
5276       boolean can_turn_left =
5277         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5278                               IS_AMOEBOID(Feld[left_x][left_y])));
5279
5280       boolean can_turn_right =
5281         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5282                               IS_AMOEBOID(Feld[right_x][right_y])));
5283
5284       if (can_turn_left && can_turn_right)
5285         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5286       else if (can_turn_left)
5287         MovDir[x][y] = left_dir;
5288       else
5289         MovDir[x][y] = right_dir;
5290     }
5291
5292     if (MovDir[x][y] != old_move_dir)
5293       MovDelay[x][y] = 9;
5294   }
5295   else if (element == EL_BALLOON)
5296   {
5297     MovDir[x][y] = game.wind_direction;
5298     MovDelay[x][y] = 0;
5299   }
5300   else if (element == EL_SPRING)
5301   {
5302 #if USE_NEW_SPRING_BUMPER
5303     if (MovDir[x][y] & MV_HORIZONTAL)
5304     {
5305       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5306           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5307       {
5308         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5309         ResetGfxAnimation(move_x, move_y);
5310         DrawLevelField(move_x, move_y);
5311
5312         MovDir[x][y] = back_dir;
5313       }
5314       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5315                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5316         MovDir[x][y] = MV_NONE;
5317     }
5318 #else
5319     if (MovDir[x][y] & MV_HORIZONTAL &&
5320         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5321          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5322       MovDir[x][y] = MV_NONE;
5323 #endif
5324
5325     MovDelay[x][y] = 0;
5326   }
5327   else if (element == EL_ROBOT ||
5328            element == EL_SATELLITE ||
5329            element == EL_PENGUIN ||
5330            element == EL_EMC_ANDROID)
5331   {
5332     int attr_x = -1, attr_y = -1;
5333
5334     if (AllPlayersGone)
5335     {
5336       attr_x = ExitX;
5337       attr_y = ExitY;
5338     }
5339     else
5340     {
5341       int i;
5342
5343       for (i = 0; i < MAX_PLAYERS; i++)
5344       {
5345         struct PlayerInfo *player = &stored_player[i];
5346         int jx = player->jx, jy = player->jy;
5347
5348         if (!player->active)
5349           continue;
5350
5351         if (attr_x == -1 ||
5352             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5353         {
5354           attr_x = jx;
5355           attr_y = jy;
5356         }
5357       }
5358     }
5359
5360     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5361         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5362          game.engine_version < VERSION_IDENT(3,1,0,0)))
5363     {
5364       attr_x = ZX;
5365       attr_y = ZY;
5366     }
5367
5368     if (element == EL_PENGUIN)
5369     {
5370       int i;
5371       static int xy[4][2] =
5372       {
5373         { 0, -1 },
5374         { -1, 0 },
5375         { +1, 0 },
5376         { 0, +1 }
5377       };
5378
5379       for (i = 0; i < NUM_DIRECTIONS; i++)
5380       {
5381         int ex = x + xy[i][0];
5382         int ey = y + xy[i][1];
5383
5384         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5385                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5386                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5387                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5388         {
5389           attr_x = ex;
5390           attr_y = ey;
5391           break;
5392         }
5393       }
5394     }
5395
5396     MovDir[x][y] = MV_NONE;
5397     if (attr_x < x)
5398       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5399     else if (attr_x > x)
5400       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5401     if (attr_y < y)
5402       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5403     else if (attr_y > y)
5404       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5405
5406     if (element == EL_ROBOT)
5407     {
5408       int newx, newy;
5409
5410       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5411         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5412       Moving2Blocked(x, y, &newx, &newy);
5413
5414       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5415         MovDelay[x][y] = 8 + 8 * !RND(3);
5416       else
5417         MovDelay[x][y] = 16;
5418     }
5419     else if (element == EL_PENGUIN)
5420     {
5421       int newx, newy;
5422
5423       MovDelay[x][y] = 1;
5424
5425       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5426       {
5427         boolean first_horiz = RND(2);
5428         int new_move_dir = MovDir[x][y];
5429
5430         MovDir[x][y] =
5431           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5432         Moving2Blocked(x, y, &newx, &newy);
5433
5434         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5435           return;
5436
5437         MovDir[x][y] =
5438           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5439         Moving2Blocked(x, y, &newx, &newy);
5440
5441         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5442           return;
5443
5444         MovDir[x][y] = old_move_dir;
5445         return;
5446       }
5447     }
5448     else if (element == EL_SATELLITE)
5449     {
5450       int newx, newy;
5451
5452       MovDelay[x][y] = 1;
5453
5454       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5455       {
5456         boolean first_horiz = RND(2);
5457         int new_move_dir = MovDir[x][y];
5458
5459         MovDir[x][y] =
5460           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5461         Moving2Blocked(x, y, &newx, &newy);
5462
5463         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5464           return;
5465
5466         MovDir[x][y] =
5467           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5468         Moving2Blocked(x, y, &newx, &newy);
5469
5470         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5471           return;
5472
5473         MovDir[x][y] = old_move_dir;
5474         return;
5475       }
5476     }
5477     else if (element == EL_EMC_ANDROID)
5478     {
5479       static int check_pos[16] =
5480       {
5481         -1,             /*  0 => (invalid)          */
5482         7,              /*  1 => MV_LEFT            */
5483         3,              /*  2 => MV_RIGHT           */
5484         -1,             /*  3 => (invalid)          */
5485         1,              /*  4 =>            MV_UP   */
5486         0,              /*  5 => MV_LEFT  | MV_UP   */
5487         2,              /*  6 => MV_RIGHT | MV_UP   */
5488         -1,             /*  7 => (invalid)          */
5489         5,              /*  8 =>            MV_DOWN */
5490         6,              /*  9 => MV_LEFT  | MV_DOWN */
5491         4,              /* 10 => MV_RIGHT | MV_DOWN */
5492         -1,             /* 11 => (invalid)          */
5493         -1,             /* 12 => (invalid)          */
5494         -1,             /* 13 => (invalid)          */
5495         -1,             /* 14 => (invalid)          */
5496         -1,             /* 15 => (invalid)          */
5497       };
5498       static struct
5499       {
5500         int dx, dy;
5501         int dir;
5502       } check_xy[8] =
5503       {
5504         { -1, -1,       MV_LEFT  | MV_UP   },
5505         {  0, -1,                  MV_UP   },
5506         { +1, -1,       MV_RIGHT | MV_UP   },
5507         { +1,  0,       MV_RIGHT           },
5508         { +1, +1,       MV_RIGHT | MV_DOWN },
5509         {  0, +1,                  MV_DOWN },
5510         { -1, +1,       MV_LEFT  | MV_DOWN },
5511         { -1,  0,       MV_LEFT            },
5512       };
5513       int start_pos, check_order;
5514       boolean can_clone = FALSE;
5515       int i;
5516
5517       /* check if there is any free field around current position */
5518       for (i = 0; i < 8; i++)
5519       {
5520         int newx = x + check_xy[i].dx;
5521         int newy = y + check_xy[i].dy;
5522
5523         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5524         {
5525           can_clone = TRUE;
5526
5527           break;
5528         }
5529       }
5530
5531       if (can_clone)            /* randomly find an element to clone */
5532       {
5533         can_clone = FALSE;
5534
5535         start_pos = check_pos[RND(8)];
5536         check_order = (RND(2) ? -1 : +1);
5537
5538         for (i = 0; i < 8; i++)
5539         {
5540           int pos_raw = start_pos + i * check_order;
5541           int pos = (pos_raw + 8) % 8;
5542           int newx = x + check_xy[pos].dx;
5543           int newy = y + check_xy[pos].dy;
5544
5545           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5546           {
5547             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5548             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5549
5550             Store[x][y] = Feld[newx][newy];
5551
5552             can_clone = TRUE;
5553
5554             break;
5555           }
5556         }
5557       }
5558
5559       if (can_clone)            /* randomly find a direction to move */
5560       {
5561         can_clone = FALSE;
5562
5563         start_pos = check_pos[RND(8)];
5564         check_order = (RND(2) ? -1 : +1);
5565
5566         for (i = 0; i < 8; i++)
5567         {
5568           int pos_raw = start_pos + i * check_order;
5569           int pos = (pos_raw + 8) % 8;
5570           int newx = x + check_xy[pos].dx;
5571           int newy = y + check_xy[pos].dy;
5572           int new_move_dir = check_xy[pos].dir;
5573
5574           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5575           {
5576             MovDir[x][y] = new_move_dir;
5577             MovDelay[x][y] = level.android_clone_time * 8 + 1;
5578
5579             can_clone = TRUE;
5580
5581             break;
5582           }
5583         }
5584       }
5585
5586       if (can_clone)            /* cloning and moving successful */
5587         return;
5588
5589       /* cannot clone -- try to move towards player */
5590
5591       start_pos = check_pos[MovDir[x][y] & 0x0f];
5592       check_order = (RND(2) ? -1 : +1);
5593
5594       for (i = 0; i < 3; i++)
5595       {
5596         /* first check start_pos, then previous/next or (next/previous) pos */
5597         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5598         int pos = (pos_raw + 8) % 8;
5599         int newx = x + check_xy[pos].dx;
5600         int newy = y + check_xy[pos].dy;
5601         int new_move_dir = check_xy[pos].dir;
5602
5603         if (IS_PLAYER(newx, newy))
5604           break;
5605
5606         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5607         {
5608           MovDir[x][y] = new_move_dir;
5609           MovDelay[x][y] = level.android_move_time * 8 + 1;
5610
5611           break;
5612         }
5613       }
5614     }
5615   }
5616   else if (move_pattern == MV_TURNING_LEFT ||
5617            move_pattern == MV_TURNING_RIGHT ||
5618            move_pattern == MV_TURNING_LEFT_RIGHT ||
5619            move_pattern == MV_TURNING_RIGHT_LEFT ||
5620            move_pattern == MV_TURNING_RANDOM ||
5621            move_pattern == MV_ALL_DIRECTIONS)
5622   {
5623     boolean can_turn_left =
5624       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5625     boolean can_turn_right =
5626       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5627
5628     if (element_info[element].move_stepsize == 0)       /* "not moving" */
5629       return;
5630
5631     if (move_pattern == MV_TURNING_LEFT)
5632       MovDir[x][y] = left_dir;
5633     else if (move_pattern == MV_TURNING_RIGHT)
5634       MovDir[x][y] = right_dir;
5635     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5636       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5637     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5638       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5639     else if (move_pattern == MV_TURNING_RANDOM)
5640       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5641                       can_turn_right && !can_turn_left ? right_dir :
5642                       RND(2) ? left_dir : right_dir);
5643     else if (can_turn_left && can_turn_right)
5644       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5645     else if (can_turn_left)
5646       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5647     else if (can_turn_right)
5648       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5649     else
5650       MovDir[x][y] = back_dir;
5651
5652     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5653   }
5654   else if (move_pattern == MV_HORIZONTAL ||
5655            move_pattern == MV_VERTICAL)
5656   {
5657     if (move_pattern & old_move_dir)
5658       MovDir[x][y] = back_dir;
5659     else if (move_pattern == MV_HORIZONTAL)
5660       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5661     else if (move_pattern == MV_VERTICAL)
5662       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5663
5664     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5665   }
5666   else if (move_pattern & MV_ANY_DIRECTION)
5667   {
5668     MovDir[x][y] = move_pattern;
5669     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5670   }
5671   else if (move_pattern & MV_WIND_DIRECTION)
5672   {
5673     MovDir[x][y] = game.wind_direction;
5674     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5675   }
5676   else if (move_pattern == MV_ALONG_LEFT_SIDE)
5677   {
5678     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5679       MovDir[x][y] = left_dir;
5680     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5681       MovDir[x][y] = right_dir;
5682
5683     if (MovDir[x][y] != old_move_dir)
5684       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5685   }
5686   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5687   {
5688     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5689       MovDir[x][y] = right_dir;
5690     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5691       MovDir[x][y] = left_dir;
5692
5693     if (MovDir[x][y] != old_move_dir)
5694       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5695   }
5696   else if (move_pattern == MV_TOWARDS_PLAYER ||
5697            move_pattern == MV_AWAY_FROM_PLAYER)
5698   {
5699     int attr_x = -1, attr_y = -1;
5700     int newx, newy;
5701     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5702
5703     if (AllPlayersGone)
5704     {
5705       attr_x = ExitX;
5706       attr_y = ExitY;
5707     }
5708     else
5709     {
5710       int i;
5711
5712       for (i = 0; i < MAX_PLAYERS; i++)
5713       {
5714         struct PlayerInfo *player = &stored_player[i];
5715         int jx = player->jx, jy = player->jy;
5716
5717         if (!player->active)
5718           continue;
5719
5720         if (attr_x == -1 ||
5721             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5722         {
5723           attr_x = jx;
5724           attr_y = jy;
5725         }
5726       }
5727     }
5728
5729     MovDir[x][y] = MV_NONE;
5730     if (attr_x < x)
5731       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5732     else if (attr_x > x)
5733       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5734     if (attr_y < y)
5735       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5736     else if (attr_y > y)
5737       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5738
5739     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5740
5741     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5742     {
5743       boolean first_horiz = RND(2);
5744       int new_move_dir = MovDir[x][y];
5745
5746       if (element_info[element].move_stepsize == 0)     /* "not moving" */
5747       {
5748         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5749         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5750
5751         return;
5752       }
5753
5754       MovDir[x][y] =
5755         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5756       Moving2Blocked(x, y, &newx, &newy);
5757
5758       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5759         return;
5760
5761       MovDir[x][y] =
5762         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5763       Moving2Blocked(x, y, &newx, &newy);
5764
5765       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5766         return;
5767
5768       MovDir[x][y] = old_move_dir;
5769     }
5770   }
5771   else if (move_pattern == MV_WHEN_PUSHED ||
5772            move_pattern == MV_WHEN_DROPPED)
5773   {
5774     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5775       MovDir[x][y] = MV_NONE;
5776
5777     MovDelay[x][y] = 0;
5778   }
5779   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5780   {
5781     static int test_xy[7][2] =
5782     {
5783       { 0, -1 },
5784       { -1, 0 },
5785       { +1, 0 },
5786       { 0, +1 },
5787       { 0, -1 },
5788       { -1, 0 },
5789       { +1, 0 },
5790     };
5791     static int test_dir[7] =
5792     {
5793       MV_UP,
5794       MV_LEFT,
5795       MV_RIGHT,
5796       MV_DOWN,
5797       MV_UP,
5798       MV_LEFT,
5799       MV_RIGHT,
5800     };
5801     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5802     int move_preference = -1000000;     /* start with very low preference */
5803     int new_move_dir = MV_NONE;
5804     int start_test = RND(4);
5805     int i;
5806
5807     for (i = 0; i < NUM_DIRECTIONS; i++)
5808     {
5809       int move_dir = test_dir[start_test + i];
5810       int move_dir_preference;
5811
5812       xx = x + test_xy[start_test + i][0];
5813       yy = y + test_xy[start_test + i][1];
5814
5815       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5816           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5817       {
5818         new_move_dir = move_dir;
5819
5820         break;
5821       }
5822
5823       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5824         continue;
5825
5826       move_dir_preference = -1 * RunnerVisit[xx][yy];
5827       if (hunter_mode && PlayerVisit[xx][yy] > 0)
5828         move_dir_preference = PlayerVisit[xx][yy];
5829
5830       if (move_dir_preference > move_preference)
5831       {
5832         /* prefer field that has not been visited for the longest time */
5833         move_preference = move_dir_preference;
5834         new_move_dir = move_dir;
5835       }
5836       else if (move_dir_preference == move_preference &&
5837                move_dir == old_move_dir)
5838       {
5839         /* prefer last direction when all directions are preferred equally */
5840         move_preference = move_dir_preference;
5841         new_move_dir = move_dir;
5842       }
5843     }
5844
5845     MovDir[x][y] = new_move_dir;
5846     if (old_move_dir != new_move_dir)
5847       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5848   }
5849 }
5850
5851 static void TurnRound(int x, int y)
5852 {
5853   int direction = MovDir[x][y];
5854
5855   TurnRoundExt(x, y);
5856
5857   GfxDir[x][y] = MovDir[x][y];
5858
5859   if (direction != MovDir[x][y])
5860     GfxFrame[x][y] = 0;
5861
5862   if (MovDelay[x][y])
5863     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5864
5865   ResetGfxFrame(x, y, FALSE);
5866 }
5867
5868 static boolean JustBeingPushed(int x, int y)
5869 {
5870   int i;
5871
5872   for (i = 0; i < MAX_PLAYERS; i++)
5873   {
5874     struct PlayerInfo *player = &stored_player[i];
5875
5876     if (player->active && player->is_pushing && player->MovPos)
5877     {
5878       int next_jx = player->jx + (player->jx - player->last_jx);
5879       int next_jy = player->jy + (player->jy - player->last_jy);
5880
5881       if (x == next_jx && y == next_jy)
5882         return TRUE;
5883     }
5884   }
5885
5886   return FALSE;
5887 }
5888
5889 void StartMoving(int x, int y)
5890 {
5891   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
5892   int element = Feld[x][y];
5893
5894   if (Stop[x][y])
5895     return;
5896
5897   if (MovDelay[x][y] == 0)
5898     GfxAction[x][y] = ACTION_DEFAULT;
5899
5900   if (CAN_FALL(element) && y < lev_fieldy - 1)
5901   {
5902     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5903         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5904       if (JustBeingPushed(x, y))
5905         return;
5906
5907     if (element == EL_QUICKSAND_FULL)
5908     {
5909       if (IS_FREE(x, y + 1))
5910       {
5911         InitMovingField(x, y, MV_DOWN);
5912         started_moving = TRUE;
5913
5914         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5915 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5916         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5917           Store[x][y] = EL_ROCK;
5918 #else
5919         Store[x][y] = EL_ROCK;
5920 #endif
5921
5922         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5923       }
5924       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5925       {
5926         if (!MovDelay[x][y])
5927           MovDelay[x][y] = TILEY + 1;
5928
5929         if (MovDelay[x][y])
5930         {
5931           MovDelay[x][y]--;
5932           if (MovDelay[x][y])
5933             return;
5934         }
5935
5936         Feld[x][y] = EL_QUICKSAND_EMPTY;
5937         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5938         Store[x][y + 1] = Store[x][y];
5939         Store[x][y] = 0;
5940
5941         PlayLevelSoundAction(x, y, ACTION_FILLING);
5942       }
5943     }
5944     else if (element == EL_QUICKSAND_FAST_FULL)
5945     {
5946       if (IS_FREE(x, y + 1))
5947       {
5948         InitMovingField(x, y, MV_DOWN);
5949         started_moving = TRUE;
5950
5951         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
5952 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5953         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5954           Store[x][y] = EL_ROCK;
5955 #else
5956         Store[x][y] = EL_ROCK;
5957 #endif
5958
5959         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5960       }
5961       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5962       {
5963         if (!MovDelay[x][y])
5964           MovDelay[x][y] = TILEY + 1;
5965
5966         if (MovDelay[x][y])
5967         {
5968           MovDelay[x][y]--;
5969           if (MovDelay[x][y])
5970             return;
5971         }
5972
5973         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
5974         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
5975         Store[x][y + 1] = Store[x][y];
5976         Store[x][y] = 0;
5977
5978         PlayLevelSoundAction(x, y, ACTION_FILLING);
5979       }
5980     }
5981     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5982              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5983     {
5984       InitMovingField(x, y, MV_DOWN);
5985       started_moving = TRUE;
5986
5987       Feld[x][y] = EL_QUICKSAND_FILLING;
5988       Store[x][y] = element;
5989
5990       PlayLevelSoundAction(x, y, ACTION_FILLING);
5991     }
5992     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5993              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5994     {
5995       InitMovingField(x, y, MV_DOWN);
5996       started_moving = TRUE;
5997
5998       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
5999       Store[x][y] = element;
6000
6001       PlayLevelSoundAction(x, y, ACTION_FILLING);
6002     }
6003     else if (element == EL_MAGIC_WALL_FULL)
6004     {
6005       if (IS_FREE(x, y + 1))
6006       {
6007         InitMovingField(x, y, MV_DOWN);
6008         started_moving = TRUE;
6009
6010         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6011         Store[x][y] = EL_CHANGED(Store[x][y]);
6012       }
6013       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6014       {
6015         if (!MovDelay[x][y])
6016           MovDelay[x][y] = TILEY/4 + 1;
6017
6018         if (MovDelay[x][y])
6019         {
6020           MovDelay[x][y]--;
6021           if (MovDelay[x][y])
6022             return;
6023         }
6024
6025         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6026         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6027         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6028         Store[x][y] = 0;
6029       }
6030     }
6031     else if (element == EL_BD_MAGIC_WALL_FULL)
6032     {
6033       if (IS_FREE(x, y + 1))
6034       {
6035         InitMovingField(x, y, MV_DOWN);
6036         started_moving = TRUE;
6037
6038         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6039         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6040       }
6041       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6042       {
6043         if (!MovDelay[x][y])
6044           MovDelay[x][y] = TILEY/4 + 1;
6045
6046         if (MovDelay[x][y])
6047         {
6048           MovDelay[x][y]--;
6049           if (MovDelay[x][y])
6050             return;
6051         }
6052
6053         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6054         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6055         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6056         Store[x][y] = 0;
6057       }
6058     }
6059     else if (element == EL_DC_MAGIC_WALL_FULL)
6060     {
6061       if (IS_FREE(x, y + 1))
6062       {
6063         InitMovingField(x, y, MV_DOWN);
6064         started_moving = TRUE;
6065
6066         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6067         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6068       }
6069       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6070       {
6071         if (!MovDelay[x][y])
6072           MovDelay[x][y] = TILEY/4 + 1;
6073
6074         if (MovDelay[x][y])
6075         {
6076           MovDelay[x][y]--;
6077           if (MovDelay[x][y])
6078             return;
6079         }
6080
6081         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6082         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6083         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6084         Store[x][y] = 0;
6085       }
6086     }
6087     else if ((CAN_PASS_MAGIC_WALL(element) &&
6088               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6089                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6090              (CAN_PASS_DC_MAGIC_WALL(element) &&
6091               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6092
6093     {
6094       InitMovingField(x, y, MV_DOWN);
6095       started_moving = TRUE;
6096
6097       Feld[x][y] =
6098         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6099          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6100          EL_DC_MAGIC_WALL_FILLING);
6101       Store[x][y] = element;
6102     }
6103     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6104     {
6105       SplashAcid(x, y + 1);
6106
6107       InitMovingField(x, y, MV_DOWN);
6108       started_moving = TRUE;
6109
6110       Store[x][y] = EL_ACID;
6111     }
6112     else if (
6113 #if USE_FIX_IMPACT_COLLISION
6114              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6115               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6116 #else
6117              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6118               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6119 #endif
6120              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6121               CAN_FALL(element) && WasJustFalling[x][y] &&
6122               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6123
6124              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6125               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6126               (Feld[x][y + 1] == EL_BLOCKED)))
6127     {
6128       /* this is needed for a special case not covered by calling "Impact()"
6129          from "ContinueMoving()": if an element moves to a tile directly below
6130          another element which was just falling on that tile (which was empty
6131          in the previous frame), the falling element above would just stop
6132          instead of smashing the element below (in previous version, the above
6133          element was just checked for "moving" instead of "falling", resulting
6134          in incorrect smashes caused by horizontal movement of the above
6135          element; also, the case of the player being the element to smash was
6136          simply not covered here... :-/ ) */
6137
6138       CheckCollision[x][y] = 0;
6139       CheckImpact[x][y] = 0;
6140
6141       Impact(x, y);
6142     }
6143     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6144     {
6145       if (MovDir[x][y] == MV_NONE)
6146       {
6147         InitMovingField(x, y, MV_DOWN);
6148         started_moving = TRUE;
6149       }
6150     }
6151     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6152     {
6153       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6154         MovDir[x][y] = MV_DOWN;
6155
6156       InitMovingField(x, y, MV_DOWN);
6157       started_moving = TRUE;
6158     }
6159     else if (element == EL_AMOEBA_DROP)
6160     {
6161       Feld[x][y] = EL_AMOEBA_GROWING;
6162       Store[x][y] = EL_AMOEBA_WET;
6163     }
6164     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6165               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6166              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6167              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6168     {
6169       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6170                                 (IS_FREE(x - 1, y + 1) ||
6171                                  Feld[x - 1][y + 1] == EL_ACID));
6172       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6173                                 (IS_FREE(x + 1, y + 1) ||
6174                                  Feld[x + 1][y + 1] == EL_ACID));
6175       boolean can_fall_any  = (can_fall_left || can_fall_right);
6176       boolean can_fall_both = (can_fall_left && can_fall_right);
6177       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6178
6179 #if USE_NEW_ALL_SLIPPERY
6180       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6181       {
6182         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6183           can_fall_right = FALSE;
6184         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6185           can_fall_left = FALSE;
6186         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6187           can_fall_right = FALSE;
6188         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6189           can_fall_left = FALSE;
6190
6191         can_fall_any  = (can_fall_left || can_fall_right);
6192         can_fall_both = FALSE;
6193       }
6194 #else
6195       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6196       {
6197         if (slippery_type == SLIPPERY_ONLY_LEFT)
6198           can_fall_right = FALSE;
6199         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6200           can_fall_left = FALSE;
6201         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6202           can_fall_right = FALSE;
6203         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6204           can_fall_left = FALSE;
6205
6206         can_fall_any  = (can_fall_left || can_fall_right);
6207         can_fall_both = (can_fall_left && can_fall_right);
6208       }
6209 #endif
6210
6211 #if USE_NEW_ALL_SLIPPERY
6212 #else
6213 #if USE_NEW_SP_SLIPPERY
6214       /* !!! better use the same properties as for custom elements here !!! */
6215       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6216                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6217       {
6218         can_fall_right = FALSE;         /* slip down on left side */
6219         can_fall_both = FALSE;
6220       }
6221 #endif
6222 #endif
6223
6224 #if USE_NEW_ALL_SLIPPERY
6225       if (can_fall_both)
6226       {
6227         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6228           can_fall_right = FALSE;       /* slip down on left side */
6229         else
6230           can_fall_left = !(can_fall_right = RND(2));
6231
6232         can_fall_both = FALSE;
6233       }
6234 #else
6235       if (can_fall_both)
6236       {
6237         if (game.emulation == EMU_BOULDERDASH ||
6238             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6239           can_fall_right = FALSE;       /* slip down on left side */
6240         else
6241           can_fall_left = !(can_fall_right = RND(2));
6242
6243         can_fall_both = FALSE;
6244       }
6245 #endif
6246
6247       if (can_fall_any)
6248       {
6249         /* if not determined otherwise, prefer left side for slipping down */
6250         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6251         started_moving = TRUE;
6252       }
6253     }
6254 #if 0
6255     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6256 #else
6257     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6258 #endif
6259     {
6260       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6261       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6262       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6263       int belt_dir = game.belt_dir[belt_nr];
6264
6265       if ((belt_dir == MV_LEFT  && left_is_free) ||
6266           (belt_dir == MV_RIGHT && right_is_free))
6267       {
6268         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6269
6270         InitMovingField(x, y, belt_dir);
6271         started_moving = TRUE;
6272
6273         Pushed[x][y] = TRUE;
6274         Pushed[nextx][y] = TRUE;
6275
6276         GfxAction[x][y] = ACTION_DEFAULT;
6277       }
6278       else
6279       {
6280         MovDir[x][y] = 0;       /* if element was moving, stop it */
6281       }
6282     }
6283   }
6284
6285   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6286 #if 0
6287   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6288 #else
6289   if (CAN_MOVE(element) && !started_moving)
6290 #endif
6291   {
6292     int move_pattern = element_info[element].move_pattern;
6293     int newx, newy;
6294
6295 #if 0
6296 #if DEBUG
6297     if (MovDir[x][y] == MV_NONE)
6298     {
6299       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6300              x, y, element, element_info[element].token_name);
6301       printf("StartMoving(): This should never happen!\n");
6302     }
6303 #endif
6304 #endif
6305
6306     Moving2Blocked(x, y, &newx, &newy);
6307
6308     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6309       return;
6310
6311     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6312         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6313     {
6314       WasJustMoving[x][y] = 0;
6315       CheckCollision[x][y] = 0;
6316
6317       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6318
6319       if (Feld[x][y] != element)        /* element has changed */
6320         return;
6321     }
6322
6323     if (!MovDelay[x][y])        /* start new movement phase */
6324     {
6325       /* all objects that can change their move direction after each step
6326          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6327
6328       if (element != EL_YAMYAM &&
6329           element != EL_DARK_YAMYAM &&
6330           element != EL_PACMAN &&
6331           !(move_pattern & MV_ANY_DIRECTION) &&
6332           move_pattern != MV_TURNING_LEFT &&
6333           move_pattern != MV_TURNING_RIGHT &&
6334           move_pattern != MV_TURNING_LEFT_RIGHT &&
6335           move_pattern != MV_TURNING_RIGHT_LEFT &&
6336           move_pattern != MV_TURNING_RANDOM)
6337       {
6338         TurnRound(x, y);
6339
6340         if (MovDelay[x][y] && (element == EL_BUG ||
6341                                element == EL_SPACESHIP ||
6342                                element == EL_SP_SNIKSNAK ||
6343                                element == EL_SP_ELECTRON ||
6344                                element == EL_MOLE))
6345           DrawLevelField(x, y);
6346       }
6347     }
6348
6349     if (MovDelay[x][y])         /* wait some time before next movement */
6350     {
6351       MovDelay[x][y]--;
6352
6353       if (element == EL_ROBOT ||
6354           element == EL_YAMYAM ||
6355           element == EL_DARK_YAMYAM)
6356       {
6357         DrawLevelElementAnimationIfNeeded(x, y, element);
6358         PlayLevelSoundAction(x, y, ACTION_WAITING);
6359       }
6360       else if (element == EL_SP_ELECTRON)
6361         DrawLevelElementAnimationIfNeeded(x, y, element);
6362       else if (element == EL_DRAGON)
6363       {
6364         int i;
6365         int dir = MovDir[x][y];
6366         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6367         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6368         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6369                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6370                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6371                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6372         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6373
6374         GfxAction[x][y] = ACTION_ATTACKING;
6375
6376         if (IS_PLAYER(x, y))
6377           DrawPlayerField(x, y);
6378         else
6379           DrawLevelField(x, y);
6380
6381         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6382
6383         for (i = 1; i <= 3; i++)
6384         {
6385           int xx = x + i * dx;
6386           int yy = y + i * dy;
6387           int sx = SCREENX(xx);
6388           int sy = SCREENY(yy);
6389           int flame_graphic = graphic + (i - 1);
6390
6391           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6392             break;
6393
6394           if (MovDelay[x][y])
6395           {
6396             int flamed = MovingOrBlocked2Element(xx, yy);
6397
6398             /* !!! */
6399 #if 0
6400             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6401               Bang(xx, yy);
6402             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6403               RemoveMovingField(xx, yy);
6404             else
6405               RemoveField(xx, yy);
6406 #else
6407             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6408               Bang(xx, yy);
6409             else
6410               RemoveMovingField(xx, yy);
6411 #endif
6412
6413             ChangeDelay[xx][yy] = 0;
6414
6415             Feld[xx][yy] = EL_FLAMES;
6416
6417             if (IN_SCR_FIELD(sx, sy))
6418             {
6419               DrawLevelFieldCrumbledSand(xx, yy);
6420               DrawGraphic(sx, sy, flame_graphic, frame);
6421             }
6422           }
6423           else
6424           {
6425             if (Feld[xx][yy] == EL_FLAMES)
6426               Feld[xx][yy] = EL_EMPTY;
6427             DrawLevelField(xx, yy);
6428           }
6429         }
6430       }
6431
6432       if (MovDelay[x][y])       /* element still has to wait some time */
6433       {
6434         PlayLevelSoundAction(x, y, ACTION_WAITING);
6435
6436         return;
6437       }
6438     }
6439
6440     /* now make next step */
6441
6442     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6443
6444     if (DONT_COLLIDE_WITH(element) &&
6445         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6446         !PLAYER_ENEMY_PROTECTED(newx, newy))
6447     {
6448       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6449
6450       return;
6451     }
6452
6453     else if (CAN_MOVE_INTO_ACID(element) &&
6454              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6455              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6456              (MovDir[x][y] == MV_DOWN ||
6457               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6458     {
6459       SplashAcid(newx, newy);
6460       Store[x][y] = EL_ACID;
6461     }
6462     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6463     {
6464       if (Feld[newx][newy] == EL_EXIT_OPEN ||
6465           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6466           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6467           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6468       {
6469         RemoveField(x, y);
6470         DrawLevelField(x, y);
6471
6472         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6473         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6474           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6475
6476         local_player->friends_still_needed--;
6477         if (!local_player->friends_still_needed &&
6478             !local_player->GameOver && AllPlayersGone)
6479           PlayerWins(local_player);
6480
6481         return;
6482       }
6483       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6484       {
6485         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6486           DrawLevelField(newx, newy);
6487         else
6488           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6489       }
6490       else if (!IS_FREE(newx, newy))
6491       {
6492         GfxAction[x][y] = ACTION_WAITING;
6493
6494         if (IS_PLAYER(x, y))
6495           DrawPlayerField(x, y);
6496         else
6497           DrawLevelField(x, y);
6498
6499         return;
6500       }
6501     }
6502     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6503     {
6504       if (IS_FOOD_PIG(Feld[newx][newy]))
6505       {
6506         if (IS_MOVING(newx, newy))
6507           RemoveMovingField(newx, newy);
6508         else
6509         {
6510           Feld[newx][newy] = EL_EMPTY;
6511           DrawLevelField(newx, newy);
6512         }
6513
6514         PlayLevelSound(x, y, SND_PIG_DIGGING);
6515       }
6516       else if (!IS_FREE(newx, newy))
6517       {
6518         if (IS_PLAYER(x, y))
6519           DrawPlayerField(x, y);
6520         else
6521           DrawLevelField(x, y);
6522
6523         return;
6524       }
6525     }
6526     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6527     {
6528       if (Store[x][y] != EL_EMPTY)
6529       {
6530         boolean can_clone = FALSE;
6531         int xx, yy;
6532
6533         /* check if element to clone is still there */
6534         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6535         {
6536           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6537           {
6538             can_clone = TRUE;
6539
6540             break;
6541           }
6542         }
6543
6544         /* cannot clone or target field not free anymore -- do not clone */
6545         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6546           Store[x][y] = EL_EMPTY;
6547       }
6548
6549       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6550       {
6551         if (IS_MV_DIAGONAL(MovDir[x][y]))
6552         {
6553           int diagonal_move_dir = MovDir[x][y];
6554           int stored = Store[x][y];
6555           int change_delay = 8;
6556           int graphic;
6557
6558           /* android is moving diagonally */
6559
6560           CreateField(x, y, EL_DIAGONAL_SHRINKING);
6561
6562           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6563           GfxElement[x][y] = EL_EMC_ANDROID;
6564           GfxAction[x][y] = ACTION_SHRINKING;
6565           GfxDir[x][y] = diagonal_move_dir;
6566           ChangeDelay[x][y] = change_delay;
6567
6568           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6569                                    GfxDir[x][y]);
6570
6571           DrawLevelGraphicAnimation(x, y, graphic);
6572           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6573
6574           if (Feld[newx][newy] == EL_ACID)
6575           {
6576             SplashAcid(newx, newy);
6577
6578             return;
6579           }
6580
6581           CreateField(newx, newy, EL_DIAGONAL_GROWING);
6582
6583           Store[newx][newy] = EL_EMC_ANDROID;
6584           GfxElement[newx][newy] = EL_EMC_ANDROID;
6585           GfxAction[newx][newy] = ACTION_GROWING;
6586           GfxDir[newx][newy] = diagonal_move_dir;
6587           ChangeDelay[newx][newy] = change_delay;
6588
6589           graphic = el_act_dir2img(GfxElement[newx][newy],
6590                                    GfxAction[newx][newy], GfxDir[newx][newy]);
6591
6592           DrawLevelGraphicAnimation(newx, newy, graphic);
6593           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6594
6595           return;
6596         }
6597         else
6598         {
6599           Feld[newx][newy] = EL_EMPTY;
6600           DrawLevelField(newx, newy);
6601
6602           PlayLevelSoundAction(x, y, ACTION_DIGGING);
6603         }
6604       }
6605       else if (!IS_FREE(newx, newy))
6606       {
6607 #if 0
6608         if (IS_PLAYER(x, y))
6609           DrawPlayerField(x, y);
6610         else
6611           DrawLevelField(x, y);
6612 #endif
6613
6614         return;
6615       }
6616     }
6617     else if (IS_CUSTOM_ELEMENT(element) &&
6618              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6619     {
6620       int new_element = Feld[newx][newy];
6621
6622       if (!IS_FREE(newx, newy))
6623       {
6624         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6625                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6626                       ACTION_BREAKING);
6627
6628         /* no element can dig solid indestructible elements */
6629         if (IS_INDESTRUCTIBLE(new_element) &&
6630             !IS_DIGGABLE(new_element) &&
6631             !IS_COLLECTIBLE(new_element))
6632           return;
6633
6634         if (AmoebaNr[newx][newy] &&
6635             (new_element == EL_AMOEBA_FULL ||
6636              new_element == EL_BD_AMOEBA ||
6637              new_element == EL_AMOEBA_GROWING))
6638         {
6639           AmoebaCnt[AmoebaNr[newx][newy]]--;
6640           AmoebaCnt2[AmoebaNr[newx][newy]]--;
6641         }
6642
6643         if (IS_MOVING(newx, newy))
6644           RemoveMovingField(newx, newy);
6645         else
6646         {
6647           RemoveField(newx, newy);
6648           DrawLevelField(newx, newy);
6649         }
6650
6651         /* if digged element was about to explode, prevent the explosion */
6652         ExplodeField[newx][newy] = EX_TYPE_NONE;
6653
6654         PlayLevelSoundAction(x, y, action);
6655       }
6656
6657       Store[newx][newy] = EL_EMPTY;
6658 #if 1
6659       /* this makes it possible to leave the removed element again */
6660       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6661         Store[newx][newy] = new_element;
6662 #else
6663       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6664       {
6665         int move_leave_element = element_info[element].move_leave_element;
6666
6667         /* this makes it possible to leave the removed element again */
6668         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6669                              new_element : move_leave_element);
6670       }
6671 #endif
6672
6673       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6674       {
6675         RunnerVisit[x][y] = FrameCounter;
6676         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6677       }
6678     }
6679     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6680     {
6681       if (!IS_FREE(newx, newy))
6682       {
6683         if (IS_PLAYER(x, y))
6684           DrawPlayerField(x, y);
6685         else
6686           DrawLevelField(x, y);
6687
6688         return;
6689       }
6690       else
6691       {
6692         boolean wanna_flame = !RND(10);
6693         int dx = newx - x, dy = newy - y;
6694         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6695         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6696         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6697                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6698         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6699                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6700
6701         if ((wanna_flame ||
6702              IS_CLASSIC_ENEMY(element1) ||
6703              IS_CLASSIC_ENEMY(element2)) &&
6704             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6705             element1 != EL_FLAMES && element2 != EL_FLAMES)
6706         {
6707           ResetGfxAnimation(x, y);
6708           GfxAction[x][y] = ACTION_ATTACKING;
6709
6710           if (IS_PLAYER(x, y))
6711             DrawPlayerField(x, y);
6712           else
6713             DrawLevelField(x, y);
6714
6715           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6716
6717           MovDelay[x][y] = 50;
6718
6719           /* !!! */
6720 #if 0
6721           RemoveField(newx, newy);
6722 #endif
6723           Feld[newx][newy] = EL_FLAMES;
6724           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6725           {
6726 #if 0
6727             RemoveField(newx1, newy1);
6728 #endif
6729             Feld[newx1][newy1] = EL_FLAMES;
6730           }
6731           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6732           {
6733 #if 0
6734             RemoveField(newx2, newy2);
6735 #endif
6736             Feld[newx2][newy2] = EL_FLAMES;
6737           }
6738
6739           return;
6740         }
6741       }
6742     }
6743     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6744              Feld[newx][newy] == EL_DIAMOND)
6745     {
6746       if (IS_MOVING(newx, newy))
6747         RemoveMovingField(newx, newy);
6748       else
6749       {
6750         Feld[newx][newy] = EL_EMPTY;
6751         DrawLevelField(newx, newy);
6752       }
6753
6754       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6755     }
6756     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6757              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6758     {
6759       if (AmoebaNr[newx][newy])
6760       {
6761         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6762         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6763             Feld[newx][newy] == EL_BD_AMOEBA)
6764           AmoebaCnt[AmoebaNr[newx][newy]]--;
6765       }
6766
6767 #if 0
6768       /* !!! test !!! */
6769       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6770       {
6771         RemoveMovingField(newx, newy);
6772       }
6773 #else
6774       if (IS_MOVING(newx, newy))
6775       {
6776         RemoveMovingField(newx, newy);
6777       }
6778 #endif
6779       else
6780       {
6781         Feld[newx][newy] = EL_EMPTY;
6782         DrawLevelField(newx, newy);
6783       }
6784
6785       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6786     }
6787     else if ((element == EL_PACMAN || element == EL_MOLE)
6788              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6789     {
6790       if (AmoebaNr[newx][newy])
6791       {
6792         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6793         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6794             Feld[newx][newy] == EL_BD_AMOEBA)
6795           AmoebaCnt[AmoebaNr[newx][newy]]--;
6796       }
6797
6798       if (element == EL_MOLE)
6799       {
6800         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6801         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6802
6803         ResetGfxAnimation(x, y);
6804         GfxAction[x][y] = ACTION_DIGGING;
6805         DrawLevelField(x, y);
6806
6807         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6808
6809         return;                         /* wait for shrinking amoeba */
6810       }
6811       else      /* element == EL_PACMAN */
6812       {
6813         Feld[newx][newy] = EL_EMPTY;
6814         DrawLevelField(newx, newy);
6815         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6816       }
6817     }
6818     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6819              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6820               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6821     {
6822       /* wait for shrinking amoeba to completely disappear */
6823       return;
6824     }
6825     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6826     {
6827       /* object was running against a wall */
6828
6829       TurnRound(x, y);
6830
6831 #if 0
6832       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6833       if (move_pattern & MV_ANY_DIRECTION &&
6834           move_pattern == MovDir[x][y])
6835       {
6836         int blocking_element =
6837           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6838
6839         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6840                                  MovDir[x][y]);
6841
6842         element = Feld[x][y];   /* element might have changed */
6843       }
6844 #endif
6845
6846       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
6847         DrawLevelElementAnimation(x, y, element);
6848
6849       if (DONT_TOUCH(element))
6850         TestIfBadThingTouchesPlayer(x, y);
6851
6852       return;
6853     }
6854
6855     InitMovingField(x, y, MovDir[x][y]);
6856
6857     PlayLevelSoundAction(x, y, ACTION_MOVING);
6858   }
6859
6860   if (MovDir[x][y])
6861     ContinueMoving(x, y);
6862 }
6863
6864 void ContinueMoving(int x, int y)
6865 {
6866   int element = Feld[x][y];
6867   struct ElementInfo *ei = &element_info[element];
6868   int direction = MovDir[x][y];
6869   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6870   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
6871   int newx = x + dx, newy = y + dy;
6872   int stored = Store[x][y];
6873   int stored_new = Store[newx][newy];
6874   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
6875   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6876   boolean last_line = (newy == lev_fieldy - 1);
6877
6878   MovPos[x][y] += getElementMoveStepsize(x, y);
6879
6880   if (pushed_by_player) /* special case: moving object pushed by player */
6881     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6882
6883   if (ABS(MovPos[x][y]) < TILEX)
6884   {
6885     DrawLevelField(x, y);
6886
6887     return;     /* element is still moving */
6888   }
6889
6890   /* element reached destination field */
6891
6892   Feld[x][y] = EL_EMPTY;
6893   Feld[newx][newy] = element;
6894   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6895
6896   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6897   {
6898     element = Feld[newx][newy] = EL_ACID;
6899   }
6900   else if (element == EL_MOLE)
6901   {
6902     Feld[x][y] = EL_SAND;
6903
6904     DrawLevelFieldCrumbledSandNeighbours(x, y);
6905   }
6906   else if (element == EL_QUICKSAND_FILLING)
6907   {
6908     element = Feld[newx][newy] = get_next_element(element);
6909     Store[newx][newy] = Store[x][y];
6910   }
6911   else if (element == EL_QUICKSAND_EMPTYING)
6912   {
6913     Feld[x][y] = get_next_element(element);
6914     element = Feld[newx][newy] = Store[x][y];
6915   }
6916   else if (element == EL_QUICKSAND_FAST_FILLING)
6917   {
6918     element = Feld[newx][newy] = get_next_element(element);
6919     Store[newx][newy] = Store[x][y];
6920   }
6921   else if (element == EL_QUICKSAND_FAST_EMPTYING)
6922   {
6923     Feld[x][y] = get_next_element(element);
6924     element = Feld[newx][newy] = Store[x][y];
6925   }
6926   else if (element == EL_MAGIC_WALL_FILLING)
6927   {
6928     element = Feld[newx][newy] = get_next_element(element);
6929     if (!game.magic_wall_active)
6930       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6931     Store[newx][newy] = Store[x][y];
6932   }
6933   else if (element == EL_MAGIC_WALL_EMPTYING)
6934   {
6935     Feld[x][y] = get_next_element(element);
6936     if (!game.magic_wall_active)
6937       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6938     element = Feld[newx][newy] = Store[x][y];
6939
6940 #if USE_NEW_CUSTOM_VALUE
6941     InitField(newx, newy, FALSE);
6942 #endif
6943   }
6944   else if (element == EL_BD_MAGIC_WALL_FILLING)
6945   {
6946     element = Feld[newx][newy] = get_next_element(element);
6947     if (!game.magic_wall_active)
6948       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6949     Store[newx][newy] = Store[x][y];
6950   }
6951   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6952   {
6953     Feld[x][y] = get_next_element(element);
6954     if (!game.magic_wall_active)
6955       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6956     element = Feld[newx][newy] = Store[x][y];
6957
6958 #if USE_NEW_CUSTOM_VALUE
6959     InitField(newx, newy, FALSE);
6960 #endif
6961   }
6962   else if (element == EL_DC_MAGIC_WALL_FILLING)
6963   {
6964     element = Feld[newx][newy] = get_next_element(element);
6965     if (!game.magic_wall_active)
6966       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
6967     Store[newx][newy] = Store[x][y];
6968   }
6969   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
6970   {
6971     Feld[x][y] = get_next_element(element);
6972     if (!game.magic_wall_active)
6973       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
6974     element = Feld[newx][newy] = Store[x][y];
6975
6976 #if USE_NEW_CUSTOM_VALUE
6977     InitField(newx, newy, FALSE);
6978 #endif
6979   }
6980   else if (element == EL_AMOEBA_DROPPING)
6981   {
6982     Feld[x][y] = get_next_element(element);
6983     element = Feld[newx][newy] = Store[x][y];
6984   }
6985   else if (element == EL_SOKOBAN_OBJECT)
6986   {
6987     if (Back[x][y])
6988       Feld[x][y] = Back[x][y];
6989
6990     if (Back[newx][newy])
6991       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6992
6993     Back[x][y] = Back[newx][newy] = 0;
6994   }
6995
6996   Store[x][y] = EL_EMPTY;
6997   MovPos[x][y] = 0;
6998   MovDir[x][y] = 0;
6999   MovDelay[x][y] = 0;
7000
7001   MovDelay[newx][newy] = 0;
7002
7003   if (CAN_CHANGE_OR_HAS_ACTION(element))
7004   {
7005     /* copy element change control values to new field */
7006     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7007     ChangePage[newx][newy]  = ChangePage[x][y];
7008     ChangeCount[newx][newy] = ChangeCount[x][y];
7009     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7010   }
7011
7012 #if USE_NEW_CUSTOM_VALUE
7013     CustomValue[newx][newy] = CustomValue[x][y];
7014 #endif
7015
7016   ChangeDelay[x][y] = 0;
7017   ChangePage[x][y] = -1;
7018   ChangeCount[x][y] = 0;
7019   ChangeEvent[x][y] = -1;
7020
7021 #if USE_NEW_CUSTOM_VALUE
7022   CustomValue[x][y] = 0;
7023 #endif
7024
7025   /* copy animation control values to new field */
7026   GfxFrame[newx][newy]  = GfxFrame[x][y];
7027   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7028   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7029   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7030
7031   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7032
7033   /* some elements can leave other elements behind after moving */
7034 #if 1
7035   if (ei->move_leave_element != EL_EMPTY &&
7036       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7037       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7038 #else
7039   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7040       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7041       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7042 #endif
7043   {
7044     int move_leave_element = ei->move_leave_element;
7045
7046 #if 1
7047 #if 1
7048     /* this makes it possible to leave the removed element again */
7049     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7050       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7051 #else
7052     /* this makes it possible to leave the removed element again */
7053     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7054       move_leave_element = stored;
7055 #endif
7056 #else
7057     /* this makes it possible to leave the removed element again */
7058     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7059         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7060       move_leave_element = stored;
7061 #endif
7062
7063     Feld[x][y] = move_leave_element;
7064
7065     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7066       MovDir[x][y] = direction;
7067
7068     InitField(x, y, FALSE);
7069
7070     if (GFX_CRUMBLED(Feld[x][y]))
7071       DrawLevelFieldCrumbledSandNeighbours(x, y);
7072
7073     if (ELEM_IS_PLAYER(move_leave_element))
7074       RelocatePlayer(x, y, move_leave_element);
7075   }
7076
7077   /* do this after checking for left-behind element */
7078   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7079
7080   if (!CAN_MOVE(element) ||
7081       (CAN_FALL(element) && direction == MV_DOWN &&
7082        (element == EL_SPRING ||
7083         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7084         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7085     GfxDir[x][y] = MovDir[newx][newy] = 0;
7086
7087   DrawLevelField(x, y);
7088   DrawLevelField(newx, newy);
7089
7090   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7091
7092   /* prevent pushed element from moving on in pushed direction */
7093   if (pushed_by_player && CAN_MOVE(element) &&
7094       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7095       !(element_info[element].move_pattern & direction))
7096     TurnRound(newx, newy);
7097
7098   /* prevent elements on conveyor belt from moving on in last direction */
7099   if (pushed_by_conveyor && CAN_FALL(element) &&
7100       direction & MV_HORIZONTAL)
7101     MovDir[newx][newy] = 0;
7102
7103   if (!pushed_by_player)
7104   {
7105     int nextx = newx + dx, nexty = newy + dy;
7106     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7107
7108     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7109
7110     if (CAN_FALL(element) && direction == MV_DOWN)
7111       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7112
7113     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7114       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7115
7116 #if USE_FIX_IMPACT_COLLISION
7117     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7118       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7119 #endif
7120   }
7121
7122   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7123   {
7124     TestIfBadThingTouchesPlayer(newx, newy);
7125     TestIfBadThingTouchesFriend(newx, newy);
7126
7127     if (!IS_CUSTOM_ELEMENT(element))
7128       TestIfBadThingTouchesOtherBadThing(newx, newy);
7129   }
7130   else if (element == EL_PENGUIN)
7131     TestIfFriendTouchesBadThing(newx, newy);
7132
7133   /* give the player one last chance (one more frame) to move away */
7134   if (CAN_FALL(element) && direction == MV_DOWN &&
7135       (last_line || (!IS_FREE(x, newy + 1) &&
7136                      (!IS_PLAYER(x, newy + 1) ||
7137                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7138     Impact(x, newy);
7139
7140   if (pushed_by_player && !game.use_change_when_pushing_bug)
7141   {
7142     int push_side = MV_DIR_OPPOSITE(direction);
7143     struct PlayerInfo *player = PLAYERINFO(x, y);
7144
7145     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7146                                player->index_bit, push_side);
7147     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7148                                         player->index_bit, push_side);
7149   }
7150
7151   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7152     MovDelay[newx][newy] = 1;
7153
7154   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7155
7156   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7157
7158 #if 0
7159   if (ChangePage[newx][newy] != -1)             /* delayed change */
7160   {
7161     int page = ChangePage[newx][newy];
7162     struct ElementChangeInfo *change = &ei->change_page[page];
7163
7164     ChangePage[newx][newy] = -1;
7165
7166     if (change->can_change)
7167     {
7168       if (ChangeElement(newx, newy, element, page))
7169       {
7170         if (change->post_change_function)
7171           change->post_change_function(newx, newy);
7172       }
7173     }
7174
7175     if (change->has_action)
7176       ExecuteCustomElementAction(newx, newy, element, page);
7177   }
7178 #endif
7179
7180   TestIfElementHitsCustomElement(newx, newy, direction);
7181   TestIfPlayerTouchesCustomElement(newx, newy);
7182   TestIfElementTouchesCustomElement(newx, newy);
7183
7184   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7185       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7186     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7187                              MV_DIR_OPPOSITE(direction));
7188 }
7189
7190 int AmoebeNachbarNr(int ax, int ay)
7191 {
7192   int i;
7193   int element = Feld[ax][ay];
7194   int group_nr = 0;
7195   static int xy[4][2] =
7196   {
7197     { 0, -1 },
7198     { -1, 0 },
7199     { +1, 0 },
7200     { 0, +1 }
7201   };
7202
7203   for (i = 0; i < NUM_DIRECTIONS; i++)
7204   {
7205     int x = ax + xy[i][0];
7206     int y = ay + xy[i][1];
7207
7208     if (!IN_LEV_FIELD(x, y))
7209       continue;
7210
7211     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7212       group_nr = AmoebaNr[x][y];
7213   }
7214
7215   return group_nr;
7216 }
7217
7218 void AmoebenVereinigen(int ax, int ay)
7219 {
7220   int i, x, y, xx, yy;
7221   int new_group_nr = AmoebaNr[ax][ay];
7222   static int xy[4][2] =
7223   {
7224     { 0, -1 },
7225     { -1, 0 },
7226     { +1, 0 },
7227     { 0, +1 }
7228   };
7229
7230   if (new_group_nr == 0)
7231     return;
7232
7233   for (i = 0; i < NUM_DIRECTIONS; i++)
7234   {
7235     x = ax + xy[i][0];
7236     y = ay + xy[i][1];
7237
7238     if (!IN_LEV_FIELD(x, y))
7239       continue;
7240
7241     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7242          Feld[x][y] == EL_BD_AMOEBA ||
7243          Feld[x][y] == EL_AMOEBA_DEAD) &&
7244         AmoebaNr[x][y] != new_group_nr)
7245     {
7246       int old_group_nr = AmoebaNr[x][y];
7247
7248       if (old_group_nr == 0)
7249         return;
7250
7251       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7252       AmoebaCnt[old_group_nr] = 0;
7253       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7254       AmoebaCnt2[old_group_nr] = 0;
7255
7256       SCAN_PLAYFIELD(xx, yy)
7257       {
7258         if (AmoebaNr[xx][yy] == old_group_nr)
7259           AmoebaNr[xx][yy] = new_group_nr;
7260       }
7261     }
7262   }
7263 }
7264
7265 void AmoebeUmwandeln(int ax, int ay)
7266 {
7267   int i, x, y;
7268
7269   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7270   {
7271     int group_nr = AmoebaNr[ax][ay];
7272
7273 #ifdef DEBUG
7274     if (group_nr == 0)
7275     {
7276       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7277       printf("AmoebeUmwandeln(): This should never happen!\n");
7278       return;
7279     }
7280 #endif
7281
7282     SCAN_PLAYFIELD(x, y)
7283     {
7284       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7285       {
7286         AmoebaNr[x][y] = 0;
7287         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7288       }
7289     }
7290
7291     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7292                             SND_AMOEBA_TURNING_TO_GEM :
7293                             SND_AMOEBA_TURNING_TO_ROCK));
7294     Bang(ax, ay);
7295   }
7296   else
7297   {
7298     static int xy[4][2] =
7299     {
7300       { 0, -1 },
7301       { -1, 0 },
7302       { +1, 0 },
7303       { 0, +1 }
7304     };
7305
7306     for (i = 0; i < NUM_DIRECTIONS; i++)
7307     {
7308       x = ax + xy[i][0];
7309       y = ay + xy[i][1];
7310
7311       if (!IN_LEV_FIELD(x, y))
7312         continue;
7313
7314       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7315       {
7316         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7317                               SND_AMOEBA_TURNING_TO_GEM :
7318                               SND_AMOEBA_TURNING_TO_ROCK));
7319         Bang(x, y);
7320       }
7321     }
7322   }
7323 }
7324
7325 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7326 {
7327   int x, y;
7328   int group_nr = AmoebaNr[ax][ay];
7329   boolean done = FALSE;
7330
7331 #ifdef DEBUG
7332   if (group_nr == 0)
7333   {
7334     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7335     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7336     return;
7337   }
7338 #endif
7339
7340   SCAN_PLAYFIELD(x, y)
7341   {
7342     if (AmoebaNr[x][y] == group_nr &&
7343         (Feld[x][y] == EL_AMOEBA_DEAD ||
7344          Feld[x][y] == EL_BD_AMOEBA ||
7345          Feld[x][y] == EL_AMOEBA_GROWING))
7346     {
7347       AmoebaNr[x][y] = 0;
7348       Feld[x][y] = new_element;
7349       InitField(x, y, FALSE);
7350       DrawLevelField(x, y);
7351       done = TRUE;
7352     }
7353   }
7354
7355   if (done)
7356     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7357                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7358                             SND_BD_AMOEBA_TURNING_TO_GEM));
7359 }
7360
7361 void AmoebeWaechst(int x, int y)
7362 {
7363   static unsigned long sound_delay = 0;
7364   static unsigned long sound_delay_value = 0;
7365
7366   if (!MovDelay[x][y])          /* start new growing cycle */
7367   {
7368     MovDelay[x][y] = 7;
7369
7370     if (DelayReached(&sound_delay, sound_delay_value))
7371     {
7372       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7373       sound_delay_value = 30;
7374     }
7375   }
7376
7377   if (MovDelay[x][y])           /* wait some time before growing bigger */
7378   {
7379     MovDelay[x][y]--;
7380     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7381     {
7382       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7383                                            6 - MovDelay[x][y]);
7384
7385       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7386     }
7387
7388     if (!MovDelay[x][y])
7389     {
7390       Feld[x][y] = Store[x][y];
7391       Store[x][y] = 0;
7392       DrawLevelField(x, y);
7393     }
7394   }
7395 }
7396
7397 void AmoebaDisappearing(int x, int y)
7398 {
7399   static unsigned long sound_delay = 0;
7400   static unsigned long sound_delay_value = 0;
7401
7402   if (!MovDelay[x][y])          /* start new shrinking cycle */
7403   {
7404     MovDelay[x][y] = 7;
7405
7406     if (DelayReached(&sound_delay, sound_delay_value))
7407       sound_delay_value = 30;
7408   }
7409
7410   if (MovDelay[x][y])           /* wait some time before shrinking */
7411   {
7412     MovDelay[x][y]--;
7413     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7414     {
7415       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7416                                            6 - MovDelay[x][y]);
7417
7418       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7419     }
7420
7421     if (!MovDelay[x][y])
7422     {
7423       Feld[x][y] = EL_EMPTY;
7424       DrawLevelField(x, y);
7425
7426       /* don't let mole enter this field in this cycle;
7427          (give priority to objects falling to this field from above) */
7428       Stop[x][y] = TRUE;
7429     }
7430   }
7431 }
7432
7433 void AmoebeAbleger(int ax, int ay)
7434 {
7435   int i;
7436   int element = Feld[ax][ay];
7437   int graphic = el2img(element);
7438   int newax = ax, neway = ay;
7439   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7440   static int xy[4][2] =
7441   {
7442     { 0, -1 },
7443     { -1, 0 },
7444     { +1, 0 },
7445     { 0, +1 }
7446   };
7447
7448   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7449   {
7450     Feld[ax][ay] = EL_AMOEBA_DEAD;
7451     DrawLevelField(ax, ay);
7452     return;
7453   }
7454
7455   if (IS_ANIMATED(graphic))
7456     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7457
7458   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7459     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7460
7461   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7462   {
7463     MovDelay[ax][ay]--;
7464     if (MovDelay[ax][ay])
7465       return;
7466   }
7467
7468   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7469   {
7470     int start = RND(4);
7471     int x = ax + xy[start][0];
7472     int y = ay + xy[start][1];
7473
7474     if (!IN_LEV_FIELD(x, y))
7475       return;
7476
7477     if (IS_FREE(x, y) ||
7478         CAN_GROW_INTO(Feld[x][y]) ||
7479         Feld[x][y] == EL_QUICKSAND_EMPTY ||
7480         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7481     {
7482       newax = x;
7483       neway = y;
7484     }
7485
7486     if (newax == ax && neway == ay)
7487       return;
7488   }
7489   else                          /* normal or "filled" (BD style) amoeba */
7490   {
7491     int start = RND(4);
7492     boolean waiting_for_player = FALSE;
7493
7494     for (i = 0; i < NUM_DIRECTIONS; i++)
7495     {
7496       int j = (start + i) % 4;
7497       int x = ax + xy[j][0];
7498       int y = ay + xy[j][1];
7499
7500       if (!IN_LEV_FIELD(x, y))
7501         continue;
7502
7503       if (IS_FREE(x, y) ||
7504           CAN_GROW_INTO(Feld[x][y]) ||
7505           Feld[x][y] == EL_QUICKSAND_EMPTY ||
7506           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7507       {
7508         newax = x;
7509         neway = y;
7510         break;
7511       }
7512       else if (IS_PLAYER(x, y))
7513         waiting_for_player = TRUE;
7514     }
7515
7516     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7517     {
7518       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7519       {
7520         Feld[ax][ay] = EL_AMOEBA_DEAD;
7521         DrawLevelField(ax, ay);
7522         AmoebaCnt[AmoebaNr[ax][ay]]--;
7523
7524         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7525         {
7526           if (element == EL_AMOEBA_FULL)
7527             AmoebeUmwandeln(ax, ay);
7528           else if (element == EL_BD_AMOEBA)
7529             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7530         }
7531       }
7532       return;
7533     }
7534     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7535     {
7536       /* amoeba gets larger by growing in some direction */
7537
7538       int new_group_nr = AmoebaNr[ax][ay];
7539
7540 #ifdef DEBUG
7541   if (new_group_nr == 0)
7542   {
7543     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7544     printf("AmoebeAbleger(): This should never happen!\n");
7545     return;
7546   }
7547 #endif
7548
7549       AmoebaNr[newax][neway] = new_group_nr;
7550       AmoebaCnt[new_group_nr]++;
7551       AmoebaCnt2[new_group_nr]++;
7552
7553       /* if amoeba touches other amoeba(s) after growing, unify them */
7554       AmoebenVereinigen(newax, neway);
7555
7556       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7557       {
7558         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7559         return;
7560       }
7561     }
7562   }
7563
7564   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7565       (neway == lev_fieldy - 1 && newax != ax))
7566   {
7567     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7568     Store[newax][neway] = element;
7569   }
7570   else if (neway == ay || element == EL_EMC_DRIPPER)
7571   {
7572     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7573
7574     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7575   }
7576   else
7577   {
7578     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7579     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7580     Store[ax][ay] = EL_AMOEBA_DROP;
7581     ContinueMoving(ax, ay);
7582     return;
7583   }
7584
7585   DrawLevelField(newax, neway);
7586 }
7587
7588 void Life(int ax, int ay)
7589 {
7590   int x1, y1, x2, y2;
7591   int life_time = 40;
7592   int element = Feld[ax][ay];
7593   int graphic = el2img(element);
7594   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7595                          level.biomaze);
7596   boolean changed = FALSE;
7597
7598   if (IS_ANIMATED(graphic))
7599     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7600
7601   if (Stop[ax][ay])
7602     return;
7603
7604   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7605     MovDelay[ax][ay] = life_time;
7606
7607   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7608   {
7609     MovDelay[ax][ay]--;
7610     if (MovDelay[ax][ay])
7611       return;
7612   }
7613
7614   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7615   {
7616     int xx = ax+x1, yy = ay+y1;
7617     int nachbarn = 0;
7618
7619     if (!IN_LEV_FIELD(xx, yy))
7620       continue;
7621
7622     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7623     {
7624       int x = xx+x2, y = yy+y2;
7625
7626       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7627         continue;
7628
7629       if (((Feld[x][y] == element ||
7630             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7631            !Stop[x][y]) ||
7632           (IS_FREE(x, y) && Stop[x][y]))
7633         nachbarn++;
7634     }
7635
7636     if (xx == ax && yy == ay)           /* field in the middle */
7637     {
7638       if (nachbarn < life_parameter[0] ||
7639           nachbarn > life_parameter[1])
7640       {
7641         Feld[xx][yy] = EL_EMPTY;
7642         if (!Stop[xx][yy])
7643           DrawLevelField(xx, yy);
7644         Stop[xx][yy] = TRUE;
7645         changed = TRUE;
7646       }
7647     }
7648     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7649     {                                   /* free border field */
7650       if (nachbarn >= life_parameter[2] &&
7651           nachbarn <= life_parameter[3])
7652       {
7653         Feld[xx][yy] = element;
7654         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7655         if (!Stop[xx][yy])
7656           DrawLevelField(xx, yy);
7657         Stop[xx][yy] = TRUE;
7658         changed = TRUE;
7659       }
7660     }
7661   }
7662
7663   if (changed)
7664     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7665                    SND_GAME_OF_LIFE_GROWING);
7666 }
7667
7668 static void InitRobotWheel(int x, int y)
7669 {
7670   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7671 }
7672
7673 static void RunRobotWheel(int x, int y)
7674 {
7675   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7676 }
7677
7678 static void StopRobotWheel(int x, int y)
7679 {
7680   if (ZX == x && ZY == y)
7681     ZX = ZY = -1;
7682 }
7683
7684 static void InitTimegateWheel(int x, int y)
7685 {
7686   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7687 }
7688
7689 static void RunTimegateWheel(int x, int y)
7690 {
7691   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7692 }
7693
7694 static void InitMagicBallDelay(int x, int y)
7695 {
7696 #if 1
7697   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7698 #else
7699   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7700 #endif
7701 }
7702
7703 static void ActivateMagicBall(int bx, int by)
7704 {
7705   int x, y;
7706
7707   if (level.ball_random)
7708   {
7709     int pos_border = RND(8);    /* select one of the eight border elements */
7710     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7711     int xx = pos_content % 3;
7712     int yy = pos_content / 3;
7713
7714     x = bx - 1 + xx;
7715     y = by - 1 + yy;
7716
7717     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7718       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7719   }
7720   else
7721   {
7722     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7723     {
7724       int xx = x - bx + 1;
7725       int yy = y - by + 1;
7726
7727       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7728         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7729     }
7730   }
7731
7732   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7733 }
7734
7735 void CheckExit(int x, int y)
7736 {
7737   if (local_player->gems_still_needed > 0 ||
7738       local_player->sokobanfields_still_needed > 0 ||
7739       local_player->lights_still_needed > 0)
7740   {
7741     int element = Feld[x][y];
7742     int graphic = el2img(element);
7743
7744     if (IS_ANIMATED(graphic))
7745       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7746
7747     return;
7748   }
7749
7750   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7751     return;
7752
7753   Feld[x][y] = EL_EXIT_OPENING;
7754
7755   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7756 }
7757
7758 void CheckExitEM(int x, int y)
7759 {
7760   if (local_player->gems_still_needed > 0 ||
7761       local_player->sokobanfields_still_needed > 0 ||
7762       local_player->lights_still_needed > 0)
7763   {
7764     int element = Feld[x][y];
7765     int graphic = el2img(element);
7766
7767     if (IS_ANIMATED(graphic))
7768       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7769
7770     return;
7771   }
7772
7773   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7774     return;
7775
7776   Feld[x][y] = EL_EM_EXIT_OPENING;
7777
7778   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7779 }
7780
7781 void CheckExitSteel(int x, int y)
7782 {
7783   if (local_player->gems_still_needed > 0 ||
7784       local_player->sokobanfields_still_needed > 0 ||
7785       local_player->lights_still_needed > 0)
7786   {
7787     int element = Feld[x][y];
7788     int graphic = el2img(element);
7789
7790     if (IS_ANIMATED(graphic))
7791       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7792
7793     return;
7794   }
7795
7796   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7797     return;
7798
7799   Feld[x][y] = EL_STEEL_EXIT_OPENING;
7800
7801   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7802 }
7803
7804 void CheckExitSteelEM(int x, int y)
7805 {
7806   if (local_player->gems_still_needed > 0 ||
7807       local_player->sokobanfields_still_needed > 0 ||
7808       local_player->lights_still_needed > 0)
7809   {
7810     int element = Feld[x][y];
7811     int graphic = el2img(element);
7812
7813     if (IS_ANIMATED(graphic))
7814       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7815
7816     return;
7817   }
7818
7819   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7820     return;
7821
7822   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7823
7824   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7825 }
7826
7827 void CheckExitSP(int x, int y)
7828 {
7829   if (local_player->gems_still_needed > 0)
7830   {
7831     int element = Feld[x][y];
7832     int graphic = el2img(element);
7833
7834     if (IS_ANIMATED(graphic))
7835       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7836
7837     return;
7838   }
7839
7840   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7841     return;
7842
7843   Feld[x][y] = EL_SP_EXIT_OPENING;
7844
7845   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7846 }
7847
7848 static void CloseAllOpenTimegates()
7849 {
7850   int x, y;
7851
7852   SCAN_PLAYFIELD(x, y)
7853   {
7854     int element = Feld[x][y];
7855
7856     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7857     {
7858       Feld[x][y] = EL_TIMEGATE_CLOSING;
7859
7860       PlayLevelSoundAction(x, y, ACTION_CLOSING);
7861     }
7862   }
7863 }
7864
7865 void DrawTwinkleOnField(int x, int y)
7866 {
7867   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7868     return;
7869
7870   if (Feld[x][y] == EL_BD_DIAMOND)
7871     return;
7872
7873   if (MovDelay[x][y] == 0)      /* next animation frame */
7874     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7875
7876   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7877   {
7878     MovDelay[x][y]--;
7879
7880     if (setup.direct_draw && MovDelay[x][y])
7881       SetDrawtoField(DRAW_BUFFERED);
7882
7883     DrawLevelElementAnimation(x, y, Feld[x][y]);
7884
7885     if (MovDelay[x][y] != 0)
7886     {
7887       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7888                                            10 - MovDelay[x][y]);
7889
7890       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7891
7892       if (setup.direct_draw)
7893       {
7894         int dest_x, dest_y;
7895
7896         dest_x = FX + SCREENX(x) * TILEX;
7897         dest_y = FY + SCREENY(y) * TILEY;
7898
7899         BlitBitmap(drawto_field, window,
7900                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7901         SetDrawtoField(DRAW_DIRECT);
7902       }
7903     }
7904   }
7905 }
7906
7907 void MauerWaechst(int x, int y)
7908 {
7909   int delay = 6;
7910
7911   if (!MovDelay[x][y])          /* next animation frame */
7912     MovDelay[x][y] = 3 * delay;
7913
7914   if (MovDelay[x][y])           /* wait some time before next frame */
7915   {
7916     MovDelay[x][y]--;
7917
7918     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7919     {
7920       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7921       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7922
7923       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7924     }
7925
7926     if (!MovDelay[x][y])
7927     {
7928       if (MovDir[x][y] == MV_LEFT)
7929       {
7930         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7931           DrawLevelField(x - 1, y);
7932       }
7933       else if (MovDir[x][y] == MV_RIGHT)
7934       {
7935         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7936           DrawLevelField(x + 1, y);
7937       }
7938       else if (MovDir[x][y] == MV_UP)
7939       {
7940         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7941           DrawLevelField(x, y - 1);
7942       }
7943       else
7944       {
7945         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7946           DrawLevelField(x, y + 1);
7947       }
7948
7949       Feld[x][y] = Store[x][y];
7950       Store[x][y] = 0;
7951       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7952       DrawLevelField(x, y);
7953     }
7954   }
7955 }
7956
7957 void MauerAbleger(int ax, int ay)
7958 {
7959   int element = Feld[ax][ay];
7960   int graphic = el2img(element);
7961   boolean oben_frei = FALSE, unten_frei = FALSE;
7962   boolean links_frei = FALSE, rechts_frei = FALSE;
7963   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7964   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7965   boolean new_wall = FALSE;
7966
7967   if (IS_ANIMATED(graphic))
7968     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7969
7970   if (!MovDelay[ax][ay])        /* start building new wall */
7971     MovDelay[ax][ay] = 6;
7972
7973   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7974   {
7975     MovDelay[ax][ay]--;
7976     if (MovDelay[ax][ay])
7977       return;
7978   }
7979
7980   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7981     oben_frei = TRUE;
7982   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7983     unten_frei = TRUE;
7984   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7985     links_frei = TRUE;
7986   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7987     rechts_frei = TRUE;
7988
7989   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7990       element == EL_EXPANDABLE_WALL_ANY)
7991   {
7992     if (oben_frei)
7993     {
7994       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7995       Store[ax][ay-1] = element;
7996       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7997       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7998         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7999                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8000       new_wall = TRUE;
8001     }
8002     if (unten_frei)
8003     {
8004       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8005       Store[ax][ay+1] = element;
8006       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8007       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8008         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8009                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8010       new_wall = TRUE;
8011     }
8012   }
8013
8014   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8015       element == EL_EXPANDABLE_WALL_ANY ||
8016       element == EL_EXPANDABLE_WALL ||
8017       element == EL_BD_EXPANDABLE_WALL)
8018   {
8019     if (links_frei)
8020     {
8021       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8022       Store[ax-1][ay] = element;
8023       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8024       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8025         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8026                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8027       new_wall = TRUE;
8028     }
8029
8030     if (rechts_frei)
8031     {
8032       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8033       Store[ax+1][ay] = element;
8034       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8035       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8036         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8037                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8038       new_wall = TRUE;
8039     }
8040   }
8041
8042   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8043     DrawLevelField(ax, ay);
8044
8045   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8046     oben_massiv = TRUE;
8047   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8048     unten_massiv = TRUE;
8049   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8050     links_massiv = TRUE;
8051   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8052     rechts_massiv = TRUE;
8053
8054   if (((oben_massiv && unten_massiv) ||
8055        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8056        element == EL_EXPANDABLE_WALL) &&
8057       ((links_massiv && rechts_massiv) ||
8058        element == EL_EXPANDABLE_WALL_VERTICAL))
8059     Feld[ax][ay] = EL_WALL;
8060
8061   if (new_wall)
8062     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8063 }
8064
8065 void MauerAblegerStahl(int ax, int ay)
8066 {
8067   int element = Feld[ax][ay];
8068   int graphic = el2img(element);
8069   boolean oben_frei = FALSE, unten_frei = FALSE;
8070   boolean links_frei = FALSE, rechts_frei = FALSE;
8071   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8072   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8073   boolean new_wall = FALSE;
8074
8075   if (IS_ANIMATED(graphic))
8076     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8077
8078   if (!MovDelay[ax][ay])        /* start building new wall */
8079     MovDelay[ax][ay] = 6;
8080
8081   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8082   {
8083     MovDelay[ax][ay]--;
8084     if (MovDelay[ax][ay])
8085       return;
8086   }
8087
8088   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8089     oben_frei = TRUE;
8090   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8091     unten_frei = TRUE;
8092   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8093     links_frei = TRUE;
8094   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8095     rechts_frei = TRUE;
8096
8097   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8098       element == EL_EXPANDABLE_STEELWALL_ANY)
8099   {
8100     if (oben_frei)
8101     {
8102       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8103       Store[ax][ay-1] = element;
8104       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8105       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8106         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8107                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8108       new_wall = TRUE;
8109     }
8110     if (unten_frei)
8111     {
8112       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8113       Store[ax][ay+1] = element;
8114       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8115       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8116         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8117                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8118       new_wall = TRUE;
8119     }
8120   }
8121
8122   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8123       element == EL_EXPANDABLE_STEELWALL_ANY)
8124   {
8125     if (links_frei)
8126     {
8127       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8128       Store[ax-1][ay] = element;
8129       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8130       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8131         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8132                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8133       new_wall = TRUE;
8134     }
8135
8136     if (rechts_frei)
8137     {
8138       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8139       Store[ax+1][ay] = element;
8140       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8141       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8142         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8143                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8144       new_wall = TRUE;
8145     }
8146   }
8147
8148   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8149     oben_massiv = TRUE;
8150   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8151     unten_massiv = TRUE;
8152   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8153     links_massiv = TRUE;
8154   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8155     rechts_massiv = TRUE;
8156
8157   if (((oben_massiv && unten_massiv) ||
8158        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8159       ((links_massiv && rechts_massiv) ||
8160        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8161     Feld[ax][ay] = EL_WALL;
8162
8163   if (new_wall)
8164     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8165 }
8166
8167 void CheckForDragon(int x, int y)
8168 {
8169   int i, j;
8170   boolean dragon_found = FALSE;
8171   static int xy[4][2] =
8172   {
8173     { 0, -1 },
8174     { -1, 0 },
8175     { +1, 0 },
8176     { 0, +1 }
8177   };
8178
8179   for (i = 0; i < NUM_DIRECTIONS; i++)
8180   {
8181     for (j = 0; j < 4; j++)
8182     {
8183       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8184
8185       if (IN_LEV_FIELD(xx, yy) &&
8186           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8187       {
8188         if (Feld[xx][yy] == EL_DRAGON)
8189           dragon_found = TRUE;
8190       }
8191       else
8192         break;
8193     }
8194   }
8195
8196   if (!dragon_found)
8197   {
8198     for (i = 0; i < NUM_DIRECTIONS; i++)
8199     {
8200       for (j = 0; j < 3; j++)
8201       {
8202         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8203   
8204         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8205         {
8206           Feld[xx][yy] = EL_EMPTY;
8207           DrawLevelField(xx, yy);
8208         }
8209         else
8210           break;
8211       }
8212     }
8213   }
8214 }
8215
8216 static void InitBuggyBase(int x, int y)
8217 {
8218   int element = Feld[x][y];
8219   int activating_delay = FRAMES_PER_SECOND / 4;
8220
8221   ChangeDelay[x][y] =
8222     (element == EL_SP_BUGGY_BASE ?
8223      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8224      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8225      activating_delay :
8226      element == EL_SP_BUGGY_BASE_ACTIVE ?
8227      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8228 }
8229
8230 static void WarnBuggyBase(int x, int y)
8231 {
8232   int i;
8233   static int xy[4][2] =
8234   {
8235     { 0, -1 },
8236     { -1, 0 },
8237     { +1, 0 },
8238     { 0, +1 }
8239   };
8240
8241   for (i = 0; i < NUM_DIRECTIONS; i++)
8242   {
8243     int xx = x + xy[i][0];
8244     int yy = y + xy[i][1];
8245
8246     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8247     {
8248       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8249
8250       break;
8251     }
8252   }
8253 }
8254
8255 static void InitTrap(int x, int y)
8256 {
8257   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8258 }
8259
8260 static void ActivateTrap(int x, int y)
8261 {
8262   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8263 }
8264
8265 static void ChangeActiveTrap(int x, int y)
8266 {
8267   int graphic = IMG_TRAP_ACTIVE;
8268
8269   /* if new animation frame was drawn, correct crumbled sand border */
8270   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8271     DrawLevelFieldCrumbledSand(x, y);
8272 }
8273
8274 static int getSpecialActionElement(int element, int number, int base_element)
8275 {
8276   return (element != EL_EMPTY ? element :
8277           number != -1 ? base_element + number - 1 :
8278           EL_EMPTY);
8279 }
8280
8281 static int getModifiedActionNumber(int value_old, int operator, int operand,
8282                                    int value_min, int value_max)
8283 {
8284   int value_new = (operator == CA_MODE_SET      ? operand :
8285                    operator == CA_MODE_ADD      ? value_old + operand :
8286                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8287                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8288                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8289                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8290                    value_old);
8291
8292   return (value_new < value_min ? value_min :
8293           value_new > value_max ? value_max :
8294           value_new);
8295 }
8296
8297 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8298 {
8299   struct ElementInfo *ei = &element_info[element];
8300   struct ElementChangeInfo *change = &ei->change_page[page];
8301   int target_element = change->target_element;
8302   int action_type = change->action_type;
8303   int action_mode = change->action_mode;
8304   int action_arg = change->action_arg;
8305   int i;
8306
8307   if (!change->has_action)
8308     return;
8309
8310   /* ---------- determine action paramater values -------------------------- */
8311
8312   int level_time_value =
8313     (level.time > 0 ? TimeLeft :
8314      TimePlayed);
8315
8316   int action_arg_element =
8317     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8318      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8319      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8320      EL_EMPTY);
8321
8322   int action_arg_direction =
8323     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8324      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8325      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8326      change->actual_trigger_side :
8327      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8328      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8329      MV_NONE);
8330
8331   int action_arg_number_min =
8332     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8333      CA_ARG_MIN);
8334
8335   int action_arg_number_max =
8336     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8337      action_type == CA_SET_LEVEL_GEMS ? 999 :
8338      action_type == CA_SET_LEVEL_TIME ? 9999 :
8339      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8340      action_type == CA_SET_CE_VALUE ? 9999 :
8341      action_type == CA_SET_CE_SCORE ? 9999 :
8342      CA_ARG_MAX);
8343
8344   int action_arg_number_reset =
8345     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8346      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8347      action_type == CA_SET_LEVEL_TIME ? level.time :
8348      action_type == CA_SET_LEVEL_SCORE ? 0 :
8349 #if USE_NEW_CUSTOM_VALUE
8350      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8351 #else
8352      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8353 #endif
8354      action_type == CA_SET_CE_SCORE ? 0 :
8355      0);
8356
8357   int action_arg_number =
8358     (action_arg <= CA_ARG_MAX ? action_arg :
8359      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8360      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8361      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8362      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8363      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8364      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8365 #if USE_NEW_CUSTOM_VALUE
8366      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8367 #else
8368      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8369 #endif
8370      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8371      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8372      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8373      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8374      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8375      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8376      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8377      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8378      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8379      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8380      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8381      -1);
8382
8383   int action_arg_number_old =
8384     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8385      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8386      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8387      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8388      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8389      0);
8390
8391   int action_arg_number_new =
8392     getModifiedActionNumber(action_arg_number_old,
8393                             action_mode, action_arg_number,
8394                             action_arg_number_min, action_arg_number_max);
8395
8396   int trigger_player_bits =
8397     (change->actual_trigger_player >= EL_PLAYER_1 &&
8398      change->actual_trigger_player <= EL_PLAYER_4 ?
8399      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8400      PLAYER_BITS_ANY);
8401
8402   int action_arg_player_bits =
8403     (action_arg >= CA_ARG_PLAYER_1 &&
8404      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8405      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8406      PLAYER_BITS_ANY);
8407
8408   /* ---------- execute action  -------------------------------------------- */
8409
8410   switch (action_type)
8411   {
8412     case CA_NO_ACTION:
8413     {
8414       return;
8415     }
8416
8417     /* ---------- level actions  ------------------------------------------- */
8418
8419     case CA_RESTART_LEVEL:
8420     {
8421       game.restart_level = TRUE;
8422
8423       break;
8424     }
8425
8426     case CA_SHOW_ENVELOPE:
8427     {
8428       int element = getSpecialActionElement(action_arg_element,
8429                                             action_arg_number, EL_ENVELOPE_1);
8430
8431       if (IS_ENVELOPE(element))
8432         local_player->show_envelope = element;
8433
8434       break;
8435     }
8436
8437     case CA_SET_LEVEL_TIME:
8438     {
8439       if (level.time > 0)       /* only modify limited time value */
8440       {
8441         TimeLeft = action_arg_number_new;
8442
8443         DrawGameValue_Time(TimeLeft);
8444
8445         if (!TimeLeft && setup.time_limit)
8446           for (i = 0; i < MAX_PLAYERS; i++)
8447             KillPlayer(&stored_player[i]);
8448       }
8449
8450       break;
8451     }
8452
8453     case CA_SET_LEVEL_SCORE:
8454     {
8455       local_player->score = action_arg_number_new;
8456
8457       DrawGameValue_Score(local_player->score);
8458
8459       break;
8460     }
8461
8462     case CA_SET_LEVEL_GEMS:
8463     {
8464       local_player->gems_still_needed = action_arg_number_new;
8465
8466       DrawGameValue_Emeralds(local_player->gems_still_needed);
8467
8468       break;
8469     }
8470
8471 #if !USE_PLAYER_GRAVITY
8472     case CA_SET_LEVEL_GRAVITY:
8473     {
8474       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
8475                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
8476                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8477                       game.gravity);
8478       break;
8479     }
8480 #endif
8481
8482     case CA_SET_LEVEL_WIND:
8483     {
8484       game.wind_direction = action_arg_direction;
8485
8486       break;
8487     }
8488
8489     /* ---------- player actions  ------------------------------------------ */
8490
8491     case CA_MOVE_PLAYER:
8492     {
8493       /* automatically move to the next field in specified direction */
8494       for (i = 0; i < MAX_PLAYERS; i++)
8495         if (trigger_player_bits & (1 << i))
8496           stored_player[i].programmed_action = action_arg_direction;
8497
8498       break;
8499     }
8500
8501     case CA_EXIT_PLAYER:
8502     {
8503       for (i = 0; i < MAX_PLAYERS; i++)
8504         if (action_arg_player_bits & (1 << i))
8505           PlayerWins(&stored_player[i]);
8506
8507       break;
8508     }
8509
8510     case CA_KILL_PLAYER:
8511     {
8512       for (i = 0; i < MAX_PLAYERS; i++)
8513         if (action_arg_player_bits & (1 << i))
8514           KillPlayer(&stored_player[i]);
8515
8516       break;
8517     }
8518
8519     case CA_SET_PLAYER_KEYS:
8520     {
8521       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8522       int element = getSpecialActionElement(action_arg_element,
8523                                             action_arg_number, EL_KEY_1);
8524
8525       if (IS_KEY(element))
8526       {
8527         for (i = 0; i < MAX_PLAYERS; i++)
8528         {
8529           if (trigger_player_bits & (1 << i))
8530           {
8531             stored_player[i].key[KEY_NR(element)] = key_state;
8532
8533             DrawGameDoorValues();
8534           }
8535         }
8536       }
8537
8538       break;
8539     }
8540
8541     case CA_SET_PLAYER_SPEED:
8542     {
8543       for (i = 0; i < MAX_PLAYERS; i++)
8544       {
8545         if (trigger_player_bits & (1 << i))
8546         {
8547           int move_stepsize = TILEX / stored_player[i].move_delay_value;
8548
8549           if (action_arg == CA_ARG_SPEED_FASTER &&
8550               stored_player[i].cannot_move)
8551           {
8552             action_arg_number = STEPSIZE_VERY_SLOW;
8553           }
8554           else if (action_arg == CA_ARG_SPEED_SLOWER ||
8555                    action_arg == CA_ARG_SPEED_FASTER)
8556           {
8557             action_arg_number = 2;
8558             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8559                            CA_MODE_MULTIPLY);
8560           }
8561           else if (action_arg == CA_ARG_NUMBER_RESET)
8562           {
8563             action_arg_number = level.initial_player_stepsize[i];
8564           }
8565
8566           move_stepsize =
8567             getModifiedActionNumber(move_stepsize,
8568                                     action_mode,
8569                                     action_arg_number,
8570                                     action_arg_number_min,
8571                                     action_arg_number_max);
8572
8573           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8574         }
8575       }
8576
8577       break;
8578     }
8579
8580     case CA_SET_PLAYER_SHIELD:
8581     {
8582       for (i = 0; i < MAX_PLAYERS; i++)
8583       {
8584         if (trigger_player_bits & (1 << i))
8585         {
8586           if (action_arg == CA_ARG_SHIELD_OFF)
8587           {
8588             stored_player[i].shield_normal_time_left = 0;
8589             stored_player[i].shield_deadly_time_left = 0;
8590           }
8591           else if (action_arg == CA_ARG_SHIELD_NORMAL)
8592           {
8593             stored_player[i].shield_normal_time_left = 999999;
8594           }
8595           else if (action_arg == CA_ARG_SHIELD_DEADLY)
8596           {
8597             stored_player[i].shield_normal_time_left = 999999;
8598             stored_player[i].shield_deadly_time_left = 999999;
8599           }
8600         }
8601       }
8602
8603       break;
8604     }
8605
8606 #if USE_PLAYER_GRAVITY
8607     case CA_SET_PLAYER_GRAVITY:
8608     {
8609       for (i = 0; i < MAX_PLAYERS; i++)
8610       {
8611         if (trigger_player_bits & (1 << i))
8612         {
8613           stored_player[i].gravity =
8614             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
8615              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
8616              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8617              stored_player[i].gravity);
8618         }
8619       }
8620
8621       break;
8622     }
8623 #endif
8624
8625     case CA_SET_PLAYER_ARTWORK:
8626     {
8627       for (i = 0; i < MAX_PLAYERS; i++)
8628       {
8629         if (trigger_player_bits & (1 << i))
8630         {
8631           int artwork_element = action_arg_element;
8632
8633           if (action_arg == CA_ARG_ELEMENT_RESET)
8634             artwork_element =
8635               (level.use_artwork_element[i] ? level.artwork_element[i] :
8636                stored_player[i].element_nr);
8637
8638 #if USE_GFX_RESET_PLAYER_ARTWORK
8639           if (stored_player[i].artwork_element != artwork_element)
8640             stored_player[i].Frame = 0;
8641 #endif
8642
8643           stored_player[i].artwork_element = artwork_element;
8644
8645           SetPlayerWaiting(&stored_player[i], FALSE);
8646
8647           /* set number of special actions for bored and sleeping animation */
8648           stored_player[i].num_special_action_bored =
8649             get_num_special_action(artwork_element,
8650                                    ACTION_BORING_1, ACTION_BORING_LAST);
8651           stored_player[i].num_special_action_sleeping =
8652             get_num_special_action(artwork_element,
8653                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8654         }
8655       }
8656
8657       break;
8658     }
8659
8660     /* ---------- CE actions  ---------------------------------------------- */
8661
8662     case CA_SET_CE_VALUE:
8663     {
8664 #if USE_NEW_CUSTOM_VALUE
8665       int last_ce_value = CustomValue[x][y];
8666
8667       CustomValue[x][y] = action_arg_number_new;
8668
8669       if (CustomValue[x][y] != last_ce_value)
8670       {
8671         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8672         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8673
8674         if (CustomValue[x][y] == 0)
8675         {
8676           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8677           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8678         }
8679       }
8680 #endif
8681
8682       break;
8683     }
8684
8685     case CA_SET_CE_SCORE:
8686     {
8687 #if USE_NEW_CUSTOM_VALUE
8688       int last_ce_score = ei->collect_score;
8689
8690       ei->collect_score = action_arg_number_new;
8691
8692       if (ei->collect_score != last_ce_score)
8693       {
8694         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8695         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8696
8697         if (ei->collect_score == 0)
8698         {
8699           int xx, yy;
8700
8701           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8702           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8703
8704           /*
8705             This is a very special case that seems to be a mixture between
8706             CheckElementChange() and CheckTriggeredElementChange(): while
8707             the first one only affects single elements that are triggered
8708             directly, the second one affects multiple elements in the playfield
8709             that are triggered indirectly by another element. This is a third
8710             case: Changing the CE score always affects multiple identical CEs,
8711             so every affected CE must be checked, not only the single CE for
8712             which the CE score was changed in the first place (as every instance
8713             of that CE shares the same CE score, and therefore also can change)!
8714           */
8715           SCAN_PLAYFIELD(xx, yy)
8716           {
8717             if (Feld[xx][yy] == element)
8718               CheckElementChange(xx, yy, element, EL_UNDEFINED,
8719                                  CE_SCORE_GETS_ZERO);
8720           }
8721         }
8722       }
8723 #endif
8724
8725       break;
8726     }
8727
8728     /* ---------- engine actions  ------------------------------------------ */
8729
8730     case CA_SET_ENGINE_SCAN_MODE:
8731     {
8732       InitPlayfieldScanMode(action_arg);
8733
8734       break;
8735     }
8736
8737     default:
8738       break;
8739   }
8740 }
8741
8742 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8743 {
8744   int old_element = Feld[x][y];
8745   int new_element = get_element_from_group_element(element);
8746   int previous_move_direction = MovDir[x][y];
8747 #if USE_NEW_CUSTOM_VALUE
8748   int last_ce_value = CustomValue[x][y];
8749 #endif
8750   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8751   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8752   boolean add_player_onto_element = (new_element_is_player &&
8753 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8754                                      /* this breaks SnakeBite when a snake is
8755                                         halfway through a door that closes */
8756                                      /* NOW FIXED AT LEVEL INIT IN files.c */
8757                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
8758 #endif
8759                                      IS_WALKABLE(old_element));
8760
8761 #if 0
8762   /* check if element under the player changes from accessible to unaccessible
8763      (needed for special case of dropping element which then changes) */
8764   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8765       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8766   {
8767     Bang(x, y);
8768
8769     return;
8770   }
8771 #endif
8772
8773   if (!add_player_onto_element)
8774   {
8775     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8776       RemoveMovingField(x, y);
8777     else
8778       RemoveField(x, y);
8779
8780     Feld[x][y] = new_element;
8781
8782 #if !USE_GFX_RESET_GFX_ANIMATION
8783     ResetGfxAnimation(x, y);
8784     ResetRandomAnimationValue(x, y);
8785 #endif
8786
8787     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8788       MovDir[x][y] = previous_move_direction;
8789
8790 #if USE_NEW_CUSTOM_VALUE
8791     if (element_info[new_element].use_last_ce_value)
8792       CustomValue[x][y] = last_ce_value;
8793 #endif
8794
8795     InitField_WithBug1(x, y, FALSE);
8796
8797     new_element = Feld[x][y];   /* element may have changed */
8798
8799 #if USE_GFX_RESET_GFX_ANIMATION
8800     ResetGfxAnimation(x, y);
8801     ResetRandomAnimationValue(x, y);
8802 #endif
8803
8804     DrawLevelField(x, y);
8805
8806     if (GFX_CRUMBLED(new_element))
8807       DrawLevelFieldCrumbledSandNeighbours(x, y);
8808   }
8809
8810 #if 1
8811   /* check if element under the player changes from accessible to unaccessible
8812      (needed for special case of dropping element which then changes) */
8813   /* (must be checked after creating new element for walkable group elements) */
8814 #if USE_FIX_KILLED_BY_NON_WALKABLE
8815   if (IS_PLAYER(x, y) && !player_explosion_protected &&
8816       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8817   {
8818     Bang(x, y);
8819
8820     return;
8821   }
8822 #else
8823   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8824       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8825   {
8826     Bang(x, y);
8827
8828     return;
8829   }
8830 #endif
8831 #endif
8832
8833   /* "ChangeCount" not set yet to allow "entered by player" change one time */
8834   if (new_element_is_player)
8835     RelocatePlayer(x, y, new_element);
8836
8837   if (is_change)
8838     ChangeCount[x][y]++;        /* count number of changes in the same frame */
8839
8840   TestIfBadThingTouchesPlayer(x, y);
8841   TestIfPlayerTouchesCustomElement(x, y);
8842   TestIfElementTouchesCustomElement(x, y);
8843 }
8844
8845 static void CreateField(int x, int y, int element)
8846 {
8847   CreateFieldExt(x, y, element, FALSE);
8848 }
8849
8850 static void CreateElementFromChange(int x, int y, int element)
8851 {
8852   element = GET_VALID_RUNTIME_ELEMENT(element);
8853
8854 #if USE_STOP_CHANGED_ELEMENTS
8855   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8856   {
8857     int old_element = Feld[x][y];
8858
8859     /* prevent changed element from moving in same engine frame
8860        unless both old and new element can either fall or move */
8861     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8862         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8863       Stop[x][y] = TRUE;
8864   }
8865 #endif
8866
8867   CreateFieldExt(x, y, element, TRUE);
8868 }
8869
8870 static boolean ChangeElement(int x, int y, int element, int page)
8871 {
8872   struct ElementInfo *ei = &element_info[element];
8873   struct ElementChangeInfo *change = &ei->change_page[page];
8874   int ce_value = CustomValue[x][y];
8875   int ce_score = ei->collect_score;
8876   int target_element;
8877   int old_element = Feld[x][y];
8878
8879   /* always use default change event to prevent running into a loop */
8880   if (ChangeEvent[x][y] == -1)
8881     ChangeEvent[x][y] = CE_DELAY;
8882
8883   if (ChangeEvent[x][y] == CE_DELAY)
8884   {
8885     /* reset actual trigger element, trigger player and action element */
8886     change->actual_trigger_element = EL_EMPTY;
8887     change->actual_trigger_player = EL_PLAYER_1;
8888     change->actual_trigger_side = CH_SIDE_NONE;
8889     change->actual_trigger_ce_value = 0;
8890     change->actual_trigger_ce_score = 0;
8891   }
8892
8893   /* do not change elements more than a specified maximum number of changes */
8894   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8895     return FALSE;
8896
8897   ChangeCount[x][y]++;          /* count number of changes in the same frame */
8898
8899   if (change->explode)
8900   {
8901     Bang(x, y);
8902
8903     return TRUE;
8904   }
8905
8906   if (change->use_target_content)
8907   {
8908     boolean complete_replace = TRUE;
8909     boolean can_replace[3][3];
8910     int xx, yy;
8911
8912     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8913     {
8914       boolean is_empty;
8915       boolean is_walkable;
8916       boolean is_diggable;
8917       boolean is_collectible;
8918       boolean is_removable;
8919       boolean is_destructible;
8920       int ex = x + xx - 1;
8921       int ey = y + yy - 1;
8922       int content_element = change->target_content.e[xx][yy];
8923       int e;
8924
8925       can_replace[xx][yy] = TRUE;
8926
8927       if (ex == x && ey == y)   /* do not check changing element itself */
8928         continue;
8929
8930       if (content_element == EL_EMPTY_SPACE)
8931       {
8932         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8933
8934         continue;
8935       }
8936
8937       if (!IN_LEV_FIELD(ex, ey))
8938       {
8939         can_replace[xx][yy] = FALSE;
8940         complete_replace = FALSE;
8941
8942         continue;
8943       }
8944
8945       e = Feld[ex][ey];
8946
8947       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8948         e = MovingOrBlocked2Element(ex, ey);
8949
8950       is_empty = (IS_FREE(ex, ey) ||
8951                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8952
8953       is_walkable     = (is_empty || IS_WALKABLE(e));
8954       is_diggable     = (is_empty || IS_DIGGABLE(e));
8955       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8956       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8957       is_removable    = (is_diggable || is_collectible);
8958
8959       can_replace[xx][yy] =
8960         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
8961           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
8962           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
8963           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
8964           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
8965           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8966          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8967
8968       if (!can_replace[xx][yy])
8969         complete_replace = FALSE;
8970     }
8971
8972     if (!change->only_if_complete || complete_replace)
8973     {
8974       boolean something_has_changed = FALSE;
8975
8976       if (change->only_if_complete && change->use_random_replace &&
8977           RND(100) < change->random_percentage)
8978         return FALSE;
8979
8980       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8981       {
8982         int ex = x + xx - 1;
8983         int ey = y + yy - 1;
8984         int content_element;
8985
8986         if (can_replace[xx][yy] && (!change->use_random_replace ||
8987                                     RND(100) < change->random_percentage))
8988         {
8989           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8990             RemoveMovingField(ex, ey);
8991
8992           ChangeEvent[ex][ey] = ChangeEvent[x][y];
8993
8994           content_element = change->target_content.e[xx][yy];
8995           target_element = GET_TARGET_ELEMENT(element, content_element, change,
8996                                               ce_value, ce_score);
8997
8998           CreateElementFromChange(ex, ey, target_element);
8999
9000           something_has_changed = TRUE;
9001
9002           /* for symmetry reasons, freeze newly created border elements */
9003           if (ex != x || ey != y)
9004             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9005         }
9006       }
9007
9008       if (something_has_changed)
9009       {
9010         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9011         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9012       }
9013     }
9014   }
9015   else
9016   {
9017     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9018                                         ce_value, ce_score);
9019
9020     if (element == EL_DIAGONAL_GROWING ||
9021         element == EL_DIAGONAL_SHRINKING)
9022     {
9023       target_element = Store[x][y];
9024
9025       Store[x][y] = EL_EMPTY;
9026     }
9027
9028     CreateElementFromChange(x, y, target_element);
9029
9030     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9031     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9032   }
9033
9034   /* this uses direct change before indirect change */
9035   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9036
9037   return TRUE;
9038 }
9039
9040 #if USE_NEW_DELAYED_ACTION
9041
9042 static void HandleElementChange(int x, int y, int page)
9043 {
9044   int element = MovingOrBlocked2Element(x, y);
9045   struct ElementInfo *ei = &element_info[element];
9046   struct ElementChangeInfo *change = &ei->change_page[page];
9047
9048 #ifdef DEBUG
9049   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9050       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9051   {
9052     printf("\n\n");
9053     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9054            x, y, element, element_info[element].token_name);
9055     printf("HandleElementChange(): This should never happen!\n");
9056     printf("\n\n");
9057   }
9058 #endif
9059
9060   /* this can happen with classic bombs on walkable, changing elements */
9061   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9062   {
9063 #if 0
9064     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9065       ChangeDelay[x][y] = 0;
9066 #endif
9067
9068     return;
9069   }
9070
9071   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9072   {
9073     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9074
9075     if (change->can_change)
9076     {
9077       ResetGfxAnimation(x, y);
9078       ResetRandomAnimationValue(x, y);
9079
9080       if (change->pre_change_function)
9081         change->pre_change_function(x, y);
9082     }
9083   }
9084
9085   ChangeDelay[x][y]--;
9086
9087   if (ChangeDelay[x][y] != 0)           /* continue element change */
9088   {
9089     if (change->can_change)
9090     {
9091       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9092
9093       if (IS_ANIMATED(graphic))
9094         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9095
9096       if (change->change_function)
9097         change->change_function(x, y);
9098     }
9099   }
9100   else                                  /* finish element change */
9101   {
9102     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9103     {
9104       page = ChangePage[x][y];
9105       ChangePage[x][y] = -1;
9106
9107       change = &ei->change_page[page];
9108     }
9109
9110     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9111     {
9112       ChangeDelay[x][y] = 1;            /* try change after next move step */
9113       ChangePage[x][y] = page;          /* remember page to use for change */
9114
9115       return;
9116     }
9117
9118     if (change->can_change)
9119     {
9120       if (ChangeElement(x, y, element, page))
9121       {
9122         if (change->post_change_function)
9123           change->post_change_function(x, y);
9124       }
9125     }
9126
9127     if (change->has_action)
9128       ExecuteCustomElementAction(x, y, element, page);
9129   }
9130 }
9131
9132 #else
9133
9134 static void HandleElementChange(int x, int y, int page)
9135 {
9136   int element = MovingOrBlocked2Element(x, y);
9137   struct ElementInfo *ei = &element_info[element];
9138   struct ElementChangeInfo *change = &ei->change_page[page];
9139
9140 #ifdef DEBUG
9141   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9142   {
9143     printf("\n\n");
9144     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9145            x, y, element, element_info[element].token_name);
9146     printf("HandleElementChange(): This should never happen!\n");
9147     printf("\n\n");
9148   }
9149 #endif
9150
9151   /* this can happen with classic bombs on walkable, changing elements */
9152   if (!CAN_CHANGE(element))
9153   {
9154 #if 0
9155     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9156       ChangeDelay[x][y] = 0;
9157 #endif
9158
9159     return;
9160   }
9161
9162   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9163   {
9164     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9165
9166     ResetGfxAnimation(x, y);
9167     ResetRandomAnimationValue(x, y);
9168
9169     if (change->pre_change_function)
9170       change->pre_change_function(x, y);
9171   }
9172
9173   ChangeDelay[x][y]--;
9174
9175   if (ChangeDelay[x][y] != 0)           /* continue element change */
9176   {
9177     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9178
9179     if (IS_ANIMATED(graphic))
9180       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9181
9182     if (change->change_function)
9183       change->change_function(x, y);
9184   }
9185   else                                  /* finish element change */
9186   {
9187     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9188     {
9189       page = ChangePage[x][y];
9190       ChangePage[x][y] = -1;
9191
9192       change = &ei->change_page[page];
9193     }
9194
9195     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9196     {
9197       ChangeDelay[x][y] = 1;            /* try change after next move step */
9198       ChangePage[x][y] = page;          /* remember page to use for change */
9199
9200       return;
9201     }
9202
9203     if (ChangeElement(x, y, element, page))
9204     {
9205       if (change->post_change_function)
9206         change->post_change_function(x, y);
9207     }
9208   }
9209 }
9210
9211 #endif
9212
9213 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9214                                               int trigger_element,
9215                                               int trigger_event,
9216                                               int trigger_player,
9217                                               int trigger_side,
9218                                               int trigger_page)
9219 {
9220   boolean change_done_any = FALSE;
9221   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9222   int i;
9223
9224   if (!(trigger_events[trigger_element][trigger_event]))
9225     return FALSE;
9226
9227 #if 0
9228   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9229          trigger_event, recursion_loop_depth, recursion_loop_detected,
9230          recursion_loop_element, EL_NAME(recursion_loop_element));
9231 #endif
9232
9233   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9234
9235   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9236   {
9237     int element = EL_CUSTOM_START + i;
9238     boolean change_done = FALSE;
9239     int p;
9240
9241     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9242         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9243       continue;
9244
9245     for (p = 0; p < element_info[element].num_change_pages; p++)
9246     {
9247       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9248
9249       if (change->can_change_or_has_action &&
9250           change->has_event[trigger_event] &&
9251           change->trigger_side & trigger_side &&
9252           change->trigger_player & trigger_player &&
9253           change->trigger_page & trigger_page_bits &&
9254           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9255       {
9256         change->actual_trigger_element = trigger_element;
9257         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9258         change->actual_trigger_side = trigger_side;
9259         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9260         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9261
9262         if ((change->can_change && !change_done) || change->has_action)
9263         {
9264           int x, y;
9265
9266           SCAN_PLAYFIELD(x, y)
9267           {
9268             if (Feld[x][y] == element)
9269             {
9270               if (change->can_change && !change_done)
9271               {
9272                 ChangeDelay[x][y] = 1;
9273                 ChangeEvent[x][y] = trigger_event;
9274
9275                 HandleElementChange(x, y, p);
9276               }
9277 #if USE_NEW_DELAYED_ACTION
9278               else if (change->has_action)
9279               {
9280                 ExecuteCustomElementAction(x, y, element, p);
9281                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9282               }
9283 #else
9284               if (change->has_action)
9285               {
9286                 ExecuteCustomElementAction(x, y, element, p);
9287                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9288               }
9289 #endif
9290             }
9291           }
9292
9293           if (change->can_change)
9294           {
9295             change_done = TRUE;
9296             change_done_any = TRUE;
9297           }
9298         }
9299       }
9300     }
9301   }
9302
9303   RECURSION_LOOP_DETECTION_END();
9304
9305   return change_done_any;
9306 }
9307
9308 static boolean CheckElementChangeExt(int x, int y,
9309                                      int element,
9310                                      int trigger_element,
9311                                      int trigger_event,
9312                                      int trigger_player,
9313                                      int trigger_side)
9314 {
9315   boolean change_done = FALSE;
9316   int p;
9317
9318   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9319       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9320     return FALSE;
9321
9322   if (Feld[x][y] == EL_BLOCKED)
9323   {
9324     Blocked2Moving(x, y, &x, &y);
9325     element = Feld[x][y];
9326   }
9327
9328 #if 0
9329   /* check if element has already changed */
9330   if (Feld[x][y] != element)
9331     return FALSE;
9332 #else
9333   /* check if element has already changed or is about to change after moving */
9334   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9335        Feld[x][y] != element) ||
9336
9337       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9338        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9339         ChangePage[x][y] != -1)))
9340     return FALSE;
9341 #endif
9342
9343 #if 0
9344   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9345          trigger_event, recursion_loop_depth, recursion_loop_detected,
9346          recursion_loop_element, EL_NAME(recursion_loop_element));
9347 #endif
9348
9349   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9350
9351   for (p = 0; p < element_info[element].num_change_pages; p++)
9352   {
9353     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9354
9355     /* check trigger element for all events where the element that is checked
9356        for changing interacts with a directly adjacent element -- this is
9357        different to element changes that affect other elements to change on the
9358        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9359     boolean check_trigger_element =
9360       (trigger_event == CE_TOUCHING_X ||
9361        trigger_event == CE_HITTING_X ||
9362        trigger_event == CE_HIT_BY_X ||
9363 #if 1
9364        /* this one was forgotten until 3.2.3 */
9365        trigger_event == CE_DIGGING_X);
9366 #endif
9367
9368     if (change->can_change_or_has_action &&
9369         change->has_event[trigger_event] &&
9370         change->trigger_side & trigger_side &&
9371         change->trigger_player & trigger_player &&
9372         (!check_trigger_element ||
9373          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9374     {
9375       change->actual_trigger_element = trigger_element;
9376       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9377       change->actual_trigger_side = trigger_side;
9378       change->actual_trigger_ce_value = CustomValue[x][y];
9379       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9380
9381       /* special case: trigger element not at (x,y) position for some events */
9382       if (check_trigger_element)
9383       {
9384         static struct
9385         {
9386           int dx, dy;
9387         } move_xy[] =
9388           {
9389             {  0,  0 },
9390             { -1,  0 },
9391             { +1,  0 },
9392             {  0,  0 },
9393             {  0, -1 },
9394             {  0,  0 }, { 0, 0 }, { 0, 0 },
9395             {  0, +1 }
9396           };
9397
9398         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9399         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9400
9401         change->actual_trigger_ce_value = CustomValue[xx][yy];
9402         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9403       }
9404
9405       if (change->can_change && !change_done)
9406       {
9407         ChangeDelay[x][y] = 1;
9408         ChangeEvent[x][y] = trigger_event;
9409
9410         HandleElementChange(x, y, p);
9411
9412         change_done = TRUE;
9413       }
9414 #if USE_NEW_DELAYED_ACTION
9415       else if (change->has_action)
9416       {
9417         ExecuteCustomElementAction(x, y, element, p);
9418         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9419       }
9420 #else
9421       if (change->has_action)
9422       {
9423         ExecuteCustomElementAction(x, y, element, p);
9424         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9425       }
9426 #endif
9427     }
9428   }
9429
9430   RECURSION_LOOP_DETECTION_END();
9431
9432   return change_done;
9433 }
9434
9435 static void PlayPlayerSound(struct PlayerInfo *player)
9436 {
9437   int jx = player->jx, jy = player->jy;
9438   int sound_element = player->artwork_element;
9439   int last_action = player->last_action_waiting;
9440   int action = player->action_waiting;
9441
9442   if (player->is_waiting)
9443   {
9444     if (action != last_action)
9445       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9446     else
9447       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9448   }
9449   else
9450   {
9451     if (action != last_action)
9452       StopSound(element_info[sound_element].sound[last_action]);
9453
9454     if (last_action == ACTION_SLEEPING)
9455       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9456   }
9457 }
9458
9459 static void PlayAllPlayersSound()
9460 {
9461   int i;
9462
9463   for (i = 0; i < MAX_PLAYERS; i++)
9464     if (stored_player[i].active)
9465       PlayPlayerSound(&stored_player[i]);
9466 }
9467
9468 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9469 {
9470   boolean last_waiting = player->is_waiting;
9471   int move_dir = player->MovDir;
9472
9473   player->dir_waiting = move_dir;
9474   player->last_action_waiting = player->action_waiting;
9475
9476   if (is_waiting)
9477   {
9478     if (!last_waiting)          /* not waiting -> waiting */
9479     {
9480       player->is_waiting = TRUE;
9481
9482       player->frame_counter_bored =
9483         FrameCounter +
9484         game.player_boring_delay_fixed +
9485         GetSimpleRandom(game.player_boring_delay_random);
9486       player->frame_counter_sleeping =
9487         FrameCounter +
9488         game.player_sleeping_delay_fixed +
9489         GetSimpleRandom(game.player_sleeping_delay_random);
9490
9491       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9492     }
9493
9494     if (game.player_sleeping_delay_fixed +
9495         game.player_sleeping_delay_random > 0 &&
9496         player->anim_delay_counter == 0 &&
9497         player->post_delay_counter == 0 &&
9498         FrameCounter >= player->frame_counter_sleeping)
9499       player->is_sleeping = TRUE;
9500     else if (game.player_boring_delay_fixed +
9501              game.player_boring_delay_random > 0 &&
9502              FrameCounter >= player->frame_counter_bored)
9503       player->is_bored = TRUE;
9504
9505     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9506                               player->is_bored ? ACTION_BORING :
9507                               ACTION_WAITING);
9508
9509     if (player->is_sleeping && player->use_murphy)
9510     {
9511       /* special case for sleeping Murphy when leaning against non-free tile */
9512
9513       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9514           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9515            !IS_MOVING(player->jx - 1, player->jy)))
9516         move_dir = MV_LEFT;
9517       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9518                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9519                 !IS_MOVING(player->jx + 1, player->jy)))
9520         move_dir = MV_RIGHT;
9521       else
9522         player->is_sleeping = FALSE;
9523
9524       player->dir_waiting = move_dir;
9525     }
9526
9527     if (player->is_sleeping)
9528     {
9529       if (player->num_special_action_sleeping > 0)
9530       {
9531         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9532         {
9533           int last_special_action = player->special_action_sleeping;
9534           int num_special_action = player->num_special_action_sleeping;
9535           int special_action =
9536             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9537              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9538              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9539              last_special_action + 1 : ACTION_SLEEPING);
9540           int special_graphic =
9541             el_act_dir2img(player->artwork_element, special_action, move_dir);
9542
9543           player->anim_delay_counter =
9544             graphic_info[special_graphic].anim_delay_fixed +
9545             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9546           player->post_delay_counter =
9547             graphic_info[special_graphic].post_delay_fixed +
9548             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9549
9550           player->special_action_sleeping = special_action;
9551         }
9552
9553         if (player->anim_delay_counter > 0)
9554         {
9555           player->action_waiting = player->special_action_sleeping;
9556           player->anim_delay_counter--;
9557         }
9558         else if (player->post_delay_counter > 0)
9559         {
9560           player->post_delay_counter--;
9561         }
9562       }
9563     }
9564     else if (player->is_bored)
9565     {
9566       if (player->num_special_action_bored > 0)
9567       {
9568         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9569         {
9570           int special_action =
9571             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9572           int special_graphic =
9573             el_act_dir2img(player->artwork_element, special_action, move_dir);
9574
9575           player->anim_delay_counter =
9576             graphic_info[special_graphic].anim_delay_fixed +
9577             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9578           player->post_delay_counter =
9579             graphic_info[special_graphic].post_delay_fixed +
9580             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9581
9582           player->special_action_bored = special_action;
9583         }
9584
9585         if (player->anim_delay_counter > 0)
9586         {
9587           player->action_waiting = player->special_action_bored;
9588           player->anim_delay_counter--;
9589         }
9590         else if (player->post_delay_counter > 0)
9591         {
9592           player->post_delay_counter--;
9593         }
9594       }
9595     }
9596   }
9597   else if (last_waiting)        /* waiting -> not waiting */
9598   {
9599     player->is_waiting = FALSE;
9600     player->is_bored = FALSE;
9601     player->is_sleeping = FALSE;
9602
9603     player->frame_counter_bored = -1;
9604     player->frame_counter_sleeping = -1;
9605
9606     player->anim_delay_counter = 0;
9607     player->post_delay_counter = 0;
9608
9609     player->dir_waiting = player->MovDir;
9610     player->action_waiting = ACTION_DEFAULT;
9611
9612     player->special_action_bored = ACTION_DEFAULT;
9613     player->special_action_sleeping = ACTION_DEFAULT;
9614   }
9615 }
9616
9617 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9618 {
9619   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9620   int left      = player_action & JOY_LEFT;
9621   int right     = player_action & JOY_RIGHT;
9622   int up        = player_action & JOY_UP;
9623   int down      = player_action & JOY_DOWN;
9624   int button1   = player_action & JOY_BUTTON_1;
9625   int button2   = player_action & JOY_BUTTON_2;
9626   int dx        = (left ? -1 : right ? 1 : 0);
9627   int dy        = (up   ? -1 : down  ? 1 : 0);
9628
9629   if (!player->active || tape.pausing)
9630     return 0;
9631
9632   if (player_action)
9633   {
9634     if (button1)
9635       snapped = SnapField(player, dx, dy);
9636     else
9637     {
9638       if (button2)
9639         dropped = DropElement(player);
9640
9641       moved = MovePlayer(player, dx, dy);
9642     }
9643
9644     if (tape.single_step && tape.recording && !tape.pausing)
9645     {
9646       if (button1 || (dropped && !moved))
9647       {
9648         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9649         SnapField(player, 0, 0);                /* stop snapping */
9650       }
9651     }
9652
9653     SetPlayerWaiting(player, FALSE);
9654
9655     return player_action;
9656   }
9657   else
9658   {
9659     /* no actions for this player (no input at player's configured device) */
9660
9661     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9662     SnapField(player, 0, 0);
9663     CheckGravityMovementWhenNotMoving(player);
9664
9665     if (player->MovPos == 0)
9666       SetPlayerWaiting(player, TRUE);
9667
9668     if (player->MovPos == 0)    /* needed for tape.playing */
9669       player->is_moving = FALSE;
9670
9671     player->is_dropping = FALSE;
9672     player->is_dropping_pressed = FALSE;
9673     player->drop_pressed_delay = 0;
9674
9675     return 0;
9676   }
9677 }
9678
9679 static void CheckLevelTime()
9680 {
9681   int i;
9682
9683   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9684   {
9685     if (level.native_em_level->lev->home == 0)  /* all players at home */
9686     {
9687       PlayerWins(local_player);
9688
9689       AllPlayersGone = TRUE;
9690
9691       level.native_em_level->lev->home = -1;
9692     }
9693
9694     if (level.native_em_level->ply[0]->alive == 0 &&
9695         level.native_em_level->ply[1]->alive == 0 &&
9696         level.native_em_level->ply[2]->alive == 0 &&
9697         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9698       AllPlayersGone = TRUE;
9699   }
9700
9701   if (TimeFrames >= FRAMES_PER_SECOND)
9702   {
9703     TimeFrames = 0;
9704     TapeTime++;
9705
9706     for (i = 0; i < MAX_PLAYERS; i++)
9707     {
9708       struct PlayerInfo *player = &stored_player[i];
9709
9710       if (SHIELD_ON(player))
9711       {
9712         player->shield_normal_time_left--;
9713
9714         if (player->shield_deadly_time_left > 0)
9715           player->shield_deadly_time_left--;
9716       }
9717     }
9718
9719     if (!local_player->LevelSolved && !level.use_step_counter)
9720     {
9721       TimePlayed++;
9722
9723       if (TimeLeft > 0)
9724       {
9725         TimeLeft--;
9726
9727         if (TimeLeft <= 10 && setup.time_limit)
9728           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9729
9730         DrawGameValue_Time(TimeLeft);
9731
9732         if (!TimeLeft && setup.time_limit)
9733         {
9734           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9735             level.native_em_level->lev->killed_out_of_time = TRUE;
9736           else
9737             for (i = 0; i < MAX_PLAYERS; i++)
9738               KillPlayer(&stored_player[i]);
9739         }
9740       }
9741       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9742         DrawGameValue_Time(TimePlayed);
9743
9744       level.native_em_level->lev->time =
9745         (level.time == 0 ? TimePlayed : TimeLeft);
9746     }
9747
9748     if (tape.recording || tape.playing)
9749       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9750   }
9751 }
9752
9753 void AdvanceFrameAndPlayerCounters(int player_nr)
9754 {
9755   int i;
9756
9757   /* advance frame counters (global frame counter and time frame counter) */
9758   FrameCounter++;
9759   TimeFrames++;
9760
9761   /* advance player counters (counters for move delay, move animation etc.) */
9762   for (i = 0; i < MAX_PLAYERS; i++)
9763   {
9764     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9765     int move_delay_value = stored_player[i].move_delay_value;
9766     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9767
9768     if (!advance_player_counters)       /* not all players may be affected */
9769       continue;
9770
9771 #if USE_NEW_PLAYER_ANIM
9772     if (move_frames == 0)       /* less than one move per game frame */
9773     {
9774       int stepsize = TILEX / move_delay_value;
9775       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9776       int count = (stored_player[i].is_moving ?
9777                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9778
9779       if (count % delay == 0)
9780         move_frames = 1;
9781     }
9782 #endif
9783
9784     stored_player[i].Frame += move_frames;
9785
9786     if (stored_player[i].MovPos != 0)
9787       stored_player[i].StepFrame += move_frames;
9788
9789     if (stored_player[i].move_delay > 0)
9790       stored_player[i].move_delay--;
9791
9792     /* due to bugs in previous versions, counter must count up, not down */
9793     if (stored_player[i].push_delay != -1)
9794       stored_player[i].push_delay++;
9795
9796     if (stored_player[i].drop_delay > 0)
9797       stored_player[i].drop_delay--;
9798
9799     if (stored_player[i].is_dropping_pressed)
9800       stored_player[i].drop_pressed_delay++;
9801   }
9802 }
9803
9804 void StartGameActions(boolean init_network_game, boolean record_tape,
9805                       long random_seed)
9806 {
9807   unsigned long new_random_seed = InitRND(random_seed);
9808
9809   if (record_tape)
9810     TapeStartRecording(new_random_seed);
9811
9812 #if defined(NETWORK_AVALIABLE)
9813   if (init_network_game)
9814   {
9815     SendToServer_StartPlaying();
9816
9817     return;
9818   }
9819 #endif
9820
9821   InitGame();
9822 }
9823
9824 void GameActions()
9825 {
9826   static unsigned long game_frame_delay = 0;
9827   unsigned long game_frame_delay_value;
9828   byte *recorded_player_action;
9829   byte summarized_player_action = 0;
9830   byte tape_action[MAX_PLAYERS];
9831   int i;
9832
9833   /* detect endless loops, caused by custom element programming */
9834   if (recursion_loop_detected && recursion_loop_depth == 0)
9835   {
9836     char *message = getStringCat3("Internal Error ! Element ",
9837                                   EL_NAME(recursion_loop_element),
9838                                   " caused endless loop ! Quit the game ?");
9839
9840     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9841           EL_NAME(recursion_loop_element));
9842
9843     RequestQuitGameExt(FALSE, level_editor_test_game, message);
9844
9845     recursion_loop_detected = FALSE;    /* if game should be continued */
9846
9847     free(message);
9848
9849     return;
9850   }
9851
9852   if (game.restart_level)
9853     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9854
9855   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9856   {
9857     if (level.native_em_level->lev->home == 0)  /* all players at home */
9858     {
9859       PlayerWins(local_player);
9860
9861       AllPlayersGone = TRUE;
9862
9863       level.native_em_level->lev->home = -1;
9864     }
9865
9866     if (level.native_em_level->ply[0]->alive == 0 &&
9867         level.native_em_level->ply[1]->alive == 0 &&
9868         level.native_em_level->ply[2]->alive == 0 &&
9869         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9870       AllPlayersGone = TRUE;
9871   }
9872
9873   if (local_player->LevelSolved)
9874     GameWon();
9875
9876   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9877     TapeStop();
9878
9879   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9880     return;
9881
9882   game_frame_delay_value =
9883     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9884
9885   if (tape.playing && tape.warp_forward && !tape.pausing)
9886     game_frame_delay_value = 0;
9887
9888   /* ---------- main game synchronization point ---------- */
9889
9890   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9891
9892   if (network_playing && !network_player_action_received)
9893   {
9894     /* try to get network player actions in time */
9895
9896 #if defined(NETWORK_AVALIABLE)
9897     /* last chance to get network player actions without main loop delay */
9898     HandleNetworking();
9899 #endif
9900
9901     /* game was quit by network peer */
9902     if (game_status != GAME_MODE_PLAYING)
9903       return;
9904
9905     if (!network_player_action_received)
9906       return;           /* failed to get network player actions in time */
9907
9908     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9909   }
9910
9911   if (tape.pausing)
9912     return;
9913
9914   /* at this point we know that we really continue executing the game */
9915
9916   network_player_action_received = FALSE;
9917
9918   /* when playing tape, read previously recorded player input from tape data */
9919   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9920
9921 #if 1
9922   /* TapePlayAction() may return NULL when toggling to "pause before death" */
9923   if (tape.pausing)
9924     return;
9925 #endif
9926
9927   if (tape.set_centered_player)
9928   {
9929     game.centered_player_nr_next = tape.centered_player_nr_next;
9930     game.set_centered_player = TRUE;
9931   }
9932
9933   for (i = 0; i < MAX_PLAYERS; i++)
9934   {
9935     summarized_player_action |= stored_player[i].action;
9936
9937     if (!network_playing)
9938       stored_player[i].effective_action = stored_player[i].action;
9939   }
9940
9941 #if defined(NETWORK_AVALIABLE)
9942   if (network_playing)
9943     SendToServer_MovePlayer(summarized_player_action);
9944 #endif
9945
9946   if (!options.network && !setup.team_mode)
9947     local_player->effective_action = summarized_player_action;
9948
9949   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9950   {
9951     for (i = 0; i < MAX_PLAYERS; i++)
9952       stored_player[i].effective_action =
9953         (i == game.centered_player_nr ? summarized_player_action : 0);
9954   }
9955
9956   if (recorded_player_action != NULL)
9957     for (i = 0; i < MAX_PLAYERS; i++)
9958       stored_player[i].effective_action = recorded_player_action[i];
9959
9960   for (i = 0; i < MAX_PLAYERS; i++)
9961   {
9962     tape_action[i] = stored_player[i].effective_action;
9963
9964     /* (this can only happen in the R'n'D game engine) */
9965     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9966       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9967   }
9968
9969   /* only record actions from input devices, but not programmed actions */
9970   if (tape.recording)
9971     TapeRecordAction(tape_action);
9972
9973   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9974   {
9975     GameActions_EM_Main();
9976   }
9977   else
9978   {
9979     GameActions_RND();
9980   }
9981 }
9982
9983 void GameActions_EM_Main()
9984 {
9985   byte effective_action[MAX_PLAYERS];
9986   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9987   int i;
9988
9989   for (i = 0; i < MAX_PLAYERS; i++)
9990     effective_action[i] = stored_player[i].effective_action;
9991
9992   GameActions_EM(effective_action, warp_mode);
9993
9994   CheckLevelTime();
9995
9996   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9997 }
9998
9999 void GameActions_RND()
10000 {
10001   int magic_wall_x = 0, magic_wall_y = 0;
10002   int i, x, y, element, graphic;
10003
10004   InitPlayfieldScanModeVars();
10005
10006 #if USE_ONE_MORE_CHANGE_PER_FRAME
10007   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10008   {
10009     SCAN_PLAYFIELD(x, y)
10010     {
10011       ChangeCount[x][y] = 0;
10012       ChangeEvent[x][y] = -1;
10013     }
10014   }
10015 #endif
10016
10017   if (game.set_centered_player)
10018   {
10019     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10020
10021     /* switching to "all players" only possible if all players fit to screen */
10022     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10023     {
10024       game.centered_player_nr_next = game.centered_player_nr;
10025       game.set_centered_player = FALSE;
10026     }
10027
10028     /* do not switch focus to non-existing (or non-active) player */
10029     if (game.centered_player_nr_next >= 0 &&
10030         !stored_player[game.centered_player_nr_next].active)
10031     {
10032       game.centered_player_nr_next = game.centered_player_nr;
10033       game.set_centered_player = FALSE;
10034     }
10035   }
10036
10037   if (game.set_centered_player &&
10038       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10039   {
10040     int sx, sy;
10041
10042     if (game.centered_player_nr_next == -1)
10043     {
10044       setScreenCenteredToAllPlayers(&sx, &sy);
10045     }
10046     else
10047     {
10048       sx = stored_player[game.centered_player_nr_next].jx;
10049       sy = stored_player[game.centered_player_nr_next].jy;
10050     }
10051
10052     game.centered_player_nr = game.centered_player_nr_next;
10053     game.set_centered_player = FALSE;
10054
10055     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10056     DrawGameDoorValues();
10057   }
10058
10059   for (i = 0; i < MAX_PLAYERS; i++)
10060   {
10061     int actual_player_action = stored_player[i].effective_action;
10062
10063 #if 1
10064     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10065        - rnd_equinox_tetrachloride 048
10066        - rnd_equinox_tetrachloride_ii 096
10067        - rnd_emanuel_schmieg 002
10068        - doctor_sloan_ww 001, 020
10069     */
10070     if (stored_player[i].MovPos == 0)
10071       CheckGravityMovement(&stored_player[i]);
10072 #endif
10073
10074     /* overwrite programmed action with tape action */
10075     if (stored_player[i].programmed_action)
10076       actual_player_action = stored_player[i].programmed_action;
10077
10078     PlayerActions(&stored_player[i], actual_player_action);
10079
10080     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10081   }
10082
10083   ScrollScreen(NULL, SCROLL_GO_ON);
10084
10085   /* for backwards compatibility, the following code emulates a fixed bug that
10086      occured when pushing elements (causing elements that just made their last
10087      pushing step to already (if possible) make their first falling step in the
10088      same game frame, which is bad); this code is also needed to use the famous
10089      "spring push bug" which is used in older levels and might be wanted to be
10090      used also in newer levels, but in this case the buggy pushing code is only
10091      affecting the "spring" element and no other elements */
10092
10093   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10094   {
10095     for (i = 0; i < MAX_PLAYERS; i++)
10096     {
10097       struct PlayerInfo *player = &stored_player[i];
10098       int x = player->jx;
10099       int y = player->jy;
10100
10101       if (player->active && player->is_pushing && player->is_moving &&
10102           IS_MOVING(x, y) &&
10103           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10104            Feld[x][y] == EL_SPRING))
10105       {
10106         ContinueMoving(x, y);
10107
10108         /* continue moving after pushing (this is actually a bug) */
10109         if (!IS_MOVING(x, y))
10110         {
10111           Stop[x][y] = FALSE;
10112         }
10113       }
10114     }
10115   }
10116
10117   SCAN_PLAYFIELD(x, y)
10118   {
10119     ChangeCount[x][y] = 0;
10120     ChangeEvent[x][y] = -1;
10121
10122     /* this must be handled before main playfield loop */
10123     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10124     {
10125       MovDelay[x][y]--;
10126       if (MovDelay[x][y] <= 0)
10127         RemoveField(x, y);
10128     }
10129
10130 #if USE_NEW_SNAP_DELAY
10131     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10132     {
10133       MovDelay[x][y]--;
10134       if (MovDelay[x][y] <= 0)
10135       {
10136         RemoveField(x, y);
10137         DrawLevelField(x, y);
10138
10139         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10140       }
10141     }
10142 #endif
10143
10144 #if DEBUG
10145     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10146     {
10147       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10148       printf("GameActions(): This should never happen!\n");
10149
10150       ChangePage[x][y] = -1;
10151     }
10152 #endif
10153
10154     Stop[x][y] = FALSE;
10155     if (WasJustMoving[x][y] > 0)
10156       WasJustMoving[x][y]--;
10157     if (WasJustFalling[x][y] > 0)
10158       WasJustFalling[x][y]--;
10159     if (CheckCollision[x][y] > 0)
10160       CheckCollision[x][y]--;
10161     if (CheckImpact[x][y] > 0)
10162       CheckImpact[x][y]--;
10163
10164     GfxFrame[x][y]++;
10165
10166     /* reset finished pushing action (not done in ContinueMoving() to allow
10167        continuous pushing animation for elements with zero push delay) */
10168     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10169     {
10170       ResetGfxAnimation(x, y);
10171       DrawLevelField(x, y);
10172     }
10173
10174 #if DEBUG
10175     if (IS_BLOCKED(x, y))
10176     {
10177       int oldx, oldy;
10178
10179       Blocked2Moving(x, y, &oldx, &oldy);
10180       if (!IS_MOVING(oldx, oldy))
10181       {
10182         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10183         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10184         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10185         printf("GameActions(): This should never happen!\n");
10186       }
10187     }
10188 #endif
10189   }
10190
10191   SCAN_PLAYFIELD(x, y)
10192   {
10193     element = Feld[x][y];
10194     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10195
10196     ResetGfxFrame(x, y, TRUE);
10197
10198     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10199         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10200       ResetRandomAnimationValue(x, y);
10201
10202     SetRandomAnimationValue(x, y);
10203
10204     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10205
10206     if (IS_INACTIVE(element))
10207     {
10208       if (IS_ANIMATED(graphic))
10209         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10210
10211       continue;
10212     }
10213
10214     /* this may take place after moving, so 'element' may have changed */
10215     if (IS_CHANGING(x, y) &&
10216         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10217     {
10218       int page = element_info[element].event_page_nr[CE_DELAY];
10219
10220 #if 1
10221       HandleElementChange(x, y, page);
10222 #else
10223       if (CAN_CHANGE(element))
10224         HandleElementChange(x, y, page);
10225
10226       if (HAS_ACTION(element))
10227         ExecuteCustomElementAction(x, y, element, page);
10228 #endif
10229
10230       element = Feld[x][y];
10231       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10232     }
10233
10234     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10235     {
10236       StartMoving(x, y);
10237
10238       element = Feld[x][y];
10239       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10240
10241       if (IS_ANIMATED(graphic) &&
10242           !IS_MOVING(x, y) &&
10243           !Stop[x][y])
10244         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10245
10246       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10247         DrawTwinkleOnField(x, y);
10248     }
10249     else if ((element == EL_ACID ||
10250               element == EL_EXIT_OPEN ||
10251               element == EL_EM_EXIT_OPEN ||
10252               element == EL_SP_EXIT_OPEN ||
10253               element == EL_STEEL_EXIT_OPEN ||
10254               element == EL_EM_STEEL_EXIT_OPEN ||
10255               element == EL_SP_TERMINAL ||
10256               element == EL_SP_TERMINAL_ACTIVE ||
10257               element == EL_EXTRA_TIME ||
10258               element == EL_SHIELD_NORMAL ||
10259               element == EL_SHIELD_DEADLY) &&
10260              IS_ANIMATED(graphic))
10261       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10262     else if (IS_MOVING(x, y))
10263       ContinueMoving(x, y);
10264     else if (IS_ACTIVE_BOMB(element))
10265       CheckDynamite(x, y);
10266     else if (element == EL_AMOEBA_GROWING)
10267       AmoebeWaechst(x, y);
10268     else if (element == EL_AMOEBA_SHRINKING)
10269       AmoebaDisappearing(x, y);
10270
10271 #if !USE_NEW_AMOEBA_CODE
10272     else if (IS_AMOEBALIVE(element))
10273       AmoebeAbleger(x, y);
10274 #endif
10275
10276     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10277       Life(x, y);
10278     else if (element == EL_EXIT_CLOSED)
10279       CheckExit(x, y);
10280     else if (element == EL_EM_EXIT_CLOSED)
10281       CheckExitEM(x, y);
10282     else if (element == EL_STEEL_EXIT_CLOSED)
10283       CheckExitSteel(x, y);
10284     else if (element == EL_EM_STEEL_EXIT_CLOSED)
10285       CheckExitSteelEM(x, y);
10286     else if (element == EL_SP_EXIT_CLOSED)
10287       CheckExitSP(x, y);
10288     else if (element == EL_EXPANDABLE_WALL_GROWING ||
10289              element == EL_EXPANDABLE_STEELWALL_GROWING)
10290       MauerWaechst(x, y);
10291     else if (element == EL_EXPANDABLE_WALL ||
10292              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10293              element == EL_EXPANDABLE_WALL_VERTICAL ||
10294              element == EL_EXPANDABLE_WALL_ANY ||
10295              element == EL_BD_EXPANDABLE_WALL)
10296       MauerAbleger(x, y);
10297     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10298              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10299              element == EL_EXPANDABLE_STEELWALL_ANY)
10300       MauerAblegerStahl(x, y);
10301     else if (element == EL_FLAMES)
10302       CheckForDragon(x, y);
10303     else if (element == EL_EXPLOSION)
10304       ; /* drawing of correct explosion animation is handled separately */
10305     else if (element == EL_ELEMENT_SNAPPING ||
10306              element == EL_DIAGONAL_SHRINKING ||
10307              element == EL_DIAGONAL_GROWING)
10308     {
10309       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10310
10311       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10312     }
10313     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10314       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10315
10316     if (IS_BELT_ACTIVE(element))
10317       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10318
10319     if (game.magic_wall_active)
10320     {
10321       int jx = local_player->jx, jy = local_player->jy;
10322
10323       /* play the element sound at the position nearest to the player */
10324       if ((element == EL_MAGIC_WALL_FULL ||
10325            element == EL_MAGIC_WALL_ACTIVE ||
10326            element == EL_MAGIC_WALL_EMPTYING ||
10327            element == EL_BD_MAGIC_WALL_FULL ||
10328            element == EL_BD_MAGIC_WALL_ACTIVE ||
10329            element == EL_BD_MAGIC_WALL_EMPTYING ||
10330            element == EL_DC_MAGIC_WALL_FULL ||
10331            element == EL_DC_MAGIC_WALL_ACTIVE ||
10332            element == EL_DC_MAGIC_WALL_EMPTYING) &&
10333           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10334       {
10335         magic_wall_x = x;
10336         magic_wall_y = y;
10337       }
10338     }
10339   }
10340
10341 #if USE_NEW_AMOEBA_CODE
10342   /* new experimental amoeba growth stuff */
10343   if (!(FrameCounter % 8))
10344   {
10345     static unsigned long random = 1684108901;
10346
10347     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10348     {
10349       x = RND(lev_fieldx);
10350       y = RND(lev_fieldy);
10351       element = Feld[x][y];
10352
10353       if (!IS_PLAYER(x,y) &&
10354           (element == EL_EMPTY ||
10355            CAN_GROW_INTO(element) ||
10356            element == EL_QUICKSAND_EMPTY ||
10357            element == EL_QUICKSAND_FAST_EMPTY ||
10358            element == EL_ACID_SPLASH_LEFT ||
10359            element == EL_ACID_SPLASH_RIGHT))
10360       {
10361         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10362             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10363             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10364             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10365           Feld[x][y] = EL_AMOEBA_DROP;
10366       }
10367
10368       random = random * 129 + 1;
10369     }
10370   }
10371 #endif
10372
10373 #if 0
10374   if (game.explosions_delayed)
10375 #endif
10376   {
10377     game.explosions_delayed = FALSE;
10378
10379     SCAN_PLAYFIELD(x, y)
10380     {
10381       element = Feld[x][y];
10382
10383       if (ExplodeField[x][y])
10384         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10385       else if (element == EL_EXPLOSION)
10386         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10387
10388       ExplodeField[x][y] = EX_TYPE_NONE;
10389     }
10390
10391     game.explosions_delayed = TRUE;
10392   }
10393
10394   if (game.magic_wall_active)
10395   {
10396     if (!(game.magic_wall_time_left % 4))
10397     {
10398       int element = Feld[magic_wall_x][magic_wall_y];
10399
10400       if (element == EL_BD_MAGIC_WALL_FULL ||
10401           element == EL_BD_MAGIC_WALL_ACTIVE ||
10402           element == EL_BD_MAGIC_WALL_EMPTYING)
10403         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10404       else if (element == EL_DC_MAGIC_WALL_FULL ||
10405                element == EL_DC_MAGIC_WALL_ACTIVE ||
10406                element == EL_DC_MAGIC_WALL_EMPTYING)
10407         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10408       else
10409         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10410     }
10411
10412     if (game.magic_wall_time_left > 0)
10413     {
10414       game.magic_wall_time_left--;
10415       if (!game.magic_wall_time_left)
10416       {
10417         SCAN_PLAYFIELD(x, y)
10418         {
10419           element = Feld[x][y];
10420
10421           if (element == EL_MAGIC_WALL_ACTIVE ||
10422               element == EL_MAGIC_WALL_FULL)
10423           {
10424             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10425             DrawLevelField(x, y);
10426           }
10427           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10428                    element == EL_BD_MAGIC_WALL_FULL)
10429           {
10430             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10431             DrawLevelField(x, y);
10432           }
10433           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10434                    element == EL_DC_MAGIC_WALL_FULL)
10435           {
10436             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10437             DrawLevelField(x, y);
10438           }
10439         }
10440
10441         game.magic_wall_active = FALSE;
10442       }
10443     }
10444   }
10445
10446   if (game.light_time_left > 0)
10447   {
10448     game.light_time_left--;
10449
10450     if (game.light_time_left == 0)
10451       RedrawAllLightSwitchesAndInvisibleElements();
10452   }
10453
10454   if (game.timegate_time_left > 0)
10455   {
10456     game.timegate_time_left--;
10457
10458     if (game.timegate_time_left == 0)
10459       CloseAllOpenTimegates();
10460   }
10461
10462   if (game.lenses_time_left > 0)
10463   {
10464     game.lenses_time_left--;
10465
10466     if (game.lenses_time_left == 0)
10467       RedrawAllInvisibleElementsForLenses();
10468   }
10469
10470   if (game.magnify_time_left > 0)
10471   {
10472     game.magnify_time_left--;
10473
10474     if (game.magnify_time_left == 0)
10475       RedrawAllInvisibleElementsForMagnifier();
10476   }
10477
10478   for (i = 0; i < MAX_PLAYERS; i++)
10479   {
10480     struct PlayerInfo *player = &stored_player[i];
10481
10482     if (SHIELD_ON(player))
10483     {
10484       if (player->shield_deadly_time_left)
10485         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10486       else if (player->shield_normal_time_left)
10487         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10488     }
10489   }
10490
10491   CheckLevelTime();
10492
10493   DrawAllPlayers();
10494   PlayAllPlayersSound();
10495
10496   if (options.debug)                    /* calculate frames per second */
10497   {
10498     static unsigned long fps_counter = 0;
10499     static int fps_frames = 0;
10500     unsigned long fps_delay_ms = Counter() - fps_counter;
10501
10502     fps_frames++;
10503
10504     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10505     {
10506       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10507
10508       fps_frames = 0;
10509       fps_counter = Counter();
10510     }
10511
10512     redraw_mask |= REDRAW_FPS;
10513   }
10514
10515   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10516
10517   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10518   {
10519     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10520
10521     local_player->show_envelope = 0;
10522   }
10523
10524   /* use random number generator in every frame to make it less predictable */
10525   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10526     RND(1);
10527 }
10528
10529 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10530 {
10531   int min_x = x, min_y = y, max_x = x, max_y = y;
10532   int i;
10533
10534   for (i = 0; i < MAX_PLAYERS; i++)
10535   {
10536     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10537
10538     if (!stored_player[i].active || &stored_player[i] == player)
10539       continue;
10540
10541     min_x = MIN(min_x, jx);
10542     min_y = MIN(min_y, jy);
10543     max_x = MAX(max_x, jx);
10544     max_y = MAX(max_y, jy);
10545   }
10546
10547   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10548 }
10549
10550 static boolean AllPlayersInVisibleScreen()
10551 {
10552   int i;
10553
10554   for (i = 0; i < MAX_PLAYERS; i++)
10555   {
10556     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10557
10558     if (!stored_player[i].active)
10559       continue;
10560
10561     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10562       return FALSE;
10563   }
10564
10565   return TRUE;
10566 }
10567
10568 void ScrollLevel(int dx, int dy)
10569 {
10570 #if 1
10571   static Bitmap *bitmap_db_field2 = NULL;
10572   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10573   int x, y;
10574 #else
10575   int i, x, y;
10576 #endif
10577
10578   /* only horizontal XOR vertical scroll direction allowed */
10579   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
10580     return;
10581
10582 #if 1
10583   if (bitmap_db_field2 == NULL)
10584     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
10585
10586   BlitBitmap(drawto_field, bitmap_db_field2,
10587              FX + TILEX * (dx == -1) - softscroll_offset,
10588              FY + TILEY * (dy == -1) - softscroll_offset,
10589              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10590              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10591              FX + TILEX * (dx == 1) - softscroll_offset,
10592              FY + TILEY * (dy == 1) - softscroll_offset);
10593   BlitBitmap(bitmap_db_field2, drawto_field,
10594              FX + TILEX * (dx == 1) - softscroll_offset,
10595              FY + TILEY * (dy == 1) - softscroll_offset,
10596              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10597              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10598              FX + TILEX * (dx == 1) - softscroll_offset,
10599              FY + TILEY * (dy == 1) - softscroll_offset);
10600
10601 #else
10602
10603 #if 1
10604   int xsize = (BX2 - BX1 + 1);
10605   int ysize = (BY2 - BY1 + 1);
10606   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
10607   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
10608   int step  = (start < end ? +1 : -1);
10609
10610   for (i = start; i != end; i += step)
10611   {
10612     BlitBitmap(drawto_field, drawto_field,
10613                FX + TILEX * (dx != 0 ? i + step : 0),
10614                FY + TILEY * (dy != 0 ? i + step : 0),
10615                TILEX * (dx != 0 ? 1 : xsize),
10616                TILEY * (dy != 0 ? 1 : ysize),
10617                FX + TILEX * (dx != 0 ? i : 0),
10618                FY + TILEY * (dy != 0 ? i : 0));
10619   }
10620
10621 #else
10622
10623   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10624
10625   BlitBitmap(drawto_field, drawto_field,
10626              FX + TILEX * (dx == -1) - softscroll_offset,
10627              FY + TILEY * (dy == -1) - softscroll_offset,
10628              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10629              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10630              FX + TILEX * (dx == 1) - softscroll_offset,
10631              FY + TILEY * (dy == 1) - softscroll_offset);
10632 #endif
10633 #endif
10634
10635   if (dx != 0)
10636   {
10637     x = (dx == 1 ? BX1 : BX2);
10638     for (y = BY1; y <= BY2; y++)
10639       DrawScreenField(x, y);
10640   }
10641
10642   if (dy != 0)
10643   {
10644     y = (dy == 1 ? BY1 : BY2);
10645     for (x = BX1; x <= BX2; x++)
10646       DrawScreenField(x, y);
10647   }
10648
10649   redraw_mask |= REDRAW_FIELD;
10650 }
10651
10652 static boolean canFallDown(struct PlayerInfo *player)
10653 {
10654   int jx = player->jx, jy = player->jy;
10655
10656   return (IN_LEV_FIELD(jx, jy + 1) &&
10657           (IS_FREE(jx, jy + 1) ||
10658            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10659           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10660           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10661 }
10662
10663 static boolean canPassField(int x, int y, int move_dir)
10664 {
10665   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10666   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10667   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10668   int nextx = x + dx;
10669   int nexty = y + dy;
10670   int element = Feld[x][y];
10671
10672   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10673           !CAN_MOVE(element) &&
10674           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10675           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10676           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10677 }
10678
10679 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10680 {
10681   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10682   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10683   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10684   int newx = x + dx;
10685   int newy = y + dy;
10686
10687   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10688           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10689           (IS_DIGGABLE(Feld[newx][newy]) ||
10690            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10691            canPassField(newx, newy, move_dir)));
10692 }
10693
10694 static void CheckGravityMovement(struct PlayerInfo *player)
10695 {
10696 #if USE_PLAYER_GRAVITY
10697   if (player->gravity && !player->programmed_action)
10698 #else
10699   if (game.gravity && !player->programmed_action)
10700 #endif
10701   {
10702     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10703     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10704     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10705     int jx = player->jx, jy = player->jy;
10706     boolean player_is_moving_to_valid_field =
10707       (!player_is_snapping &&
10708        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10709         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10710     boolean player_can_fall_down = canFallDown(player);
10711
10712     if (player_can_fall_down &&
10713         !player_is_moving_to_valid_field)
10714       player->programmed_action = MV_DOWN;
10715   }
10716 }
10717
10718 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10719 {
10720   return CheckGravityMovement(player);
10721
10722 #if USE_PLAYER_GRAVITY
10723   if (player->gravity && !player->programmed_action)
10724 #else
10725   if (game.gravity && !player->programmed_action)
10726 #endif
10727   {
10728     int jx = player->jx, jy = player->jy;
10729     boolean field_under_player_is_free =
10730       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10731     boolean player_is_standing_on_valid_field =
10732       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10733        (IS_WALKABLE(Feld[jx][jy]) &&
10734         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10735
10736     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10737       player->programmed_action = MV_DOWN;
10738   }
10739 }
10740
10741 /*
10742   MovePlayerOneStep()
10743   -----------------------------------------------------------------------------
10744   dx, dy:               direction (non-diagonal) to try to move the player to
10745   real_dx, real_dy:     direction as read from input device (can be diagonal)
10746 */
10747
10748 boolean MovePlayerOneStep(struct PlayerInfo *player,
10749                           int dx, int dy, int real_dx, int real_dy)
10750 {
10751   int jx = player->jx, jy = player->jy;
10752   int new_jx = jx + dx, new_jy = jy + dy;
10753 #if !USE_FIXED_DONT_RUN_INTO
10754   int element;
10755 #endif
10756   int can_move;
10757   boolean player_can_move = !player->cannot_move;
10758
10759   if (!player->active || (!dx && !dy))
10760     return MP_NO_ACTION;
10761
10762   player->MovDir = (dx < 0 ? MV_LEFT :
10763                     dx > 0 ? MV_RIGHT :
10764                     dy < 0 ? MV_UP :
10765                     dy > 0 ? MV_DOWN :  MV_NONE);
10766
10767   if (!IN_LEV_FIELD(new_jx, new_jy))
10768     return MP_NO_ACTION;
10769
10770   if (!player_can_move)
10771   {
10772     if (player->MovPos == 0)
10773     {
10774       player->is_moving = FALSE;
10775       player->is_digging = FALSE;
10776       player->is_collecting = FALSE;
10777       player->is_snapping = FALSE;
10778       player->is_pushing = FALSE;
10779     }
10780   }
10781
10782 #if 1
10783   if (!options.network && game.centered_player_nr == -1 &&
10784       !AllPlayersInSight(player, new_jx, new_jy))
10785     return MP_NO_ACTION;
10786 #else
10787   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10788     return MP_NO_ACTION;
10789 #endif
10790
10791 #if !USE_FIXED_DONT_RUN_INTO
10792   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10793
10794   /* (moved to DigField()) */
10795   if (player_can_move && DONT_RUN_INTO(element))
10796   {
10797     if (element == EL_ACID && dx == 0 && dy == 1)
10798     {
10799       SplashAcid(new_jx, new_jy);
10800       Feld[jx][jy] = EL_PLAYER_1;
10801       InitMovingField(jx, jy, MV_DOWN);
10802       Store[jx][jy] = EL_ACID;
10803       ContinueMoving(jx, jy);
10804       BuryPlayer(player);
10805     }
10806     else
10807       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10808
10809     return MP_MOVING;
10810   }
10811 #endif
10812
10813   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10814   if (can_move != MP_MOVING)
10815     return can_move;
10816
10817   /* check if DigField() has caused relocation of the player */
10818   if (player->jx != jx || player->jy != jy)
10819     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10820
10821   StorePlayer[jx][jy] = 0;
10822   player->last_jx = jx;
10823   player->last_jy = jy;
10824   player->jx = new_jx;
10825   player->jy = new_jy;
10826   StorePlayer[new_jx][new_jy] = player->element_nr;
10827
10828   if (player->move_delay_value_next != -1)
10829   {
10830     player->move_delay_value = player->move_delay_value_next;
10831     player->move_delay_value_next = -1;
10832   }
10833
10834   player->MovPos =
10835     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10836
10837   player->step_counter++;
10838
10839   PlayerVisit[jx][jy] = FrameCounter;
10840
10841 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10842   player->is_moving = TRUE;
10843 #endif
10844
10845 #if 1
10846   /* should better be called in MovePlayer(), but this breaks some tapes */
10847   ScrollPlayer(player, SCROLL_INIT);
10848 #endif
10849
10850   return MP_MOVING;
10851 }
10852
10853 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10854 {
10855   int jx = player->jx, jy = player->jy;
10856   int old_jx = jx, old_jy = jy;
10857   int moved = MP_NO_ACTION;
10858
10859   if (!player->active)
10860     return FALSE;
10861
10862   if (!dx && !dy)
10863   {
10864     if (player->MovPos == 0)
10865     {
10866       player->is_moving = FALSE;
10867       player->is_digging = FALSE;
10868       player->is_collecting = FALSE;
10869       player->is_snapping = FALSE;
10870       player->is_pushing = FALSE;
10871     }
10872
10873     return FALSE;
10874   }
10875
10876   if (player->move_delay > 0)
10877     return FALSE;
10878
10879   player->move_delay = -1;              /* set to "uninitialized" value */
10880
10881   /* store if player is automatically moved to next field */
10882   player->is_auto_moving = (player->programmed_action != MV_NONE);
10883
10884   /* remove the last programmed player action */
10885   player->programmed_action = 0;
10886
10887   if (player->MovPos)
10888   {
10889     /* should only happen if pre-1.2 tape recordings are played */
10890     /* this is only for backward compatibility */
10891
10892     int original_move_delay_value = player->move_delay_value;
10893
10894 #if DEBUG
10895     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10896            tape.counter);
10897 #endif
10898
10899     /* scroll remaining steps with finest movement resolution */
10900     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10901
10902     while (player->MovPos)
10903     {
10904       ScrollPlayer(player, SCROLL_GO_ON);
10905       ScrollScreen(NULL, SCROLL_GO_ON);
10906
10907       AdvanceFrameAndPlayerCounters(player->index_nr);
10908
10909       DrawAllPlayers();
10910       BackToFront();
10911     }
10912
10913     player->move_delay_value = original_move_delay_value;
10914   }
10915
10916   player->is_active = FALSE;
10917
10918   if (player->last_move_dir & MV_HORIZONTAL)
10919   {
10920     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10921       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10922   }
10923   else
10924   {
10925     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10926       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10927   }
10928
10929 #if USE_FIXED_BORDER_RUNNING_GFX
10930   if (!moved && !player->is_active)
10931   {
10932     player->is_moving = FALSE;
10933     player->is_digging = FALSE;
10934     player->is_collecting = FALSE;
10935     player->is_snapping = FALSE;
10936     player->is_pushing = FALSE;
10937   }
10938 #endif
10939
10940   jx = player->jx;
10941   jy = player->jy;
10942
10943 #if 1
10944   if (moved & MP_MOVING && !ScreenMovPos &&
10945       (player->index_nr == game.centered_player_nr ||
10946        game.centered_player_nr == -1))
10947 #else
10948   if (moved & MP_MOVING && !ScreenMovPos &&
10949       (player == local_player || !options.network))
10950 #endif
10951   {
10952     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10953     int offset = (setup.scroll_delay ? 3 : 0);
10954
10955     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10956     {
10957       /* actual player has left the screen -- scroll in that direction */
10958       if (jx != old_jx)         /* player has moved horizontally */
10959         scroll_x += (jx - old_jx);
10960       else                      /* player has moved vertically */
10961         scroll_y += (jy - old_jy);
10962     }
10963     else
10964     {
10965       if (jx != old_jx)         /* player has moved horizontally */
10966       {
10967         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10968             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10969           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10970
10971         /* don't scroll over playfield boundaries */
10972         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10973           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10974
10975         /* don't scroll more than one field at a time */
10976         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10977
10978         /* don't scroll against the player's moving direction */
10979         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10980             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10981           scroll_x = old_scroll_x;
10982       }
10983       else                      /* player has moved vertically */
10984       {
10985         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10986             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10987           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10988
10989         /* don't scroll over playfield boundaries */
10990         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10991           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10992
10993         /* don't scroll more than one field at a time */
10994         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10995
10996         /* don't scroll against the player's moving direction */
10997         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10998             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10999           scroll_y = old_scroll_y;
11000       }
11001     }
11002
11003     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11004     {
11005 #if 1
11006       if (!options.network && game.centered_player_nr == -1 &&
11007           !AllPlayersInVisibleScreen())
11008       {
11009         scroll_x = old_scroll_x;
11010         scroll_y = old_scroll_y;
11011       }
11012       else
11013 #else
11014       if (!options.network && !AllPlayersInVisibleScreen())
11015       {
11016         scroll_x = old_scroll_x;
11017         scroll_y = old_scroll_y;
11018       }
11019       else
11020 #endif
11021       {
11022         ScrollScreen(player, SCROLL_INIT);
11023         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11024       }
11025     }
11026   }
11027
11028   player->StepFrame = 0;
11029
11030   if (moved & MP_MOVING)
11031   {
11032     if (old_jx != jx && old_jy == jy)
11033       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11034     else if (old_jx == jx && old_jy != jy)
11035       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11036
11037     DrawLevelField(jx, jy);     /* for "crumbled sand" */
11038
11039     player->last_move_dir = player->MovDir;
11040     player->is_moving = TRUE;
11041     player->is_snapping = FALSE;
11042     player->is_switching = FALSE;
11043     player->is_dropping = FALSE;
11044     player->is_dropping_pressed = FALSE;
11045     player->drop_pressed_delay = 0;
11046
11047 #if 0
11048     /* should better be called here than above, but this breaks some tapes */
11049     ScrollPlayer(player, SCROLL_INIT);
11050 #endif
11051   }
11052   else
11053   {
11054     CheckGravityMovementWhenNotMoving(player);
11055
11056     player->is_moving = FALSE;
11057
11058     /* at this point, the player is allowed to move, but cannot move right now
11059        (e.g. because of something blocking the way) -- ensure that the player
11060        is also allowed to move in the next frame (in old versions before 3.1.1,
11061        the player was forced to wait again for eight frames before next try) */
11062
11063     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11064       player->move_delay = 0;   /* allow direct movement in the next frame */
11065   }
11066
11067   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11068     player->move_delay = player->move_delay_value;
11069
11070   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11071   {
11072     TestIfPlayerTouchesBadThing(jx, jy);
11073     TestIfPlayerTouchesCustomElement(jx, jy);
11074   }
11075
11076   if (!player->active)
11077     RemovePlayer(player);
11078
11079   return moved;
11080 }
11081
11082 void ScrollPlayer(struct PlayerInfo *player, int mode)
11083 {
11084   int jx = player->jx, jy = player->jy;
11085   int last_jx = player->last_jx, last_jy = player->last_jy;
11086   int move_stepsize = TILEX / player->move_delay_value;
11087
11088 #if USE_NEW_PLAYER_SPEED
11089   if (!player->active)
11090     return;
11091
11092   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11093     return;
11094 #else
11095   if (!player->active || player->MovPos == 0)
11096     return;
11097 #endif
11098
11099   if (mode == SCROLL_INIT)
11100   {
11101     player->actual_frame_counter = FrameCounter;
11102     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11103
11104     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11105         Feld[last_jx][last_jy] == EL_EMPTY)
11106     {
11107       int last_field_block_delay = 0;   /* start with no blocking at all */
11108       int block_delay_adjustment = player->block_delay_adjustment;
11109
11110       /* if player blocks last field, add delay for exactly one move */
11111       if (player->block_last_field)
11112       {
11113         last_field_block_delay += player->move_delay_value;
11114
11115         /* when blocking enabled, prevent moving up despite gravity */
11116 #if USE_PLAYER_GRAVITY
11117         if (player->gravity && player->MovDir == MV_UP)
11118           block_delay_adjustment = -1;
11119 #else
11120         if (game.gravity && player->MovDir == MV_UP)
11121           block_delay_adjustment = -1;
11122 #endif
11123       }
11124
11125       /* add block delay adjustment (also possible when not blocking) */
11126       last_field_block_delay += block_delay_adjustment;
11127
11128       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11129       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11130     }
11131
11132 #if USE_NEW_PLAYER_SPEED
11133     if (player->MovPos != 0)    /* player has not yet reached destination */
11134       return;
11135 #else
11136     return;
11137 #endif
11138   }
11139   else if (!FrameReached(&player->actual_frame_counter, 1))
11140     return;
11141
11142 #if USE_NEW_PLAYER_SPEED
11143   if (player->MovPos != 0)
11144   {
11145     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11146     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11147
11148     /* before DrawPlayer() to draw correct player graphic for this case */
11149     if (player->MovPos == 0)
11150       CheckGravityMovement(player);
11151   }
11152 #else
11153   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11154   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11155
11156   /* before DrawPlayer() to draw correct player graphic for this case */
11157   if (player->MovPos == 0)
11158     CheckGravityMovement(player);
11159 #endif
11160
11161   if (player->MovPos == 0)      /* player reached destination field */
11162   {
11163     if (player->move_delay_reset_counter > 0)
11164     {
11165       player->move_delay_reset_counter--;
11166
11167       if (player->move_delay_reset_counter == 0)
11168       {
11169         /* continue with normal speed after quickly moving through gate */
11170         HALVE_PLAYER_SPEED(player);
11171
11172         /* be able to make the next move without delay */
11173         player->move_delay = 0;
11174       }
11175     }
11176
11177     player->last_jx = jx;
11178     player->last_jy = jy;
11179
11180     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11181         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11182         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11183         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11184         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11185         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11186     {
11187       DrawPlayer(player);       /* needed here only to cleanup last field */
11188       RemovePlayer(player);
11189
11190       if (local_player->friends_still_needed == 0 ||
11191           IS_SP_ELEMENT(Feld[jx][jy]))
11192         PlayerWins(player);
11193     }
11194
11195     /* this breaks one level: "machine", level 000 */
11196     {
11197       int move_direction = player->MovDir;
11198       int enter_side = MV_DIR_OPPOSITE(move_direction);
11199       int leave_side = move_direction;
11200       int old_jx = last_jx;
11201       int old_jy = last_jy;
11202       int old_element = Feld[old_jx][old_jy];
11203       int new_element = Feld[jx][jy];
11204
11205       if (IS_CUSTOM_ELEMENT(old_element))
11206         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11207                                    CE_LEFT_BY_PLAYER,
11208                                    player->index_bit, leave_side);
11209
11210       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11211                                           CE_PLAYER_LEAVES_X,
11212                                           player->index_bit, leave_side);
11213
11214       if (IS_CUSTOM_ELEMENT(new_element))
11215         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11216                                    player->index_bit, enter_side);
11217
11218       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11219                                           CE_PLAYER_ENTERS_X,
11220                                           player->index_bit, enter_side);
11221
11222       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11223                                         CE_MOVE_OF_X, move_direction);
11224     }
11225
11226     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11227     {
11228       TestIfPlayerTouchesBadThing(jx, jy);
11229       TestIfPlayerTouchesCustomElement(jx, jy);
11230
11231       /* needed because pushed element has not yet reached its destination,
11232          so it would trigger a change event at its previous field location */
11233       if (!player->is_pushing)
11234         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
11235
11236       if (!player->active)
11237         RemovePlayer(player);
11238     }
11239
11240     if (!local_player->LevelSolved && level.use_step_counter)
11241     {
11242       int i;
11243
11244       TimePlayed++;
11245
11246       if (TimeLeft > 0)
11247       {
11248         TimeLeft--;
11249
11250         if (TimeLeft <= 10 && setup.time_limit)
11251           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11252
11253         DrawGameValue_Time(TimeLeft);
11254
11255         if (!TimeLeft && setup.time_limit)
11256           for (i = 0; i < MAX_PLAYERS; i++)
11257             KillPlayer(&stored_player[i]);
11258       }
11259       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11260         DrawGameValue_Time(TimePlayed);
11261     }
11262
11263     if (tape.single_step && tape.recording && !tape.pausing &&
11264         !player->programmed_action)
11265       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11266   }
11267 }
11268
11269 void ScrollScreen(struct PlayerInfo *player, int mode)
11270 {
11271   static unsigned long screen_frame_counter = 0;
11272
11273   if (mode == SCROLL_INIT)
11274   {
11275     /* set scrolling step size according to actual player's moving speed */
11276     ScrollStepSize = TILEX / player->move_delay_value;
11277
11278     screen_frame_counter = FrameCounter;
11279     ScreenMovDir = player->MovDir;
11280     ScreenMovPos = player->MovPos;
11281     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11282     return;
11283   }
11284   else if (!FrameReached(&screen_frame_counter, 1))
11285     return;
11286
11287   if (ScreenMovPos)
11288   {
11289     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11290     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11291     redraw_mask |= REDRAW_FIELD;
11292   }
11293   else
11294     ScreenMovDir = MV_NONE;
11295 }
11296
11297 void TestIfPlayerTouchesCustomElement(int x, int y)
11298 {
11299   static int xy[4][2] =
11300   {
11301     { 0, -1 },
11302     { -1, 0 },
11303     { +1, 0 },
11304     { 0, +1 }
11305   };
11306   static int trigger_sides[4][2] =
11307   {
11308     /* center side       border side */
11309     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11310     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11311     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11312     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11313   };
11314   static int touch_dir[4] =
11315   {
11316     MV_LEFT | MV_RIGHT,
11317     MV_UP   | MV_DOWN,
11318     MV_UP   | MV_DOWN,
11319     MV_LEFT | MV_RIGHT
11320   };
11321   int center_element = Feld[x][y];      /* should always be non-moving! */
11322   int i;
11323
11324   for (i = 0; i < NUM_DIRECTIONS; i++)
11325   {
11326     int xx = x + xy[i][0];
11327     int yy = y + xy[i][1];
11328     int center_side = trigger_sides[i][0];
11329     int border_side = trigger_sides[i][1];
11330     int border_element;
11331
11332     if (!IN_LEV_FIELD(xx, yy))
11333       continue;
11334
11335     if (IS_PLAYER(x, y))
11336     {
11337       struct PlayerInfo *player = PLAYERINFO(x, y);
11338
11339       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11340         border_element = Feld[xx][yy];          /* may be moving! */
11341       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11342         border_element = Feld[xx][yy];
11343       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11344         border_element = MovingOrBlocked2Element(xx, yy);
11345       else
11346         continue;               /* center and border element do not touch */
11347
11348       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11349                                  player->index_bit, border_side);
11350       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11351                                           CE_PLAYER_TOUCHES_X,
11352                                           player->index_bit, border_side);
11353     }
11354     else if (IS_PLAYER(xx, yy))
11355     {
11356       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11357
11358       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11359       {
11360         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11361           continue;             /* center and border element do not touch */
11362       }
11363
11364       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11365                                  player->index_bit, center_side);
11366       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11367                                           CE_PLAYER_TOUCHES_X,
11368                                           player->index_bit, center_side);
11369       break;
11370     }
11371   }
11372 }
11373
11374 #if USE_ELEMENT_TOUCHING_BUGFIX
11375
11376 void TestIfElementTouchesCustomElement(int x, int y)
11377 {
11378   static int xy[4][2] =
11379   {
11380     { 0, -1 },
11381     { -1, 0 },
11382     { +1, 0 },
11383     { 0, +1 }
11384   };
11385   static int trigger_sides[4][2] =
11386   {
11387     /* center side      border side */
11388     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11389     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11390     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11391     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11392   };
11393   static int touch_dir[4] =
11394   {
11395     MV_LEFT | MV_RIGHT,
11396     MV_UP   | MV_DOWN,
11397     MV_UP   | MV_DOWN,
11398     MV_LEFT | MV_RIGHT
11399   };
11400   boolean change_center_element = FALSE;
11401   int center_element = Feld[x][y];      /* should always be non-moving! */
11402   int border_element_old[NUM_DIRECTIONS];
11403   int i;
11404
11405   for (i = 0; i < NUM_DIRECTIONS; i++)
11406   {
11407     int xx = x + xy[i][0];
11408     int yy = y + xy[i][1];
11409     int border_element;
11410
11411     border_element_old[i] = -1;
11412
11413     if (!IN_LEV_FIELD(xx, yy))
11414       continue;
11415
11416     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11417       border_element = Feld[xx][yy];    /* may be moving! */
11418     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11419       border_element = Feld[xx][yy];
11420     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11421       border_element = MovingOrBlocked2Element(xx, yy);
11422     else
11423       continue;                 /* center and border element do not touch */
11424
11425     border_element_old[i] = border_element;
11426   }
11427
11428   for (i = 0; i < NUM_DIRECTIONS; i++)
11429   {
11430     int xx = x + xy[i][0];
11431     int yy = y + xy[i][1];
11432     int center_side = trigger_sides[i][0];
11433     int border_element = border_element_old[i];
11434
11435     if (border_element == -1)
11436       continue;
11437
11438     /* check for change of border element */
11439     CheckElementChangeBySide(xx, yy, border_element, center_element,
11440                              CE_TOUCHING_X, center_side);
11441   }
11442
11443   for (i = 0; i < NUM_DIRECTIONS; i++)
11444   {
11445     int border_side = trigger_sides[i][1];
11446     int border_element = border_element_old[i];
11447
11448     if (border_element == -1)
11449       continue;
11450
11451     /* check for change of center element (but change it only once) */
11452     if (!change_center_element)
11453       change_center_element =
11454         CheckElementChangeBySide(x, y, center_element, border_element,
11455                                  CE_TOUCHING_X, border_side);
11456   }
11457 }
11458
11459 #else
11460
11461 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11462 {
11463   static int xy[4][2] =
11464   {
11465     { 0, -1 },
11466     { -1, 0 },
11467     { +1, 0 },
11468     { 0, +1 }
11469   };
11470   static int trigger_sides[4][2] =
11471   {
11472     /* center side      border side */
11473     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11474     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11475     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11476     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11477   };
11478   static int touch_dir[4] =
11479   {
11480     MV_LEFT | MV_RIGHT,
11481     MV_UP   | MV_DOWN,
11482     MV_UP   | MV_DOWN,
11483     MV_LEFT | MV_RIGHT
11484   };
11485   boolean change_center_element = FALSE;
11486   int center_element = Feld[x][y];      /* should always be non-moving! */
11487   int i;
11488
11489   for (i = 0; i < NUM_DIRECTIONS; i++)
11490   {
11491     int xx = x + xy[i][0];
11492     int yy = y + xy[i][1];
11493     int center_side = trigger_sides[i][0];
11494     int border_side = trigger_sides[i][1];
11495     int border_element;
11496
11497     if (!IN_LEV_FIELD(xx, yy))
11498       continue;
11499
11500     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11501       border_element = Feld[xx][yy];    /* may be moving! */
11502     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11503       border_element = Feld[xx][yy];
11504     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11505       border_element = MovingOrBlocked2Element(xx, yy);
11506     else
11507       continue;                 /* center and border element do not touch */
11508
11509     /* check for change of center element (but change it only once) */
11510     if (!change_center_element)
11511       change_center_element =
11512         CheckElementChangeBySide(x, y, center_element, border_element,
11513                                  CE_TOUCHING_X, border_side);
11514
11515     /* check for change of border element */
11516     CheckElementChangeBySide(xx, yy, border_element, center_element,
11517                              CE_TOUCHING_X, center_side);
11518   }
11519 }
11520
11521 #endif
11522
11523 void TestIfElementHitsCustomElement(int x, int y, int direction)
11524 {
11525   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11526   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11527   int hitx = x + dx, hity = y + dy;
11528   int hitting_element = Feld[x][y];
11529   int touched_element;
11530
11531   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11532     return;
11533
11534   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11535                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11536
11537   if (IN_LEV_FIELD(hitx, hity))
11538   {
11539     int opposite_direction = MV_DIR_OPPOSITE(direction);
11540     int hitting_side = direction;
11541     int touched_side = opposite_direction;
11542     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11543                           MovDir[hitx][hity] != direction ||
11544                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11545
11546     object_hit = TRUE;
11547
11548     if (object_hit)
11549     {
11550       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11551                                CE_HITTING_X, touched_side);
11552
11553       CheckElementChangeBySide(hitx, hity, touched_element,
11554                                hitting_element, CE_HIT_BY_X, hitting_side);
11555
11556       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11557                                CE_HIT_BY_SOMETHING, opposite_direction);
11558     }
11559   }
11560
11561   /* "hitting something" is also true when hitting the playfield border */
11562   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11563                            CE_HITTING_SOMETHING, direction);
11564 }
11565
11566 #if 0
11567 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11568 {
11569   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11570   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11571   int hitx = x + dx, hity = y + dy;
11572   int hitting_element = Feld[x][y];
11573   int touched_element;
11574 #if 0
11575   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11576                         !IS_FREE(hitx, hity) &&
11577                         (!IS_MOVING(hitx, hity) ||
11578                          MovDir[hitx][hity] != direction ||
11579                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11580 #endif
11581
11582   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11583     return;
11584
11585 #if 0
11586   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11587     return;
11588 #endif
11589
11590   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11591                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11592
11593   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11594                            EP_CAN_SMASH_EVERYTHING, direction);
11595
11596   if (IN_LEV_FIELD(hitx, hity))
11597   {
11598     int opposite_direction = MV_DIR_OPPOSITE(direction);
11599     int hitting_side = direction;
11600     int touched_side = opposite_direction;
11601 #if 0
11602     int touched_element = MovingOrBlocked2Element(hitx, hity);
11603 #endif
11604 #if 1
11605     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11606                           MovDir[hitx][hity] != direction ||
11607                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11608
11609     object_hit = TRUE;
11610 #endif
11611
11612     if (object_hit)
11613     {
11614       int i;
11615
11616       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11617                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11618
11619       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11620                                CE_OTHER_IS_SMASHING, touched_side);
11621
11622       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11623                                CE_OTHER_GETS_SMASHED, hitting_side);
11624     }
11625   }
11626 }
11627 #endif
11628
11629 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11630 {
11631   int i, kill_x = -1, kill_y = -1;
11632
11633   int bad_element = -1;
11634   static int test_xy[4][2] =
11635   {
11636     { 0, -1 },
11637     { -1, 0 },
11638     { +1, 0 },
11639     { 0, +1 }
11640   };
11641   static int test_dir[4] =
11642   {
11643     MV_UP,
11644     MV_LEFT,
11645     MV_RIGHT,
11646     MV_DOWN
11647   };
11648
11649   for (i = 0; i < NUM_DIRECTIONS; i++)
11650   {
11651     int test_x, test_y, test_move_dir, test_element;
11652
11653     test_x = good_x + test_xy[i][0];
11654     test_y = good_y + test_xy[i][1];
11655
11656     if (!IN_LEV_FIELD(test_x, test_y))
11657       continue;
11658
11659     test_move_dir =
11660       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11661
11662     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11663
11664     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11665        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11666     */
11667     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11668         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11669     {
11670       kill_x = test_x;
11671       kill_y = test_y;
11672       bad_element = test_element;
11673
11674       break;
11675     }
11676   }
11677
11678   if (kill_x != -1 || kill_y != -1)
11679   {
11680     if (IS_PLAYER(good_x, good_y))
11681     {
11682       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11683
11684       if (player->shield_deadly_time_left > 0 &&
11685           !IS_INDESTRUCTIBLE(bad_element))
11686         Bang(kill_x, kill_y);
11687       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11688         KillPlayer(player);
11689     }
11690     else
11691       Bang(good_x, good_y);
11692   }
11693 }
11694
11695 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11696 {
11697   int i, kill_x = -1, kill_y = -1;
11698   int bad_element = Feld[bad_x][bad_y];
11699   static int test_xy[4][2] =
11700   {
11701     { 0, -1 },
11702     { -1, 0 },
11703     { +1, 0 },
11704     { 0, +1 }
11705   };
11706   static int touch_dir[4] =
11707   {
11708     MV_LEFT | MV_RIGHT,
11709     MV_UP   | MV_DOWN,
11710     MV_UP   | MV_DOWN,
11711     MV_LEFT | MV_RIGHT
11712   };
11713   static int test_dir[4] =
11714   {
11715     MV_UP,
11716     MV_LEFT,
11717     MV_RIGHT,
11718     MV_DOWN
11719   };
11720
11721   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11722     return;
11723
11724   for (i = 0; i < NUM_DIRECTIONS; i++)
11725   {
11726     int test_x, test_y, test_move_dir, test_element;
11727
11728     test_x = bad_x + test_xy[i][0];
11729     test_y = bad_y + test_xy[i][1];
11730     if (!IN_LEV_FIELD(test_x, test_y))
11731       continue;
11732
11733     test_move_dir =
11734       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11735
11736     test_element = Feld[test_x][test_y];
11737
11738     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11739        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11740     */
11741     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11742         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11743     {
11744       /* good thing is player or penguin that does not move away */
11745       if (IS_PLAYER(test_x, test_y))
11746       {
11747         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11748
11749         if (bad_element == EL_ROBOT && player->is_moving)
11750           continue;     /* robot does not kill player if he is moving */
11751
11752         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11753         {
11754           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11755             continue;           /* center and border element do not touch */
11756         }
11757
11758         kill_x = test_x;
11759         kill_y = test_y;
11760         break;
11761       }
11762       else if (test_element == EL_PENGUIN)
11763       {
11764         kill_x = test_x;
11765         kill_y = test_y;
11766         break;
11767       }
11768     }
11769   }
11770
11771   if (kill_x != -1 || kill_y != -1)
11772   {
11773     if (IS_PLAYER(kill_x, kill_y))
11774     {
11775       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11776
11777       if (player->shield_deadly_time_left > 0 &&
11778           !IS_INDESTRUCTIBLE(bad_element))
11779         Bang(bad_x, bad_y);
11780       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11781         KillPlayer(player);
11782     }
11783     else
11784       Bang(kill_x, kill_y);
11785   }
11786 }
11787
11788 void TestIfPlayerTouchesBadThing(int x, int y)
11789 {
11790   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11791 }
11792
11793 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11794 {
11795   TestIfGoodThingHitsBadThing(x, y, move_dir);
11796 }
11797
11798 void TestIfBadThingTouchesPlayer(int x, int y)
11799 {
11800   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11801 }
11802
11803 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11804 {
11805   TestIfBadThingHitsGoodThing(x, y, move_dir);
11806 }
11807
11808 void TestIfFriendTouchesBadThing(int x, int y)
11809 {
11810   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11811 }
11812
11813 void TestIfBadThingTouchesFriend(int x, int y)
11814 {
11815   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11816 }
11817
11818 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11819 {
11820   int i, kill_x = bad_x, kill_y = bad_y;
11821   static int xy[4][2] =
11822   {
11823     { 0, -1 },
11824     { -1, 0 },
11825     { +1, 0 },
11826     { 0, +1 }
11827   };
11828
11829   for (i = 0; i < NUM_DIRECTIONS; i++)
11830   {
11831     int x, y, element;
11832
11833     x = bad_x + xy[i][0];
11834     y = bad_y + xy[i][1];
11835     if (!IN_LEV_FIELD(x, y))
11836       continue;
11837
11838     element = Feld[x][y];
11839     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11840         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11841     {
11842       kill_x = x;
11843       kill_y = y;
11844       break;
11845     }
11846   }
11847
11848   if (kill_x != bad_x || kill_y != bad_y)
11849     Bang(bad_x, bad_y);
11850 }
11851
11852 void KillPlayer(struct PlayerInfo *player)
11853 {
11854   int jx = player->jx, jy = player->jy;
11855
11856   if (!player->active)
11857     return;
11858
11859   /* the following code was introduced to prevent an infinite loop when calling
11860      -> Bang()
11861      -> CheckTriggeredElementChangeExt()
11862      -> ExecuteCustomElementAction()
11863      -> KillPlayer()
11864      -> (infinitely repeating the above sequence of function calls)
11865      which occurs when killing the player while having a CE with the setting
11866      "kill player X when explosion of <player X>"; the solution using a new
11867      field "player->killed" was chosen for backwards compatibility, although
11868      clever use of the fields "player->active" etc. would probably also work */
11869 #if 1
11870   if (player->killed)
11871     return;
11872 #endif
11873
11874   player->killed = TRUE;
11875
11876   /* remove accessible field at the player's position */
11877   Feld[jx][jy] = EL_EMPTY;
11878
11879   /* deactivate shield (else Bang()/Explode() would not work right) */
11880   player->shield_normal_time_left = 0;
11881   player->shield_deadly_time_left = 0;
11882
11883   Bang(jx, jy);
11884   BuryPlayer(player);
11885 }
11886
11887 static void KillPlayerUnlessEnemyProtected(int x, int y)
11888 {
11889   if (!PLAYER_ENEMY_PROTECTED(x, y))
11890     KillPlayer(PLAYERINFO(x, y));
11891 }
11892
11893 static void KillPlayerUnlessExplosionProtected(int x, int y)
11894 {
11895   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11896     KillPlayer(PLAYERINFO(x, y));
11897 }
11898
11899 void BuryPlayer(struct PlayerInfo *player)
11900 {
11901   int jx = player->jx, jy = player->jy;
11902
11903   if (!player->active)
11904     return;
11905
11906   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11907   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11908
11909   player->GameOver = TRUE;
11910   RemovePlayer(player);
11911 }
11912
11913 void RemovePlayer(struct PlayerInfo *player)
11914 {
11915   int jx = player->jx, jy = player->jy;
11916   int i, found = FALSE;
11917
11918   player->present = FALSE;
11919   player->active = FALSE;
11920
11921   if (!ExplodeField[jx][jy])
11922     StorePlayer[jx][jy] = 0;
11923
11924   if (player->is_moving)
11925     DrawLevelField(player->last_jx, player->last_jy);
11926
11927   for (i = 0; i < MAX_PLAYERS; i++)
11928     if (stored_player[i].active)
11929       found = TRUE;
11930
11931   if (!found)
11932     AllPlayersGone = TRUE;
11933
11934   ExitX = ZX = jx;
11935   ExitY = ZY = jy;
11936 }
11937
11938 #if USE_NEW_SNAP_DELAY
11939 static void setFieldForSnapping(int x, int y, int element, int direction)
11940 {
11941   struct ElementInfo *ei = &element_info[element];
11942   int direction_bit = MV_DIR_TO_BIT(direction);
11943   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11944   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11945                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11946
11947   Feld[x][y] = EL_ELEMENT_SNAPPING;
11948   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11949
11950   ResetGfxAnimation(x, y);
11951
11952   GfxElement[x][y] = element;
11953   GfxAction[x][y] = action;
11954   GfxDir[x][y] = direction;
11955   GfxFrame[x][y] = -1;
11956 }
11957 #endif
11958
11959 /*
11960   =============================================================================
11961   checkDiagonalPushing()
11962   -----------------------------------------------------------------------------
11963   check if diagonal input device direction results in pushing of object
11964   (by checking if the alternative direction is walkable, diggable, ...)
11965   =============================================================================
11966 */
11967
11968 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11969                                     int x, int y, int real_dx, int real_dy)
11970 {
11971   int jx, jy, dx, dy, xx, yy;
11972
11973   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11974     return TRUE;
11975
11976   /* diagonal direction: check alternative direction */
11977   jx = player->jx;
11978   jy = player->jy;
11979   dx = x - jx;
11980   dy = y - jy;
11981   xx = jx + (dx == 0 ? real_dx : 0);
11982   yy = jy + (dy == 0 ? real_dy : 0);
11983
11984   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11985 }
11986
11987 /*
11988   =============================================================================
11989   DigField()
11990   -----------------------------------------------------------------------------
11991   x, y:                 field next to player (non-diagonal) to try to dig to
11992   real_dx, real_dy:     direction as read from input device (can be diagonal)
11993   =============================================================================
11994 */
11995
11996 int DigField(struct PlayerInfo *player,
11997              int oldx, int oldy, int x, int y,
11998              int real_dx, int real_dy, int mode)
11999 {
12000   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12001   boolean player_was_pushing = player->is_pushing;
12002   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12003   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12004   int jx = oldx, jy = oldy;
12005   int dx = x - jx, dy = y - jy;
12006   int nextx = x + dx, nexty = y + dy;
12007   int move_direction = (dx == -1 ? MV_LEFT  :
12008                         dx == +1 ? MV_RIGHT :
12009                         dy == -1 ? MV_UP    :
12010                         dy == +1 ? MV_DOWN  : MV_NONE);
12011   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12012   int dig_side = MV_DIR_OPPOSITE(move_direction);
12013   int old_element = Feld[jx][jy];
12014 #if USE_FIXED_DONT_RUN_INTO
12015   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12016 #else
12017   int element;
12018 #endif
12019   int collect_count;
12020
12021   if (is_player)                /* function can also be called by EL_PENGUIN */
12022   {
12023     if (player->MovPos == 0)
12024     {
12025       player->is_digging = FALSE;
12026       player->is_collecting = FALSE;
12027     }
12028
12029     if (player->MovPos == 0)    /* last pushing move finished */
12030       player->is_pushing = FALSE;
12031
12032     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
12033     {
12034       player->is_switching = FALSE;
12035       player->push_delay = -1;
12036
12037       return MP_NO_ACTION;
12038     }
12039   }
12040
12041 #if !USE_FIXED_DONT_RUN_INTO
12042   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12043     return MP_NO_ACTION;
12044 #endif
12045
12046   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12047     old_element = Back[jx][jy];
12048
12049   /* in case of element dropped at player position, check background */
12050   else if (Back[jx][jy] != EL_EMPTY &&
12051            game.engine_version >= VERSION_IDENT(2,2,0,0))
12052     old_element = Back[jx][jy];
12053
12054   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12055     return MP_NO_ACTION;        /* field has no opening in this direction */
12056
12057   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12058     return MP_NO_ACTION;        /* field has no opening in this direction */
12059
12060 #if USE_FIXED_DONT_RUN_INTO
12061   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12062   {
12063     SplashAcid(x, y);
12064
12065     Feld[jx][jy] = player->artwork_element;
12066     InitMovingField(jx, jy, MV_DOWN);
12067     Store[jx][jy] = EL_ACID;
12068     ContinueMoving(jx, jy);
12069     BuryPlayer(player);
12070
12071     return MP_DONT_RUN_INTO;
12072   }
12073 #endif
12074
12075 #if USE_FIXED_DONT_RUN_INTO
12076   if (player_can_move && DONT_RUN_INTO(element))
12077   {
12078     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12079
12080     return MP_DONT_RUN_INTO;
12081   }
12082 #endif
12083
12084 #if USE_FIXED_DONT_RUN_INTO
12085   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12086     return MP_NO_ACTION;
12087 #endif
12088
12089 #if !USE_FIXED_DONT_RUN_INTO
12090   element = Feld[x][y];
12091 #endif
12092
12093   collect_count = element_info[element].collect_count_initial;
12094
12095   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12096     return MP_NO_ACTION;
12097
12098   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12099     player_can_move = player_can_move_or_snap;
12100
12101   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12102       game.engine_version >= VERSION_IDENT(2,2,0,0))
12103   {
12104     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12105                                player->index_bit, dig_side);
12106     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12107                                         player->index_bit, dig_side);
12108
12109     if (element == EL_DC_LANDMINE)
12110       Bang(x, y);
12111
12112     if (Feld[x][y] != element)          /* field changed by snapping */
12113       return MP_ACTION;
12114
12115     return MP_NO_ACTION;
12116   }
12117
12118 #if USE_PLAYER_GRAVITY
12119   if (player->gravity && is_player && !player->is_auto_moving &&
12120       canFallDown(player) && move_direction != MV_DOWN &&
12121       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12122     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12123 #else
12124   if (game.gravity && is_player && !player->is_auto_moving &&
12125       canFallDown(player) && move_direction != MV_DOWN &&
12126       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12127     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12128 #endif
12129
12130   if (player_can_move &&
12131       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12132   {
12133     int sound_element = SND_ELEMENT(element);
12134     int sound_action = ACTION_WALKING;
12135
12136     if (IS_RND_GATE(element))
12137     {
12138       if (!player->key[RND_GATE_NR(element)])
12139         return MP_NO_ACTION;
12140     }
12141     else if (IS_RND_GATE_GRAY(element))
12142     {
12143       if (!player->key[RND_GATE_GRAY_NR(element)])
12144         return MP_NO_ACTION;
12145     }
12146     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12147     {
12148       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12149         return MP_NO_ACTION;
12150     }
12151     else if (element == EL_EXIT_OPEN ||
12152              element == EL_EM_EXIT_OPEN ||
12153              element == EL_STEEL_EXIT_OPEN ||
12154              element == EL_EM_STEEL_EXIT_OPEN ||
12155              element == EL_SP_EXIT_OPEN ||
12156              element == EL_SP_EXIT_OPENING)
12157     {
12158       sound_action = ACTION_PASSING;    /* player is passing exit */
12159     }
12160     else if (element == EL_EMPTY)
12161     {
12162       sound_action = ACTION_MOVING;             /* nothing to walk on */
12163     }
12164
12165     /* play sound from background or player, whatever is available */
12166     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12167       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12168     else
12169       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12170   }
12171   else if (player_can_move &&
12172            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12173   {
12174     if (!ACCESS_FROM(element, opposite_direction))
12175       return MP_NO_ACTION;      /* field not accessible from this direction */
12176
12177     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12178       return MP_NO_ACTION;
12179
12180     if (IS_EM_GATE(element))
12181     {
12182       if (!player->key[EM_GATE_NR(element)])
12183         return MP_NO_ACTION;
12184     }
12185     else if (IS_EM_GATE_GRAY(element))
12186     {
12187       if (!player->key[EM_GATE_GRAY_NR(element)])
12188         return MP_NO_ACTION;
12189     }
12190     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12191     {
12192       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12193         return MP_NO_ACTION;
12194     }
12195     else if (IS_EMC_GATE(element))
12196     {
12197       if (!player->key[EMC_GATE_NR(element)])
12198         return MP_NO_ACTION;
12199     }
12200     else if (IS_EMC_GATE_GRAY(element))
12201     {
12202       if (!player->key[EMC_GATE_GRAY_NR(element)])
12203         return MP_NO_ACTION;
12204     }
12205     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12206     {
12207       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12208         return MP_NO_ACTION;
12209     }
12210     else if (element == EL_DC_GATE_WHITE ||
12211              element == EL_DC_GATE_WHITE_GRAY ||
12212              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12213     {
12214       if (player->num_white_keys == 0)
12215         return MP_NO_ACTION;
12216
12217       player->num_white_keys--;
12218     }
12219     else if (IS_SP_PORT(element))
12220     {
12221       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12222           element == EL_SP_GRAVITY_PORT_RIGHT ||
12223           element == EL_SP_GRAVITY_PORT_UP ||
12224           element == EL_SP_GRAVITY_PORT_DOWN)
12225 #if USE_PLAYER_GRAVITY
12226         player->gravity = !player->gravity;
12227 #else
12228         game.gravity = !game.gravity;
12229 #endif
12230       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12231                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12232                element == EL_SP_GRAVITY_ON_PORT_UP ||
12233                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12234 #if USE_PLAYER_GRAVITY
12235         player->gravity = TRUE;
12236 #else
12237         game.gravity = TRUE;
12238 #endif
12239       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12240                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12241                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12242                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12243 #if USE_PLAYER_GRAVITY
12244         player->gravity = FALSE;
12245 #else
12246         game.gravity = FALSE;
12247 #endif
12248     }
12249
12250     /* automatically move to the next field with double speed */
12251     player->programmed_action = move_direction;
12252
12253     if (player->move_delay_reset_counter == 0)
12254     {
12255       player->move_delay_reset_counter = 2;     /* two double speed steps */
12256
12257       DOUBLE_PLAYER_SPEED(player);
12258     }
12259
12260     PlayLevelSoundAction(x, y, ACTION_PASSING);
12261   }
12262   else if (player_can_move_or_snap && IS_DIGGABLE(element))
12263   {
12264     RemoveField(x, y);
12265
12266     if (mode != DF_SNAP)
12267     {
12268       GfxElement[x][y] = GFX_ELEMENT(element);
12269       player->is_digging = TRUE;
12270     }
12271
12272     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12273
12274     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12275                                         player->index_bit, dig_side);
12276
12277     if (mode == DF_SNAP)
12278     {
12279 #if USE_NEW_SNAP_DELAY
12280       if (level.block_snap_field)
12281         setFieldForSnapping(x, y, element, move_direction);
12282       else
12283         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12284 #else
12285       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12286 #endif
12287
12288       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12289                                           player->index_bit, dig_side);
12290     }
12291   }
12292   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12293   {
12294     RemoveField(x, y);
12295
12296     if (is_player && mode != DF_SNAP)
12297     {
12298       GfxElement[x][y] = element;
12299       player->is_collecting = TRUE;
12300     }
12301
12302     if (element == EL_SPEED_PILL)
12303     {
12304       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12305     }
12306     else if (element == EL_EXTRA_TIME && level.time > 0)
12307     {
12308       TimeLeft += level.extra_time;
12309       DrawGameValue_Time(TimeLeft);
12310     }
12311     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12312     {
12313       player->shield_normal_time_left += level.shield_normal_time;
12314       if (element == EL_SHIELD_DEADLY)
12315         player->shield_deadly_time_left += level.shield_deadly_time;
12316     }
12317     else if (element == EL_DYNAMITE ||
12318              element == EL_EM_DYNAMITE ||
12319              element == EL_SP_DISK_RED)
12320     {
12321       if (player->inventory_size < MAX_INVENTORY_SIZE)
12322         player->inventory_element[player->inventory_size++] = element;
12323
12324       DrawGameDoorValues();
12325     }
12326     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12327     {
12328       player->dynabomb_count++;
12329       player->dynabombs_left++;
12330     }
12331     else if (element == EL_DYNABOMB_INCREASE_SIZE)
12332     {
12333       player->dynabomb_size++;
12334     }
12335     else if (element == EL_DYNABOMB_INCREASE_POWER)
12336     {
12337       player->dynabomb_xl = TRUE;
12338     }
12339     else if (IS_KEY(element))
12340     {
12341       player->key[KEY_NR(element)] = TRUE;
12342
12343       DrawGameDoorValues();
12344     }
12345     else if (element == EL_DC_KEY_WHITE)
12346     {
12347       player->num_white_keys++;
12348
12349       /* display white keys? */
12350       /* DrawGameDoorValues(); */
12351     }
12352     else if (IS_ENVELOPE(element))
12353     {
12354       player->show_envelope = element;
12355     }
12356     else if (element == EL_EMC_LENSES)
12357     {
12358       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12359
12360       RedrawAllInvisibleElementsForLenses();
12361     }
12362     else if (element == EL_EMC_MAGNIFIER)
12363     {
12364       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12365
12366       RedrawAllInvisibleElementsForMagnifier();
12367     }
12368     else if (IS_DROPPABLE(element) ||
12369              IS_THROWABLE(element))     /* can be collected and dropped */
12370     {
12371       int i;
12372
12373       if (collect_count == 0)
12374         player->inventory_infinite_element = element;
12375       else
12376         for (i = 0; i < collect_count; i++)
12377           if (player->inventory_size < MAX_INVENTORY_SIZE)
12378             player->inventory_element[player->inventory_size++] = element;
12379
12380       DrawGameDoorValues();
12381     }
12382     else if (collect_count > 0)
12383     {
12384       local_player->gems_still_needed -= collect_count;
12385       if (local_player->gems_still_needed < 0)
12386         local_player->gems_still_needed = 0;
12387
12388       DrawGameValue_Emeralds(local_player->gems_still_needed);
12389     }
12390
12391     RaiseScoreElement(element);
12392     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12393
12394     if (is_player)
12395       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12396                                           player->index_bit, dig_side);
12397
12398     if (mode == DF_SNAP)
12399     {
12400 #if USE_NEW_SNAP_DELAY
12401       if (level.block_snap_field)
12402         setFieldForSnapping(x, y, element, move_direction);
12403       else
12404         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12405 #else
12406       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12407 #endif
12408
12409       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12410                                           player->index_bit, dig_side);
12411     }
12412   }
12413   else if (player_can_move_or_snap && IS_PUSHABLE(element))
12414   {
12415     if (mode == DF_SNAP && element != EL_BD_ROCK)
12416       return MP_NO_ACTION;
12417
12418     if (CAN_FALL(element) && dy)
12419       return MP_NO_ACTION;
12420
12421     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12422         !(element == EL_SPRING && level.use_spring_bug))
12423       return MP_NO_ACTION;
12424
12425     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12426         ((move_direction & MV_VERTICAL &&
12427           ((element_info[element].move_pattern & MV_LEFT &&
12428             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12429            (element_info[element].move_pattern & MV_RIGHT &&
12430             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12431          (move_direction & MV_HORIZONTAL &&
12432           ((element_info[element].move_pattern & MV_UP &&
12433             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12434            (element_info[element].move_pattern & MV_DOWN &&
12435             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12436       return MP_NO_ACTION;
12437
12438     /* do not push elements already moving away faster than player */
12439     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12440         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12441       return MP_NO_ACTION;
12442
12443     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12444     {
12445       if (player->push_delay_value == -1 || !player_was_pushing)
12446         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12447     }
12448     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12449     {
12450       if (player->push_delay_value == -1)
12451         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12452     }
12453     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12454     {
12455       if (!player->is_pushing)
12456         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12457     }
12458
12459     player->is_pushing = TRUE;
12460     player->is_active = TRUE;
12461
12462     if (!(IN_LEV_FIELD(nextx, nexty) &&
12463           (IS_FREE(nextx, nexty) ||
12464            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12465             IS_SB_ELEMENT(element)))))
12466       return MP_NO_ACTION;
12467
12468     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12469       return MP_NO_ACTION;
12470
12471     if (player->push_delay == -1)       /* new pushing; restart delay */
12472       player->push_delay = 0;
12473
12474     if (player->push_delay < player->push_delay_value &&
12475         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12476         element != EL_SPRING && element != EL_BALLOON)
12477     {
12478       /* make sure that there is no move delay before next try to push */
12479       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12480         player->move_delay = 0;
12481
12482       return MP_NO_ACTION;
12483     }
12484
12485     if (IS_SB_ELEMENT(element))
12486     {
12487       if (element == EL_SOKOBAN_FIELD_FULL)
12488       {
12489         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12490         local_player->sokobanfields_still_needed++;
12491       }
12492
12493       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12494       {
12495         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12496         local_player->sokobanfields_still_needed--;
12497       }
12498
12499       Feld[x][y] = EL_SOKOBAN_OBJECT;
12500
12501       if (Back[x][y] == Back[nextx][nexty])
12502         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12503       else if (Back[x][y] != 0)
12504         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12505                                     ACTION_EMPTYING);
12506       else
12507         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12508                                     ACTION_FILLING);
12509
12510       if (local_player->sokobanfields_still_needed == 0 &&
12511           game.emulation == EMU_SOKOBAN)
12512       {
12513         PlayerWins(player);
12514
12515         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12516       }
12517     }
12518     else
12519       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12520
12521     InitMovingField(x, y, move_direction);
12522     GfxAction[x][y] = ACTION_PUSHING;
12523
12524     if (mode == DF_SNAP)
12525       ContinueMoving(x, y);
12526     else
12527       MovPos[x][y] = (dx != 0 ? dx : dy);
12528
12529     Pushed[x][y] = TRUE;
12530     Pushed[nextx][nexty] = TRUE;
12531
12532     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12533       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12534     else
12535       player->push_delay_value = -1;    /* get new value later */
12536
12537     /* check for element change _after_ element has been pushed */
12538     if (game.use_change_when_pushing_bug)
12539     {
12540       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12541                                  player->index_bit, dig_side);
12542       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12543                                           player->index_bit, dig_side);
12544     }
12545   }
12546   else if (IS_SWITCHABLE(element))
12547   {
12548     if (PLAYER_SWITCHING(player, x, y))
12549     {
12550       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12551                                           player->index_bit, dig_side);
12552
12553       return MP_ACTION;
12554     }
12555
12556     player->is_switching = TRUE;
12557     player->switch_x = x;
12558     player->switch_y = y;
12559
12560     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12561
12562     if (element == EL_ROBOT_WHEEL)
12563     {
12564       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12565       ZX = x;
12566       ZY = y;
12567
12568       DrawLevelField(x, y);
12569     }
12570     else if (element == EL_SP_TERMINAL)
12571     {
12572       int xx, yy;
12573
12574       SCAN_PLAYFIELD(xx, yy)
12575       {
12576         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12577           Bang(xx, yy);
12578         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12579           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12580       }
12581     }
12582     else if (IS_BELT_SWITCH(element))
12583     {
12584       ToggleBeltSwitch(x, y);
12585     }
12586     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12587              element == EL_SWITCHGATE_SWITCH_DOWN ||
12588              element == EL_DC_SWITCHGATE_SWITCH_UP ||
12589              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12590     {
12591       ToggleSwitchgateSwitch(x, y);
12592     }
12593     else if (element == EL_LIGHT_SWITCH ||
12594              element == EL_LIGHT_SWITCH_ACTIVE)
12595     {
12596       ToggleLightSwitch(x, y);
12597     }
12598     else if (element == EL_TIMEGATE_SWITCH ||
12599              element == EL_DC_TIMEGATE_SWITCH)
12600     {
12601       ActivateTimegateSwitch(x, y);
12602     }
12603     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12604              element == EL_BALLOON_SWITCH_RIGHT ||
12605              element == EL_BALLOON_SWITCH_UP    ||
12606              element == EL_BALLOON_SWITCH_DOWN  ||
12607              element == EL_BALLOON_SWITCH_NONE  ||
12608              element == EL_BALLOON_SWITCH_ANY)
12609     {
12610       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12611                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12612                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12613                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12614                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12615                              move_direction);
12616     }
12617     else if (element == EL_LAMP)
12618     {
12619       Feld[x][y] = EL_LAMP_ACTIVE;
12620       local_player->lights_still_needed--;
12621
12622       ResetGfxAnimation(x, y);
12623       DrawLevelField(x, y);
12624     }
12625     else if (element == EL_TIME_ORB_FULL)
12626     {
12627       Feld[x][y] = EL_TIME_ORB_EMPTY;
12628
12629       if (level.time > 0 || level.use_time_orb_bug)
12630       {
12631         TimeLeft += level.time_orb_time;
12632         DrawGameValue_Time(TimeLeft);
12633       }
12634
12635       ResetGfxAnimation(x, y);
12636       DrawLevelField(x, y);
12637     }
12638     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12639              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12640     {
12641       int xx, yy;
12642
12643       game.ball_state = !game.ball_state;
12644
12645       SCAN_PLAYFIELD(xx, yy)
12646       {
12647         int e = Feld[xx][yy];
12648
12649         if (game.ball_state)
12650         {
12651           if (e == EL_EMC_MAGIC_BALL)
12652             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12653           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12654             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12655         }
12656         else
12657         {
12658           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12659             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12660           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12661             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12662         }
12663       }
12664     }
12665
12666     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12667                                         player->index_bit, dig_side);
12668
12669     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12670                                         player->index_bit, dig_side);
12671
12672     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12673                                         player->index_bit, dig_side);
12674
12675     return MP_ACTION;
12676   }
12677   else
12678   {
12679     if (!PLAYER_SWITCHING(player, x, y))
12680     {
12681       player->is_switching = TRUE;
12682       player->switch_x = x;
12683       player->switch_y = y;
12684
12685       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12686                                  player->index_bit, dig_side);
12687       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12688                                           player->index_bit, dig_side);
12689
12690       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12691                                  player->index_bit, dig_side);
12692       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12693                                           player->index_bit, dig_side);
12694     }
12695
12696     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12697                                player->index_bit, dig_side);
12698     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12699                                         player->index_bit, dig_side);
12700
12701     return MP_NO_ACTION;
12702   }
12703
12704   player->push_delay = -1;
12705
12706   if (is_player)                /* function can also be called by EL_PENGUIN */
12707   {
12708     if (Feld[x][y] != element)          /* really digged/collected something */
12709     {
12710       player->is_collecting = !player->is_digging;
12711       player->is_active = TRUE;
12712     }
12713   }
12714
12715   return MP_MOVING;
12716 }
12717
12718 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12719 {
12720   int jx = player->jx, jy = player->jy;
12721   int x = jx + dx, y = jy + dy;
12722   int snap_direction = (dx == -1 ? MV_LEFT  :
12723                         dx == +1 ? MV_RIGHT :
12724                         dy == -1 ? MV_UP    :
12725                         dy == +1 ? MV_DOWN  : MV_NONE);
12726   boolean can_continue_snapping = (level.continuous_snapping &&
12727                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12728
12729   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12730     return FALSE;
12731
12732   if (!player->active || !IN_LEV_FIELD(x, y))
12733     return FALSE;
12734
12735   if (dx && dy)
12736     return FALSE;
12737
12738   if (!dx && !dy)
12739   {
12740     if (player->MovPos == 0)
12741       player->is_pushing = FALSE;
12742
12743     player->is_snapping = FALSE;
12744
12745     if (player->MovPos == 0)
12746     {
12747       player->is_moving = FALSE;
12748       player->is_digging = FALSE;
12749       player->is_collecting = FALSE;
12750     }
12751
12752     return FALSE;
12753   }
12754
12755 #if USE_NEW_CONTINUOUS_SNAPPING
12756   /* prevent snapping with already pressed snap key when not allowed */
12757   if (player->is_snapping && !can_continue_snapping)
12758     return FALSE;
12759 #else
12760   if (player->is_snapping)
12761     return FALSE;
12762 #endif
12763
12764   player->MovDir = snap_direction;
12765
12766   if (player->MovPos == 0)
12767   {
12768     player->is_moving = FALSE;
12769     player->is_digging = FALSE;
12770     player->is_collecting = FALSE;
12771   }
12772
12773   player->is_dropping = FALSE;
12774   player->is_dropping_pressed = FALSE;
12775   player->drop_pressed_delay = 0;
12776
12777   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12778     return FALSE;
12779
12780   player->is_snapping = TRUE;
12781   player->is_active = TRUE;
12782
12783   if (player->MovPos == 0)
12784   {
12785     player->is_moving = FALSE;
12786     player->is_digging = FALSE;
12787     player->is_collecting = FALSE;
12788   }
12789
12790   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12791     DrawLevelField(player->last_jx, player->last_jy);
12792
12793   DrawLevelField(x, y);
12794
12795   return TRUE;
12796 }
12797
12798 boolean DropElement(struct PlayerInfo *player)
12799 {
12800   int old_element, new_element;
12801   int dropx = player->jx, dropy = player->jy;
12802   int drop_direction = player->MovDir;
12803   int drop_side = drop_direction;
12804   int drop_element = (player->inventory_size > 0 ?
12805                       player->inventory_element[player->inventory_size - 1] :
12806                       player->inventory_infinite_element != EL_UNDEFINED ?
12807                       player->inventory_infinite_element :
12808                       player->dynabombs_left > 0 ?
12809                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12810                       EL_UNDEFINED);
12811
12812   player->is_dropping_pressed = TRUE;
12813
12814   /* do not drop an element on top of another element; when holding drop key
12815      pressed without moving, dropped element must move away before the next
12816      element can be dropped (this is especially important if the next element
12817      is dynamite, which can be placed on background for historical reasons) */
12818   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12819     return MP_ACTION;
12820
12821   if (IS_THROWABLE(drop_element))
12822   {
12823     dropx += GET_DX_FROM_DIR(drop_direction);
12824     dropy += GET_DY_FROM_DIR(drop_direction);
12825
12826     if (!IN_LEV_FIELD(dropx, dropy))
12827       return FALSE;
12828   }
12829
12830   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12831   new_element = drop_element;           /* default: no change when dropping */
12832
12833   /* check if player is active, not moving and ready to drop */
12834   if (!player->active || player->MovPos || player->drop_delay > 0)
12835     return FALSE;
12836
12837   /* check if player has anything that can be dropped */
12838   if (new_element == EL_UNDEFINED)
12839     return FALSE;
12840
12841   /* check if drop key was pressed long enough for EM style dynamite */
12842   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12843     return FALSE;
12844
12845   /* check if anything can be dropped at the current position */
12846   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12847     return FALSE;
12848
12849   /* collected custom elements can only be dropped on empty fields */
12850   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12851     return FALSE;
12852
12853   if (old_element != EL_EMPTY)
12854     Back[dropx][dropy] = old_element;   /* store old element on this field */
12855
12856   ResetGfxAnimation(dropx, dropy);
12857   ResetRandomAnimationValue(dropx, dropy);
12858
12859   if (player->inventory_size > 0 ||
12860       player->inventory_infinite_element != EL_UNDEFINED)
12861   {
12862     if (player->inventory_size > 0)
12863     {
12864       player->inventory_size--;
12865
12866       DrawGameDoorValues();
12867
12868       if (new_element == EL_DYNAMITE)
12869         new_element = EL_DYNAMITE_ACTIVE;
12870       else if (new_element == EL_EM_DYNAMITE)
12871         new_element = EL_EM_DYNAMITE_ACTIVE;
12872       else if (new_element == EL_SP_DISK_RED)
12873         new_element = EL_SP_DISK_RED_ACTIVE;
12874     }
12875
12876     Feld[dropx][dropy] = new_element;
12877
12878     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12879       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12880                           el2img(Feld[dropx][dropy]), 0);
12881
12882     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12883
12884     /* needed if previous element just changed to "empty" in the last frame */
12885     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12886
12887     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12888                                player->index_bit, drop_side);
12889     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12890                                         CE_PLAYER_DROPS_X,
12891                                         player->index_bit, drop_side);
12892
12893     TestIfElementTouchesCustomElement(dropx, dropy);
12894   }
12895   else          /* player is dropping a dyna bomb */
12896   {
12897     player->dynabombs_left--;
12898
12899     Feld[dropx][dropy] = new_element;
12900
12901     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12902       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12903                           el2img(Feld[dropx][dropy]), 0);
12904
12905     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12906   }
12907
12908   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12909     InitField_WithBug1(dropx, dropy, FALSE);
12910
12911   new_element = Feld[dropx][dropy];     /* element might have changed */
12912
12913   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12914       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12915   {
12916     int move_direction, nextx, nexty;
12917
12918     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12919       MovDir[dropx][dropy] = drop_direction;
12920
12921     move_direction = MovDir[dropx][dropy];
12922     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12923     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12924
12925     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12926
12927 #if USE_FIX_IMPACT_COLLISION
12928     /* do not cause impact style collision by dropping elements that can fall */
12929     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12930 #else
12931     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12932 #endif
12933   }
12934
12935   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12936   player->is_dropping = TRUE;
12937
12938   player->drop_pressed_delay = 0;
12939   player->is_dropping_pressed = FALSE;
12940
12941   player->drop_x = dropx;
12942   player->drop_y = dropy;
12943
12944   return TRUE;
12945 }
12946
12947 /* ------------------------------------------------------------------------- */
12948 /* game sound playing functions                                              */
12949 /* ------------------------------------------------------------------------- */
12950
12951 static int *loop_sound_frame = NULL;
12952 static int *loop_sound_volume = NULL;
12953
12954 void InitPlayLevelSound()
12955 {
12956   int num_sounds = getSoundListSize();
12957
12958   checked_free(loop_sound_frame);
12959   checked_free(loop_sound_volume);
12960
12961   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12962   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12963 }
12964
12965 static void PlayLevelSound(int x, int y, int nr)
12966 {
12967   int sx = SCREENX(x), sy = SCREENY(y);
12968   int volume, stereo_position;
12969   int max_distance = 8;
12970   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12971
12972   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12973       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12974     return;
12975
12976   if (!IN_LEV_FIELD(x, y) ||
12977       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12978       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12979     return;
12980
12981   volume = SOUND_MAX_VOLUME;
12982
12983   if (!IN_SCR_FIELD(sx, sy))
12984   {
12985     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12986     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12987
12988     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12989   }
12990
12991   stereo_position = (SOUND_MAX_LEFT +
12992                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12993                      (SCR_FIELDX + 2 * max_distance));
12994
12995   if (IS_LOOP_SOUND(nr))
12996   {
12997     /* This assures that quieter loop sounds do not overwrite louder ones,
12998        while restarting sound volume comparison with each new game frame. */
12999
13000     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13001       return;
13002
13003     loop_sound_volume[nr] = volume;
13004     loop_sound_frame[nr] = FrameCounter;
13005   }
13006
13007   PlaySoundExt(nr, volume, stereo_position, type);
13008 }
13009
13010 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13011 {
13012   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13013                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13014                  y < LEVELY(BY1) ? LEVELY(BY1) :
13015                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13016                  sound_action);
13017 }
13018
13019 static void PlayLevelSoundAction(int x, int y, int action)
13020 {
13021   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13022 }
13023
13024 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13025 {
13026   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13027
13028   if (sound_effect != SND_UNDEFINED)
13029     PlayLevelSound(x, y, sound_effect);
13030 }
13031
13032 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13033                                               int action)
13034 {
13035   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13036
13037   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13038     PlayLevelSound(x, y, sound_effect);
13039 }
13040
13041 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13042 {
13043   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13044
13045   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13046     PlayLevelSound(x, y, sound_effect);
13047 }
13048
13049 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13050 {
13051   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13052
13053   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13054     StopSound(sound_effect);
13055 }
13056
13057 static void PlayLevelMusic()
13058 {
13059   if (levelset.music[level_nr] != MUS_UNDEFINED)
13060     PlayMusic(levelset.music[level_nr]);        /* from config file */
13061   else
13062     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13063 }
13064
13065 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13066 {
13067   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13068   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13069   int x = xx - 1 - offset;
13070   int y = yy - 1 - offset;
13071
13072   switch (sample)
13073   {
13074     case SAMPLE_blank:
13075       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13076       break;
13077
13078     case SAMPLE_roll:
13079       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13080       break;
13081
13082     case SAMPLE_stone:
13083       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13084       break;
13085
13086     case SAMPLE_nut:
13087       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13088       break;
13089
13090     case SAMPLE_crack:
13091       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13092       break;
13093
13094     case SAMPLE_bug:
13095       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13096       break;
13097
13098     case SAMPLE_tank:
13099       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13100       break;
13101
13102     case SAMPLE_android_clone:
13103       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13104       break;
13105
13106     case SAMPLE_android_move:
13107       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13108       break;
13109
13110     case SAMPLE_spring:
13111       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13112       break;
13113
13114     case SAMPLE_slurp:
13115       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13116       break;
13117
13118     case SAMPLE_eater:
13119       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13120       break;
13121
13122     case SAMPLE_eater_eat:
13123       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13124       break;
13125
13126     case SAMPLE_alien:
13127       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13128       break;
13129
13130     case SAMPLE_collect:
13131       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13132       break;
13133
13134     case SAMPLE_diamond:
13135       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13136       break;
13137
13138     case SAMPLE_squash:
13139       /* !!! CHECK THIS !!! */
13140 #if 1
13141       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13142 #else
13143       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13144 #endif
13145       break;
13146
13147     case SAMPLE_wonderfall:
13148       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13149       break;
13150
13151     case SAMPLE_drip:
13152       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13153       break;
13154
13155     case SAMPLE_push:
13156       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13157       break;
13158
13159     case SAMPLE_dirt:
13160       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13161       break;
13162
13163     case SAMPLE_acid:
13164       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13165       break;
13166
13167     case SAMPLE_ball:
13168       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13169       break;
13170
13171     case SAMPLE_grow:
13172       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13173       break;
13174
13175     case SAMPLE_wonder:
13176       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13177       break;
13178
13179     case SAMPLE_door:
13180       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13181       break;
13182
13183     case SAMPLE_exit_open:
13184       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13185       break;
13186
13187     case SAMPLE_exit_leave:
13188       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13189       break;
13190
13191     case SAMPLE_dynamite:
13192       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13193       break;
13194
13195     case SAMPLE_tick:
13196       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13197       break;
13198
13199     case SAMPLE_press:
13200       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13201       break;
13202
13203     case SAMPLE_wheel:
13204       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13205       break;
13206
13207     case SAMPLE_boom:
13208       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13209       break;
13210
13211     case SAMPLE_die:
13212       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13213       break;
13214
13215     case SAMPLE_time:
13216       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13217       break;
13218
13219     default:
13220       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13221       break;
13222   }
13223 }
13224
13225 #if 0
13226 void ChangeTime(int value)
13227 {
13228   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13229
13230   *time += value;
13231
13232   /* EMC game engine uses value from time counter of RND game engine */
13233   level.native_em_level->lev->time = *time;
13234
13235   DrawGameValue_Time(*time);
13236 }
13237
13238 void RaiseScore(int value)
13239 {
13240   /* EMC game engine and RND game engine have separate score counters */
13241   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13242                 &level.native_em_level->lev->score : &local_player->score);
13243
13244   *score += value;
13245
13246   DrawGameValue_Score(*score);
13247 }
13248 #endif
13249
13250 void RaiseScore(int value)
13251 {
13252   local_player->score += value;
13253
13254   DrawGameValue_Score(local_player->score);
13255 }
13256
13257 void RaiseScoreElement(int element)
13258 {
13259   switch (element)
13260   {
13261     case EL_EMERALD:
13262     case EL_BD_DIAMOND:
13263     case EL_EMERALD_YELLOW:
13264     case EL_EMERALD_RED:
13265     case EL_EMERALD_PURPLE:
13266     case EL_SP_INFOTRON:
13267       RaiseScore(level.score[SC_EMERALD]);
13268       break;
13269     case EL_DIAMOND:
13270       RaiseScore(level.score[SC_DIAMOND]);
13271       break;
13272     case EL_CRYSTAL:
13273       RaiseScore(level.score[SC_CRYSTAL]);
13274       break;
13275     case EL_PEARL:
13276       RaiseScore(level.score[SC_PEARL]);
13277       break;
13278     case EL_BUG:
13279     case EL_BD_BUTTERFLY:
13280     case EL_SP_ELECTRON:
13281       RaiseScore(level.score[SC_BUG]);
13282       break;
13283     case EL_SPACESHIP:
13284     case EL_BD_FIREFLY:
13285     case EL_SP_SNIKSNAK:
13286       RaiseScore(level.score[SC_SPACESHIP]);
13287       break;
13288     case EL_YAMYAM:
13289     case EL_DARK_YAMYAM:
13290       RaiseScore(level.score[SC_YAMYAM]);
13291       break;
13292     case EL_ROBOT:
13293       RaiseScore(level.score[SC_ROBOT]);
13294       break;
13295     case EL_PACMAN:
13296       RaiseScore(level.score[SC_PACMAN]);
13297       break;
13298     case EL_NUT:
13299       RaiseScore(level.score[SC_NUT]);
13300       break;
13301     case EL_DYNAMITE:
13302     case EL_EM_DYNAMITE:
13303     case EL_SP_DISK_RED:
13304     case EL_DYNABOMB_INCREASE_NUMBER:
13305     case EL_DYNABOMB_INCREASE_SIZE:
13306     case EL_DYNABOMB_INCREASE_POWER:
13307       RaiseScore(level.score[SC_DYNAMITE]);
13308       break;
13309     case EL_SHIELD_NORMAL:
13310     case EL_SHIELD_DEADLY:
13311       RaiseScore(level.score[SC_SHIELD]);
13312       break;
13313     case EL_EXTRA_TIME:
13314       RaiseScore(level.extra_time_score);
13315       break;
13316     case EL_KEY_1:
13317     case EL_KEY_2:
13318     case EL_KEY_3:
13319     case EL_KEY_4:
13320     case EL_EM_KEY_1:
13321     case EL_EM_KEY_2:
13322     case EL_EM_KEY_3:
13323     case EL_EM_KEY_4:
13324     case EL_EMC_KEY_5:
13325     case EL_EMC_KEY_6:
13326     case EL_EMC_KEY_7:
13327     case EL_EMC_KEY_8:
13328     case EL_DC_KEY_WHITE:
13329       RaiseScore(level.score[SC_KEY]);
13330       break;
13331     default:
13332       RaiseScore(element_info[element].collect_score);
13333       break;
13334   }
13335 }
13336
13337 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13338 {
13339   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13340   {
13341 #if defined(NETWORK_AVALIABLE)
13342     if (options.network)
13343       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13344     else
13345 #endif
13346     {
13347       if (quick_quit)
13348       {
13349         game_status = GAME_MODE_MAIN;
13350
13351         DrawMainMenu();
13352       }
13353       else
13354       {
13355         FadeOut(REDRAW_FIELD);
13356
13357         game_status = GAME_MODE_MAIN;
13358
13359         DrawAndFadeInMainMenu(REDRAW_FIELD);
13360       }
13361     }
13362   }
13363   else          /* continue playing the game */
13364   {
13365     if (tape.playing && tape.deactivate_display)
13366       TapeDeactivateDisplayOff(TRUE);
13367
13368     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13369
13370     if (tape.playing && tape.deactivate_display)
13371       TapeDeactivateDisplayOn();
13372   }
13373 }
13374
13375 void RequestQuitGame(boolean ask_if_really_quit)
13376 {
13377   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13378   boolean skip_request = AllPlayersGone || quick_quit;
13379
13380   RequestQuitGameExt(skip_request, quick_quit,
13381                      "Do you really want to quit the game ?");
13382 }
13383
13384
13385 /* ------------------------------------------------------------------------- */
13386 /* random generator functions                                                */
13387 /* ------------------------------------------------------------------------- */
13388
13389 unsigned int InitEngineRandom_RND(long seed)
13390 {
13391   game.num_random_calls = 0;
13392
13393 #if 0
13394   unsigned int rnd_seed = InitEngineRandom(seed);
13395
13396   printf("::: START RND: %d\n", rnd_seed);
13397
13398   return rnd_seed;
13399 #else
13400
13401   return InitEngineRandom(seed);
13402
13403 #endif
13404
13405 }
13406
13407 unsigned int RND(int max)
13408 {
13409   if (max > 0)
13410   {
13411     game.num_random_calls++;
13412
13413     return GetEngineRandom(max);
13414   }
13415
13416   return 0;
13417 }
13418
13419
13420 /* ------------------------------------------------------------------------- */
13421 /* game engine snapshot handling functions                                   */
13422 /* ------------------------------------------------------------------------- */
13423
13424 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
13425
13426 struct EngineSnapshotInfo
13427 {
13428   /* runtime values for custom element collect score */
13429   int collect_score[NUM_CUSTOM_ELEMENTS];
13430
13431   /* runtime values for group element choice position */
13432   int choice_pos[NUM_GROUP_ELEMENTS];
13433
13434   /* runtime values for belt position animations */
13435   int belt_graphic[4 * NUM_BELT_PARTS];
13436   int belt_anim_mode[4 * NUM_BELT_PARTS];
13437 };
13438
13439 struct EngineSnapshotNodeInfo
13440 {
13441   void *buffer_orig;
13442   void *buffer_copy;
13443   int size;
13444 };
13445
13446 static struct EngineSnapshotInfo engine_snapshot_rnd;
13447 static ListNode *engine_snapshot_list = NULL;
13448 static char *snapshot_level_identifier = NULL;
13449 static int snapshot_level_nr = -1;
13450
13451 void FreeEngineSnapshot()
13452 {
13453   while (engine_snapshot_list != NULL)
13454     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13455                        checked_free);
13456
13457   setString(&snapshot_level_identifier, NULL);
13458   snapshot_level_nr = -1;
13459 }
13460
13461 static void SaveEngineSnapshotValues_RND()
13462 {
13463   static int belt_base_active_element[4] =
13464   {
13465     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13466     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13467     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13468     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13469   };
13470   int i, j;
13471
13472   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13473   {
13474     int element = EL_CUSTOM_START + i;
13475
13476     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13477   }
13478
13479   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13480   {
13481     int element = EL_GROUP_START + i;
13482
13483     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13484   }
13485
13486   for (i = 0; i < 4; i++)
13487   {
13488     for (j = 0; j < NUM_BELT_PARTS; j++)
13489     {
13490       int element = belt_base_active_element[i] + j;
13491       int graphic = el2img(element);
13492       int anim_mode = graphic_info[graphic].anim_mode;
13493
13494       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13495       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13496     }
13497   }
13498 }
13499
13500 static void LoadEngineSnapshotValues_RND()
13501 {
13502   unsigned long num_random_calls = game.num_random_calls;
13503   int i, j;
13504
13505   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13506   {
13507     int element = EL_CUSTOM_START + i;
13508
13509     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13510   }
13511
13512   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13513   {
13514     int element = EL_GROUP_START + i;
13515
13516     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13517   }
13518
13519   for (i = 0; i < 4; i++)
13520   {
13521     for (j = 0; j < NUM_BELT_PARTS; j++)
13522     {
13523       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13524       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13525
13526       graphic_info[graphic].anim_mode = anim_mode;
13527     }
13528   }
13529
13530   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13531   {
13532     InitRND(tape.random_seed);
13533     for (i = 0; i < num_random_calls; i++)
13534       RND(1);
13535   }
13536
13537   if (game.num_random_calls != num_random_calls)
13538   {
13539     Error(ERR_RETURN, "number of random calls out of sync");
13540     Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
13541     Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
13542     Error(ERR_EXIT, "this should not happen -- please debug");
13543   }
13544 }
13545
13546 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13547 {
13548   struct EngineSnapshotNodeInfo *bi =
13549     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13550
13551   bi->buffer_orig = buffer;
13552   bi->buffer_copy = checked_malloc(size);
13553   bi->size = size;
13554
13555   memcpy(bi->buffer_copy, buffer, size);
13556
13557   addNodeToList(&engine_snapshot_list, NULL, bi);
13558 }
13559
13560 void SaveEngineSnapshot()
13561 {
13562   FreeEngineSnapshot();         /* free previous snapshot, if needed */
13563
13564   if (level_editor_test_game)   /* do not save snapshots from editor */
13565     return;
13566
13567   /* copy some special values to a structure better suited for the snapshot */
13568
13569   SaveEngineSnapshotValues_RND();
13570   SaveEngineSnapshotValues_EM();
13571
13572   /* save values stored in special snapshot structure */
13573
13574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13576
13577   /* save further RND engine values */
13578
13579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13582
13583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13587
13588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13593
13594   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13597
13598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13599
13600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13601
13602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13604
13605   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13609   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13611   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13613   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13616   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13617   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13620   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13621   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13622   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13623
13624   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13625   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13626
13627   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13628   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13629   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13630
13631   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13633
13634   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13635   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13636   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13637   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13638   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13639
13640   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13641   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13642
13643   /* save level identification information */
13644
13645   setString(&snapshot_level_identifier, leveldir_current->identifier);
13646   snapshot_level_nr = level_nr;
13647
13648 #if 0
13649   ListNode *node = engine_snapshot_list;
13650   int num_bytes = 0;
13651
13652   while (node != NULL)
13653   {
13654     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13655
13656     node = node->next;
13657   }
13658
13659   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13660 #endif
13661 }
13662
13663 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13664 {
13665   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13666 }
13667
13668 void LoadEngineSnapshot()
13669 {
13670   ListNode *node = engine_snapshot_list;
13671
13672   if (engine_snapshot_list == NULL)
13673     return;
13674
13675   while (node != NULL)
13676   {
13677     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13678
13679     node = node->next;
13680   }
13681
13682   /* restore special values from snapshot structure */
13683
13684   LoadEngineSnapshotValues_RND();
13685   LoadEngineSnapshotValues_EM();
13686 }
13687
13688 boolean CheckEngineSnapshot()
13689 {
13690   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13691           snapshot_level_nr == level_nr);
13692 }
13693
13694
13695 /* ---------- new game button stuff ---------------------------------------- */
13696
13697 /* graphic position values for game buttons */
13698 #define GAME_BUTTON_XSIZE       30
13699 #define GAME_BUTTON_YSIZE       30
13700 #define GAME_BUTTON_XPOS        5
13701 #define GAME_BUTTON_YPOS        215
13702 #define SOUND_BUTTON_XPOS       5
13703 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13704
13705 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13706 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13707 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13708 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13709 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13710 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13711
13712 static struct
13713 {
13714   int x, y;
13715   int gadget_id;
13716   char *infotext;
13717 } gamebutton_info[NUM_GAME_BUTTONS] =
13718 {
13719   {
13720     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13721     GAME_CTRL_ID_STOP,
13722     "stop game"
13723   },
13724   {
13725     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13726     GAME_CTRL_ID_PAUSE,
13727     "pause game"
13728   },
13729   {
13730     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13731     GAME_CTRL_ID_PLAY,
13732     "play game"
13733   },
13734   {
13735     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13736     SOUND_CTRL_ID_MUSIC,
13737     "background music on/off"
13738   },
13739   {
13740     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13741     SOUND_CTRL_ID_LOOPS,
13742     "sound loops on/off"
13743   },
13744   {
13745     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13746     SOUND_CTRL_ID_SIMPLE,
13747     "normal sounds on/off"
13748   }
13749 };
13750
13751 void CreateGameButtons()
13752 {
13753   int i;
13754
13755   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13756   {
13757     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13758     struct GadgetInfo *gi;
13759     int button_type;
13760     boolean checked;
13761     unsigned long event_mask;
13762     int gd_xoffset, gd_yoffset;
13763     int gd_x1, gd_x2, gd_y1, gd_y2;
13764     int id = i;
13765
13766     gd_xoffset = gamebutton_info[i].x;
13767     gd_yoffset = gamebutton_info[i].y;
13768     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13769     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13770
13771     if (id == GAME_CTRL_ID_STOP ||
13772         id == GAME_CTRL_ID_PAUSE ||
13773         id == GAME_CTRL_ID_PLAY)
13774     {
13775       button_type = GD_TYPE_NORMAL_BUTTON;
13776       checked = FALSE;
13777       event_mask = GD_EVENT_RELEASED;
13778       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13779       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13780     }
13781     else
13782     {
13783       button_type = GD_TYPE_CHECK_BUTTON;
13784       checked =
13785         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13786          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13787          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13788       event_mask = GD_EVENT_PRESSED;
13789       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
13790       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13791     }
13792
13793     gi = CreateGadget(GDI_CUSTOM_ID, id,
13794                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
13795                       GDI_X, DX + gd_xoffset,
13796                       GDI_Y, DY + gd_yoffset,
13797                       GDI_WIDTH, GAME_BUTTON_XSIZE,
13798                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
13799                       GDI_TYPE, button_type,
13800                       GDI_STATE, GD_BUTTON_UNPRESSED,
13801                       GDI_CHECKED, checked,
13802                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13803                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13804                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13805                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13806                       GDI_EVENT_MASK, event_mask,
13807                       GDI_CALLBACK_ACTION, HandleGameButtons,
13808                       GDI_END);
13809
13810     if (gi == NULL)
13811       Error(ERR_EXIT, "cannot create gadget");
13812
13813     game_gadget[id] = gi;
13814   }
13815 }
13816
13817 void FreeGameButtons()
13818 {
13819   int i;
13820
13821   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13822     FreeGadget(game_gadget[i]);
13823 }
13824
13825 static void MapGameButtons()
13826 {
13827   int i;
13828
13829   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13830     MapGadget(game_gadget[i]);
13831 }
13832
13833 void UnmapGameButtons()
13834 {
13835   int i;
13836
13837   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13838     UnmapGadget(game_gadget[i]);
13839 }
13840
13841 static void HandleGameButtons(struct GadgetInfo *gi)
13842 {
13843   int id = gi->custom_id;
13844
13845   if (game_status != GAME_MODE_PLAYING)
13846     return;
13847
13848   switch (id)
13849   {
13850     case GAME_CTRL_ID_STOP:
13851       if (tape.playing)
13852         TapeStop();
13853       else
13854         RequestQuitGame(TRUE);
13855       break;
13856
13857     case GAME_CTRL_ID_PAUSE:
13858       if (options.network)
13859       {
13860 #if defined(NETWORK_AVALIABLE)
13861         if (tape.pausing)
13862           SendToServer_ContinuePlaying();
13863         else
13864           SendToServer_PausePlaying();
13865 #endif
13866       }
13867       else
13868         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13869       break;
13870
13871     case GAME_CTRL_ID_PLAY:
13872       if (tape.pausing)
13873       {
13874 #if defined(NETWORK_AVALIABLE)
13875         if (options.network)
13876           SendToServer_ContinuePlaying();
13877         else
13878 #endif
13879         {
13880           tape.pausing = FALSE;
13881           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13882         }
13883       }
13884       break;
13885
13886     case SOUND_CTRL_ID_MUSIC:
13887       if (setup.sound_music)
13888       { 
13889         setup.sound_music = FALSE;
13890         FadeMusic();
13891       }
13892       else if (audio.music_available)
13893       { 
13894         setup.sound = setup.sound_music = TRUE;
13895
13896         SetAudioMode(setup.sound);
13897
13898         PlayLevelMusic();
13899       }
13900       break;
13901
13902     case SOUND_CTRL_ID_LOOPS:
13903       if (setup.sound_loops)
13904         setup.sound_loops = FALSE;
13905       else if (audio.loops_available)
13906       {
13907         setup.sound = setup.sound_loops = TRUE;
13908         SetAudioMode(setup.sound);
13909       }
13910       break;
13911
13912     case SOUND_CTRL_ID_SIMPLE:
13913       if (setup.sound_simple)
13914         setup.sound_simple = FALSE;
13915       else if (audio.sound_available)
13916       {
13917         setup.sound = setup.sound_simple = TRUE;
13918         SetAudioMode(setup.sound);
13919       }
13920       break;
13921
13922     default:
13923       break;
13924   }
13925 }