237395e75ee33699d5647763f146b3a39d04323b
[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 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #if 1
91 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
92 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
93 #define PANEL_XPOS(p)           (DX + ALIGNED_MENU_XPOS(p))
94 #define PANEL_YPOS(p)           (DY + ALIGNED_MENU_YPOS(p))
95 #else
96 #define PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
97 #define PANEL_XPOS(p)           (ALIGNED_XPOS((p).x, (p).width, (p).align))
98 #define PANEL_YPOS(p)           ((p).y)
99 #endif
100
101 /* special positions in the game control window (relative to control window) */
102 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
103 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
104 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
105 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
106 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
107 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
108 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
109 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
110 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
111 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
112 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
113 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
114 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
115 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
116 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
117 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
118
119 /* special positions in the game control window (relative to main window) */
120 #define DX_LEVEL1               (DX + XX_LEVEL1)
121 #define DX_LEVEL2               (DX + XX_LEVEL2)
122 #define DX_LEVEL                (DX + XX_LEVEL)
123 #define DY_LEVEL                (DY + YY_LEVEL)
124 #define DX_EMERALDS             (DX + XX_EMERALDS)
125 #define DY_EMERALDS             (DY + YY_EMERALDS)
126 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
127 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
128 #define DX_KEYS                 (DX + XX_KEYS)
129 #define DY_KEYS                 (DY + YY_KEYS)
130 #define DX_SCORE                (DX + XX_SCORE)
131 #define DY_SCORE                (DY + YY_SCORE)
132 #define DX_TIME1                (DX + XX_TIME1)
133 #define DX_TIME2                (DX + XX_TIME2)
134 #define DX_TIME                 (DX + XX_TIME)
135 #define DY_TIME                 (DY + YY_TIME)
136
137 /* values for delayed check of falling and moving elements and for collision */
138 #define CHECK_DELAY_MOVING      3
139 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
140 #define CHECK_DELAY_COLLISION   2
141 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
142
143 /* values for initial player move delay (initial delay counter value) */
144 #define INITIAL_MOVE_DELAY_OFF  -1
145 #define INITIAL_MOVE_DELAY_ON   0
146
147 /* values for player movement speed (which is in fact a delay value) */
148 #define MOVE_DELAY_MIN_SPEED    32
149 #define MOVE_DELAY_NORMAL_SPEED 8
150 #define MOVE_DELAY_HIGH_SPEED   4
151 #define MOVE_DELAY_MAX_SPEED    1
152
153 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
154 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
155
156 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
157 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
158
159 /* values for other actions */
160 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
161 #define MOVE_STEPSIZE_MIN       (1)
162 #define MOVE_STEPSIZE_MAX       (TILEX)
163
164 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
165 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
166
167 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
168
169 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
170                                  RND(element_info[e].push_delay_random))
171 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
172                                  RND(element_info[e].drop_delay_random))
173 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
174                                  RND(element_info[e].move_delay_random))
175 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
176                                     (element_info[e].move_delay_random))
177 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
178                                  RND(element_info[e].ce_value_random_initial))
179 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
180 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
181                                  RND((c)->delay_random * (c)->delay_frames))
182 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
183                                  RND((c)->delay_random))
184
185
186 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
187          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
188
189 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
190         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
191          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
192          (be) + (e) - EL_SELF)
193
194 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
195         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
196          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
197          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
198          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
199          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
200          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
201          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
202          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
203          (e))
204
205 #define CAN_GROW_INTO(e)                                                \
206         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
207
208 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
209                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
210                                         (condition)))
211
212 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
213                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
214                                         (CAN_MOVE_INTO_ACID(e) &&       \
215                                          Feld[x][y] == EL_ACID) ||      \
216                                         (condition)))
217
218 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
219                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
220                                         (CAN_MOVE_INTO_ACID(e) &&       \
221                                          Feld[x][y] == EL_ACID) ||      \
222                                         (condition)))
223
224 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
225                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
226                                         (condition) ||                  \
227                                         (CAN_MOVE_INTO_ACID(e) &&       \
228                                          Feld[x][y] == EL_ACID) ||      \
229                                         (DONT_COLLIDE_WITH(e) &&        \
230                                          IS_PLAYER(x, y) &&             \
231                                          !PLAYER_ENEMY_PROTECTED(x, y))))
232
233 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
234         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
235
236 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
237         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
238
239 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
240         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
241
242 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
243         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
244                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
245
246 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
247         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
248
249 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
250         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
251
252 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
253         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
254
255 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
256         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
257
258 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
259         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
260
261 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
262         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
263                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
264                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
265                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
266                                                  IS_FOOD_PENGUIN(Feld[x][y])))
267 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
268         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
269
270 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
271         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
272
273 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
274         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
275
276 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
277         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
278                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
279
280 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
281
282 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
283                 (!IS_PLAYER(x, y) &&                                    \
284                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
285
286 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
287         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
288
289 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
290 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
291
292 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
293 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
294 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
295 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
296
297 /* game button identifiers */
298 #define GAME_CTRL_ID_STOP               0
299 #define GAME_CTRL_ID_PAUSE              1
300 #define GAME_CTRL_ID_PLAY               2
301 #define SOUND_CTRL_ID_MUSIC             3
302 #define SOUND_CTRL_ID_LOOPS             4
303 #define SOUND_CTRL_ID_SIMPLE            5
304
305 #define NUM_GAME_BUTTONS                6
306
307
308 /* forward declaration for internal use */
309
310 static void CreateField(int, int, int);
311
312 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
313 static void AdvanceFrameAndPlayerCounters(int);
314
315 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
316 static boolean MovePlayer(struct PlayerInfo *, int, int);
317 static void ScrollPlayer(struct PlayerInfo *, int);
318 static void ScrollScreen(struct PlayerInfo *, int);
319
320 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
321
322 static void InitBeltMovement(void);
323 static void CloseAllOpenTimegates(void);
324 static void CheckGravityMovement(struct PlayerInfo *);
325 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
326 static void KillPlayerUnlessEnemyProtected(int, int);
327 static void KillPlayerUnlessExplosionProtected(int, int);
328
329 static void TestIfPlayerTouchesCustomElement(int, int);
330 static void TestIfElementTouchesCustomElement(int, int);
331 static void TestIfElementHitsCustomElement(int, int, int);
332 #if 0
333 static void TestIfElementSmashesCustomElement(int, int, int);
334 #endif
335
336 static void HandleElementChange(int, int, int);
337 static void ExecuteCustomElementAction(int, int, int, int);
338 static boolean ChangeElement(int, int, int, int);
339
340 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
341 #define CheckTriggeredElementChange(x, y, e, ev)                        \
342         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
343 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
344         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
345 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
346         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
347 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
348         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
349
350 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
351 #define CheckElementChange(x, y, e, te, ev)                             \
352         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
353 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
354         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
355 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
356         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
357
358 static void PlayLevelSound(int, int, int);
359 static void PlayLevelSoundNearest(int, int, int);
360 static void PlayLevelSoundAction(int, int, int);
361 static void PlayLevelSoundElementAction(int, int, int, int);
362 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
363 static void PlayLevelSoundActionIfLoop(int, int, int);
364 static void StopLevelSoundActionIfLoop(int, int, int);
365 static void PlayLevelMusic();
366
367 static void MapGameButtons();
368 static void HandleGameButtons(struct GadgetInfo *);
369
370 int AmoebeNachbarNr(int, int);
371 void AmoebeUmwandeln(int, int);
372 void ContinueMoving(int, int);
373 void Bang(int, int);
374 void InitMovDir(int, int);
375 void InitAmoebaNr(int, int);
376 int NewHiScore(void);
377
378 void TestIfGoodThingHitsBadThing(int, int, int);
379 void TestIfBadThingHitsGoodThing(int, int, int);
380 void TestIfPlayerTouchesBadThing(int, int);
381 void TestIfPlayerRunsIntoBadThing(int, int, int);
382 void TestIfBadThingTouchesPlayer(int, int);
383 void TestIfBadThingRunsIntoPlayer(int, int, int);
384 void TestIfFriendTouchesBadThing(int, int);
385 void TestIfBadThingTouchesFriend(int, int);
386 void TestIfBadThingTouchesOtherBadThing(int, int);
387
388 void KillPlayer(struct PlayerInfo *);
389 void BuryPlayer(struct PlayerInfo *);
390 void RemovePlayer(struct PlayerInfo *);
391
392 boolean SnapField(struct PlayerInfo *, int, int);
393 boolean DropElement(struct PlayerInfo *);
394
395 static int getInvisibleActiveFromInvisibleElement(int);
396 static int getInvisibleFromInvisibleActiveElement(int);
397
398 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
399
400 /* for detection of endless loops, caused by custom element programming */
401 /* (using maximal playfield width x 10 is just a rough approximation) */
402 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
403
404 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
405 {                                                                       \
406   if (recursion_loop_detected)                                          \
407     return (rc);                                                        \
408                                                                         \
409   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
410   {                                                                     \
411     recursion_loop_detected = TRUE;                                     \
412     recursion_loop_element = (e);                                       \
413   }                                                                     \
414                                                                         \
415   recursion_loop_depth++;                                               \
416 }
417
418 #define RECURSION_LOOP_DETECTION_END()                                  \
419 {                                                                       \
420   recursion_loop_depth--;                                               \
421 }
422
423 static int recursion_loop_depth;
424 static boolean recursion_loop_detected;
425 static boolean recursion_loop_element;
426
427
428 /* ------------------------------------------------------------------------- */
429 /* definition of elements that automatically change to other elements after  */
430 /* a specified time, eventually calling a function when changing             */
431 /* ------------------------------------------------------------------------- */
432
433 /* forward declaration for changer functions */
434 static void InitBuggyBase(int, int);
435 static void WarnBuggyBase(int, int);
436
437 static void InitTrap(int, int);
438 static void ActivateTrap(int, int);
439 static void ChangeActiveTrap(int, int);
440
441 static void InitRobotWheel(int, int);
442 static void RunRobotWheel(int, int);
443 static void StopRobotWheel(int, int);
444
445 static void InitTimegateWheel(int, int);
446 static void RunTimegateWheel(int, int);
447
448 static void InitMagicBallDelay(int, int);
449 static void ActivateMagicBall(int, int);
450
451 struct ChangingElementInfo
452 {
453   int element;
454   int target_element;
455   int change_delay;
456   void (*pre_change_function)(int x, int y);
457   void (*change_function)(int x, int y);
458   void (*post_change_function)(int x, int y);
459 };
460
461 static struct ChangingElementInfo change_delay_list[] =
462 {
463   {
464     EL_NUT_BREAKING,
465     EL_EMERALD,
466     6,
467     NULL,
468     NULL,
469     NULL
470   },
471   {
472     EL_PEARL_BREAKING,
473     EL_EMPTY,
474     8,
475     NULL,
476     NULL,
477     NULL
478   },
479   {
480     EL_EXIT_OPENING,
481     EL_EXIT_OPEN,
482     29,
483     NULL,
484     NULL,
485     NULL
486   },
487   {
488     EL_EXIT_CLOSING,
489     EL_EXIT_CLOSED,
490     29,
491     NULL,
492     NULL,
493     NULL
494   },
495   {
496     EL_STEEL_EXIT_OPENING,
497     EL_STEEL_EXIT_OPEN,
498     29,
499     NULL,
500     NULL,
501     NULL
502   },
503   {
504     EL_STEEL_EXIT_CLOSING,
505     EL_STEEL_EXIT_CLOSED,
506     29,
507     NULL,
508     NULL,
509     NULL
510   },
511   {
512     EL_EM_EXIT_OPENING,
513     EL_EM_EXIT_OPEN,
514     29,
515     NULL,
516     NULL,
517     NULL
518   },
519   {
520     EL_EM_EXIT_CLOSING,
521 #if 1
522     EL_EMPTY,
523 #else
524     EL_EM_EXIT_CLOSED,
525 #endif
526     29,
527     NULL,
528     NULL,
529     NULL
530   },
531   {
532     EL_EM_STEEL_EXIT_OPENING,
533     EL_EM_STEEL_EXIT_OPEN,
534     29,
535     NULL,
536     NULL,
537     NULL
538   },
539   {
540     EL_EM_STEEL_EXIT_CLOSING,
541 #if 1
542     EL_STEELWALL,
543 #else
544     EL_EM_STEEL_EXIT_CLOSED,
545 #endif
546     29,
547     NULL,
548     NULL,
549     NULL
550   },
551   {
552     EL_SP_EXIT_OPENING,
553     EL_SP_EXIT_OPEN,
554     29,
555     NULL,
556     NULL,
557     NULL
558   },
559   {
560     EL_SP_EXIT_CLOSING,
561     EL_SP_EXIT_CLOSED,
562     29,
563     NULL,
564     NULL,
565     NULL
566   },
567   {
568     EL_SWITCHGATE_OPENING,
569     EL_SWITCHGATE_OPEN,
570     29,
571     NULL,
572     NULL,
573     NULL
574   },
575   {
576     EL_SWITCHGATE_CLOSING,
577     EL_SWITCHGATE_CLOSED,
578     29,
579     NULL,
580     NULL,
581     NULL
582   },
583   {
584     EL_TIMEGATE_OPENING,
585     EL_TIMEGATE_OPEN,
586     29,
587     NULL,
588     NULL,
589     NULL
590   },
591   {
592     EL_TIMEGATE_CLOSING,
593     EL_TIMEGATE_CLOSED,
594     29,
595     NULL,
596     NULL,
597     NULL
598   },
599
600   {
601     EL_ACID_SPLASH_LEFT,
602     EL_EMPTY,
603     8,
604     NULL,
605     NULL,
606     NULL
607   },
608   {
609     EL_ACID_SPLASH_RIGHT,
610     EL_EMPTY,
611     8,
612     NULL,
613     NULL,
614     NULL
615   },
616   {
617     EL_SP_BUGGY_BASE,
618     EL_SP_BUGGY_BASE_ACTIVATING,
619     0,
620     InitBuggyBase,
621     NULL,
622     NULL
623   },
624   {
625     EL_SP_BUGGY_BASE_ACTIVATING,
626     EL_SP_BUGGY_BASE_ACTIVE,
627     0,
628     InitBuggyBase,
629     NULL,
630     NULL
631   },
632   {
633     EL_SP_BUGGY_BASE_ACTIVE,
634     EL_SP_BUGGY_BASE,
635     0,
636     InitBuggyBase,
637     WarnBuggyBase,
638     NULL
639   },
640   {
641     EL_TRAP,
642     EL_TRAP_ACTIVE,
643     0,
644     InitTrap,
645     NULL,
646     ActivateTrap
647   },
648   {
649     EL_TRAP_ACTIVE,
650     EL_TRAP,
651     31,
652     NULL,
653     ChangeActiveTrap,
654     NULL
655   },
656   {
657     EL_ROBOT_WHEEL_ACTIVE,
658     EL_ROBOT_WHEEL,
659     0,
660     InitRobotWheel,
661     RunRobotWheel,
662     StopRobotWheel
663   },
664   {
665     EL_TIMEGATE_SWITCH_ACTIVE,
666     EL_TIMEGATE_SWITCH,
667     0,
668     InitTimegateWheel,
669     RunTimegateWheel,
670     NULL
671   },
672   {
673     EL_DC_TIMEGATE_SWITCH_ACTIVE,
674     EL_DC_TIMEGATE_SWITCH,
675     0,
676     InitTimegateWheel,
677     RunTimegateWheel,
678     NULL
679   },
680   {
681     EL_EMC_MAGIC_BALL_ACTIVE,
682     EL_EMC_MAGIC_BALL_ACTIVE,
683     0,
684     InitMagicBallDelay,
685     NULL,
686     ActivateMagicBall
687   },
688   {
689     EL_EMC_SPRING_BUMPER_ACTIVE,
690     EL_EMC_SPRING_BUMPER,
691     8,
692     NULL,
693     NULL,
694     NULL
695   },
696   {
697     EL_DIAGONAL_SHRINKING,
698     EL_UNDEFINED,
699     0,
700     NULL,
701     NULL,
702     NULL
703   },
704   {
705     EL_DIAGONAL_GROWING,
706     EL_UNDEFINED,
707     0,
708     NULL,
709     NULL,
710     NULL,
711   },
712
713   {
714     EL_UNDEFINED,
715     EL_UNDEFINED,
716     -1,
717     NULL,
718     NULL,
719     NULL
720   }
721 };
722
723 struct
724 {
725   int element;
726   int push_delay_fixed, push_delay_random;
727 }
728 push_delay_list[] =
729 {
730   { EL_SPRING,                  0, 0 },
731   { EL_BALLOON,                 0, 0 },
732
733   { EL_SOKOBAN_OBJECT,          2, 0 },
734   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
735   { EL_SATELLITE,               2, 0 },
736   { EL_SP_DISK_YELLOW,          2, 0 },
737
738   { EL_UNDEFINED,               0, 0 },
739 };
740
741 struct
742 {
743   int element;
744   int move_stepsize;
745 }
746 move_stepsize_list[] =
747 {
748   { EL_AMOEBA_DROP,             2 },
749   { EL_AMOEBA_DROPPING,         2 },
750   { EL_QUICKSAND_FILLING,       1 },
751   { EL_QUICKSAND_EMPTYING,      1 },
752   { EL_QUICKSAND_FAST_FILLING,  2 },
753   { EL_QUICKSAND_FAST_EMPTYING, 2 },
754   { EL_MAGIC_WALL_FILLING,      2 },
755   { EL_MAGIC_WALL_EMPTYING,     2 },
756   { EL_BD_MAGIC_WALL_FILLING,   2 },
757   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
758   { EL_DC_MAGIC_WALL_FILLING,   2 },
759   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
760
761   { EL_UNDEFINED,               0 },
762 };
763
764 struct
765 {
766   int element;
767   int count;
768 }
769 collect_count_list[] =
770 {
771   { EL_EMERALD,                 1 },
772   { EL_BD_DIAMOND,              1 },
773   { EL_EMERALD_YELLOW,          1 },
774   { EL_EMERALD_RED,             1 },
775   { EL_EMERALD_PURPLE,          1 },
776   { EL_DIAMOND,                 3 },
777   { EL_SP_INFOTRON,             1 },
778   { EL_PEARL,                   5 },
779   { EL_CRYSTAL,                 8 },
780
781   { EL_UNDEFINED,               0 },
782 };
783
784 struct
785 {
786   int element;
787   int direction;
788 }
789 access_direction_list[] =
790 {
791   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
792   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
793   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
794   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
795   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
796   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
797   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
798   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
799   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
800   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
801   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
802
803   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
804   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
805   { EL_SP_PORT_UP,                                                   MV_DOWN },
806   { EL_SP_PORT_DOWN,                                         MV_UP           },
807   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
808   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
809   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
810   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
811   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
812   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
813   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
814   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
815   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
816   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
817   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
818   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
819   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
820   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
821   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
822
823   { EL_UNDEFINED,                       MV_NONE                              }
824 };
825
826 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
827
828 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
829 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
830 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
831                                  IS_JUST_CHANGING(x, y))
832
833 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
834
835 /* static variables for playfield scan mode (scanning forward or backward) */
836 static int playfield_scan_start_x = 0;
837 static int playfield_scan_start_y = 0;
838 static int playfield_scan_delta_x = 1;
839 static int playfield_scan_delta_y = 1;
840
841 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
842                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
843                                      (y) += playfield_scan_delta_y)     \
844                                 for ((x) = playfield_scan_start_x;      \
845                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
846                                      (x) += playfield_scan_delta_x)     \
847
848 #ifdef DEBUG
849 void DEBUG_SetMaximumDynamite()
850 {
851   int i;
852
853   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
854     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
855       local_player->inventory_element[local_player->inventory_size++] =
856         EL_DYNAMITE;
857 }
858 #endif
859
860 static void InitPlayfieldScanModeVars()
861 {
862   if (game.use_reverse_scan_direction)
863   {
864     playfield_scan_start_x = lev_fieldx - 1;
865     playfield_scan_start_y = lev_fieldy - 1;
866
867     playfield_scan_delta_x = -1;
868     playfield_scan_delta_y = -1;
869   }
870   else
871   {
872     playfield_scan_start_x = 0;
873     playfield_scan_start_y = 0;
874
875     playfield_scan_delta_x = 1;
876     playfield_scan_delta_y = 1;
877   }
878 }
879
880 static void InitPlayfieldScanMode(int mode)
881 {
882   game.use_reverse_scan_direction =
883     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
884
885   InitPlayfieldScanModeVars();
886 }
887
888 static int get_move_delay_from_stepsize(int move_stepsize)
889 {
890   move_stepsize =
891     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
892
893   /* make sure that stepsize value is always a power of 2 */
894   move_stepsize = (1 << log_2(move_stepsize));
895
896   return TILEX / move_stepsize;
897 }
898
899 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
900                                boolean init_game)
901 {
902   int player_nr = player->index_nr;
903   int move_delay = get_move_delay_from_stepsize(move_stepsize);
904   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
905
906   /* do no immediately change move delay -- the player might just be moving */
907   player->move_delay_value_next = move_delay;
908
909   /* information if player can move must be set separately */
910   player->cannot_move = cannot_move;
911
912   if (init_game)
913   {
914     player->move_delay       = game.initial_move_delay[player_nr];
915     player->move_delay_value = game.initial_move_delay_value[player_nr];
916
917     player->move_delay_value_next = -1;
918
919     player->move_delay_reset_counter = 0;
920   }
921 }
922
923 void GetPlayerConfig()
924 {
925   GameFrameDelay = setup.game_frame_delay;
926
927   if (!audio.sound_available)
928     setup.sound_simple = FALSE;
929
930   if (!audio.loops_available)
931     setup.sound_loops = FALSE;
932
933   if (!audio.music_available)
934     setup.sound_music = FALSE;
935
936   if (!video.fullscreen_available)
937     setup.fullscreen = FALSE;
938
939   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
940
941   SetAudioMode(setup.sound);
942   InitJoysticks();
943 }
944
945 static int get_element_from_group_element(int element)
946 {
947   if (IS_GROUP_ELEMENT(element))
948   {
949     struct ElementGroupInfo *group = element_info[element].group;
950     int last_anim_random_frame = gfx.anim_random_frame;
951     int element_pos;
952
953     if (group->choice_mode == ANIM_RANDOM)
954       gfx.anim_random_frame = RND(group->num_elements_resolved);
955
956     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
957                                     group->choice_mode, 0,
958                                     group->choice_pos);
959
960     if (group->choice_mode == ANIM_RANDOM)
961       gfx.anim_random_frame = last_anim_random_frame;
962
963     group->choice_pos++;
964
965     element = group->element_resolved[element_pos];
966   }
967
968   return element;
969 }
970
971 static void InitPlayerField(int x, int y, int element, boolean init_game)
972 {
973   if (element == EL_SP_MURPHY)
974   {
975     if (init_game)
976     {
977       if (stored_player[0].present)
978       {
979         Feld[x][y] = EL_SP_MURPHY_CLONE;
980
981         return;
982       }
983       else
984       {
985         stored_player[0].use_murphy = TRUE;
986
987         if (!level.use_artwork_element[0])
988           stored_player[0].artwork_element = EL_SP_MURPHY;
989       }
990
991       Feld[x][y] = EL_PLAYER_1;
992     }
993   }
994
995   if (init_game)
996   {
997     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
998     int jx = player->jx, jy = player->jy;
999
1000     player->present = TRUE;
1001
1002     player->block_last_field = (element == EL_SP_MURPHY ?
1003                                 level.sp_block_last_field :
1004                                 level.block_last_field);
1005
1006     /* ---------- initialize player's last field block delay --------------- */
1007
1008     /* always start with reliable default value (no adjustment needed) */
1009     player->block_delay_adjustment = 0;
1010
1011     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1012     if (player->block_last_field && element == EL_SP_MURPHY)
1013       player->block_delay_adjustment = 1;
1014
1015     /* special case 2: in game engines before 3.1.1, blocking was different */
1016     if (game.use_block_last_field_bug)
1017       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1018
1019     if (!options.network || player->connected)
1020     {
1021       player->active = TRUE;
1022
1023       /* remove potentially duplicate players */
1024       if (StorePlayer[jx][jy] == Feld[x][y])
1025         StorePlayer[jx][jy] = 0;
1026
1027       StorePlayer[x][y] = Feld[x][y];
1028
1029       if (options.debug)
1030       {
1031         printf("Player %d activated.\n", player->element_nr);
1032         printf("[Local player is %d and currently %s.]\n",
1033                local_player->element_nr,
1034                local_player->active ? "active" : "not active");
1035       }
1036     }
1037
1038     Feld[x][y] = EL_EMPTY;
1039
1040     player->jx = player->last_jx = x;
1041     player->jy = player->last_jy = y;
1042   }
1043 }
1044
1045 static void InitField(int x, int y, boolean init_game)
1046 {
1047   int element = Feld[x][y];
1048
1049   switch (element)
1050   {
1051     case EL_SP_MURPHY:
1052     case EL_PLAYER_1:
1053     case EL_PLAYER_2:
1054     case EL_PLAYER_3:
1055     case EL_PLAYER_4:
1056       InitPlayerField(x, y, element, init_game);
1057       break;
1058
1059     case EL_SOKOBAN_FIELD_PLAYER:
1060       element = Feld[x][y] = EL_PLAYER_1;
1061       InitField(x, y, init_game);
1062
1063       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1064       InitField(x, y, init_game);
1065       break;
1066
1067     case EL_SOKOBAN_FIELD_EMPTY:
1068       local_player->sokobanfields_still_needed++;
1069       break;
1070
1071     case EL_STONEBLOCK:
1072       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1073         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1074       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1075         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1076       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1077         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1078       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1079         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1080       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1081         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1082       break;
1083
1084     case EL_BUG:
1085     case EL_BUG_RIGHT:
1086     case EL_BUG_UP:
1087     case EL_BUG_LEFT:
1088     case EL_BUG_DOWN:
1089     case EL_SPACESHIP:
1090     case EL_SPACESHIP_RIGHT:
1091     case EL_SPACESHIP_UP:
1092     case EL_SPACESHIP_LEFT:
1093     case EL_SPACESHIP_DOWN:
1094     case EL_BD_BUTTERFLY:
1095     case EL_BD_BUTTERFLY_RIGHT:
1096     case EL_BD_BUTTERFLY_UP:
1097     case EL_BD_BUTTERFLY_LEFT:
1098     case EL_BD_BUTTERFLY_DOWN:
1099     case EL_BD_FIREFLY:
1100     case EL_BD_FIREFLY_RIGHT:
1101     case EL_BD_FIREFLY_UP:
1102     case EL_BD_FIREFLY_LEFT:
1103     case EL_BD_FIREFLY_DOWN:
1104     case EL_PACMAN_RIGHT:
1105     case EL_PACMAN_UP:
1106     case EL_PACMAN_LEFT:
1107     case EL_PACMAN_DOWN:
1108     case EL_YAMYAM:
1109     case EL_YAMYAM_LEFT:
1110     case EL_YAMYAM_RIGHT:
1111     case EL_YAMYAM_UP:
1112     case EL_YAMYAM_DOWN:
1113     case EL_DARK_YAMYAM:
1114     case EL_ROBOT:
1115     case EL_PACMAN:
1116     case EL_SP_SNIKSNAK:
1117     case EL_SP_ELECTRON:
1118     case EL_MOLE:
1119     case EL_MOLE_LEFT:
1120     case EL_MOLE_RIGHT:
1121     case EL_MOLE_UP:
1122     case EL_MOLE_DOWN:
1123       InitMovDir(x, y);
1124       break;
1125
1126     case EL_AMOEBA_FULL:
1127     case EL_BD_AMOEBA:
1128       InitAmoebaNr(x, y);
1129       break;
1130
1131     case EL_AMOEBA_DROP:
1132       if (y == lev_fieldy - 1)
1133       {
1134         Feld[x][y] = EL_AMOEBA_GROWING;
1135         Store[x][y] = EL_AMOEBA_WET;
1136       }
1137       break;
1138
1139     case EL_DYNAMITE_ACTIVE:
1140     case EL_SP_DISK_RED_ACTIVE:
1141     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1142     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1143     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1144     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1145       MovDelay[x][y] = 96;
1146       break;
1147
1148     case EL_EM_DYNAMITE_ACTIVE:
1149       MovDelay[x][y] = 32;
1150       break;
1151
1152     case EL_LAMP:
1153       local_player->lights_still_needed++;
1154       break;
1155
1156     case EL_PENGUIN:
1157       local_player->friends_still_needed++;
1158       break;
1159
1160     case EL_PIG:
1161     case EL_DRAGON:
1162       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1163       break;
1164
1165     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1166     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1167     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1168     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1169     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1170     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1171     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1172     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1173     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1174     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1175     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1176     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1177       if (init_game)
1178       {
1179         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1180         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1181         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1182
1183         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1184         {
1185           game.belt_dir[belt_nr] = belt_dir;
1186           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1187         }
1188         else    /* more than one switch -- set it like the first switch */
1189         {
1190           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1191         }
1192       }
1193       break;
1194
1195 #if !USE_BOTH_SWITCHGATE_SWITCHES
1196     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1197       if (init_game)
1198         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1199       break;
1200
1201     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1202       if (init_game)
1203         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1204       break;
1205 #endif
1206
1207     case EL_LIGHT_SWITCH_ACTIVE:
1208       if (init_game)
1209         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1210       break;
1211
1212     case EL_INVISIBLE_STEELWALL:
1213     case EL_INVISIBLE_WALL:
1214     case EL_INVISIBLE_SAND:
1215       if (game.light_time_left > 0 ||
1216           game.lenses_time_left > 0)
1217         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1218       break;
1219
1220     case EL_EMC_MAGIC_BALL:
1221       if (game.ball_state)
1222         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1223       break;
1224
1225     case EL_EMC_MAGIC_BALL_SWITCH:
1226       if (game.ball_state)
1227         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1228       break;
1229
1230     default:
1231       if (IS_CUSTOM_ELEMENT(element))
1232       {
1233         if (CAN_MOVE(element))
1234           InitMovDir(x, y);
1235
1236 #if USE_NEW_CUSTOM_VALUE
1237         if (!element_info[element].use_last_ce_value || init_game)
1238           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1239 #endif
1240       }
1241       else if (IS_GROUP_ELEMENT(element))
1242       {
1243         Feld[x][y] = get_element_from_group_element(element);
1244
1245         InitField(x, y, init_game);
1246       }
1247
1248       break;
1249   }
1250
1251   if (!init_game)
1252     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1253 }
1254
1255 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1256 {
1257   InitField(x, y, init_game);
1258
1259   /* not needed to call InitMovDir() -- already done by InitField()! */
1260   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1261       CAN_MOVE(Feld[x][y]))
1262     InitMovDir(x, y);
1263 }
1264
1265 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1266 {
1267   int old_element = Feld[x][y];
1268
1269   InitField(x, y, init_game);
1270
1271   /* not needed to call InitMovDir() -- already done by InitField()! */
1272   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1273       CAN_MOVE(old_element) &&
1274       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1275     InitMovDir(x, y);
1276
1277   /* this case is in fact a combination of not less than three bugs:
1278      first, it calls InitMovDir() for elements that can move, although this is
1279      already done by InitField(); then, it checks the element that was at this
1280      field _before_ the call to InitField() (which can change it); lastly, it
1281      was not called for "mole with direction" elements, which were treated as
1282      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1283   */
1284 }
1285
1286 #if 1
1287
1288 void DrawGameValue_Emeralds(int value)
1289 {
1290   struct TextPosInfo *pos = &game.panel.gems;
1291   int font_nr = FONT_TEXT_2;
1292   int font_width = getFontWidth(font_nr);
1293   int digits = pos->chars;
1294
1295   if (PANEL_DEACTIVATED(pos))
1296     return;
1297
1298   pos->width = digits * font_width;
1299
1300   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1301 }
1302
1303 void DrawGameValue_Dynamite(int value)
1304 {
1305   struct TextPosInfo *pos = &game.panel.inventory;
1306   int font_nr = FONT_TEXT_2;
1307   int font_width = getFontWidth(font_nr);
1308   int digits = pos->chars;
1309
1310   if (PANEL_DEACTIVATED(pos))
1311     return;
1312
1313   pos->width = digits * font_width;
1314
1315   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1316 }
1317
1318 void DrawGameValue_Score(int value)
1319 {
1320   struct TextPosInfo *pos = &game.panel.score;
1321   int font_nr = FONT_TEXT_2;
1322   int font_width = getFontWidth(font_nr);
1323   int digits = pos->chars;
1324
1325   if (PANEL_DEACTIVATED(pos))
1326     return;
1327
1328   pos->width = digits * font_width;
1329
1330   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1331 }
1332
1333 void DrawGameValue_Time(int value)
1334 {
1335   struct TextPosInfo *pos = &game.panel.time;
1336   static int last_value = -1;
1337   int digits1 = 3;
1338   int digits2 = 4;
1339   int digits = pos->chars;
1340   int font1_nr = FONT_TEXT_2;
1341   int font2_nr = FONT_TEXT_1;
1342   int font_nr = font1_nr;
1343   boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1344
1345   if (PANEL_DEACTIVATED(pos))
1346     return;
1347
1348   if (use_dynamic_digits)               /* use dynamic number of digits */
1349   {
1350     digits  = (value < 1000 ? digits1  : digits2);
1351     font_nr = (value < 1000 ? font1_nr : font2_nr);
1352   }
1353
1354   /* clear background if value just changed its size (dynamic digits only) */
1355   if (use_dynamic_digits && (last_value < 1000) != (value < 1000))
1356   {
1357     int width1 = digits1 * getFontWidth(font1_nr);
1358     int width2 = digits2 * getFontWidth(font2_nr);
1359     int max_width = MAX(width1, width2);
1360     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1361
1362     pos->width = max_width;
1363
1364     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1365                                max_width, max_height);
1366   }
1367
1368   pos->width = digits * getFontWidth(font_nr);
1369
1370   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1371
1372   last_value = value;
1373 }
1374
1375 void DrawGameValue_Level(int value)
1376 {
1377   struct TextPosInfo *pos = &game.panel.level;
1378   int digits1 = 2;
1379   int digits2 = 3;
1380   int digits = pos->chars;
1381   int font1_nr = FONT_TEXT_2;
1382   int font2_nr = FONT_TEXT_1;
1383   int font_nr = font1_nr;
1384   boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1385
1386   if (PANEL_DEACTIVATED(pos))
1387     return;
1388
1389   if (use_dynamic_digits)               /* use dynamic number of digits */
1390   {
1391     digits  = (level_nr < 100 ? digits1  : digits2);
1392     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1393   }
1394
1395   pos->width = digits * getFontWidth(font_nr);
1396
1397   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1398 }
1399
1400 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1401 {
1402   struct TextPosInfo *pos = &game.panel.keys;
1403   int base_key_graphic = EL_KEY_1;
1404   int i;
1405
1406   if (PANEL_DEACTIVATED(pos))
1407     return;
1408
1409   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1410     base_key_graphic = EL_EM_KEY_1;
1411
1412   pos->width = 4 * MINI_TILEX;
1413
1414   /* currently only 4 of 8 possible keys are displayed */
1415   for (i = 0; i < STD_NUM_KEYS; i++)
1416   {
1417     int src_x = DOOR_GFX_PAGEX5 + 18;
1418     int src_y = DOOR_GFX_PAGEY1 + 123;
1419     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1420     int dst_y = PANEL_YPOS(pos);
1421
1422     if (key[i])
1423       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1424     else
1425       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1426                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1427   }
1428 }
1429
1430 #else
1431
1432 void DrawGameValue_Emeralds(int value)
1433 {
1434   int font_nr = FONT_TEXT_2;
1435   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1436
1437   if (PANEL_DEACTIVATED(game.panel.gems))
1438     return;
1439
1440   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1441 }
1442
1443 void DrawGameValue_Dynamite(int value)
1444 {
1445   int font_nr = FONT_TEXT_2;
1446   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1447
1448   if (PANEL_DEACTIVATED(game.panel.inventory))
1449     return;
1450
1451   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1452 }
1453
1454 void DrawGameValue_Score(int value)
1455 {
1456   int font_nr = FONT_TEXT_2;
1457   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1458
1459   if (PANEL_DEACTIVATED(game.panel.score))
1460     return;
1461
1462   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1463 }
1464
1465 void DrawGameValue_Time(int value)
1466 {
1467   int font1_nr = FONT_TEXT_2;
1468 #if 1
1469   int font2_nr = FONT_TEXT_1;
1470 #else
1471   int font2_nr = FONT_LEVEL_NUMBER;
1472 #endif
1473   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1474   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1475
1476   if (PANEL_DEACTIVATED(game.panel.time))
1477     return;
1478
1479   /* clear background if value just changed its size */
1480   if (value == 999 || value == 1000)
1481     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1482
1483   if (value < 1000)
1484     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1485   else
1486     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1487 }
1488
1489 void DrawGameValue_Level(int value)
1490 {
1491   int font1_nr = FONT_TEXT_2;
1492 #if 1
1493   int font2_nr = FONT_TEXT_1;
1494 #else
1495   int font2_nr = FONT_LEVEL_NUMBER;
1496 #endif
1497
1498   if (PANEL_DEACTIVATED(game.panel.level))
1499     return;
1500
1501   if (level_nr < 100)
1502     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1503   else
1504     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1505 }
1506
1507 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1508 {
1509   int base_key_graphic = EL_KEY_1;
1510   int i;
1511
1512   if (PANEL_DEACTIVATED(game.panel.keys))
1513     return;
1514
1515   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1516     base_key_graphic = EL_EM_KEY_1;
1517
1518   /* currently only 4 of 8 possible keys are displayed */
1519   for (i = 0; i < STD_NUM_KEYS; i++)
1520   {
1521     int x = XX_KEYS + i * MINI_TILEX;
1522     int y = YY_KEYS;
1523
1524     if (key[i])
1525       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1526     else
1527       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1528                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1529   }
1530 }
1531
1532 #endif
1533
1534 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1535                        int key_bits)
1536 {
1537   int key[MAX_NUM_KEYS];
1538   int i;
1539
1540   /* prevent EM engine from updating time/score values parallel to GameWon() */
1541   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1542       local_player->LevelSolved)
1543     return;
1544
1545   for (i = 0; i < MAX_NUM_KEYS; i++)
1546     key[i] = key_bits & (1 << i);
1547
1548   DrawGameValue_Level(level_nr);
1549
1550   DrawGameValue_Emeralds(emeralds);
1551   DrawGameValue_Dynamite(dynamite);
1552   DrawGameValue_Score(score);
1553   DrawGameValue_Time(time);
1554
1555   DrawGameValue_Keys(key);
1556 }
1557
1558 void DrawGameDoorValues()
1559 {
1560   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1561   int dynamite_value = 0;
1562   int score_value = (local_player->LevelSolved ? local_player->score_final :
1563                      local_player->score);
1564   int gems_value = local_player->gems_still_needed;
1565   int key_bits = 0;
1566   int i, j;
1567
1568   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1569   {
1570     DrawGameDoorValues_EM();
1571
1572     return;
1573   }
1574
1575   if (game.centered_player_nr == -1)
1576   {
1577     for (i = 0; i < MAX_PLAYERS; i++)
1578     {
1579       for (j = 0; j < MAX_NUM_KEYS; j++)
1580         if (stored_player[i].key[j])
1581           key_bits |= (1 << j);
1582
1583       dynamite_value += stored_player[i].inventory_size;
1584     }
1585   }
1586   else
1587   {
1588     int player_nr = game.centered_player_nr;
1589
1590     for (i = 0; i < MAX_NUM_KEYS; i++)
1591       if (stored_player[player_nr].key[i])
1592         key_bits |= (1 << i);
1593
1594     dynamite_value = stored_player[player_nr].inventory_size;
1595   }
1596
1597   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1598                     key_bits);
1599 }
1600
1601
1602 /*
1603   =============================================================================
1604   InitGameEngine()
1605   -----------------------------------------------------------------------------
1606   initialize game engine due to level / tape version number
1607   =============================================================================
1608 */
1609
1610 static void InitGameEngine()
1611 {
1612   int i, j, k, l, x, y;
1613
1614   /* set game engine from tape file when re-playing, else from level file */
1615   game.engine_version = (tape.playing ? tape.engine_version :
1616                          level.game_version);
1617
1618   /* ---------------------------------------------------------------------- */
1619   /* set flags for bugs and changes according to active game engine version */
1620   /* ---------------------------------------------------------------------- */
1621
1622   /*
1623     Summary of bugfix/change:
1624     Fixed handling for custom elements that change when pushed by the player.
1625
1626     Fixed/changed in version:
1627     3.1.0
1628
1629     Description:
1630     Before 3.1.0, custom elements that "change when pushing" changed directly
1631     after the player started pushing them (until then handled in "DigField()").
1632     Since 3.1.0, these custom elements are not changed until the "pushing"
1633     move of the element is finished (now handled in "ContinueMoving()").
1634
1635     Affected levels/tapes:
1636     The first condition is generally needed for all levels/tapes before version
1637     3.1.0, which might use the old behaviour before it was changed; known tapes
1638     that are affected are some tapes from the level set "Walpurgis Gardens" by
1639     Jamie Cullen.
1640     The second condition is an exception from the above case and is needed for
1641     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1642     above (including some development versions of 3.1.0), but before it was
1643     known that this change would break tapes like the above and was fixed in
1644     3.1.1, so that the changed behaviour was active although the engine version
1645     while recording maybe was before 3.1.0. There is at least one tape that is
1646     affected by this exception, which is the tape for the one-level set "Bug
1647     Machine" by Juergen Bonhagen.
1648   */
1649
1650   game.use_change_when_pushing_bug =
1651     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1652      !(tape.playing &&
1653        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1654        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1655
1656   /*
1657     Summary of bugfix/change:
1658     Fixed handling for blocking the field the player leaves when moving.
1659
1660     Fixed/changed in version:
1661     3.1.1
1662
1663     Description:
1664     Before 3.1.1, when "block last field when moving" was enabled, the field
1665     the player is leaving when moving was blocked for the time of the move,
1666     and was directly unblocked afterwards. This resulted in the last field
1667     being blocked for exactly one less than the number of frames of one player
1668     move. Additionally, even when blocking was disabled, the last field was
1669     blocked for exactly one frame.
1670     Since 3.1.1, due to changes in player movement handling, the last field
1671     is not blocked at all when blocking is disabled. When blocking is enabled,
1672     the last field is blocked for exactly the number of frames of one player
1673     move. Additionally, if the player is Murphy, the hero of Supaplex, the
1674     last field is blocked for exactly one more than the number of frames of
1675     one player move.
1676
1677     Affected levels/tapes:
1678     (!!! yet to be determined -- probably many !!!)
1679   */
1680
1681   game.use_block_last_field_bug =
1682     (game.engine_version < VERSION_IDENT(3,1,1,0));
1683
1684   /*
1685     Summary of bugfix/change:
1686     Changed behaviour of CE changes with multiple changes per single frame.
1687
1688     Fixed/changed in version:
1689     3.2.0-6
1690
1691     Description:
1692     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1693     This resulted in race conditions where CEs seem to behave strange in some
1694     situations (where triggered CE changes were just skipped because there was
1695     already a CE change on that tile in the playfield in that engine frame).
1696     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1697     (The number of changes per frame must be limited in any case, because else
1698     it is easily possible to define CE changes that would result in an infinite
1699     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1700     should be set large enough so that it would only be reached in cases where
1701     the corresponding CE change conditions run into a loop. Therefore, it seems
1702     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1703     maximal number of change pages for custom elements.)
1704
1705     Affected levels/tapes:
1706     Probably many.
1707   */
1708
1709 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1710   game.max_num_changes_per_frame = 1;
1711 #else
1712   game.max_num_changes_per_frame =
1713     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1714 #endif
1715
1716   /* ---------------------------------------------------------------------- */
1717
1718   /* default scan direction: scan playfield from top/left to bottom/right */
1719   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1720
1721   /* dynamically adjust element properties according to game engine version */
1722   InitElementPropertiesEngine(game.engine_version);
1723
1724 #if 0
1725   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1726   printf("          tape version == %06d [%s] [file: %06d]\n",
1727          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1728          tape.file_version);
1729   printf("       => game.engine_version == %06d\n", game.engine_version);
1730 #endif
1731
1732   /* ---------- initialize player's initial move delay --------------------- */
1733
1734   /* dynamically adjust player properties according to level information */
1735   for (i = 0; i < MAX_PLAYERS; i++)
1736     game.initial_move_delay_value[i] =
1737       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1738
1739   /* dynamically adjust player properties according to game engine version */
1740   for (i = 0; i < MAX_PLAYERS; i++)
1741     game.initial_move_delay[i] =
1742       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1743        game.initial_move_delay_value[i] : 0);
1744
1745   /* ---------- initialize player's initial push delay --------------------- */
1746
1747   /* dynamically adjust player properties according to game engine version */
1748   game.initial_push_delay_value =
1749     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1750
1751   /* ---------- initialize changing elements ------------------------------- */
1752
1753   /* initialize changing elements information */
1754   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1755   {
1756     struct ElementInfo *ei = &element_info[i];
1757
1758     /* this pointer might have been changed in the level editor */
1759     ei->change = &ei->change_page[0];
1760
1761     if (!IS_CUSTOM_ELEMENT(i))
1762     {
1763       ei->change->target_element = EL_EMPTY_SPACE;
1764       ei->change->delay_fixed = 0;
1765       ei->change->delay_random = 0;
1766       ei->change->delay_frames = 1;
1767     }
1768
1769     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1770     {
1771       ei->has_change_event[j] = FALSE;
1772
1773       ei->event_page_nr[j] = 0;
1774       ei->event_page[j] = &ei->change_page[0];
1775     }
1776   }
1777
1778   /* add changing elements from pre-defined list */
1779   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1780   {
1781     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1782     struct ElementInfo *ei = &element_info[ch_delay->element];
1783
1784     ei->change->target_element       = ch_delay->target_element;
1785     ei->change->delay_fixed          = ch_delay->change_delay;
1786
1787     ei->change->pre_change_function  = ch_delay->pre_change_function;
1788     ei->change->change_function      = ch_delay->change_function;
1789     ei->change->post_change_function = ch_delay->post_change_function;
1790
1791     ei->change->can_change = TRUE;
1792     ei->change->can_change_or_has_action = TRUE;
1793
1794     ei->has_change_event[CE_DELAY] = TRUE;
1795
1796     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1797     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1798   }
1799
1800   /* ---------- initialize internal run-time variables ------------- */
1801
1802   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1803   {
1804     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1805
1806     for (j = 0; j < ei->num_change_pages; j++)
1807     {
1808       ei->change_page[j].can_change_or_has_action =
1809         (ei->change_page[j].can_change |
1810          ei->change_page[j].has_action);
1811     }
1812   }
1813
1814   /* add change events from custom element configuration */
1815   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1816   {
1817     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1818
1819     for (j = 0; j < ei->num_change_pages; j++)
1820     {
1821       if (!ei->change_page[j].can_change_or_has_action)
1822         continue;
1823
1824       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1825       {
1826         /* only add event page for the first page found with this event */
1827         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1828         {
1829           ei->has_change_event[k] = TRUE;
1830
1831           ei->event_page_nr[k] = j;
1832           ei->event_page[k] = &ei->change_page[j];
1833         }
1834       }
1835     }
1836   }
1837
1838   /* ---------- initialize run-time trigger player and element ------------- */
1839
1840   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1841   {
1842     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1843
1844     for (j = 0; j < ei->num_change_pages; j++)
1845     {
1846       ei->change_page[j].actual_trigger_element = EL_EMPTY;
1847       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1848       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1849       ei->change_page[j].actual_trigger_ce_value = 0;
1850       ei->change_page[j].actual_trigger_ce_score = 0;
1851     }
1852   }
1853
1854   /* ---------- initialize trigger events ---------------------------------- */
1855
1856   /* initialize trigger events information */
1857   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1858     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1859       trigger_events[i][j] = FALSE;
1860
1861   /* add trigger events from element change event properties */
1862   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1863   {
1864     struct ElementInfo *ei = &element_info[i];
1865
1866     for (j = 0; j < ei->num_change_pages; j++)
1867     {
1868       if (!ei->change_page[j].can_change_or_has_action)
1869         continue;
1870
1871       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1872       {
1873         int trigger_element = ei->change_page[j].trigger_element;
1874
1875         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1876         {
1877           if (ei->change_page[j].has_event[k])
1878           {
1879             if (IS_GROUP_ELEMENT(trigger_element))
1880             {
1881               struct ElementGroupInfo *group =
1882                 element_info[trigger_element].group;
1883
1884               for (l = 0; l < group->num_elements_resolved; l++)
1885                 trigger_events[group->element_resolved[l]][k] = TRUE;
1886             }
1887             else if (trigger_element == EL_ANY_ELEMENT)
1888               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1889                 trigger_events[l][k] = TRUE;
1890             else
1891               trigger_events[trigger_element][k] = TRUE;
1892           }
1893         }
1894       }
1895     }
1896   }
1897
1898   /* ---------- initialize push delay -------------------------------------- */
1899
1900   /* initialize push delay values to default */
1901   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1902   {
1903     if (!IS_CUSTOM_ELEMENT(i))
1904     {
1905       /* set default push delay values (corrected since version 3.0.7-1) */
1906       if (game.engine_version < VERSION_IDENT(3,0,7,1))
1907       {
1908         element_info[i].push_delay_fixed = 2;
1909         element_info[i].push_delay_random = 8;
1910       }
1911       else
1912       {
1913         element_info[i].push_delay_fixed = 8;
1914         element_info[i].push_delay_random = 8;
1915       }
1916     }
1917   }
1918
1919   /* set push delay value for certain elements from pre-defined list */
1920   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1921   {
1922     int e = push_delay_list[i].element;
1923
1924     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1925     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1926   }
1927
1928   /* set push delay value for Supaplex elements for newer engine versions */
1929   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1930   {
1931     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1932     {
1933       if (IS_SP_ELEMENT(i))
1934       {
1935         /* set SP push delay to just enough to push under a falling zonk */
1936         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1937
1938         element_info[i].push_delay_fixed  = delay;
1939         element_info[i].push_delay_random = 0;
1940       }
1941     }
1942   }
1943
1944   /* ---------- initialize move stepsize ----------------------------------- */
1945
1946   /* initialize move stepsize values to default */
1947   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1948     if (!IS_CUSTOM_ELEMENT(i))
1949       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1950
1951   /* set move stepsize value for certain elements from pre-defined list */
1952   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1953   {
1954     int e = move_stepsize_list[i].element;
1955
1956     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1957   }
1958
1959   /* ---------- initialize collect score ----------------------------------- */
1960
1961   /* initialize collect score values for custom elements from initial value */
1962   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1963     if (IS_CUSTOM_ELEMENT(i))
1964       element_info[i].collect_score = element_info[i].collect_score_initial;
1965
1966   /* ---------- initialize collect count ----------------------------------- */
1967
1968   /* initialize collect count values for non-custom elements */
1969   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1970     if (!IS_CUSTOM_ELEMENT(i))
1971       element_info[i].collect_count_initial = 0;
1972
1973   /* add collect count values for all elements from pre-defined list */
1974   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1975     element_info[collect_count_list[i].element].collect_count_initial =
1976       collect_count_list[i].count;
1977
1978   /* ---------- initialize access direction -------------------------------- */
1979
1980   /* initialize access direction values to default (access from every side) */
1981   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1982     if (!IS_CUSTOM_ELEMENT(i))
1983       element_info[i].access_direction = MV_ALL_DIRECTIONS;
1984
1985   /* set access direction value for certain elements from pre-defined list */
1986   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1987     element_info[access_direction_list[i].element].access_direction =
1988       access_direction_list[i].direction;
1989
1990   /* ---------- initialize explosion content ------------------------------- */
1991   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1992   {
1993     if (IS_CUSTOM_ELEMENT(i))
1994       continue;
1995
1996     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1997     {
1998       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1999
2000       element_info[i].content.e[x][y] =
2001         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2002          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2003          i == EL_PLAYER_3 ? EL_EMERALD :
2004          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2005          i == EL_MOLE ? EL_EMERALD_RED :
2006          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2007          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2008          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2009          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2010          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2011          i == EL_WALL_EMERALD ? EL_EMERALD :
2012          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2013          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2014          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2015          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2016          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2017          i == EL_WALL_PEARL ? EL_PEARL :
2018          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2019          EL_EMPTY);
2020     }
2021   }
2022
2023   /* ---------- initialize recursion detection ------------------------------ */
2024   recursion_loop_depth = 0;
2025   recursion_loop_detected = FALSE;
2026   recursion_loop_element = EL_UNDEFINED;
2027 }
2028
2029 int get_num_special_action(int element, int action_first, int action_last)
2030 {
2031   int num_special_action = 0;
2032   int i, j;
2033
2034   for (i = action_first; i <= action_last; i++)
2035   {
2036     boolean found = FALSE;
2037
2038     for (j = 0; j < NUM_DIRECTIONS; j++)
2039       if (el_act_dir2img(element, i, j) !=
2040           el_act_dir2img(element, ACTION_DEFAULT, j))
2041         found = TRUE;
2042
2043     if (found)
2044       num_special_action++;
2045     else
2046       break;
2047   }
2048
2049   return num_special_action;
2050 }
2051
2052
2053 /*
2054   =============================================================================
2055   InitGame()
2056   -----------------------------------------------------------------------------
2057   initialize and start new game
2058   =============================================================================
2059 */
2060
2061 void InitGame()
2062 {
2063   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2064   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2065   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2066   boolean do_fading = (game_status == GAME_MODE_MAIN);
2067   int i, j, x, y;
2068
2069   game_status = GAME_MODE_PLAYING;
2070
2071   InitGameEngine();
2072
2073   /* don't play tapes over network */
2074   network_playing = (options.network && !tape.playing);
2075
2076   for (i = 0; i < MAX_PLAYERS; i++)
2077   {
2078     struct PlayerInfo *player = &stored_player[i];
2079
2080     player->index_nr = i;
2081     player->index_bit = (1 << i);
2082     player->element_nr = EL_PLAYER_1 + i;
2083
2084     player->present = FALSE;
2085     player->active = FALSE;
2086     player->killed = FALSE;
2087
2088     player->action = 0;
2089     player->effective_action = 0;
2090     player->programmed_action = 0;
2091
2092     player->score = 0;
2093     player->score_final = 0;
2094
2095     player->gems_still_needed = level.gems_needed;
2096     player->sokobanfields_still_needed = 0;
2097     player->lights_still_needed = 0;
2098     player->friends_still_needed = 0;
2099
2100     for (j = 0; j < MAX_NUM_KEYS; j++)
2101       player->key[j] = FALSE;
2102
2103     player->num_white_keys = 0;
2104
2105     player->dynabomb_count = 0;
2106     player->dynabomb_size = 1;
2107     player->dynabombs_left = 0;
2108     player->dynabomb_xl = FALSE;
2109
2110     player->MovDir = MV_NONE;
2111     player->MovPos = 0;
2112     player->GfxPos = 0;
2113     player->GfxDir = MV_NONE;
2114     player->GfxAction = ACTION_DEFAULT;
2115     player->Frame = 0;
2116     player->StepFrame = 0;
2117
2118     player->use_murphy = FALSE;
2119     player->artwork_element =
2120       (level.use_artwork_element[i] ? level.artwork_element[i] :
2121        player->element_nr);
2122
2123     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2124     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2125
2126     player->gravity = level.initial_player_gravity[i];
2127
2128     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2129
2130     player->actual_frame_counter = 0;
2131
2132     player->step_counter = 0;
2133
2134     player->last_move_dir = MV_NONE;
2135
2136     player->is_active = FALSE;
2137
2138     player->is_waiting = FALSE;
2139     player->is_moving = FALSE;
2140     player->is_auto_moving = FALSE;
2141     player->is_digging = FALSE;
2142     player->is_snapping = FALSE;
2143     player->is_collecting = FALSE;
2144     player->is_pushing = FALSE;
2145     player->is_switching = FALSE;
2146     player->is_dropping = FALSE;
2147     player->is_dropping_pressed = FALSE;
2148
2149     player->is_bored = FALSE;
2150     player->is_sleeping = FALSE;
2151
2152     player->frame_counter_bored = -1;
2153     player->frame_counter_sleeping = -1;
2154
2155     player->anim_delay_counter = 0;
2156     player->post_delay_counter = 0;
2157
2158     player->dir_waiting = MV_NONE;
2159     player->action_waiting = ACTION_DEFAULT;
2160     player->last_action_waiting = ACTION_DEFAULT;
2161     player->special_action_bored = ACTION_DEFAULT;
2162     player->special_action_sleeping = ACTION_DEFAULT;
2163
2164     player->switch_x = -1;
2165     player->switch_y = -1;
2166
2167     player->drop_x = -1;
2168     player->drop_y = -1;
2169
2170     player->show_envelope = 0;
2171
2172     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2173
2174     player->push_delay       = -1;      /* initialized when pushing starts */
2175     player->push_delay_value = game.initial_push_delay_value;
2176
2177     player->drop_delay = 0;
2178     player->drop_pressed_delay = 0;
2179
2180     player->last_jx = -1;
2181     player->last_jy = -1;
2182     player->jx = -1;
2183     player->jy = -1;
2184
2185     player->shield_normal_time_left = 0;
2186     player->shield_deadly_time_left = 0;
2187
2188     player->inventory_infinite_element = EL_UNDEFINED;
2189     player->inventory_size = 0;
2190
2191     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2192     SnapField(player, 0, 0);
2193
2194     player->LevelSolved = FALSE;
2195     player->GameOver = FALSE;
2196
2197     player->LevelSolved_GameWon = FALSE;
2198     player->LevelSolved_GameEnd = FALSE;
2199     player->LevelSolved_PanelOff = FALSE;
2200     player->LevelSolved_SaveTape = FALSE;
2201     player->LevelSolved_SaveScore = FALSE;
2202   }
2203
2204   network_player_action_received = FALSE;
2205
2206 #if defined(NETWORK_AVALIABLE)
2207   /* initial null action */
2208   if (network_playing)
2209     SendToServer_MovePlayer(MV_NONE);
2210 #endif
2211
2212   ZX = ZY = -1;
2213   ExitX = ExitY = -1;
2214
2215   FrameCounter = 0;
2216   TimeFrames = 0;
2217   TimePlayed = 0;
2218   TimeLeft = level.time;
2219   TapeTime = 0;
2220
2221   ScreenMovDir = MV_NONE;
2222   ScreenMovPos = 0;
2223   ScreenGfxPos = 0;
2224
2225   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2226
2227   AllPlayersGone = FALSE;
2228
2229   game.yamyam_content_nr = 0;
2230   game.magic_wall_active = FALSE;
2231   game.magic_wall_time_left = 0;
2232   game.light_time_left = 0;
2233   game.timegate_time_left = 0;
2234   game.switchgate_pos = 0;
2235   game.wind_direction = level.wind_direction_initial;
2236
2237 #if !USE_PLAYER_GRAVITY
2238   game.gravity = FALSE;
2239   game.explosions_delayed = TRUE;
2240 #endif
2241
2242   game.lenses_time_left = 0;
2243   game.magnify_time_left = 0;
2244
2245   game.ball_state = level.ball_state_initial;
2246   game.ball_content_nr = 0;
2247
2248   game.envelope_active = FALSE;
2249
2250   /* set focus to local player for network games, else to all players */
2251   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2252   game.centered_player_nr_next = game.centered_player_nr;
2253   game.set_centered_player = FALSE;
2254
2255   if (network_playing && tape.recording)
2256   {
2257     /* store client dependent player focus when recording network games */
2258     tape.centered_player_nr_next = game.centered_player_nr_next;
2259     tape.set_centered_player = TRUE;
2260   }
2261
2262   for (i = 0; i < NUM_BELTS; i++)
2263   {
2264     game.belt_dir[i] = MV_NONE;
2265     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2266   }
2267
2268   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2269     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2270
2271   SCAN_PLAYFIELD(x, y)
2272   {
2273     Feld[x][y] = level.field[x][y];
2274     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2275     ChangeDelay[x][y] = 0;
2276     ChangePage[x][y] = -1;
2277 #if USE_NEW_CUSTOM_VALUE
2278     CustomValue[x][y] = 0;              /* initialized in InitField() */
2279 #endif
2280     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2281     AmoebaNr[x][y] = 0;
2282     WasJustMoving[x][y] = 0;
2283     WasJustFalling[x][y] = 0;
2284     CheckCollision[x][y] = 0;
2285     CheckImpact[x][y] = 0;
2286     Stop[x][y] = FALSE;
2287     Pushed[x][y] = FALSE;
2288
2289     ChangeCount[x][y] = 0;
2290     ChangeEvent[x][y] = -1;
2291
2292     ExplodePhase[x][y] = 0;
2293     ExplodeDelay[x][y] = 0;
2294     ExplodeField[x][y] = EX_TYPE_NONE;
2295
2296     RunnerVisit[x][y] = 0;
2297     PlayerVisit[x][y] = 0;
2298
2299     GfxFrame[x][y] = 0;
2300     GfxRandom[x][y] = INIT_GFX_RANDOM();
2301     GfxElement[x][y] = EL_UNDEFINED;
2302     GfxAction[x][y] = ACTION_DEFAULT;
2303     GfxDir[x][y] = MV_NONE;
2304   }
2305
2306   SCAN_PLAYFIELD(x, y)
2307   {
2308     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2309       emulate_bd = FALSE;
2310     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2311       emulate_sb = FALSE;
2312     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2313       emulate_sp = FALSE;
2314
2315     InitField(x, y, TRUE);
2316   }
2317
2318   InitBeltMovement();
2319
2320   for (i = 0; i < MAX_PLAYERS; i++)
2321   {
2322     struct PlayerInfo *player = &stored_player[i];
2323
2324     /* set number of special actions for bored and sleeping animation */
2325     player->num_special_action_bored =
2326       get_num_special_action(player->artwork_element,
2327                              ACTION_BORING_1, ACTION_BORING_LAST);
2328     player->num_special_action_sleeping =
2329       get_num_special_action(player->artwork_element,
2330                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2331   }
2332
2333   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2334                     emulate_sb ? EMU_SOKOBAN :
2335                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2336
2337 #if USE_NEW_ALL_SLIPPERY
2338   /* initialize type of slippery elements */
2339   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2340   {
2341     if (!IS_CUSTOM_ELEMENT(i))
2342     {
2343       /* default: elements slip down either to the left or right randomly */
2344       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2345
2346       /* SP style elements prefer to slip down on the left side */
2347       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2348         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2349
2350       /* BD style elements prefer to slip down on the left side */
2351       if (game.emulation == EMU_BOULDERDASH)
2352         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2353     }
2354   }
2355 #endif
2356
2357   /* initialize explosion and ignition delay */
2358   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2359   {
2360     if (!IS_CUSTOM_ELEMENT(i))
2361     {
2362       int num_phase = 8;
2363       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2364                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2365                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2366       int last_phase = (num_phase + 1) * delay;
2367       int half_phase = (num_phase / 2) * delay;
2368
2369       element_info[i].explosion_delay = last_phase - 1;
2370       element_info[i].ignition_delay = half_phase;
2371
2372       if (i == EL_BLACK_ORB)
2373         element_info[i].ignition_delay = 1;
2374     }
2375
2376 #if 0
2377     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2378       element_info[i].explosion_delay = 1;
2379
2380     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2381       element_info[i].ignition_delay = 1;
2382 #endif
2383   }
2384
2385   /* correct non-moving belts to start moving left */
2386   for (i = 0; i < NUM_BELTS; i++)
2387     if (game.belt_dir[i] == MV_NONE)
2388       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2389
2390   /* check if any connected player was not found in playfield */
2391   for (i = 0; i < MAX_PLAYERS; i++)
2392   {
2393     struct PlayerInfo *player = &stored_player[i];
2394
2395     if (player->connected && !player->present)
2396     {
2397       for (j = 0; j < MAX_PLAYERS; j++)
2398       {
2399         struct PlayerInfo *some_player = &stored_player[j];
2400         int jx = some_player->jx, jy = some_player->jy;
2401
2402         /* assign first free player found that is present in the playfield */
2403         if (some_player->present && !some_player->connected)
2404         {
2405           player->present = TRUE;
2406           player->active = TRUE;
2407
2408           some_player->present = FALSE;
2409           some_player->active = FALSE;
2410
2411           player->artwork_element = some_player->artwork_element;
2412
2413           player->block_last_field       = some_player->block_last_field;
2414           player->block_delay_adjustment = some_player->block_delay_adjustment;
2415
2416           StorePlayer[jx][jy] = player->element_nr;
2417           player->jx = player->last_jx = jx;
2418           player->jy = player->last_jy = jy;
2419
2420           break;
2421         }
2422       }
2423     }
2424   }
2425
2426   if (tape.playing)
2427   {
2428     /* when playing a tape, eliminate all players who do not participate */
2429
2430     for (i = 0; i < MAX_PLAYERS; i++)
2431     {
2432       if (stored_player[i].active && !tape.player_participates[i])
2433       {
2434         struct PlayerInfo *player = &stored_player[i];
2435         int jx = player->jx, jy = player->jy;
2436
2437         player->active = FALSE;
2438         StorePlayer[jx][jy] = 0;
2439         Feld[jx][jy] = EL_EMPTY;
2440       }
2441     }
2442   }
2443   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2444   {
2445     /* when in single player mode, eliminate all but the first active player */
2446
2447     for (i = 0; i < MAX_PLAYERS; i++)
2448     {
2449       if (stored_player[i].active)
2450       {
2451         for (j = i + 1; j < MAX_PLAYERS; j++)
2452         {
2453           if (stored_player[j].active)
2454           {
2455             struct PlayerInfo *player = &stored_player[j];
2456             int jx = player->jx, jy = player->jy;
2457
2458             player->active = FALSE;
2459             player->present = FALSE;
2460
2461             StorePlayer[jx][jy] = 0;
2462             Feld[jx][jy] = EL_EMPTY;
2463           }
2464         }
2465       }
2466     }
2467   }
2468
2469   /* when recording the game, store which players take part in the game */
2470   if (tape.recording)
2471   {
2472     for (i = 0; i < MAX_PLAYERS; i++)
2473       if (stored_player[i].active)
2474         tape.player_participates[i] = TRUE;
2475   }
2476
2477   if (options.debug)
2478   {
2479     for (i = 0; i < MAX_PLAYERS; i++)
2480     {
2481       struct PlayerInfo *player = &stored_player[i];
2482
2483       printf("Player %d: present == %d, connected == %d, active == %d.\n",
2484              i+1,
2485              player->present,
2486              player->connected,
2487              player->active);
2488       if (local_player == player)
2489         printf("Player  %d is local player.\n", i+1);
2490     }
2491   }
2492
2493   if (BorderElement == EL_EMPTY)
2494   {
2495     SBX_Left = 0;
2496     SBX_Right = lev_fieldx - SCR_FIELDX;
2497     SBY_Upper = 0;
2498     SBY_Lower = lev_fieldy - SCR_FIELDY;
2499   }
2500   else
2501   {
2502     SBX_Left = -1;
2503     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2504     SBY_Upper = -1;
2505     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2506   }
2507
2508   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2509     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2510
2511   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2512     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2513
2514   /* if local player not found, look for custom element that might create
2515      the player (make some assumptions about the right custom element) */
2516   if (!local_player->present)
2517   {
2518     int start_x = 0, start_y = 0;
2519     int found_rating = 0;
2520     int found_element = EL_UNDEFINED;
2521     int player_nr = local_player->index_nr;
2522
2523     SCAN_PLAYFIELD(x, y)
2524     {
2525       int element = Feld[x][y];
2526       int content;
2527       int xx, yy;
2528       boolean is_player;
2529
2530       if (level.use_start_element[player_nr] &&
2531           level.start_element[player_nr] == element &&
2532           found_rating < 4)
2533       {
2534         start_x = x;
2535         start_y = y;
2536
2537         found_rating = 4;
2538         found_element = element;
2539       }
2540
2541       if (!IS_CUSTOM_ELEMENT(element))
2542         continue;
2543
2544       if (CAN_CHANGE(element))
2545       {
2546         for (i = 0; i < element_info[element].num_change_pages; i++)
2547         {
2548           /* check for player created from custom element as single target */
2549           content = element_info[element].change_page[i].target_element;
2550           is_player = ELEM_IS_PLAYER(content);
2551
2552           if (is_player && (found_rating < 3 || element < found_element))
2553           {
2554             start_x = x;
2555             start_y = y;
2556
2557             found_rating = 3;
2558             found_element = element;
2559           }
2560         }
2561       }
2562
2563       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2564       {
2565         /* check for player created from custom element as explosion content */
2566         content = element_info[element].content.e[xx][yy];
2567         is_player = ELEM_IS_PLAYER(content);
2568
2569         if (is_player && (found_rating < 2 || element < found_element))
2570         {
2571           start_x = x + xx - 1;
2572           start_y = y + yy - 1;
2573
2574           found_rating = 2;
2575           found_element = element;
2576         }
2577
2578         if (!CAN_CHANGE(element))
2579           continue;
2580
2581         for (i = 0; i < element_info[element].num_change_pages; i++)
2582         {
2583           /* check for player created from custom element as extended target */
2584           content =
2585             element_info[element].change_page[i].target_content.e[xx][yy];
2586
2587           is_player = ELEM_IS_PLAYER(content);
2588
2589           if (is_player && (found_rating < 1 || element < found_element))
2590           {
2591             start_x = x + xx - 1;
2592             start_y = y + yy - 1;
2593
2594             found_rating = 1;
2595             found_element = element;
2596           }
2597         }
2598       }
2599     }
2600
2601     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
2602                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2603                 start_x - MIDPOSX);
2604
2605     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2606                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2607                 start_y - MIDPOSY);
2608   }
2609   else
2610   {
2611     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2612                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2613                 local_player->jx - MIDPOSX);
2614
2615     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2616                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2617                 local_player->jy - MIDPOSY);
2618   }
2619
2620   StopAnimation();
2621
2622   if (!game.restart_level)
2623     CloseDoor(DOOR_CLOSE_1);
2624
2625   if (do_fading)
2626     FadeOut(REDRAW_FIELD);
2627
2628   /* !!! FIX THIS (START) !!! */
2629   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2630   {
2631     InitGameEngine_EM();
2632
2633     /* blit playfield from scroll buffer to normal back buffer for fading in */
2634     BlitScreenToBitmap_EM(backbuffer);
2635   }
2636   else
2637   {
2638     DrawLevel();
2639     DrawAllPlayers();
2640
2641     /* after drawing the level, correct some elements */
2642     if (game.timegate_time_left == 0)
2643       CloseAllOpenTimegates();
2644
2645     /* blit playfield from scroll buffer to normal back buffer for fading in */
2646     if (setup.soft_scrolling)
2647       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2648
2649     redraw_mask |= REDRAW_FROM_BACKBUFFER;
2650   }
2651   /* !!! FIX THIS (END) !!! */
2652
2653   if (do_fading)
2654     FadeIn(REDRAW_FIELD);
2655
2656   BackToFront();
2657
2658   if (!game.restart_level)
2659   {
2660     /* copy default game door content to main double buffer */
2661     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2662                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2663   }
2664
2665   SetPanelBackground();
2666   SetDrawBackgroundMask(REDRAW_DOOR_1);
2667
2668   DrawGameDoorValues();
2669
2670   if (!game.restart_level)
2671   {
2672     UnmapGameButtons();
2673     UnmapTapeButtons();
2674     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2675     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2676     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2677     MapGameButtons();
2678     MapTapeButtons();
2679
2680     /* copy actual game door content to door double buffer for OpenDoor() */
2681     BlitBitmap(drawto, bitmap_db_door,
2682                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2683
2684     OpenDoor(DOOR_OPEN_ALL);
2685
2686     PlaySound(SND_GAME_STARTING);
2687
2688     if (setup.sound_music)
2689       PlayLevelMusic();
2690
2691     KeyboardAutoRepeatOffUnlessAutoplay();
2692
2693     if (options.debug)
2694     {
2695       for (i = 0; i < MAX_PLAYERS; i++)
2696         printf("Player %d %sactive.\n",
2697                i + 1, (stored_player[i].active ? "" : "not "));
2698     }
2699   }
2700
2701 #if 1
2702   UnmapAllGadgets();
2703
2704   MapGameButtons();
2705   MapTapeButtons();
2706 #endif
2707
2708   game.restart_level = FALSE;
2709 }
2710
2711 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2712 {
2713   /* this is used for non-R'n'D game engines to update certain engine values */
2714
2715   /* needed to determine if sounds are played within the visible screen area */
2716   scroll_x = actual_scroll_x;
2717   scroll_y = actual_scroll_y;
2718 }
2719
2720 void InitMovDir(int x, int y)
2721 {
2722   int i, element = Feld[x][y];
2723   static int xy[4][2] =
2724   {
2725     {  0, +1 },
2726     { +1,  0 },
2727     {  0, -1 },
2728     { -1,  0 }
2729   };
2730   static int direction[3][4] =
2731   {
2732     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
2733     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
2734     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
2735   };
2736
2737   switch (element)
2738   {
2739     case EL_BUG_RIGHT:
2740     case EL_BUG_UP:
2741     case EL_BUG_LEFT:
2742     case EL_BUG_DOWN:
2743       Feld[x][y] = EL_BUG;
2744       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2745       break;
2746
2747     case EL_SPACESHIP_RIGHT:
2748     case EL_SPACESHIP_UP:
2749     case EL_SPACESHIP_LEFT:
2750     case EL_SPACESHIP_DOWN:
2751       Feld[x][y] = EL_SPACESHIP;
2752       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2753       break;
2754
2755     case EL_BD_BUTTERFLY_RIGHT:
2756     case EL_BD_BUTTERFLY_UP:
2757     case EL_BD_BUTTERFLY_LEFT:
2758     case EL_BD_BUTTERFLY_DOWN:
2759       Feld[x][y] = EL_BD_BUTTERFLY;
2760       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2761       break;
2762
2763     case EL_BD_FIREFLY_RIGHT:
2764     case EL_BD_FIREFLY_UP:
2765     case EL_BD_FIREFLY_LEFT:
2766     case EL_BD_FIREFLY_DOWN:
2767       Feld[x][y] = EL_BD_FIREFLY;
2768       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2769       break;
2770
2771     case EL_PACMAN_RIGHT:
2772     case EL_PACMAN_UP:
2773     case EL_PACMAN_LEFT:
2774     case EL_PACMAN_DOWN:
2775       Feld[x][y] = EL_PACMAN;
2776       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2777       break;
2778
2779     case EL_YAMYAM_LEFT:
2780     case EL_YAMYAM_RIGHT:
2781     case EL_YAMYAM_UP:
2782     case EL_YAMYAM_DOWN:
2783       Feld[x][y] = EL_YAMYAM;
2784       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2785       break;
2786
2787     case EL_SP_SNIKSNAK:
2788       MovDir[x][y] = MV_UP;
2789       break;
2790
2791     case EL_SP_ELECTRON:
2792       MovDir[x][y] = MV_LEFT;
2793       break;
2794
2795     case EL_MOLE_LEFT:
2796     case EL_MOLE_RIGHT:
2797     case EL_MOLE_UP:
2798     case EL_MOLE_DOWN:
2799       Feld[x][y] = EL_MOLE;
2800       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2801       break;
2802
2803     default:
2804       if (IS_CUSTOM_ELEMENT(element))
2805       {
2806         struct ElementInfo *ei = &element_info[element];
2807         int move_direction_initial = ei->move_direction_initial;
2808         int move_pattern = ei->move_pattern;
2809
2810         if (move_direction_initial == MV_START_PREVIOUS)
2811         {
2812           if (MovDir[x][y] != MV_NONE)
2813             return;
2814
2815           move_direction_initial = MV_START_AUTOMATIC;
2816         }
2817
2818         if (move_direction_initial == MV_START_RANDOM)
2819           MovDir[x][y] = 1 << RND(4);
2820         else if (move_direction_initial & MV_ANY_DIRECTION)
2821           MovDir[x][y] = move_direction_initial;
2822         else if (move_pattern == MV_ALL_DIRECTIONS ||
2823                  move_pattern == MV_TURNING_LEFT ||
2824                  move_pattern == MV_TURNING_RIGHT ||
2825                  move_pattern == MV_TURNING_LEFT_RIGHT ||
2826                  move_pattern == MV_TURNING_RIGHT_LEFT ||
2827                  move_pattern == MV_TURNING_RANDOM)
2828           MovDir[x][y] = 1 << RND(4);
2829         else if (move_pattern == MV_HORIZONTAL)
2830           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2831         else if (move_pattern == MV_VERTICAL)
2832           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2833         else if (move_pattern & MV_ANY_DIRECTION)
2834           MovDir[x][y] = element_info[element].move_pattern;
2835         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2836                  move_pattern == MV_ALONG_RIGHT_SIDE)
2837         {
2838           /* use random direction as default start direction */
2839           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2840             MovDir[x][y] = 1 << RND(4);
2841
2842           for (i = 0; i < NUM_DIRECTIONS; i++)
2843           {
2844             int x1 = x + xy[i][0];
2845             int y1 = y + xy[i][1];
2846
2847             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2848             {
2849               if (move_pattern == MV_ALONG_RIGHT_SIDE)
2850                 MovDir[x][y] = direction[0][i];
2851               else
2852                 MovDir[x][y] = direction[1][i];
2853
2854               break;
2855             }
2856           }
2857         }                
2858       }
2859       else
2860       {
2861         MovDir[x][y] = 1 << RND(4);
2862
2863         if (element != EL_BUG &&
2864             element != EL_SPACESHIP &&
2865             element != EL_BD_BUTTERFLY &&
2866             element != EL_BD_FIREFLY)
2867           break;
2868
2869         for (i = 0; i < NUM_DIRECTIONS; i++)
2870         {
2871           int x1 = x + xy[i][0];
2872           int y1 = y + xy[i][1];
2873
2874           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2875           {
2876             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2877             {
2878               MovDir[x][y] = direction[0][i];
2879               break;
2880             }
2881             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2882                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2883             {
2884               MovDir[x][y] = direction[1][i];
2885               break;
2886             }
2887           }
2888         }
2889       }
2890       break;
2891   }
2892
2893   GfxDir[x][y] = MovDir[x][y];
2894 }
2895
2896 void InitAmoebaNr(int x, int y)
2897 {
2898   int i;
2899   int group_nr = AmoebeNachbarNr(x, y);
2900
2901   if (group_nr == 0)
2902   {
2903     for (i = 1; i < MAX_NUM_AMOEBA; i++)
2904     {
2905       if (AmoebaCnt[i] == 0)
2906       {
2907         group_nr = i;
2908         break;
2909       }
2910     }
2911   }
2912
2913   AmoebaNr[x][y] = group_nr;
2914   AmoebaCnt[group_nr]++;
2915   AmoebaCnt2[group_nr]++;
2916 }
2917
2918 static void PlayerWins(struct PlayerInfo *player)
2919 {
2920   player->LevelSolved = TRUE;
2921   player->GameOver = TRUE;
2922
2923   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2924                          level.native_em_level->lev->score : player->score);
2925 }
2926
2927 void GameWon()
2928 {
2929   static int time, time_final;
2930   static int score, score_final;
2931   static int game_over_delay_1 = 0;
2932   static int game_over_delay_2 = 0;
2933   int game_over_delay_value_1 = 50;
2934   int game_over_delay_value_2 = 50;
2935
2936   if (!local_player->LevelSolved_GameWon)
2937   {
2938     int i;
2939
2940     /* do not start end game actions before the player stops moving (to exit) */
2941     if (local_player->MovPos)
2942       return;
2943
2944     local_player->LevelSolved_GameWon = TRUE;
2945     local_player->LevelSolved_SaveTape = tape.recording;
2946     local_player->LevelSolved_SaveScore = !tape.playing;
2947
2948     if (tape.auto_play)         /* tape might already be stopped here */
2949       tape.auto_play_level_solved = TRUE;
2950
2951 #if 1
2952     TapeStop();
2953 #endif
2954
2955     game_over_delay_1 = game_over_delay_value_1;
2956     game_over_delay_2 = game_over_delay_value_2;
2957
2958     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2959     score = score_final = local_player->score_final;
2960
2961     if (TimeLeft > 0)
2962     {
2963       time_final = 0;
2964       score_final += TimeLeft * level.score[SC_TIME_BONUS];
2965     }
2966     else if (level.time == 0 && TimePlayed < 999)
2967     {
2968       time_final = 999;
2969       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
2970     }
2971
2972     local_player->score_final = score_final;
2973
2974     if (level_editor_test_game)
2975     {
2976       time = time_final;
2977       score = score_final;
2978
2979       DrawGameValue_Time(time);
2980       DrawGameValue_Score(score);
2981     }
2982
2983     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
2984     {
2985       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
2986       {
2987         /* close exit door after last player */
2988         if ((AllPlayersGone &&
2989              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2990               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
2991               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
2992             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
2993             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
2994         {
2995           int element = Feld[ExitX][ExitY];
2996
2997 #if 0
2998           if (element == EL_EM_EXIT_OPEN ||
2999               element == EL_EM_STEEL_EXIT_OPEN)
3000           {
3001             Bang(ExitX, ExitY);
3002           }
3003           else
3004 #endif
3005           {
3006             Feld[ExitX][ExitY] =
3007               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3008                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3009                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3010                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3011                EL_EM_STEEL_EXIT_CLOSING);
3012
3013             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3014           }
3015         }
3016
3017         /* player disappears */
3018         DrawLevelField(ExitX, ExitY);
3019       }
3020
3021       for (i = 0; i < MAX_PLAYERS; i++)
3022       {
3023         struct PlayerInfo *player = &stored_player[i];
3024
3025         if (player->present)
3026         {
3027           RemovePlayer(player);
3028
3029           /* player disappears */
3030           DrawLevelField(player->jx, player->jy);
3031         }
3032       }
3033     }
3034
3035     PlaySound(SND_GAME_WINNING);
3036   }
3037
3038   if (game_over_delay_1 > 0)
3039   {
3040     game_over_delay_1--;
3041
3042     return;
3043   }
3044
3045   if (time != time_final)
3046   {
3047     int time_to_go = ABS(time_final - time);
3048     int time_count_dir = (time < time_final ? +1 : -1);
3049     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3050
3051     time  += time_count_steps * time_count_dir;
3052     score += time_count_steps * level.score[SC_TIME_BONUS];
3053
3054     DrawGameValue_Time(time);
3055     DrawGameValue_Score(score);
3056
3057     if (time == time_final)
3058       StopSound(SND_GAME_LEVELTIME_BONUS);
3059     else if (setup.sound_loops)
3060       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3061     else
3062       PlaySound(SND_GAME_LEVELTIME_BONUS);
3063
3064     return;
3065   }
3066
3067   local_player->LevelSolved_PanelOff = TRUE;
3068
3069   if (game_over_delay_2 > 0)
3070   {
3071     game_over_delay_2--;
3072
3073     return;
3074   }
3075
3076 #if 1
3077   GameEnd();
3078 #endif
3079 }
3080
3081 void GameEnd()
3082 {
3083   int hi_pos;
3084   boolean raise_level = FALSE;
3085
3086   local_player->LevelSolved_GameEnd = TRUE;
3087
3088   CloseDoor(DOOR_CLOSE_1);
3089
3090   if (local_player->LevelSolved_SaveTape)
3091   {
3092 #if 0
3093     TapeStop();
3094 #endif
3095
3096 #if 1
3097     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3098 #else
3099     SaveTape(tape.level_nr);            /* ask to save tape */
3100 #endif
3101   }
3102
3103   if (level_editor_test_game)
3104   {
3105     game_status = GAME_MODE_MAIN;
3106
3107     DrawMainMenu();
3108
3109     return;
3110   }
3111
3112   if (!local_player->LevelSolved_SaveScore)
3113   {
3114     FadeOut(REDRAW_FIELD);
3115
3116     game_status = GAME_MODE_MAIN;
3117
3118     DrawAndFadeInMainMenu(REDRAW_FIELD);
3119
3120     return;
3121   }
3122
3123   if (level_nr == leveldir_current->handicap_level)
3124   {
3125     leveldir_current->handicap_level++;
3126     SaveLevelSetup_SeriesInfo();
3127   }
3128
3129   if (level_nr < leveldir_current->last_level)
3130     raise_level = TRUE;                 /* advance to next level */
3131
3132   if ((hi_pos = NewHiScore()) >= 0) 
3133   {
3134     game_status = GAME_MODE_SCORES;
3135
3136     DrawHallOfFame(hi_pos);
3137
3138     if (raise_level)
3139     {
3140       level_nr++;
3141       TapeErase();
3142     }
3143   }
3144   else
3145   {
3146     FadeOut(REDRAW_FIELD);
3147
3148     game_status = GAME_MODE_MAIN;
3149
3150     if (raise_level)
3151     {
3152       level_nr++;
3153       TapeErase();
3154     }
3155
3156     DrawAndFadeInMainMenu(REDRAW_FIELD);
3157   }
3158 }
3159
3160 int NewHiScore()
3161 {
3162   int k, l;
3163   int position = -1;
3164
3165   LoadScore(level_nr);
3166
3167   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3168       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3169     return -1;
3170
3171   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3172   {
3173     if (local_player->score_final > highscore[k].Score)
3174     {
3175       /* player has made it to the hall of fame */
3176
3177       if (k < MAX_SCORE_ENTRIES - 1)
3178       {
3179         int m = MAX_SCORE_ENTRIES - 1;
3180
3181 #ifdef ONE_PER_NAME
3182         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3183           if (strEqual(setup.player_name, highscore[l].Name))
3184             m = l;
3185         if (m == k)     /* player's new highscore overwrites his old one */
3186           goto put_into_list;
3187 #endif
3188
3189         for (l = m; l > k; l--)
3190         {
3191           strcpy(highscore[l].Name, highscore[l - 1].Name);
3192           highscore[l].Score = highscore[l - 1].Score;
3193         }
3194       }
3195
3196 #ifdef ONE_PER_NAME
3197       put_into_list:
3198 #endif
3199       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3200       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3201       highscore[k].Score = local_player->score_final; 
3202       position = k;
3203       break;
3204     }
3205
3206 #ifdef ONE_PER_NAME
3207     else if (!strncmp(setup.player_name, highscore[k].Name,
3208                       MAX_PLAYER_NAME_LEN))
3209       break;    /* player already there with a higher score */
3210 #endif
3211
3212   }
3213
3214   if (position >= 0) 
3215     SaveScore(level_nr);
3216
3217   return position;
3218 }
3219
3220 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3221 {
3222   int element = Feld[x][y];
3223   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3224   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3225   int horiz_move = (dx != 0);
3226   int sign = (horiz_move ? dx : dy);
3227   int step = sign * element_info[element].move_stepsize;
3228
3229   /* special values for move stepsize for spring and things on conveyor belt */
3230   if (horiz_move)
3231   {
3232     if (CAN_FALL(element) &&
3233         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3234       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3235     else if (element == EL_SPRING)
3236       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3237   }
3238
3239   return step;
3240 }
3241
3242 inline static int getElementMoveStepsize(int x, int y)
3243 {
3244   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3245 }
3246
3247 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3248 {
3249   if (player->GfxAction != action || player->GfxDir != dir)
3250   {
3251 #if 0
3252     printf("Player frame reset! (%d => %d, %d => %d)\n",
3253            player->GfxAction, action, player->GfxDir, dir);
3254 #endif
3255
3256     player->GfxAction = action;
3257     player->GfxDir = dir;
3258     player->Frame = 0;
3259     player->StepFrame = 0;
3260   }
3261 }
3262
3263 #if USE_GFX_RESET_GFX_ANIMATION
3264 static void ResetGfxFrame(int x, int y, boolean redraw)
3265 {
3266   int element = Feld[x][y];
3267   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3268   int last_gfx_frame = GfxFrame[x][y];
3269
3270   if (graphic_info[graphic].anim_global_sync)
3271     GfxFrame[x][y] = FrameCounter;
3272   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3273     GfxFrame[x][y] = CustomValue[x][y];
3274   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3275     GfxFrame[x][y] = element_info[element].collect_score;
3276   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3277     GfxFrame[x][y] = ChangeDelay[x][y];
3278
3279   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3280     DrawLevelGraphicAnimation(x, y, graphic);
3281 }
3282 #endif
3283
3284 static void ResetGfxAnimation(int x, int y)
3285 {
3286   GfxAction[x][y] = ACTION_DEFAULT;
3287   GfxDir[x][y] = MovDir[x][y];
3288   GfxFrame[x][y] = 0;
3289
3290 #if USE_GFX_RESET_GFX_ANIMATION
3291   ResetGfxFrame(x, y, FALSE);
3292 #endif
3293 }
3294
3295 static void ResetRandomAnimationValue(int x, int y)
3296 {
3297   GfxRandom[x][y] = INIT_GFX_RANDOM();
3298 }
3299
3300 void InitMovingField(int x, int y, int direction)
3301 {
3302   int element = Feld[x][y];
3303   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3304   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3305   int newx = x + dx;
3306   int newy = y + dy;
3307   boolean is_moving_before, is_moving_after;
3308 #if 0
3309   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3310 #endif
3311
3312   /* check if element was/is moving or being moved before/after mode change */
3313 #if 1
3314 #if 1
3315   is_moving_before = (WasJustMoving[x][y] != 0);
3316 #else
3317   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
3318   is_moving_before = WasJustMoving[x][y];
3319 #endif
3320 #else
3321   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3322 #endif
3323   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
3324
3325   /* reset animation only for moving elements which change direction of moving
3326      or which just started or stopped moving
3327      (else CEs with property "can move" / "not moving" are reset each frame) */
3328 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3329 #if 1
3330   if (is_moving_before != is_moving_after ||
3331       direction != MovDir[x][y])
3332     ResetGfxAnimation(x, y);
3333 #else
3334   if ((is_moving_before || is_moving_after) && !continues_moving)
3335     ResetGfxAnimation(x, y);
3336 #endif
3337 #else
3338   if (!continues_moving)
3339     ResetGfxAnimation(x, y);
3340 #endif
3341
3342   MovDir[x][y] = direction;
3343   GfxDir[x][y] = direction;
3344
3345 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3346   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3347                      direction == MV_DOWN && CAN_FALL(element) ?
3348                      ACTION_FALLING : ACTION_MOVING);
3349 #else
3350   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3351                      ACTION_FALLING : ACTION_MOVING);
3352 #endif
3353
3354   /* this is needed for CEs with property "can move" / "not moving" */
3355
3356   if (is_moving_after)
3357   {
3358     if (Feld[newx][newy] == EL_EMPTY)
3359       Feld[newx][newy] = EL_BLOCKED;
3360
3361     MovDir[newx][newy] = MovDir[x][y];
3362
3363 #if USE_NEW_CUSTOM_VALUE
3364     CustomValue[newx][newy] = CustomValue[x][y];
3365 #endif
3366
3367     GfxFrame[newx][newy] = GfxFrame[x][y];
3368     GfxRandom[newx][newy] = GfxRandom[x][y];
3369     GfxAction[newx][newy] = GfxAction[x][y];
3370     GfxDir[newx][newy] = GfxDir[x][y];
3371   }
3372 }
3373
3374 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3375 {
3376   int direction = MovDir[x][y];
3377   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3378   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3379
3380   *goes_to_x = newx;
3381   *goes_to_y = newy;
3382 }
3383
3384 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3385 {
3386   int oldx = x, oldy = y;
3387   int direction = MovDir[x][y];
3388
3389   if (direction == MV_LEFT)
3390     oldx++;
3391   else if (direction == MV_RIGHT)
3392     oldx--;
3393   else if (direction == MV_UP)
3394     oldy++;
3395   else if (direction == MV_DOWN)
3396     oldy--;
3397
3398   *comes_from_x = oldx;
3399   *comes_from_y = oldy;
3400 }
3401
3402 int MovingOrBlocked2Element(int x, int y)
3403 {
3404   int element = Feld[x][y];
3405
3406   if (element == EL_BLOCKED)
3407   {
3408     int oldx, oldy;
3409
3410     Blocked2Moving(x, y, &oldx, &oldy);
3411     return Feld[oldx][oldy];
3412   }
3413   else
3414     return element;
3415 }
3416
3417 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3418 {
3419   /* like MovingOrBlocked2Element(), but if element is moving
3420      and (x,y) is the field the moving element is just leaving,
3421      return EL_BLOCKED instead of the element value */
3422   int element = Feld[x][y];
3423
3424   if (IS_MOVING(x, y))
3425   {
3426     if (element == EL_BLOCKED)
3427     {
3428       int oldx, oldy;
3429
3430       Blocked2Moving(x, y, &oldx, &oldy);
3431       return Feld[oldx][oldy];
3432     }
3433     else
3434       return EL_BLOCKED;
3435   }
3436   else
3437     return element;
3438 }
3439
3440 static void RemoveField(int x, int y)
3441 {
3442   Feld[x][y] = EL_EMPTY;
3443
3444   MovPos[x][y] = 0;
3445   MovDir[x][y] = 0;
3446   MovDelay[x][y] = 0;
3447
3448 #if USE_NEW_CUSTOM_VALUE
3449   CustomValue[x][y] = 0;
3450 #endif
3451
3452   AmoebaNr[x][y] = 0;
3453   ChangeDelay[x][y] = 0;
3454   ChangePage[x][y] = -1;
3455   Pushed[x][y] = FALSE;
3456
3457 #if 0
3458   ExplodeField[x][y] = EX_TYPE_NONE;
3459 #endif
3460
3461   GfxElement[x][y] = EL_UNDEFINED;
3462   GfxAction[x][y] = ACTION_DEFAULT;
3463   GfxDir[x][y] = MV_NONE;
3464 }
3465
3466 void RemoveMovingField(int x, int y)
3467 {
3468   int oldx = x, oldy = y, newx = x, newy = y;
3469   int element = Feld[x][y];
3470   int next_element = EL_UNDEFINED;
3471
3472   if (element != EL_BLOCKED && !IS_MOVING(x, y))
3473     return;
3474
3475   if (IS_MOVING(x, y))
3476   {
3477     Moving2Blocked(x, y, &newx, &newy);
3478
3479     if (Feld[newx][newy] != EL_BLOCKED)
3480     {
3481       /* element is moving, but target field is not free (blocked), but
3482          already occupied by something different (example: acid pool);
3483          in this case, only remove the moving field, but not the target */
3484
3485       RemoveField(oldx, oldy);
3486
3487       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3488
3489       DrawLevelField(oldx, oldy);
3490
3491       return;
3492     }
3493   }
3494   else if (element == EL_BLOCKED)
3495   {
3496     Blocked2Moving(x, y, &oldx, &oldy);
3497     if (!IS_MOVING(oldx, oldy))
3498       return;
3499   }
3500
3501   if (element == EL_BLOCKED &&
3502       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3503        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3504        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3505        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3506        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3507        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3508     next_element = get_next_element(Feld[oldx][oldy]);
3509
3510   RemoveField(oldx, oldy);
3511   RemoveField(newx, newy);
3512
3513   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3514
3515   if (next_element != EL_UNDEFINED)
3516     Feld[oldx][oldy] = next_element;
3517
3518   DrawLevelField(oldx, oldy);
3519   DrawLevelField(newx, newy);
3520 }
3521
3522 void DrawDynamite(int x, int y)
3523 {
3524   int sx = SCREENX(x), sy = SCREENY(y);
3525   int graphic = el2img(Feld[x][y]);
3526   int frame;
3527
3528   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3529     return;
3530
3531   if (IS_WALKABLE_INSIDE(Back[x][y]))
3532     return;
3533
3534   if (Back[x][y])
3535     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3536   else if (Store[x][y])
3537     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3538
3539   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3540
3541   if (Back[x][y] || Store[x][y])
3542     DrawGraphicThruMask(sx, sy, graphic, frame);
3543   else
3544     DrawGraphic(sx, sy, graphic, frame);
3545 }
3546
3547 void CheckDynamite(int x, int y)
3548 {
3549   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
3550   {
3551     MovDelay[x][y]--;
3552
3553     if (MovDelay[x][y] != 0)
3554     {
3555       DrawDynamite(x, y);
3556       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3557
3558       return;
3559     }
3560   }
3561
3562   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3563
3564   Bang(x, y);
3565 }
3566
3567 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3568 {
3569   boolean num_checked_players = 0;
3570   int i;
3571
3572   for (i = 0; i < MAX_PLAYERS; i++)
3573   {
3574     if (stored_player[i].active)
3575     {
3576       int sx = stored_player[i].jx;
3577       int sy = stored_player[i].jy;
3578
3579       if (num_checked_players == 0)
3580       {
3581         *sx1 = *sx2 = sx;
3582         *sy1 = *sy2 = sy;
3583       }
3584       else
3585       {
3586         *sx1 = MIN(*sx1, sx);
3587         *sy1 = MIN(*sy1, sy);
3588         *sx2 = MAX(*sx2, sx);
3589         *sy2 = MAX(*sy2, sy);
3590       }
3591
3592       num_checked_players++;
3593     }
3594   }
3595 }
3596
3597 static boolean checkIfAllPlayersFitToScreen_RND()
3598 {
3599   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3600
3601   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3602
3603   return (sx2 - sx1 < SCR_FIELDX &&
3604           sy2 - sy1 < SCR_FIELDY);
3605 }
3606
3607 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3608 {
3609   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3610
3611   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3612
3613   *sx = (sx1 + sx2) / 2;
3614   *sy = (sy1 + sy2) / 2;
3615 }
3616
3617 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3618                         boolean center_screen, boolean quick_relocation)
3619 {
3620   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3621   boolean no_delay = (tape.warp_forward);
3622   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3623   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3624
3625   if (quick_relocation)
3626   {
3627     int offset = (setup.scroll_delay ? 3 : 0);
3628
3629     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3630     {
3631       if (center_screen)
3632       {
3633         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3634                     x > SBX_Right + MIDPOSX ? SBX_Right :
3635                     x - MIDPOSX);
3636
3637         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3638                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
3639                     y - MIDPOSY);
3640       }
3641       else
3642       {
3643         /* quick relocation (without scrolling), but do not center screen */
3644
3645         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
3646                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
3647                                old_x - MIDPOSX);
3648
3649         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3650                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3651                                old_y - MIDPOSY);
3652
3653         int offset_x = x + (scroll_x - center_scroll_x);
3654         int offset_y = y + (scroll_y - center_scroll_y);
3655
3656         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
3657                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3658                     offset_x - MIDPOSX);
3659
3660         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3661                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3662                     offset_y - MIDPOSY);
3663       }
3664     }
3665     else
3666     {
3667       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
3668           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3669         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3670
3671       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
3672           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3673         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3674
3675       /* don't scroll over playfield boundaries */
3676       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3677         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3678
3679       /* don't scroll over playfield boundaries */
3680       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3681         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3682     }
3683
3684     RedrawPlayfield(TRUE, 0,0,0,0);
3685   }
3686   else
3687   {
3688     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3689                      x > SBX_Right + MIDPOSX ? SBX_Right :
3690                      x - MIDPOSX);
3691
3692     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3693                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
3694                      y - MIDPOSY);
3695
3696     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3697
3698     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3699     {
3700       int dx = 0, dy = 0;
3701       int fx = FX, fy = FY;
3702
3703       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3704       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3705
3706       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3707         break;
3708
3709       scroll_x -= dx;
3710       scroll_y -= dy;
3711
3712       fx += dx * TILEX / 2;
3713       fy += dy * TILEY / 2;
3714
3715       ScrollLevel(dx, dy);
3716       DrawAllPlayers();
3717
3718       /* scroll in two steps of half tile size to make things smoother */
3719       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3720       FlushDisplay();
3721       Delay(wait_delay_value);
3722
3723       /* scroll second step to align at full tile size */
3724       BackToFront();
3725       Delay(wait_delay_value);
3726     }
3727
3728     DrawAllPlayers();
3729     BackToFront();
3730     Delay(wait_delay_value);
3731   }
3732 }
3733
3734 void RelocatePlayer(int jx, int jy, int el_player_raw)
3735 {
3736   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3737   int player_nr = GET_PLAYER_NR(el_player);
3738   struct PlayerInfo *player = &stored_player[player_nr];
3739   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3740   boolean no_delay = (tape.warp_forward);
3741   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3742   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3743   int old_jx = player->jx;
3744   int old_jy = player->jy;
3745   int old_element = Feld[old_jx][old_jy];
3746   int element = Feld[jx][jy];
3747   boolean player_relocated = (old_jx != jx || old_jy != jy);
3748
3749   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3750   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
3751   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3752   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
3753   int leave_side_horiz = move_dir_horiz;
3754   int leave_side_vert  = move_dir_vert;
3755   int enter_side = enter_side_horiz | enter_side_vert;
3756   int leave_side = leave_side_horiz | leave_side_vert;
3757
3758   if (player->GameOver)         /* do not reanimate dead player */
3759     return;
3760
3761   if (!player_relocated)        /* no need to relocate the player */
3762     return;
3763
3764   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
3765   {
3766     RemoveField(jx, jy);        /* temporarily remove newly placed player */
3767     DrawLevelField(jx, jy);
3768   }
3769
3770   if (player->present)
3771   {
3772     while (player->MovPos)
3773     {
3774       ScrollPlayer(player, SCROLL_GO_ON);
3775       ScrollScreen(NULL, SCROLL_GO_ON);
3776
3777       AdvanceFrameAndPlayerCounters(player->index_nr);
3778
3779       DrawPlayer(player);
3780
3781       BackToFront();
3782       Delay(wait_delay_value);
3783     }
3784
3785     DrawPlayer(player);         /* needed here only to cleanup last field */
3786     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
3787
3788     player->is_moving = FALSE;
3789   }
3790
3791   if (IS_CUSTOM_ELEMENT(old_element))
3792     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3793                                CE_LEFT_BY_PLAYER,
3794                                player->index_bit, leave_side);
3795
3796   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3797                                       CE_PLAYER_LEAVES_X,
3798                                       player->index_bit, leave_side);
3799
3800   Feld[jx][jy] = el_player;
3801   InitPlayerField(jx, jy, el_player, TRUE);
3802
3803   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3804   {
3805     Feld[jx][jy] = element;
3806     InitField(jx, jy, FALSE);
3807   }
3808
3809   /* only visually relocate centered player */
3810   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3811                      FALSE, level.instant_relocation);
3812
3813   TestIfPlayerTouchesBadThing(jx, jy);
3814   TestIfPlayerTouchesCustomElement(jx, jy);
3815
3816   if (IS_CUSTOM_ELEMENT(element))
3817     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3818                                player->index_bit, enter_side);
3819
3820   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3821                                       player->index_bit, enter_side);
3822 }
3823
3824 void Explode(int ex, int ey, int phase, int mode)
3825 {
3826   int x, y;
3827   int last_phase;
3828   int border_element;
3829
3830   /* !!! eliminate this variable !!! */
3831   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3832
3833   if (game.explosions_delayed)
3834   {
3835     ExplodeField[ex][ey] = mode;
3836     return;
3837   }
3838
3839   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
3840   {
3841     int center_element = Feld[ex][ey];
3842     int artwork_element, explosion_element;     /* set these values later */
3843
3844 #if 0
3845     /* --- This is only really needed (and now handled) in "Impact()". --- */
3846     /* do not explode moving elements that left the explode field in time */
3847     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3848         center_element == EL_EMPTY &&
3849         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3850       return;
3851 #endif
3852
3853 #if 0
3854     /* !!! at this place, the center element may be EL_BLOCKED !!! */
3855     if (mode == EX_TYPE_NORMAL ||
3856         mode == EX_TYPE_CENTER ||
3857         mode == EX_TYPE_CROSS)
3858       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3859 #endif
3860
3861     /* remove things displayed in background while burning dynamite */
3862     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3863       Back[ex][ey] = 0;
3864
3865     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3866     {
3867       /* put moving element to center field (and let it explode there) */
3868       center_element = MovingOrBlocked2Element(ex, ey);
3869       RemoveMovingField(ex, ey);
3870       Feld[ex][ey] = center_element;
3871     }
3872
3873     /* now "center_element" is finally determined -- set related values now */
3874     artwork_element = center_element;           /* for custom player artwork */
3875     explosion_element = center_element;         /* for custom player artwork */
3876
3877     if (IS_PLAYER(ex, ey))
3878     {
3879       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3880
3881       artwork_element = stored_player[player_nr].artwork_element;
3882
3883       if (level.use_explosion_element[player_nr])
3884       {
3885         explosion_element = level.explosion_element[player_nr];
3886         artwork_element = explosion_element;
3887       }
3888     }
3889
3890 #if 1
3891     if (mode == EX_TYPE_NORMAL ||
3892         mode == EX_TYPE_CENTER ||
3893         mode == EX_TYPE_CROSS)
3894       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3895 #endif
3896
3897     last_phase = element_info[explosion_element].explosion_delay + 1;
3898
3899     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3900     {
3901       int xx = x - ex + 1;
3902       int yy = y - ey + 1;
3903       int element;
3904
3905       if (!IN_LEV_FIELD(x, y) ||
3906           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3907           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
3908         continue;
3909
3910       element = Feld[x][y];
3911
3912       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3913       {
3914         element = MovingOrBlocked2Element(x, y);
3915
3916         if (!IS_EXPLOSION_PROOF(element))
3917           RemoveMovingField(x, y);
3918       }
3919
3920       /* indestructible elements can only explode in center (but not flames) */
3921       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3922                                            mode == EX_TYPE_BORDER)) ||
3923           element == EL_FLAMES)
3924         continue;
3925
3926       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3927          behaviour, for example when touching a yamyam that explodes to rocks
3928          with active deadly shield, a rock is created under the player !!! */
3929       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3930 #if 0
3931       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3932           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3933            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3934 #else
3935       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3936 #endif
3937       {
3938         if (IS_ACTIVE_BOMB(element))
3939         {
3940           /* re-activate things under the bomb like gate or penguin */
3941           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3942           Back[x][y] = 0;
3943         }
3944
3945         continue;
3946       }
3947
3948       /* save walkable background elements while explosion on same tile */
3949       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3950           (x != ex || y != ey || mode == EX_TYPE_BORDER))
3951         Back[x][y] = element;
3952
3953       /* ignite explodable elements reached by other explosion */
3954       if (element == EL_EXPLOSION)
3955         element = Store2[x][y];
3956
3957       if (AmoebaNr[x][y] &&
3958           (element == EL_AMOEBA_FULL ||
3959            element == EL_BD_AMOEBA ||
3960            element == EL_AMOEBA_GROWING))
3961       {
3962         AmoebaCnt[AmoebaNr[x][y]]--;
3963         AmoebaCnt2[AmoebaNr[x][y]]--;
3964       }
3965
3966       RemoveField(x, y);
3967
3968       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3969       {
3970         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3971
3972         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3973
3974         if (PLAYERINFO(ex, ey)->use_murphy)
3975           Store[x][y] = EL_EMPTY;
3976       }
3977
3978       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3979          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3980       else if (ELEM_IS_PLAYER(center_element))
3981         Store[x][y] = EL_EMPTY;
3982       else if (center_element == EL_YAMYAM)
3983         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3984       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3985         Store[x][y] = element_info[center_element].content.e[xx][yy];
3986 #if 1
3987       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3988          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3989          otherwise) -- FIX THIS !!! */
3990       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3991         Store[x][y] = element_info[element].content.e[1][1];
3992 #else
3993       else if (!CAN_EXPLODE(element))
3994         Store[x][y] = element_info[element].content.e[1][1];
3995 #endif
3996       else
3997         Store[x][y] = EL_EMPTY;
3998
3999       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4000           center_element == EL_AMOEBA_TO_DIAMOND)
4001         Store2[x][y] = element;
4002
4003       Feld[x][y] = EL_EXPLOSION;
4004       GfxElement[x][y] = artwork_element;
4005
4006       ExplodePhase[x][y] = 1;
4007       ExplodeDelay[x][y] = last_phase;
4008
4009       Stop[x][y] = TRUE;
4010     }
4011
4012     if (center_element == EL_YAMYAM)
4013       game.yamyam_content_nr =
4014         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4015
4016     return;
4017   }
4018
4019   if (Stop[ex][ey])
4020     return;
4021
4022   x = ex;
4023   y = ey;
4024
4025   if (phase == 1)
4026     GfxFrame[x][y] = 0;         /* restart explosion animation */
4027
4028   last_phase = ExplodeDelay[x][y];
4029
4030   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4031
4032 #ifdef DEBUG
4033
4034   /* activate this even in non-DEBUG version until cause for crash in
4035      getGraphicAnimationFrame() (see below) is found and eliminated */
4036
4037 #endif
4038 #if 1
4039
4040 #if 1
4041   /* this can happen if the player leaves an explosion just in time */
4042   if (GfxElement[x][y] == EL_UNDEFINED)
4043     GfxElement[x][y] = EL_EMPTY;
4044 #else
4045   if (GfxElement[x][y] == EL_UNDEFINED)
4046   {
4047     printf("\n\n");
4048     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4049     printf("Explode(): This should never happen!\n");
4050     printf("\n\n");
4051
4052     GfxElement[x][y] = EL_EMPTY;
4053   }
4054 #endif
4055
4056 #endif
4057
4058   border_element = Store2[x][y];
4059   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4060     border_element = StorePlayer[x][y];
4061
4062   if (phase == element_info[border_element].ignition_delay ||
4063       phase == last_phase)
4064   {
4065     boolean border_explosion = FALSE;
4066
4067     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4068         !PLAYER_EXPLOSION_PROTECTED(x, y))
4069     {
4070       KillPlayerUnlessExplosionProtected(x, y);
4071       border_explosion = TRUE;
4072     }
4073     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4074     {
4075       Feld[x][y] = Store2[x][y];
4076       Store2[x][y] = 0;
4077       Bang(x, y);
4078       border_explosion = TRUE;
4079     }
4080     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4081     {
4082       AmoebeUmwandeln(x, y);
4083       Store2[x][y] = 0;
4084       border_explosion = TRUE;
4085     }
4086
4087     /* if an element just explodes due to another explosion (chain-reaction),
4088        do not immediately end the new explosion when it was the last frame of
4089        the explosion (as it would be done in the following "if"-statement!) */
4090     if (border_explosion && phase == last_phase)
4091       return;
4092   }
4093
4094   if (phase == last_phase)
4095   {
4096     int element;
4097
4098     element = Feld[x][y] = Store[x][y];
4099     Store[x][y] = Store2[x][y] = 0;
4100     GfxElement[x][y] = EL_UNDEFINED;
4101
4102     /* player can escape from explosions and might therefore be still alive */
4103     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4104         element <= EL_PLAYER_IS_EXPLODING_4)
4105     {
4106       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4107       int explosion_element = EL_PLAYER_1 + player_nr;
4108       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4109       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4110
4111       if (level.use_explosion_element[player_nr])
4112         explosion_element = level.explosion_element[player_nr];
4113
4114       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4115                     element_info[explosion_element].content.e[xx][yy]);
4116     }
4117
4118     /* restore probably existing indestructible background element */
4119     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4120       element = Feld[x][y] = Back[x][y];
4121     Back[x][y] = 0;
4122
4123     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4124     GfxDir[x][y] = MV_NONE;
4125     ChangeDelay[x][y] = 0;
4126     ChangePage[x][y] = -1;
4127
4128 #if USE_NEW_CUSTOM_VALUE
4129     CustomValue[x][y] = 0;
4130 #endif
4131
4132     InitField_WithBug2(x, y, FALSE);
4133
4134     DrawLevelField(x, y);
4135
4136     TestIfElementTouchesCustomElement(x, y);
4137
4138     if (GFX_CRUMBLED(element))
4139       DrawLevelFieldCrumbledSandNeighbours(x, y);
4140
4141     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4142       StorePlayer[x][y] = 0;
4143
4144     if (ELEM_IS_PLAYER(element))
4145       RelocatePlayer(x, y, element);
4146   }
4147   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4148   {
4149     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4150     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4151
4152     if (phase == delay)
4153       DrawLevelFieldCrumbledSand(x, y);
4154
4155     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4156     {
4157       DrawLevelElement(x, y, Back[x][y]);
4158       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4159     }
4160     else if (IS_WALKABLE_UNDER(Back[x][y]))
4161     {
4162       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4163       DrawLevelElementThruMask(x, y, Back[x][y]);
4164     }
4165     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4166       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4167   }
4168 }
4169
4170 void DynaExplode(int ex, int ey)
4171 {
4172   int i, j;
4173   int dynabomb_element = Feld[ex][ey];
4174   int dynabomb_size = 1;
4175   boolean dynabomb_xl = FALSE;
4176   struct PlayerInfo *player;
4177   static int xy[4][2] =
4178   {
4179     { 0, -1 },
4180     { -1, 0 },
4181     { +1, 0 },
4182     { 0, +1 }
4183   };
4184
4185   if (IS_ACTIVE_BOMB(dynabomb_element))
4186   {
4187     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4188     dynabomb_size = player->dynabomb_size;
4189     dynabomb_xl = player->dynabomb_xl;
4190     player->dynabombs_left++;
4191   }
4192
4193   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4194
4195   for (i = 0; i < NUM_DIRECTIONS; i++)
4196   {
4197     for (j = 1; j <= dynabomb_size; j++)
4198     {
4199       int x = ex + j * xy[i][0];
4200       int y = ey + j * xy[i][1];
4201       int element;
4202
4203       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4204         break;
4205
4206       element = Feld[x][y];
4207
4208       /* do not restart explosions of fields with active bombs */
4209       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4210         continue;
4211
4212       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4213
4214       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4215           !IS_DIGGABLE(element) && !dynabomb_xl)
4216         break;
4217     }
4218   }
4219 }
4220
4221 void Bang(int x, int y)
4222 {
4223   int element = MovingOrBlocked2Element(x, y);
4224   int explosion_type = EX_TYPE_NORMAL;
4225
4226   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4227   {
4228     struct PlayerInfo *player = PLAYERINFO(x, y);
4229
4230     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4231                             player->element_nr);
4232
4233     if (level.use_explosion_element[player->index_nr])
4234     {
4235       int explosion_element = level.explosion_element[player->index_nr];
4236
4237       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4238         explosion_type = EX_TYPE_CROSS;
4239       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4240         explosion_type = EX_TYPE_CENTER;
4241     }
4242   }
4243
4244   switch (element)
4245   {
4246     case EL_BUG:
4247     case EL_SPACESHIP:
4248     case EL_BD_BUTTERFLY:
4249     case EL_BD_FIREFLY:
4250     case EL_YAMYAM:
4251     case EL_DARK_YAMYAM:
4252     case EL_ROBOT:
4253     case EL_PACMAN:
4254     case EL_MOLE:
4255       RaiseScoreElement(element);
4256       break;
4257
4258     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4259     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4260     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4261     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4262     case EL_DYNABOMB_INCREASE_NUMBER:
4263     case EL_DYNABOMB_INCREASE_SIZE:
4264     case EL_DYNABOMB_INCREASE_POWER:
4265       explosion_type = EX_TYPE_DYNA;
4266       break;
4267
4268     case EL_DC_LANDMINE:
4269 #if 0
4270     case EL_EM_EXIT_OPEN:
4271     case EL_EM_STEEL_EXIT_OPEN:
4272 #endif
4273       explosion_type = EX_TYPE_CENTER;
4274       break;
4275
4276     case EL_PENGUIN:
4277     case EL_LAMP:
4278     case EL_LAMP_ACTIVE:
4279     case EL_AMOEBA_TO_DIAMOND:
4280       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4281         explosion_type = EX_TYPE_CENTER;
4282       break;
4283
4284     default:
4285       if (element_info[element].explosion_type == EXPLODES_CROSS)
4286         explosion_type = EX_TYPE_CROSS;
4287       else if (element_info[element].explosion_type == EXPLODES_1X1)
4288         explosion_type = EX_TYPE_CENTER;
4289       break;
4290   }
4291
4292   if (explosion_type == EX_TYPE_DYNA)
4293     DynaExplode(x, y);
4294   else
4295     Explode(x, y, EX_PHASE_START, explosion_type);
4296
4297   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4298 }
4299
4300 void SplashAcid(int x, int y)
4301 {
4302   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4303       (!IN_LEV_FIELD(x - 1, y - 2) ||
4304        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4305     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4306
4307   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4308       (!IN_LEV_FIELD(x + 1, y - 2) ||
4309        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4310     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4311
4312   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4313 }
4314
4315 static void InitBeltMovement()
4316 {
4317   static int belt_base_element[4] =
4318   {
4319     EL_CONVEYOR_BELT_1_LEFT,
4320     EL_CONVEYOR_BELT_2_LEFT,
4321     EL_CONVEYOR_BELT_3_LEFT,
4322     EL_CONVEYOR_BELT_4_LEFT
4323   };
4324   static int belt_base_active_element[4] =
4325   {
4326     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4327     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4328     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4329     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4330   };
4331
4332   int x, y, i, j;
4333
4334   /* set frame order for belt animation graphic according to belt direction */
4335   for (i = 0; i < NUM_BELTS; i++)
4336   {
4337     int belt_nr = i;
4338
4339     for (j = 0; j < NUM_BELT_PARTS; j++)
4340     {
4341       int element = belt_base_active_element[belt_nr] + j;
4342       int graphic = el2img(element);
4343
4344       if (game.belt_dir[i] == MV_LEFT)
4345         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4346       else
4347         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4348     }
4349   }
4350
4351   SCAN_PLAYFIELD(x, y)
4352   {
4353     int element = Feld[x][y];
4354
4355     for (i = 0; i < NUM_BELTS; i++)
4356     {
4357       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4358       {
4359         int e_belt_nr = getBeltNrFromBeltElement(element);
4360         int belt_nr = i;
4361
4362         if (e_belt_nr == belt_nr)
4363         {
4364           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4365
4366           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4367         }
4368       }
4369     }
4370   }
4371 }
4372
4373 static void ToggleBeltSwitch(int x, int y)
4374 {
4375   static int belt_base_element[4] =
4376   {
4377     EL_CONVEYOR_BELT_1_LEFT,
4378     EL_CONVEYOR_BELT_2_LEFT,
4379     EL_CONVEYOR_BELT_3_LEFT,
4380     EL_CONVEYOR_BELT_4_LEFT
4381   };
4382   static int belt_base_active_element[4] =
4383   {
4384     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4385     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4386     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4387     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4388   };
4389   static int belt_base_switch_element[4] =
4390   {
4391     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4392     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4393     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4394     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4395   };
4396   static int belt_move_dir[4] =
4397   {
4398     MV_LEFT,
4399     MV_NONE,
4400     MV_RIGHT,
4401     MV_NONE,
4402   };
4403
4404   int element = Feld[x][y];
4405   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4406   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4407   int belt_dir = belt_move_dir[belt_dir_nr];
4408   int xx, yy, i;
4409
4410   if (!IS_BELT_SWITCH(element))
4411     return;
4412
4413   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4414   game.belt_dir[belt_nr] = belt_dir;
4415
4416   if (belt_dir_nr == 3)
4417     belt_dir_nr = 1;
4418
4419   /* set frame order for belt animation graphic according to belt direction */
4420   for (i = 0; i < NUM_BELT_PARTS; i++)
4421   {
4422     int element = belt_base_active_element[belt_nr] + i;
4423     int graphic = el2img(element);
4424
4425     if (belt_dir == MV_LEFT)
4426       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4427     else
4428       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4429   }
4430
4431   SCAN_PLAYFIELD(xx, yy)
4432   {
4433     int element = Feld[xx][yy];
4434
4435     if (IS_BELT_SWITCH(element))
4436     {
4437       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4438
4439       if (e_belt_nr == belt_nr)
4440       {
4441         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4442         DrawLevelField(xx, yy);
4443       }
4444     }
4445     else if (IS_BELT(element) && belt_dir != MV_NONE)
4446     {
4447       int e_belt_nr = getBeltNrFromBeltElement(element);
4448
4449       if (e_belt_nr == belt_nr)
4450       {
4451         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4452
4453         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4454         DrawLevelField(xx, yy);
4455       }
4456     }
4457     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4458     {
4459       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4460
4461       if (e_belt_nr == belt_nr)
4462       {
4463         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4464
4465         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4466         DrawLevelField(xx, yy);
4467       }
4468     }
4469   }
4470 }
4471
4472 static void ToggleSwitchgateSwitch(int x, int y)
4473 {
4474   int xx, yy;
4475
4476   game.switchgate_pos = !game.switchgate_pos;
4477
4478   SCAN_PLAYFIELD(xx, yy)
4479   {
4480     int element = Feld[xx][yy];
4481
4482 #if !USE_BOTH_SWITCHGATE_SWITCHES
4483     if (element == EL_SWITCHGATE_SWITCH_UP ||
4484         element == EL_SWITCHGATE_SWITCH_DOWN)
4485     {
4486       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4487       DrawLevelField(xx, yy);
4488     }
4489     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4490              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4491     {
4492       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4493       DrawLevelField(xx, yy);
4494     }
4495 #else
4496     if (element == EL_SWITCHGATE_SWITCH_UP)
4497     {
4498       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4499       DrawLevelField(xx, yy);
4500     }
4501     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4502     {
4503       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4504       DrawLevelField(xx, yy);
4505     }
4506     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4507     {
4508       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4509       DrawLevelField(xx, yy);
4510     }
4511     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4512     {
4513       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4514       DrawLevelField(xx, yy);
4515     }
4516 #endif
4517     else if (element == EL_SWITCHGATE_OPEN ||
4518              element == EL_SWITCHGATE_OPENING)
4519     {
4520       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4521
4522       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4523     }
4524     else if (element == EL_SWITCHGATE_CLOSED ||
4525              element == EL_SWITCHGATE_CLOSING)
4526     {
4527       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4528
4529       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4530     }
4531   }
4532 }
4533
4534 static int getInvisibleActiveFromInvisibleElement(int element)
4535 {
4536   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4537           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
4538           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
4539           element);
4540 }
4541
4542 static int getInvisibleFromInvisibleActiveElement(int element)
4543 {
4544   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4545           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
4546           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
4547           element);
4548 }
4549
4550 static void RedrawAllLightSwitchesAndInvisibleElements()
4551 {
4552   int x, y;
4553
4554   SCAN_PLAYFIELD(x, y)
4555   {
4556     int element = Feld[x][y];
4557
4558     if (element == EL_LIGHT_SWITCH &&
4559         game.light_time_left > 0)
4560     {
4561       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4562       DrawLevelField(x, y);
4563     }
4564     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4565              game.light_time_left == 0)
4566     {
4567       Feld[x][y] = EL_LIGHT_SWITCH;
4568       DrawLevelField(x, y);
4569     }
4570     else if (element == EL_EMC_DRIPPER &&
4571              game.light_time_left > 0)
4572     {
4573       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4574       DrawLevelField(x, y);
4575     }
4576     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4577              game.light_time_left == 0)
4578     {
4579       Feld[x][y] = EL_EMC_DRIPPER;
4580       DrawLevelField(x, y);
4581     }
4582     else if (element == EL_INVISIBLE_STEELWALL ||
4583              element == EL_INVISIBLE_WALL ||
4584              element == EL_INVISIBLE_SAND)
4585     {
4586       if (game.light_time_left > 0)
4587         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4588
4589       DrawLevelField(x, y);
4590
4591       /* uncrumble neighbour fields, if needed */
4592       if (element == EL_INVISIBLE_SAND)
4593         DrawLevelFieldCrumbledSandNeighbours(x, y);
4594     }
4595     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4596              element == EL_INVISIBLE_WALL_ACTIVE ||
4597              element == EL_INVISIBLE_SAND_ACTIVE)
4598     {
4599       if (game.light_time_left == 0)
4600         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4601
4602       DrawLevelField(x, y);
4603
4604       /* re-crumble neighbour fields, if needed */
4605       if (element == EL_INVISIBLE_SAND)
4606         DrawLevelFieldCrumbledSandNeighbours(x, y);
4607     }
4608   }
4609 }
4610
4611 static void RedrawAllInvisibleElementsForLenses()
4612 {
4613   int x, y;
4614
4615   SCAN_PLAYFIELD(x, y)
4616   {
4617     int element = Feld[x][y];
4618
4619     if (element == EL_EMC_DRIPPER &&
4620         game.lenses_time_left > 0)
4621     {
4622       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4623       DrawLevelField(x, y);
4624     }
4625     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4626              game.lenses_time_left == 0)
4627     {
4628       Feld[x][y] = EL_EMC_DRIPPER;
4629       DrawLevelField(x, y);
4630     }
4631     else if (element == EL_INVISIBLE_STEELWALL ||
4632              element == EL_INVISIBLE_WALL ||
4633              element == EL_INVISIBLE_SAND)
4634     {
4635       if (game.lenses_time_left > 0)
4636         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4637
4638       DrawLevelField(x, y);
4639
4640       /* uncrumble neighbour fields, if needed */
4641       if (element == EL_INVISIBLE_SAND)
4642         DrawLevelFieldCrumbledSandNeighbours(x, y);
4643     }
4644     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4645              element == EL_INVISIBLE_WALL_ACTIVE ||
4646              element == EL_INVISIBLE_SAND_ACTIVE)
4647     {
4648       if (game.lenses_time_left == 0)
4649         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4650
4651       DrawLevelField(x, y);
4652
4653       /* re-crumble neighbour fields, if needed */
4654       if (element == EL_INVISIBLE_SAND)
4655         DrawLevelFieldCrumbledSandNeighbours(x, y);
4656     }
4657   }
4658 }
4659
4660 static void RedrawAllInvisibleElementsForMagnifier()
4661 {
4662   int x, y;
4663
4664   SCAN_PLAYFIELD(x, y)
4665   {
4666     int element = Feld[x][y];
4667
4668     if (element == EL_EMC_FAKE_GRASS &&
4669         game.magnify_time_left > 0)
4670     {
4671       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4672       DrawLevelField(x, y);
4673     }
4674     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4675              game.magnify_time_left == 0)
4676     {
4677       Feld[x][y] = EL_EMC_FAKE_GRASS;
4678       DrawLevelField(x, y);
4679     }
4680     else if (IS_GATE_GRAY(element) &&
4681              game.magnify_time_left > 0)
4682     {
4683       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4684                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4685                     IS_EM_GATE_GRAY(element) ?
4686                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4687                     IS_EMC_GATE_GRAY(element) ?
4688                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4689                     element);
4690       DrawLevelField(x, y);
4691     }
4692     else if (IS_GATE_GRAY_ACTIVE(element) &&
4693              game.magnify_time_left == 0)
4694     {
4695       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4696                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4697                     IS_EM_GATE_GRAY_ACTIVE(element) ?
4698                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4699                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
4700                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4701                     element);
4702       DrawLevelField(x, y);
4703     }
4704   }
4705 }
4706
4707 static void ToggleLightSwitch(int x, int y)
4708 {
4709   int element = Feld[x][y];
4710
4711   game.light_time_left =
4712     (element == EL_LIGHT_SWITCH ?
4713      level.time_light * FRAMES_PER_SECOND : 0);
4714
4715   RedrawAllLightSwitchesAndInvisibleElements();
4716 }
4717
4718 static void ActivateTimegateSwitch(int x, int y)
4719 {
4720   int xx, yy;
4721
4722   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4723
4724   SCAN_PLAYFIELD(xx, yy)
4725   {
4726     int element = Feld[xx][yy];
4727
4728     if (element == EL_TIMEGATE_CLOSED ||
4729         element == EL_TIMEGATE_CLOSING)
4730     {
4731       Feld[xx][yy] = EL_TIMEGATE_OPENING;
4732       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4733     }
4734
4735     /*
4736     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4737     {
4738       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4739       DrawLevelField(xx, yy);
4740     }
4741     */
4742
4743   }
4744
4745 #if 1
4746   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4747                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4748 #else
4749   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4750 #endif
4751 }
4752
4753 void Impact(int x, int y)
4754 {
4755   boolean last_line = (y == lev_fieldy - 1);
4756   boolean object_hit = FALSE;
4757   boolean impact = (last_line || object_hit);
4758   int element = Feld[x][y];
4759   int smashed = EL_STEELWALL;
4760
4761   if (!last_line)       /* check if element below was hit */
4762   {
4763     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4764       return;
4765
4766     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4767                                          MovDir[x][y + 1] != MV_DOWN ||
4768                                          MovPos[x][y + 1] <= TILEY / 2));
4769
4770     /* do not smash moving elements that left the smashed field in time */
4771     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4772         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4773       object_hit = FALSE;
4774
4775 #if USE_QUICKSAND_IMPACT_BUGFIX
4776     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4777     {
4778       RemoveMovingField(x, y + 1);
4779       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4780       Feld[x][y + 2] = EL_ROCK;
4781       DrawLevelField(x, y + 2);
4782
4783       object_hit = TRUE;
4784     }
4785
4786     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
4787     {
4788       RemoveMovingField(x, y + 1);
4789       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
4790       Feld[x][y + 2] = EL_ROCK;
4791       DrawLevelField(x, y + 2);
4792
4793       object_hit = TRUE;
4794     }
4795 #endif
4796
4797     if (object_hit)
4798       smashed = MovingOrBlocked2Element(x, y + 1);
4799
4800     impact = (last_line || object_hit);
4801   }
4802
4803   if (!last_line && smashed == EL_ACID) /* element falls into acid */
4804   {
4805     SplashAcid(x, y + 1);
4806     return;
4807   }
4808
4809   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4810   /* only reset graphic animation if graphic really changes after impact */
4811   if (impact &&
4812       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4813   {
4814     ResetGfxAnimation(x, y);
4815     DrawLevelField(x, y);
4816   }
4817
4818   if (impact && CAN_EXPLODE_IMPACT(element))
4819   {
4820     Bang(x, y);
4821     return;
4822   }
4823   else if (impact && element == EL_PEARL &&
4824            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
4825   {
4826     ResetGfxAnimation(x, y);
4827
4828     Feld[x][y] = EL_PEARL_BREAKING;
4829     PlayLevelSound(x, y, SND_PEARL_BREAKING);
4830     return;
4831   }
4832   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4833   {
4834     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4835
4836     return;
4837   }
4838
4839   if (impact && element == EL_AMOEBA_DROP)
4840   {
4841     if (object_hit && IS_PLAYER(x, y + 1))
4842       KillPlayerUnlessEnemyProtected(x, y + 1);
4843     else if (object_hit && smashed == EL_PENGUIN)
4844       Bang(x, y + 1);
4845     else
4846     {
4847       Feld[x][y] = EL_AMOEBA_GROWING;
4848       Store[x][y] = EL_AMOEBA_WET;
4849
4850       ResetRandomAnimationValue(x, y);
4851     }
4852     return;
4853   }
4854
4855   if (object_hit)               /* check which object was hit */
4856   {
4857     if ((CAN_PASS_MAGIC_WALL(element) && 
4858          (smashed == EL_MAGIC_WALL ||
4859           smashed == EL_BD_MAGIC_WALL)) ||
4860         (CAN_PASS_DC_MAGIC_WALL(element) &&
4861          smashed == EL_DC_MAGIC_WALL))
4862     {
4863       int xx, yy;
4864       int activated_magic_wall =
4865         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4866          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
4867          EL_DC_MAGIC_WALL_ACTIVE);
4868
4869       /* activate magic wall / mill */
4870       SCAN_PLAYFIELD(xx, yy)
4871       {
4872         if (Feld[xx][yy] == smashed)
4873           Feld[xx][yy] = activated_magic_wall;
4874       }
4875
4876       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4877       game.magic_wall_active = TRUE;
4878
4879       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4880                             SND_MAGIC_WALL_ACTIVATING :
4881                             smashed == EL_BD_MAGIC_WALL ?
4882                             SND_BD_MAGIC_WALL_ACTIVATING :
4883                             SND_DC_MAGIC_WALL_ACTIVATING));
4884     }
4885
4886     if (IS_PLAYER(x, y + 1))
4887     {
4888       if (CAN_SMASH_PLAYER(element))
4889       {
4890         KillPlayerUnlessEnemyProtected(x, y + 1);
4891         return;
4892       }
4893     }
4894     else if (smashed == EL_PENGUIN)
4895     {
4896       if (CAN_SMASH_PLAYER(element))
4897       {
4898         Bang(x, y + 1);
4899         return;
4900       }
4901     }
4902     else if (element == EL_BD_DIAMOND)
4903     {
4904       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4905       {
4906         Bang(x, y + 1);
4907         return;
4908       }
4909     }
4910     else if (((element == EL_SP_INFOTRON ||
4911                element == EL_SP_ZONK) &&
4912               (smashed == EL_SP_SNIKSNAK ||
4913                smashed == EL_SP_ELECTRON ||
4914                smashed == EL_SP_DISK_ORANGE)) ||
4915              (element == EL_SP_INFOTRON &&
4916               smashed == EL_SP_DISK_YELLOW))
4917     {
4918       Bang(x, y + 1);
4919       return;
4920     }
4921     else if (CAN_SMASH_EVERYTHING(element))
4922     {
4923       if (IS_CLASSIC_ENEMY(smashed) ||
4924           CAN_EXPLODE_SMASHED(smashed))
4925       {
4926         Bang(x, y + 1);
4927         return;
4928       }
4929       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4930       {
4931         if (smashed == EL_LAMP ||
4932             smashed == EL_LAMP_ACTIVE)
4933         {
4934           Bang(x, y + 1);
4935           return;
4936         }
4937         else if (smashed == EL_NUT)
4938         {
4939           Feld[x][y + 1] = EL_NUT_BREAKING;
4940           PlayLevelSound(x, y, SND_NUT_BREAKING);
4941           RaiseScoreElement(EL_NUT);
4942           return;
4943         }
4944         else if (smashed == EL_PEARL)
4945         {
4946           ResetGfxAnimation(x, y);
4947
4948           Feld[x][y + 1] = EL_PEARL_BREAKING;
4949           PlayLevelSound(x, y, SND_PEARL_BREAKING);
4950           return;
4951         }
4952         else if (smashed == EL_DIAMOND)
4953         {
4954           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4955           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4956           return;
4957         }
4958         else if (IS_BELT_SWITCH(smashed))
4959         {
4960           ToggleBeltSwitch(x, y + 1);
4961         }
4962         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4963                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
4964                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
4965                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
4966         {
4967           ToggleSwitchgateSwitch(x, y + 1);
4968         }
4969         else if (smashed == EL_LIGHT_SWITCH ||
4970                  smashed == EL_LIGHT_SWITCH_ACTIVE)
4971         {
4972           ToggleLightSwitch(x, y + 1);
4973         }
4974         else
4975         {
4976 #if 0
4977           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4978 #endif
4979
4980           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4981
4982           CheckElementChangeBySide(x, y + 1, smashed, element,
4983                                    CE_SWITCHED, CH_SIDE_TOP);
4984           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4985                                             CH_SIDE_TOP);
4986         }
4987       }
4988       else
4989       {
4990         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4991       }
4992     }
4993   }
4994
4995   /* play sound of magic wall / mill */
4996   if (!last_line &&
4997       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4998        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
4999        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5000   {
5001     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5002       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5003     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5004       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5005     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5006       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5007
5008     return;
5009   }
5010
5011   /* play sound of object that hits the ground */
5012   if (last_line || object_hit)
5013     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5014 }
5015
5016 inline static void TurnRoundExt(int x, int y)
5017 {
5018   static struct
5019   {
5020     int dx, dy;
5021   } move_xy[] =
5022   {
5023     {  0,  0 },
5024     { -1,  0 },
5025     { +1,  0 },
5026     {  0,  0 },
5027     {  0, -1 },
5028     {  0,  0 }, { 0, 0 }, { 0, 0 },
5029     {  0, +1 }
5030   };
5031   static struct
5032   {
5033     int left, right, back;
5034   } turn[] =
5035   {
5036     { 0,        0,              0        },
5037     { MV_DOWN,  MV_UP,          MV_RIGHT },
5038     { MV_UP,    MV_DOWN,        MV_LEFT  },
5039     { 0,        0,              0        },
5040     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5041     { 0,        0,              0        },
5042     { 0,        0,              0        },
5043     { 0,        0,              0        },
5044     { MV_RIGHT, MV_LEFT,        MV_UP    }
5045   };
5046
5047   int element = Feld[x][y];
5048   int move_pattern = element_info[element].move_pattern;
5049
5050   int old_move_dir = MovDir[x][y];
5051   int left_dir  = turn[old_move_dir].left;
5052   int right_dir = turn[old_move_dir].right;
5053   int back_dir  = turn[old_move_dir].back;
5054
5055   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5056   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5057   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5058   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5059
5060   int left_x  = x + left_dx,  left_y  = y + left_dy;
5061   int right_x = x + right_dx, right_y = y + right_dy;
5062   int move_x  = x + move_dx,  move_y  = y + move_dy;
5063
5064   int xx, yy;
5065
5066   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5067   {
5068     TestIfBadThingTouchesOtherBadThing(x, y);
5069
5070     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5071       MovDir[x][y] = right_dir;
5072     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5073       MovDir[x][y] = left_dir;
5074
5075     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5076       MovDelay[x][y] = 9;
5077     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5078       MovDelay[x][y] = 1;
5079   }
5080   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5081   {
5082     TestIfBadThingTouchesOtherBadThing(x, y);
5083
5084     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5085       MovDir[x][y] = left_dir;
5086     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5087       MovDir[x][y] = right_dir;
5088
5089     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5090       MovDelay[x][y] = 9;
5091     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5092       MovDelay[x][y] = 1;
5093   }
5094   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5095   {
5096     TestIfBadThingTouchesOtherBadThing(x, y);
5097
5098     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5099       MovDir[x][y] = left_dir;
5100     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5101       MovDir[x][y] = right_dir;
5102
5103     if (MovDir[x][y] != old_move_dir)
5104       MovDelay[x][y] = 9;
5105   }
5106   else if (element == EL_YAMYAM)
5107   {
5108     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5109     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5110
5111     if (can_turn_left && can_turn_right)
5112       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5113     else if (can_turn_left)
5114       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5115     else if (can_turn_right)
5116       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5117     else
5118       MovDir[x][y] = back_dir;
5119
5120     MovDelay[x][y] = 16 + 16 * RND(3);
5121   }
5122   else if (element == EL_DARK_YAMYAM)
5123   {
5124     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5125                                                          left_x, left_y);
5126     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5127                                                          right_x, right_y);
5128
5129     if (can_turn_left && can_turn_right)
5130       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5131     else if (can_turn_left)
5132       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5133     else if (can_turn_right)
5134       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5135     else
5136       MovDir[x][y] = back_dir;
5137
5138     MovDelay[x][y] = 16 + 16 * RND(3);
5139   }
5140   else if (element == EL_PACMAN)
5141   {
5142     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5143     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5144
5145     if (can_turn_left && can_turn_right)
5146       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5147     else if (can_turn_left)
5148       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5149     else if (can_turn_right)
5150       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5151     else
5152       MovDir[x][y] = back_dir;
5153
5154     MovDelay[x][y] = 6 + RND(40);
5155   }
5156   else if (element == EL_PIG)
5157   {
5158     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5159     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5160     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5161     boolean should_turn_left, should_turn_right, should_move_on;
5162     int rnd_value = 24;
5163     int rnd = RND(rnd_value);
5164
5165     should_turn_left = (can_turn_left &&
5166                         (!can_move_on ||
5167                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5168                                                    y + back_dy + left_dy)));
5169     should_turn_right = (can_turn_right &&
5170                          (!can_move_on ||
5171                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5172                                                     y + back_dy + right_dy)));
5173     should_move_on = (can_move_on &&
5174                       (!can_turn_left ||
5175                        !can_turn_right ||
5176                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5177                                                  y + move_dy + left_dy) ||
5178                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5179                                                  y + move_dy + right_dy)));
5180
5181     if (should_turn_left || should_turn_right || should_move_on)
5182     {
5183       if (should_turn_left && should_turn_right && should_move_on)
5184         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5185                         rnd < 2 * rnd_value / 3 ? right_dir :
5186                         old_move_dir);
5187       else if (should_turn_left && should_turn_right)
5188         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5189       else if (should_turn_left && should_move_on)
5190         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5191       else if (should_turn_right && should_move_on)
5192         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5193       else if (should_turn_left)
5194         MovDir[x][y] = left_dir;
5195       else if (should_turn_right)
5196         MovDir[x][y] = right_dir;
5197       else if (should_move_on)
5198         MovDir[x][y] = old_move_dir;
5199     }
5200     else if (can_move_on && rnd > rnd_value / 8)
5201       MovDir[x][y] = old_move_dir;
5202     else if (can_turn_left && can_turn_right)
5203       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5204     else if (can_turn_left && rnd > rnd_value / 8)
5205       MovDir[x][y] = left_dir;
5206     else if (can_turn_right && rnd > rnd_value/8)
5207       MovDir[x][y] = right_dir;
5208     else
5209       MovDir[x][y] = back_dir;
5210
5211     xx = x + move_xy[MovDir[x][y]].dx;
5212     yy = y + move_xy[MovDir[x][y]].dy;
5213
5214     if (!IN_LEV_FIELD(xx, yy) ||
5215         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5216       MovDir[x][y] = old_move_dir;
5217
5218     MovDelay[x][y] = 0;
5219   }
5220   else if (element == EL_DRAGON)
5221   {
5222     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5223     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5224     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5225     int rnd_value = 24;
5226     int rnd = RND(rnd_value);
5227
5228     if (can_move_on && rnd > rnd_value / 8)
5229       MovDir[x][y] = old_move_dir;
5230     else if (can_turn_left && can_turn_right)
5231       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5232     else if (can_turn_left && rnd > rnd_value / 8)
5233       MovDir[x][y] = left_dir;
5234     else if (can_turn_right && rnd > rnd_value / 8)
5235       MovDir[x][y] = right_dir;
5236     else
5237       MovDir[x][y] = back_dir;
5238
5239     xx = x + move_xy[MovDir[x][y]].dx;
5240     yy = y + move_xy[MovDir[x][y]].dy;
5241
5242     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5243       MovDir[x][y] = old_move_dir;
5244
5245     MovDelay[x][y] = 0;
5246   }
5247   else if (element == EL_MOLE)
5248   {
5249     boolean can_move_on =
5250       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5251                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5252                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5253     if (!can_move_on)
5254     {
5255       boolean can_turn_left =
5256         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5257                               IS_AMOEBOID(Feld[left_x][left_y])));
5258
5259       boolean can_turn_right =
5260         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5261                               IS_AMOEBOID(Feld[right_x][right_y])));
5262
5263       if (can_turn_left && can_turn_right)
5264         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5265       else if (can_turn_left)
5266         MovDir[x][y] = left_dir;
5267       else
5268         MovDir[x][y] = right_dir;
5269     }
5270
5271     if (MovDir[x][y] != old_move_dir)
5272       MovDelay[x][y] = 9;
5273   }
5274   else if (element == EL_BALLOON)
5275   {
5276     MovDir[x][y] = game.wind_direction;
5277     MovDelay[x][y] = 0;
5278   }
5279   else if (element == EL_SPRING)
5280   {
5281 #if USE_NEW_SPRING_BUMPER
5282     if (MovDir[x][y] & MV_HORIZONTAL)
5283     {
5284       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5285           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5286       {
5287         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5288         ResetGfxAnimation(move_x, move_y);
5289         DrawLevelField(move_x, move_y);
5290
5291         MovDir[x][y] = back_dir;
5292       }
5293       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5294                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5295         MovDir[x][y] = MV_NONE;
5296     }
5297 #else
5298     if (MovDir[x][y] & MV_HORIZONTAL &&
5299         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5300          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5301       MovDir[x][y] = MV_NONE;
5302 #endif
5303
5304     MovDelay[x][y] = 0;
5305   }
5306   else if (element == EL_ROBOT ||
5307            element == EL_SATELLITE ||
5308            element == EL_PENGUIN ||
5309            element == EL_EMC_ANDROID)
5310   {
5311     int attr_x = -1, attr_y = -1;
5312
5313     if (AllPlayersGone)
5314     {
5315       attr_x = ExitX;
5316       attr_y = ExitY;
5317     }
5318     else
5319     {
5320       int i;
5321
5322       for (i = 0; i < MAX_PLAYERS; i++)
5323       {
5324         struct PlayerInfo *player = &stored_player[i];
5325         int jx = player->jx, jy = player->jy;
5326
5327         if (!player->active)
5328           continue;
5329
5330         if (attr_x == -1 ||
5331             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5332         {
5333           attr_x = jx;
5334           attr_y = jy;
5335         }
5336       }
5337     }
5338
5339     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5340         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5341          game.engine_version < VERSION_IDENT(3,1,0,0)))
5342     {
5343       attr_x = ZX;
5344       attr_y = ZY;
5345     }
5346
5347     if (element == EL_PENGUIN)
5348     {
5349       int i;
5350       static int xy[4][2] =
5351       {
5352         { 0, -1 },
5353         { -1, 0 },
5354         { +1, 0 },
5355         { 0, +1 }
5356       };
5357
5358       for (i = 0; i < NUM_DIRECTIONS; i++)
5359       {
5360         int ex = x + xy[i][0];
5361         int ey = y + xy[i][1];
5362
5363         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5364                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5365                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5366                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5367         {
5368           attr_x = ex;
5369           attr_y = ey;
5370           break;
5371         }
5372       }
5373     }
5374
5375     MovDir[x][y] = MV_NONE;
5376     if (attr_x < x)
5377       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5378     else if (attr_x > x)
5379       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5380     if (attr_y < y)
5381       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5382     else if (attr_y > y)
5383       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5384
5385     if (element == EL_ROBOT)
5386     {
5387       int newx, newy;
5388
5389       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5390         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5391       Moving2Blocked(x, y, &newx, &newy);
5392
5393       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5394         MovDelay[x][y] = 8 + 8 * !RND(3);
5395       else
5396         MovDelay[x][y] = 16;
5397     }
5398     else if (element == EL_PENGUIN)
5399     {
5400       int newx, newy;
5401
5402       MovDelay[x][y] = 1;
5403
5404       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5405       {
5406         boolean first_horiz = RND(2);
5407         int new_move_dir = MovDir[x][y];
5408
5409         MovDir[x][y] =
5410           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5411         Moving2Blocked(x, y, &newx, &newy);
5412
5413         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5414           return;
5415
5416         MovDir[x][y] =
5417           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5418         Moving2Blocked(x, y, &newx, &newy);
5419
5420         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5421           return;
5422
5423         MovDir[x][y] = old_move_dir;
5424         return;
5425       }
5426     }
5427     else if (element == EL_SATELLITE)
5428     {
5429       int newx, newy;
5430
5431       MovDelay[x][y] = 1;
5432
5433       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5434       {
5435         boolean first_horiz = RND(2);
5436         int new_move_dir = MovDir[x][y];
5437
5438         MovDir[x][y] =
5439           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5440         Moving2Blocked(x, y, &newx, &newy);
5441
5442         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5443           return;
5444
5445         MovDir[x][y] =
5446           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5447         Moving2Blocked(x, y, &newx, &newy);
5448
5449         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5450           return;
5451
5452         MovDir[x][y] = old_move_dir;
5453         return;
5454       }
5455     }
5456     else if (element == EL_EMC_ANDROID)
5457     {
5458       static int check_pos[16] =
5459       {
5460         -1,             /*  0 => (invalid)          */
5461         7,              /*  1 => MV_LEFT            */
5462         3,              /*  2 => MV_RIGHT           */
5463         -1,             /*  3 => (invalid)          */
5464         1,              /*  4 =>            MV_UP   */
5465         0,              /*  5 => MV_LEFT  | MV_UP   */
5466         2,              /*  6 => MV_RIGHT | MV_UP   */
5467         -1,             /*  7 => (invalid)          */
5468         5,              /*  8 =>            MV_DOWN */
5469         6,              /*  9 => MV_LEFT  | MV_DOWN */
5470         4,              /* 10 => MV_RIGHT | MV_DOWN */
5471         -1,             /* 11 => (invalid)          */
5472         -1,             /* 12 => (invalid)          */
5473         -1,             /* 13 => (invalid)          */
5474         -1,             /* 14 => (invalid)          */
5475         -1,             /* 15 => (invalid)          */
5476       };
5477       static struct
5478       {
5479         int dx, dy;
5480         int dir;
5481       } check_xy[8] =
5482       {
5483         { -1, -1,       MV_LEFT  | MV_UP   },
5484         {  0, -1,                  MV_UP   },
5485         { +1, -1,       MV_RIGHT | MV_UP   },
5486         { +1,  0,       MV_RIGHT           },
5487         { +1, +1,       MV_RIGHT | MV_DOWN },
5488         {  0, +1,                  MV_DOWN },
5489         { -1, +1,       MV_LEFT  | MV_DOWN },
5490         { -1,  0,       MV_LEFT            },
5491       };
5492       int start_pos, check_order;
5493       boolean can_clone = FALSE;
5494       int i;
5495
5496       /* check if there is any free field around current position */
5497       for (i = 0; i < 8; i++)
5498       {
5499         int newx = x + check_xy[i].dx;
5500         int newy = y + check_xy[i].dy;
5501
5502         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5503         {
5504           can_clone = TRUE;
5505
5506           break;
5507         }
5508       }
5509
5510       if (can_clone)            /* randomly find an element to clone */
5511       {
5512         can_clone = FALSE;
5513
5514         start_pos = check_pos[RND(8)];
5515         check_order = (RND(2) ? -1 : +1);
5516
5517         for (i = 0; i < 8; i++)
5518         {
5519           int pos_raw = start_pos + i * check_order;
5520           int pos = (pos_raw + 8) % 8;
5521           int newx = x + check_xy[pos].dx;
5522           int newy = y + check_xy[pos].dy;
5523
5524           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5525           {
5526             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5527             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5528
5529             Store[x][y] = Feld[newx][newy];
5530
5531             can_clone = TRUE;
5532
5533             break;
5534           }
5535         }
5536       }
5537
5538       if (can_clone)            /* randomly find a direction to move */
5539       {
5540         can_clone = FALSE;
5541
5542         start_pos = check_pos[RND(8)];
5543         check_order = (RND(2) ? -1 : +1);
5544
5545         for (i = 0; i < 8; i++)
5546         {
5547           int pos_raw = start_pos + i * check_order;
5548           int pos = (pos_raw + 8) % 8;
5549           int newx = x + check_xy[pos].dx;
5550           int newy = y + check_xy[pos].dy;
5551           int new_move_dir = check_xy[pos].dir;
5552
5553           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5554           {
5555             MovDir[x][y] = new_move_dir;
5556             MovDelay[x][y] = level.android_clone_time * 8 + 1;
5557
5558             can_clone = TRUE;
5559
5560             break;
5561           }
5562         }
5563       }
5564
5565       if (can_clone)            /* cloning and moving successful */
5566         return;
5567
5568       /* cannot clone -- try to move towards player */
5569
5570       start_pos = check_pos[MovDir[x][y] & 0x0f];
5571       check_order = (RND(2) ? -1 : +1);
5572
5573       for (i = 0; i < 3; i++)
5574       {
5575         /* first check start_pos, then previous/next or (next/previous) pos */
5576         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5577         int pos = (pos_raw + 8) % 8;
5578         int newx = x + check_xy[pos].dx;
5579         int newy = y + check_xy[pos].dy;
5580         int new_move_dir = check_xy[pos].dir;
5581
5582         if (IS_PLAYER(newx, newy))
5583           break;
5584
5585         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5586         {
5587           MovDir[x][y] = new_move_dir;
5588           MovDelay[x][y] = level.android_move_time * 8 + 1;
5589
5590           break;
5591         }
5592       }
5593     }
5594   }
5595   else if (move_pattern == MV_TURNING_LEFT ||
5596            move_pattern == MV_TURNING_RIGHT ||
5597            move_pattern == MV_TURNING_LEFT_RIGHT ||
5598            move_pattern == MV_TURNING_RIGHT_LEFT ||
5599            move_pattern == MV_TURNING_RANDOM ||
5600            move_pattern == MV_ALL_DIRECTIONS)
5601   {
5602     boolean can_turn_left =
5603       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5604     boolean can_turn_right =
5605       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5606
5607     if (element_info[element].move_stepsize == 0)       /* "not moving" */
5608       return;
5609
5610     if (move_pattern == MV_TURNING_LEFT)
5611       MovDir[x][y] = left_dir;
5612     else if (move_pattern == MV_TURNING_RIGHT)
5613       MovDir[x][y] = right_dir;
5614     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5615       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5616     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5617       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5618     else if (move_pattern == MV_TURNING_RANDOM)
5619       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5620                       can_turn_right && !can_turn_left ? right_dir :
5621                       RND(2) ? left_dir : right_dir);
5622     else if (can_turn_left && can_turn_right)
5623       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5624     else if (can_turn_left)
5625       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5626     else if (can_turn_right)
5627       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5628     else
5629       MovDir[x][y] = back_dir;
5630
5631     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5632   }
5633   else if (move_pattern == MV_HORIZONTAL ||
5634            move_pattern == MV_VERTICAL)
5635   {
5636     if (move_pattern & old_move_dir)
5637       MovDir[x][y] = back_dir;
5638     else if (move_pattern == MV_HORIZONTAL)
5639       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5640     else if (move_pattern == MV_VERTICAL)
5641       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5642
5643     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5644   }
5645   else if (move_pattern & MV_ANY_DIRECTION)
5646   {
5647     MovDir[x][y] = move_pattern;
5648     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5649   }
5650   else if (move_pattern & MV_WIND_DIRECTION)
5651   {
5652     MovDir[x][y] = game.wind_direction;
5653     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5654   }
5655   else if (move_pattern == MV_ALONG_LEFT_SIDE)
5656   {
5657     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5658       MovDir[x][y] = left_dir;
5659     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5660       MovDir[x][y] = right_dir;
5661
5662     if (MovDir[x][y] != old_move_dir)
5663       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5664   }
5665   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5666   {
5667     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5668       MovDir[x][y] = right_dir;
5669     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5670       MovDir[x][y] = left_dir;
5671
5672     if (MovDir[x][y] != old_move_dir)
5673       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5674   }
5675   else if (move_pattern == MV_TOWARDS_PLAYER ||
5676            move_pattern == MV_AWAY_FROM_PLAYER)
5677   {
5678     int attr_x = -1, attr_y = -1;
5679     int newx, newy;
5680     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5681
5682     if (AllPlayersGone)
5683     {
5684       attr_x = ExitX;
5685       attr_y = ExitY;
5686     }
5687     else
5688     {
5689       int i;
5690
5691       for (i = 0; i < MAX_PLAYERS; i++)
5692       {
5693         struct PlayerInfo *player = &stored_player[i];
5694         int jx = player->jx, jy = player->jy;
5695
5696         if (!player->active)
5697           continue;
5698
5699         if (attr_x == -1 ||
5700             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5701         {
5702           attr_x = jx;
5703           attr_y = jy;
5704         }
5705       }
5706     }
5707
5708     MovDir[x][y] = MV_NONE;
5709     if (attr_x < x)
5710       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5711     else if (attr_x > x)
5712       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5713     if (attr_y < y)
5714       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5715     else if (attr_y > y)
5716       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5717
5718     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5719
5720     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5721     {
5722       boolean first_horiz = RND(2);
5723       int new_move_dir = MovDir[x][y];
5724
5725       if (element_info[element].move_stepsize == 0)     /* "not moving" */
5726       {
5727         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5728         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5729
5730         return;
5731       }
5732
5733       MovDir[x][y] =
5734         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5735       Moving2Blocked(x, y, &newx, &newy);
5736
5737       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5738         return;
5739
5740       MovDir[x][y] =
5741         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5742       Moving2Blocked(x, y, &newx, &newy);
5743
5744       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5745         return;
5746
5747       MovDir[x][y] = old_move_dir;
5748     }
5749   }
5750   else if (move_pattern == MV_WHEN_PUSHED ||
5751            move_pattern == MV_WHEN_DROPPED)
5752   {
5753     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5754       MovDir[x][y] = MV_NONE;
5755
5756     MovDelay[x][y] = 0;
5757   }
5758   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5759   {
5760     static int test_xy[7][2] =
5761     {
5762       { 0, -1 },
5763       { -1, 0 },
5764       { +1, 0 },
5765       { 0, +1 },
5766       { 0, -1 },
5767       { -1, 0 },
5768       { +1, 0 },
5769     };
5770     static int test_dir[7] =
5771     {
5772       MV_UP,
5773       MV_LEFT,
5774       MV_RIGHT,
5775       MV_DOWN,
5776       MV_UP,
5777       MV_LEFT,
5778       MV_RIGHT,
5779     };
5780     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5781     int move_preference = -1000000;     /* start with very low preference */
5782     int new_move_dir = MV_NONE;
5783     int start_test = RND(4);
5784     int i;
5785
5786     for (i = 0; i < NUM_DIRECTIONS; i++)
5787     {
5788       int move_dir = test_dir[start_test + i];
5789       int move_dir_preference;
5790
5791       xx = x + test_xy[start_test + i][0];
5792       yy = y + test_xy[start_test + i][1];
5793
5794       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5795           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5796       {
5797         new_move_dir = move_dir;
5798
5799         break;
5800       }
5801
5802       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5803         continue;
5804
5805       move_dir_preference = -1 * RunnerVisit[xx][yy];
5806       if (hunter_mode && PlayerVisit[xx][yy] > 0)
5807         move_dir_preference = PlayerVisit[xx][yy];
5808
5809       if (move_dir_preference > move_preference)
5810       {
5811         /* prefer field that has not been visited for the longest time */
5812         move_preference = move_dir_preference;
5813         new_move_dir = move_dir;
5814       }
5815       else if (move_dir_preference == move_preference &&
5816                move_dir == old_move_dir)
5817       {
5818         /* prefer last direction when all directions are preferred equally */
5819         move_preference = move_dir_preference;
5820         new_move_dir = move_dir;
5821       }
5822     }
5823
5824     MovDir[x][y] = new_move_dir;
5825     if (old_move_dir != new_move_dir)
5826       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5827   }
5828 }
5829
5830 static void TurnRound(int x, int y)
5831 {
5832   int direction = MovDir[x][y];
5833
5834   TurnRoundExt(x, y);
5835
5836   GfxDir[x][y] = MovDir[x][y];
5837
5838   if (direction != MovDir[x][y])
5839     GfxFrame[x][y] = 0;
5840
5841   if (MovDelay[x][y])
5842     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5843
5844   ResetGfxFrame(x, y, FALSE);
5845 }
5846
5847 static boolean JustBeingPushed(int x, int y)
5848 {
5849   int i;
5850
5851   for (i = 0; i < MAX_PLAYERS; i++)
5852   {
5853     struct PlayerInfo *player = &stored_player[i];
5854
5855     if (player->active && player->is_pushing && player->MovPos)
5856     {
5857       int next_jx = player->jx + (player->jx - player->last_jx);
5858       int next_jy = player->jy + (player->jy - player->last_jy);
5859
5860       if (x == next_jx && y == next_jy)
5861         return TRUE;
5862     }
5863   }
5864
5865   return FALSE;
5866 }
5867
5868 void StartMoving(int x, int y)
5869 {
5870   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
5871   int element = Feld[x][y];
5872
5873   if (Stop[x][y])
5874     return;
5875
5876   if (MovDelay[x][y] == 0)
5877     GfxAction[x][y] = ACTION_DEFAULT;
5878
5879   if (CAN_FALL(element) && y < lev_fieldy - 1)
5880   {
5881     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5882         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5883       if (JustBeingPushed(x, y))
5884         return;
5885
5886     if (element == EL_QUICKSAND_FULL)
5887     {
5888       if (IS_FREE(x, y + 1))
5889       {
5890         InitMovingField(x, y, MV_DOWN);
5891         started_moving = TRUE;
5892
5893         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5894 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5895         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5896           Store[x][y] = EL_ROCK;
5897 #else
5898         Store[x][y] = EL_ROCK;
5899 #endif
5900
5901         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5902       }
5903       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5904       {
5905         if (!MovDelay[x][y])
5906           MovDelay[x][y] = TILEY + 1;
5907
5908         if (MovDelay[x][y])
5909         {
5910           MovDelay[x][y]--;
5911           if (MovDelay[x][y])
5912             return;
5913         }
5914
5915         Feld[x][y] = EL_QUICKSAND_EMPTY;
5916         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5917         Store[x][y + 1] = Store[x][y];
5918         Store[x][y] = 0;
5919
5920         PlayLevelSoundAction(x, y, ACTION_FILLING);
5921       }
5922     }
5923     else if (element == EL_QUICKSAND_FAST_FULL)
5924     {
5925       if (IS_FREE(x, y + 1))
5926       {
5927         InitMovingField(x, y, MV_DOWN);
5928         started_moving = TRUE;
5929
5930         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
5931 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5932         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5933           Store[x][y] = EL_ROCK;
5934 #else
5935         Store[x][y] = EL_ROCK;
5936 #endif
5937
5938         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5939       }
5940       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5941       {
5942         if (!MovDelay[x][y])
5943           MovDelay[x][y] = TILEY + 1;
5944
5945         if (MovDelay[x][y])
5946         {
5947           MovDelay[x][y]--;
5948           if (MovDelay[x][y])
5949             return;
5950         }
5951
5952         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
5953         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
5954         Store[x][y + 1] = Store[x][y];
5955         Store[x][y] = 0;
5956
5957         PlayLevelSoundAction(x, y, ACTION_FILLING);
5958       }
5959     }
5960     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5961              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5962     {
5963       InitMovingField(x, y, MV_DOWN);
5964       started_moving = TRUE;
5965
5966       Feld[x][y] = EL_QUICKSAND_FILLING;
5967       Store[x][y] = element;
5968
5969       PlayLevelSoundAction(x, y, ACTION_FILLING);
5970     }
5971     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5972              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5973     {
5974       InitMovingField(x, y, MV_DOWN);
5975       started_moving = TRUE;
5976
5977       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
5978       Store[x][y] = element;
5979
5980       PlayLevelSoundAction(x, y, ACTION_FILLING);
5981     }
5982     else if (element == EL_MAGIC_WALL_FULL)
5983     {
5984       if (IS_FREE(x, y + 1))
5985       {
5986         InitMovingField(x, y, MV_DOWN);
5987         started_moving = TRUE;
5988
5989         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5990         Store[x][y] = EL_CHANGED(Store[x][y]);
5991       }
5992       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5993       {
5994         if (!MovDelay[x][y])
5995           MovDelay[x][y] = TILEY/4 + 1;
5996
5997         if (MovDelay[x][y])
5998         {
5999           MovDelay[x][y]--;
6000           if (MovDelay[x][y])
6001             return;
6002         }
6003
6004         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6005         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6006         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6007         Store[x][y] = 0;
6008       }
6009     }
6010     else if (element == EL_BD_MAGIC_WALL_FULL)
6011     {
6012       if (IS_FREE(x, y + 1))
6013       {
6014         InitMovingField(x, y, MV_DOWN);
6015         started_moving = TRUE;
6016
6017         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6018         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6019       }
6020       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6021       {
6022         if (!MovDelay[x][y])
6023           MovDelay[x][y] = TILEY/4 + 1;
6024
6025         if (MovDelay[x][y])
6026         {
6027           MovDelay[x][y]--;
6028           if (MovDelay[x][y])
6029             return;
6030         }
6031
6032         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6033         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6034         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6035         Store[x][y] = 0;
6036       }
6037     }
6038     else if (element == EL_DC_MAGIC_WALL_FULL)
6039     {
6040       if (IS_FREE(x, y + 1))
6041       {
6042         InitMovingField(x, y, MV_DOWN);
6043         started_moving = TRUE;
6044
6045         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6046         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6047       }
6048       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6049       {
6050         if (!MovDelay[x][y])
6051           MovDelay[x][y] = TILEY/4 + 1;
6052
6053         if (MovDelay[x][y])
6054         {
6055           MovDelay[x][y]--;
6056           if (MovDelay[x][y])
6057             return;
6058         }
6059
6060         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6061         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6062         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6063         Store[x][y] = 0;
6064       }
6065     }
6066     else if ((CAN_PASS_MAGIC_WALL(element) &&
6067               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6068                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6069              (CAN_PASS_DC_MAGIC_WALL(element) &&
6070               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6071
6072     {
6073       InitMovingField(x, y, MV_DOWN);
6074       started_moving = TRUE;
6075
6076       Feld[x][y] =
6077         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6078          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6079          EL_DC_MAGIC_WALL_FILLING);
6080       Store[x][y] = element;
6081     }
6082     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6083     {
6084       SplashAcid(x, y + 1);
6085
6086       InitMovingField(x, y, MV_DOWN);
6087       started_moving = TRUE;
6088
6089       Store[x][y] = EL_ACID;
6090     }
6091     else if (
6092 #if USE_FIX_IMPACT_COLLISION
6093              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6094               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6095 #else
6096              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6097               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6098 #endif
6099              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6100               CAN_FALL(element) && WasJustFalling[x][y] &&
6101               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6102
6103              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6104               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6105               (Feld[x][y + 1] == EL_BLOCKED)))
6106     {
6107       /* this is needed for a special case not covered by calling "Impact()"
6108          from "ContinueMoving()": if an element moves to a tile directly below
6109          another element which was just falling on that tile (which was empty
6110          in the previous frame), the falling element above would just stop
6111          instead of smashing the element below (in previous version, the above
6112          element was just checked for "moving" instead of "falling", resulting
6113          in incorrect smashes caused by horizontal movement of the above
6114          element; also, the case of the player being the element to smash was
6115          simply not covered here... :-/ ) */
6116
6117       CheckCollision[x][y] = 0;
6118       CheckImpact[x][y] = 0;
6119
6120       Impact(x, y);
6121     }
6122     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6123     {
6124       if (MovDir[x][y] == MV_NONE)
6125       {
6126         InitMovingField(x, y, MV_DOWN);
6127         started_moving = TRUE;
6128       }
6129     }
6130     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6131     {
6132       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6133         MovDir[x][y] = MV_DOWN;
6134
6135       InitMovingField(x, y, MV_DOWN);
6136       started_moving = TRUE;
6137     }
6138     else if (element == EL_AMOEBA_DROP)
6139     {
6140       Feld[x][y] = EL_AMOEBA_GROWING;
6141       Store[x][y] = EL_AMOEBA_WET;
6142     }
6143     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6144               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6145              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6146              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6147     {
6148       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6149                                 (IS_FREE(x - 1, y + 1) ||
6150                                  Feld[x - 1][y + 1] == EL_ACID));
6151       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6152                                 (IS_FREE(x + 1, y + 1) ||
6153                                  Feld[x + 1][y + 1] == EL_ACID));
6154       boolean can_fall_any  = (can_fall_left || can_fall_right);
6155       boolean can_fall_both = (can_fall_left && can_fall_right);
6156       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6157
6158 #if USE_NEW_ALL_SLIPPERY
6159       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6160       {
6161         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6162           can_fall_right = FALSE;
6163         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6164           can_fall_left = FALSE;
6165         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6166           can_fall_right = FALSE;
6167         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6168           can_fall_left = FALSE;
6169
6170         can_fall_any  = (can_fall_left || can_fall_right);
6171         can_fall_both = FALSE;
6172       }
6173 #else
6174       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6175       {
6176         if (slippery_type == SLIPPERY_ONLY_LEFT)
6177           can_fall_right = FALSE;
6178         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6179           can_fall_left = FALSE;
6180         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6181           can_fall_right = FALSE;
6182         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6183           can_fall_left = FALSE;
6184
6185         can_fall_any  = (can_fall_left || can_fall_right);
6186         can_fall_both = (can_fall_left && can_fall_right);
6187       }
6188 #endif
6189
6190 #if USE_NEW_ALL_SLIPPERY
6191 #else
6192 #if USE_NEW_SP_SLIPPERY
6193       /* !!! better use the same properties as for custom elements here !!! */
6194       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6195                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6196       {
6197         can_fall_right = FALSE;         /* slip down on left side */
6198         can_fall_both = FALSE;
6199       }
6200 #endif
6201 #endif
6202
6203 #if USE_NEW_ALL_SLIPPERY
6204       if (can_fall_both)
6205       {
6206         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6207           can_fall_right = FALSE;       /* slip down on left side */
6208         else
6209           can_fall_left = !(can_fall_right = RND(2));
6210
6211         can_fall_both = FALSE;
6212       }
6213 #else
6214       if (can_fall_both)
6215       {
6216         if (game.emulation == EMU_BOULDERDASH ||
6217             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6218           can_fall_right = FALSE;       /* slip down on left side */
6219         else
6220           can_fall_left = !(can_fall_right = RND(2));
6221
6222         can_fall_both = FALSE;
6223       }
6224 #endif
6225
6226       if (can_fall_any)
6227       {
6228         /* if not determined otherwise, prefer left side for slipping down */
6229         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6230         started_moving = TRUE;
6231       }
6232     }
6233 #if 0
6234     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6235 #else
6236     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6237 #endif
6238     {
6239       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6240       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6241       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6242       int belt_dir = game.belt_dir[belt_nr];
6243
6244       if ((belt_dir == MV_LEFT  && left_is_free) ||
6245           (belt_dir == MV_RIGHT && right_is_free))
6246       {
6247         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6248
6249         InitMovingField(x, y, belt_dir);
6250         started_moving = TRUE;
6251
6252         Pushed[x][y] = TRUE;
6253         Pushed[nextx][y] = TRUE;
6254
6255         GfxAction[x][y] = ACTION_DEFAULT;
6256       }
6257       else
6258       {
6259         MovDir[x][y] = 0;       /* if element was moving, stop it */
6260       }
6261     }
6262   }
6263
6264   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6265 #if 0
6266   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6267 #else
6268   if (CAN_MOVE(element) && !started_moving)
6269 #endif
6270   {
6271     int move_pattern = element_info[element].move_pattern;
6272     int newx, newy;
6273
6274 #if 0
6275 #if DEBUG
6276     if (MovDir[x][y] == MV_NONE)
6277     {
6278       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6279              x, y, element, element_info[element].token_name);
6280       printf("StartMoving(): This should never happen!\n");
6281     }
6282 #endif
6283 #endif
6284
6285     Moving2Blocked(x, y, &newx, &newy);
6286
6287     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6288       return;
6289
6290     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6291         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6292     {
6293       WasJustMoving[x][y] = 0;
6294       CheckCollision[x][y] = 0;
6295
6296       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6297
6298       if (Feld[x][y] != element)        /* element has changed */
6299         return;
6300     }
6301
6302     if (!MovDelay[x][y])        /* start new movement phase */
6303     {
6304       /* all objects that can change their move direction after each step
6305          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6306
6307       if (element != EL_YAMYAM &&
6308           element != EL_DARK_YAMYAM &&
6309           element != EL_PACMAN &&
6310           !(move_pattern & MV_ANY_DIRECTION) &&
6311           move_pattern != MV_TURNING_LEFT &&
6312           move_pattern != MV_TURNING_RIGHT &&
6313           move_pattern != MV_TURNING_LEFT_RIGHT &&
6314           move_pattern != MV_TURNING_RIGHT_LEFT &&
6315           move_pattern != MV_TURNING_RANDOM)
6316       {
6317         TurnRound(x, y);
6318
6319         if (MovDelay[x][y] && (element == EL_BUG ||
6320                                element == EL_SPACESHIP ||
6321                                element == EL_SP_SNIKSNAK ||
6322                                element == EL_SP_ELECTRON ||
6323                                element == EL_MOLE))
6324           DrawLevelField(x, y);
6325       }
6326     }
6327
6328     if (MovDelay[x][y])         /* wait some time before next movement */
6329     {
6330       MovDelay[x][y]--;
6331
6332       if (element == EL_ROBOT ||
6333           element == EL_YAMYAM ||
6334           element == EL_DARK_YAMYAM)
6335       {
6336         DrawLevelElementAnimationIfNeeded(x, y, element);
6337         PlayLevelSoundAction(x, y, ACTION_WAITING);
6338       }
6339       else if (element == EL_SP_ELECTRON)
6340         DrawLevelElementAnimationIfNeeded(x, y, element);
6341       else if (element == EL_DRAGON)
6342       {
6343         int i;
6344         int dir = MovDir[x][y];
6345         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6346         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6347         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6348                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6349                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6350                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6351         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6352
6353         GfxAction[x][y] = ACTION_ATTACKING;
6354
6355         if (IS_PLAYER(x, y))
6356           DrawPlayerField(x, y);
6357         else
6358           DrawLevelField(x, y);
6359
6360         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6361
6362         for (i = 1; i <= 3; i++)
6363         {
6364           int xx = x + i * dx;
6365           int yy = y + i * dy;
6366           int sx = SCREENX(xx);
6367           int sy = SCREENY(yy);
6368           int flame_graphic = graphic + (i - 1);
6369
6370           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6371             break;
6372
6373           if (MovDelay[x][y])
6374           {
6375             int flamed = MovingOrBlocked2Element(xx, yy);
6376
6377             /* !!! */
6378 #if 0
6379             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6380               Bang(xx, yy);
6381             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6382               RemoveMovingField(xx, yy);
6383             else
6384               RemoveField(xx, yy);
6385 #else
6386             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6387               Bang(xx, yy);
6388             else
6389               RemoveMovingField(xx, yy);
6390 #endif
6391
6392             ChangeDelay[xx][yy] = 0;
6393
6394             Feld[xx][yy] = EL_FLAMES;
6395
6396             if (IN_SCR_FIELD(sx, sy))
6397             {
6398               DrawLevelFieldCrumbledSand(xx, yy);
6399               DrawGraphic(sx, sy, flame_graphic, frame);
6400             }
6401           }
6402           else
6403           {
6404             if (Feld[xx][yy] == EL_FLAMES)
6405               Feld[xx][yy] = EL_EMPTY;
6406             DrawLevelField(xx, yy);
6407           }
6408         }
6409       }
6410
6411       if (MovDelay[x][y])       /* element still has to wait some time */
6412       {
6413         PlayLevelSoundAction(x, y, ACTION_WAITING);
6414
6415         return;
6416       }
6417     }
6418
6419     /* now make next step */
6420
6421     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6422
6423     if (DONT_COLLIDE_WITH(element) &&
6424         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6425         !PLAYER_ENEMY_PROTECTED(newx, newy))
6426     {
6427       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6428
6429       return;
6430     }
6431
6432     else if (CAN_MOVE_INTO_ACID(element) &&
6433              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6434              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6435              (MovDir[x][y] == MV_DOWN ||
6436               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6437     {
6438       SplashAcid(newx, newy);
6439       Store[x][y] = EL_ACID;
6440     }
6441     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6442     {
6443       if (Feld[newx][newy] == EL_EXIT_OPEN ||
6444           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6445           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6446           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6447       {
6448         RemoveField(x, y);
6449         DrawLevelField(x, y);
6450
6451         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6452         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6453           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6454
6455         local_player->friends_still_needed--;
6456         if (!local_player->friends_still_needed &&
6457             !local_player->GameOver && AllPlayersGone)
6458           PlayerWins(local_player);
6459
6460         return;
6461       }
6462       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6463       {
6464         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6465           DrawLevelField(newx, newy);
6466         else
6467           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6468       }
6469       else if (!IS_FREE(newx, newy))
6470       {
6471         GfxAction[x][y] = ACTION_WAITING;
6472
6473         if (IS_PLAYER(x, y))
6474           DrawPlayerField(x, y);
6475         else
6476           DrawLevelField(x, y);
6477
6478         return;
6479       }
6480     }
6481     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6482     {
6483       if (IS_FOOD_PIG(Feld[newx][newy]))
6484       {
6485         if (IS_MOVING(newx, newy))
6486           RemoveMovingField(newx, newy);
6487         else
6488         {
6489           Feld[newx][newy] = EL_EMPTY;
6490           DrawLevelField(newx, newy);
6491         }
6492
6493         PlayLevelSound(x, y, SND_PIG_DIGGING);
6494       }
6495       else if (!IS_FREE(newx, newy))
6496       {
6497         if (IS_PLAYER(x, y))
6498           DrawPlayerField(x, y);
6499         else
6500           DrawLevelField(x, y);
6501
6502         return;
6503       }
6504     }
6505     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6506     {
6507       if (Store[x][y] != EL_EMPTY)
6508       {
6509         boolean can_clone = FALSE;
6510         int xx, yy;
6511
6512         /* check if element to clone is still there */
6513         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6514         {
6515           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6516           {
6517             can_clone = TRUE;
6518
6519             break;
6520           }
6521         }
6522
6523         /* cannot clone or target field not free anymore -- do not clone */
6524         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6525           Store[x][y] = EL_EMPTY;
6526       }
6527
6528       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6529       {
6530         if (IS_MV_DIAGONAL(MovDir[x][y]))
6531         {
6532           int diagonal_move_dir = MovDir[x][y];
6533           int stored = Store[x][y];
6534           int change_delay = 8;
6535           int graphic;
6536
6537           /* android is moving diagonally */
6538
6539           CreateField(x, y, EL_DIAGONAL_SHRINKING);
6540
6541           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6542           GfxElement[x][y] = EL_EMC_ANDROID;
6543           GfxAction[x][y] = ACTION_SHRINKING;
6544           GfxDir[x][y] = diagonal_move_dir;
6545           ChangeDelay[x][y] = change_delay;
6546
6547           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6548                                    GfxDir[x][y]);
6549
6550           DrawLevelGraphicAnimation(x, y, graphic);
6551           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6552
6553           if (Feld[newx][newy] == EL_ACID)
6554           {
6555             SplashAcid(newx, newy);
6556
6557             return;
6558           }
6559
6560           CreateField(newx, newy, EL_DIAGONAL_GROWING);
6561
6562           Store[newx][newy] = EL_EMC_ANDROID;
6563           GfxElement[newx][newy] = EL_EMC_ANDROID;
6564           GfxAction[newx][newy] = ACTION_GROWING;
6565           GfxDir[newx][newy] = diagonal_move_dir;
6566           ChangeDelay[newx][newy] = change_delay;
6567
6568           graphic = el_act_dir2img(GfxElement[newx][newy],
6569                                    GfxAction[newx][newy], GfxDir[newx][newy]);
6570
6571           DrawLevelGraphicAnimation(newx, newy, graphic);
6572           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6573
6574           return;
6575         }
6576         else
6577         {
6578           Feld[newx][newy] = EL_EMPTY;
6579           DrawLevelField(newx, newy);
6580
6581           PlayLevelSoundAction(x, y, ACTION_DIGGING);
6582         }
6583       }
6584       else if (!IS_FREE(newx, newy))
6585       {
6586 #if 0
6587         if (IS_PLAYER(x, y))
6588           DrawPlayerField(x, y);
6589         else
6590           DrawLevelField(x, y);
6591 #endif
6592
6593         return;
6594       }
6595     }
6596     else if (IS_CUSTOM_ELEMENT(element) &&
6597              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6598     {
6599       int new_element = Feld[newx][newy];
6600
6601       if (!IS_FREE(newx, newy))
6602       {
6603         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6604                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6605                       ACTION_BREAKING);
6606
6607         /* no element can dig solid indestructible elements */
6608         if (IS_INDESTRUCTIBLE(new_element) &&
6609             !IS_DIGGABLE(new_element) &&
6610             !IS_COLLECTIBLE(new_element))
6611           return;
6612
6613         if (AmoebaNr[newx][newy] &&
6614             (new_element == EL_AMOEBA_FULL ||
6615              new_element == EL_BD_AMOEBA ||
6616              new_element == EL_AMOEBA_GROWING))
6617         {
6618           AmoebaCnt[AmoebaNr[newx][newy]]--;
6619           AmoebaCnt2[AmoebaNr[newx][newy]]--;
6620         }
6621
6622         if (IS_MOVING(newx, newy))
6623           RemoveMovingField(newx, newy);
6624         else
6625         {
6626           RemoveField(newx, newy);
6627           DrawLevelField(newx, newy);
6628         }
6629
6630         /* if digged element was about to explode, prevent the explosion */
6631         ExplodeField[newx][newy] = EX_TYPE_NONE;
6632
6633         PlayLevelSoundAction(x, y, action);
6634       }
6635
6636       Store[newx][newy] = EL_EMPTY;
6637 #if 1
6638       /* this makes it possible to leave the removed element again */
6639       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6640         Store[newx][newy] = new_element;
6641 #else
6642       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6643       {
6644         int move_leave_element = element_info[element].move_leave_element;
6645
6646         /* this makes it possible to leave the removed element again */
6647         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6648                              new_element : move_leave_element);
6649       }
6650 #endif
6651
6652       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6653       {
6654         RunnerVisit[x][y] = FrameCounter;
6655         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6656       }
6657     }
6658     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6659     {
6660       if (!IS_FREE(newx, newy))
6661       {
6662         if (IS_PLAYER(x, y))
6663           DrawPlayerField(x, y);
6664         else
6665           DrawLevelField(x, y);
6666
6667         return;
6668       }
6669       else
6670       {
6671         boolean wanna_flame = !RND(10);
6672         int dx = newx - x, dy = newy - y;
6673         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6674         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6675         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6676                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6677         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6678                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6679
6680         if ((wanna_flame ||
6681              IS_CLASSIC_ENEMY(element1) ||
6682              IS_CLASSIC_ENEMY(element2)) &&
6683             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6684             element1 != EL_FLAMES && element2 != EL_FLAMES)
6685         {
6686           ResetGfxAnimation(x, y);
6687           GfxAction[x][y] = ACTION_ATTACKING;
6688
6689           if (IS_PLAYER(x, y))
6690             DrawPlayerField(x, y);
6691           else
6692             DrawLevelField(x, y);
6693
6694           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6695
6696           MovDelay[x][y] = 50;
6697
6698           /* !!! */
6699 #if 0
6700           RemoveField(newx, newy);
6701 #endif
6702           Feld[newx][newy] = EL_FLAMES;
6703           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6704           {
6705 #if 0
6706             RemoveField(newx1, newy1);
6707 #endif
6708             Feld[newx1][newy1] = EL_FLAMES;
6709           }
6710           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6711           {
6712 #if 0
6713             RemoveField(newx2, newy2);
6714 #endif
6715             Feld[newx2][newy2] = EL_FLAMES;
6716           }
6717
6718           return;
6719         }
6720       }
6721     }
6722     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6723              Feld[newx][newy] == EL_DIAMOND)
6724     {
6725       if (IS_MOVING(newx, newy))
6726         RemoveMovingField(newx, newy);
6727       else
6728       {
6729         Feld[newx][newy] = EL_EMPTY;
6730         DrawLevelField(newx, newy);
6731       }
6732
6733       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6734     }
6735     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6736              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6737     {
6738       if (AmoebaNr[newx][newy])
6739       {
6740         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6741         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6742             Feld[newx][newy] == EL_BD_AMOEBA)
6743           AmoebaCnt[AmoebaNr[newx][newy]]--;
6744       }
6745
6746 #if 0
6747       /* !!! test !!! */
6748       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6749       {
6750         RemoveMovingField(newx, newy);
6751       }
6752 #else
6753       if (IS_MOVING(newx, newy))
6754       {
6755         RemoveMovingField(newx, newy);
6756       }
6757 #endif
6758       else
6759       {
6760         Feld[newx][newy] = EL_EMPTY;
6761         DrawLevelField(newx, newy);
6762       }
6763
6764       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6765     }
6766     else if ((element == EL_PACMAN || element == EL_MOLE)
6767              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6768     {
6769       if (AmoebaNr[newx][newy])
6770       {
6771         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6772         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6773             Feld[newx][newy] == EL_BD_AMOEBA)
6774           AmoebaCnt[AmoebaNr[newx][newy]]--;
6775       }
6776
6777       if (element == EL_MOLE)
6778       {
6779         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6780         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6781
6782         ResetGfxAnimation(x, y);
6783         GfxAction[x][y] = ACTION_DIGGING;
6784         DrawLevelField(x, y);
6785
6786         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6787
6788         return;                         /* wait for shrinking amoeba */
6789       }
6790       else      /* element == EL_PACMAN */
6791       {
6792         Feld[newx][newy] = EL_EMPTY;
6793         DrawLevelField(newx, newy);
6794         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6795       }
6796     }
6797     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6798              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6799               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6800     {
6801       /* wait for shrinking amoeba to completely disappear */
6802       return;
6803     }
6804     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6805     {
6806       /* object was running against a wall */
6807
6808       TurnRound(x, y);
6809
6810 #if 0
6811       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6812       if (move_pattern & MV_ANY_DIRECTION &&
6813           move_pattern == MovDir[x][y])
6814       {
6815         int blocking_element =
6816           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6817
6818         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6819                                  MovDir[x][y]);
6820
6821         element = Feld[x][y];   /* element might have changed */
6822       }
6823 #endif
6824
6825       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
6826         DrawLevelElementAnimation(x, y, element);
6827
6828       if (DONT_TOUCH(element))
6829         TestIfBadThingTouchesPlayer(x, y);
6830
6831       return;
6832     }
6833
6834     InitMovingField(x, y, MovDir[x][y]);
6835
6836     PlayLevelSoundAction(x, y, ACTION_MOVING);
6837   }
6838
6839   if (MovDir[x][y])
6840     ContinueMoving(x, y);
6841 }
6842
6843 void ContinueMoving(int x, int y)
6844 {
6845   int element = Feld[x][y];
6846   struct ElementInfo *ei = &element_info[element];
6847   int direction = MovDir[x][y];
6848   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6849   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
6850   int newx = x + dx, newy = y + dy;
6851   int stored = Store[x][y];
6852   int stored_new = Store[newx][newy];
6853   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
6854   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6855   boolean last_line = (newy == lev_fieldy - 1);
6856
6857   MovPos[x][y] += getElementMoveStepsize(x, y);
6858
6859   if (pushed_by_player) /* special case: moving object pushed by player */
6860     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6861
6862   if (ABS(MovPos[x][y]) < TILEX)
6863   {
6864 #if 0
6865     int ee = Feld[x][y];
6866     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6867     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
6868
6869     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
6870            x, y, ABS(MovPos[x][y]),
6871            ee, gg, ff,
6872            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
6873 #endif
6874
6875     DrawLevelField(x, y);
6876
6877     return;     /* element is still moving */
6878   }
6879
6880   /* element reached destination field */
6881
6882   Feld[x][y] = EL_EMPTY;
6883   Feld[newx][newy] = element;
6884   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6885
6886   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6887   {
6888     element = Feld[newx][newy] = EL_ACID;
6889   }
6890   else if (element == EL_MOLE)
6891   {
6892     Feld[x][y] = EL_SAND;
6893
6894     DrawLevelFieldCrumbledSandNeighbours(x, y);
6895   }
6896   else if (element == EL_QUICKSAND_FILLING)
6897   {
6898     element = Feld[newx][newy] = get_next_element(element);
6899     Store[newx][newy] = Store[x][y];
6900   }
6901   else if (element == EL_QUICKSAND_EMPTYING)
6902   {
6903     Feld[x][y] = get_next_element(element);
6904     element = Feld[newx][newy] = Store[x][y];
6905   }
6906   else if (element == EL_QUICKSAND_FAST_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_FAST_EMPTYING)
6912   {
6913     Feld[x][y] = get_next_element(element);
6914     element = Feld[newx][newy] = Store[x][y];
6915   }
6916   else if (element == EL_MAGIC_WALL_FILLING)
6917   {
6918     element = Feld[newx][newy] = get_next_element(element);
6919     if (!game.magic_wall_active)
6920       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6921     Store[newx][newy] = Store[x][y];
6922   }
6923   else if (element == EL_MAGIC_WALL_EMPTYING)
6924   {
6925     Feld[x][y] = get_next_element(element);
6926     if (!game.magic_wall_active)
6927       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6928     element = Feld[newx][newy] = Store[x][y];
6929
6930 #if USE_NEW_CUSTOM_VALUE
6931     InitField(newx, newy, FALSE);
6932 #endif
6933   }
6934   else if (element == EL_BD_MAGIC_WALL_FILLING)
6935   {
6936     element = Feld[newx][newy] = get_next_element(element);
6937     if (!game.magic_wall_active)
6938       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6939     Store[newx][newy] = Store[x][y];
6940   }
6941   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6942   {
6943     Feld[x][y] = get_next_element(element);
6944     if (!game.magic_wall_active)
6945       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6946     element = Feld[newx][newy] = Store[x][y];
6947
6948 #if USE_NEW_CUSTOM_VALUE
6949     InitField(newx, newy, FALSE);
6950 #endif
6951   }
6952   else if (element == EL_DC_MAGIC_WALL_FILLING)
6953   {
6954     element = Feld[newx][newy] = get_next_element(element);
6955     if (!game.magic_wall_active)
6956       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
6957     Store[newx][newy] = Store[x][y];
6958   }
6959   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
6960   {
6961     Feld[x][y] = get_next_element(element);
6962     if (!game.magic_wall_active)
6963       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
6964     element = Feld[newx][newy] = Store[x][y];
6965
6966 #if USE_NEW_CUSTOM_VALUE
6967     InitField(newx, newy, FALSE);
6968 #endif
6969   }
6970   else if (element == EL_AMOEBA_DROPPING)
6971   {
6972     Feld[x][y] = get_next_element(element);
6973     element = Feld[newx][newy] = Store[x][y];
6974   }
6975   else if (element == EL_SOKOBAN_OBJECT)
6976   {
6977     if (Back[x][y])
6978       Feld[x][y] = Back[x][y];
6979
6980     if (Back[newx][newy])
6981       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6982
6983     Back[x][y] = Back[newx][newy] = 0;
6984   }
6985
6986   Store[x][y] = EL_EMPTY;
6987   MovPos[x][y] = 0;
6988   MovDir[x][y] = 0;
6989   MovDelay[x][y] = 0;
6990
6991   MovDelay[newx][newy] = 0;
6992
6993   if (CAN_CHANGE_OR_HAS_ACTION(element))
6994   {
6995     /* copy element change control values to new field */
6996     ChangeDelay[newx][newy] = ChangeDelay[x][y];
6997     ChangePage[newx][newy]  = ChangePage[x][y];
6998     ChangeCount[newx][newy] = ChangeCount[x][y];
6999     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7000   }
7001
7002 #if USE_NEW_CUSTOM_VALUE
7003     CustomValue[newx][newy] = CustomValue[x][y];
7004 #endif
7005
7006   ChangeDelay[x][y] = 0;
7007   ChangePage[x][y] = -1;
7008   ChangeCount[x][y] = 0;
7009   ChangeEvent[x][y] = -1;
7010
7011 #if USE_NEW_CUSTOM_VALUE
7012   CustomValue[x][y] = 0;
7013 #endif
7014
7015   /* copy animation control values to new field */
7016   GfxFrame[newx][newy]  = GfxFrame[x][y];
7017   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7018   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7019   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7020
7021   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7022
7023   /* some elements can leave other elements behind after moving */
7024 #if 1
7025   if (ei->move_leave_element != EL_EMPTY &&
7026       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7027       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7028 #else
7029   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7030       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7031       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7032 #endif
7033   {
7034     int move_leave_element = ei->move_leave_element;
7035
7036 #if 1
7037 #if 1
7038     /* this makes it possible to leave the removed element again */
7039     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7040       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7041 #else
7042     /* this makes it possible to leave the removed element again */
7043     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7044       move_leave_element = stored;
7045 #endif
7046 #else
7047     /* this makes it possible to leave the removed element again */
7048     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7049         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7050       move_leave_element = stored;
7051 #endif
7052
7053     Feld[x][y] = move_leave_element;
7054
7055     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7056       MovDir[x][y] = direction;
7057
7058     InitField(x, y, FALSE);
7059
7060     if (GFX_CRUMBLED(Feld[x][y]))
7061       DrawLevelFieldCrumbledSandNeighbours(x, y);
7062
7063     if (ELEM_IS_PLAYER(move_leave_element))
7064       RelocatePlayer(x, y, move_leave_element);
7065   }
7066
7067   /* do this after checking for left-behind element */
7068   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7069
7070   if (!CAN_MOVE(element) ||
7071       (CAN_FALL(element) && direction == MV_DOWN &&
7072        (element == EL_SPRING ||
7073         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7074         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7075     GfxDir[x][y] = MovDir[newx][newy] = 0;
7076
7077   DrawLevelField(x, y);
7078   DrawLevelField(newx, newy);
7079
7080   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7081
7082   /* prevent pushed element from moving on in pushed direction */
7083   if (pushed_by_player && CAN_MOVE(element) &&
7084       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7085       !(element_info[element].move_pattern & direction))
7086     TurnRound(newx, newy);
7087
7088   /* prevent elements on conveyor belt from moving on in last direction */
7089   if (pushed_by_conveyor && CAN_FALL(element) &&
7090       direction & MV_HORIZONTAL)
7091     MovDir[newx][newy] = 0;
7092
7093   if (!pushed_by_player)
7094   {
7095     int nextx = newx + dx, nexty = newy + dy;
7096     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7097
7098     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7099
7100     if (CAN_FALL(element) && direction == MV_DOWN)
7101       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7102
7103     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7104       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7105
7106 #if USE_FIX_IMPACT_COLLISION
7107     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7108       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7109 #endif
7110   }
7111
7112   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7113   {
7114     TestIfBadThingTouchesPlayer(newx, newy);
7115     TestIfBadThingTouchesFriend(newx, newy);
7116
7117     if (!IS_CUSTOM_ELEMENT(element))
7118       TestIfBadThingTouchesOtherBadThing(newx, newy);
7119   }
7120   else if (element == EL_PENGUIN)
7121     TestIfFriendTouchesBadThing(newx, newy);
7122
7123   /* give the player one last chance (one more frame) to move away */
7124   if (CAN_FALL(element) && direction == MV_DOWN &&
7125       (last_line || (!IS_FREE(x, newy + 1) &&
7126                      (!IS_PLAYER(x, newy + 1) ||
7127                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7128     Impact(x, newy);
7129
7130   if (pushed_by_player && !game.use_change_when_pushing_bug)
7131   {
7132     int push_side = MV_DIR_OPPOSITE(direction);
7133     struct PlayerInfo *player = PLAYERINFO(x, y);
7134
7135     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7136                                player->index_bit, push_side);
7137     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7138                                         player->index_bit, push_side);
7139   }
7140
7141   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7142     MovDelay[newx][newy] = 1;
7143
7144   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7145
7146   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7147
7148 #if 0
7149   if (ChangePage[newx][newy] != -1)             /* delayed change */
7150   {
7151     int page = ChangePage[newx][newy];
7152     struct ElementChangeInfo *change = &ei->change_page[page];
7153
7154     ChangePage[newx][newy] = -1;
7155
7156     if (change->can_change)
7157     {
7158       if (ChangeElement(newx, newy, element, page))
7159       {
7160         if (change->post_change_function)
7161           change->post_change_function(newx, newy);
7162       }
7163     }
7164
7165     if (change->has_action)
7166       ExecuteCustomElementAction(newx, newy, element, page);
7167   }
7168 #endif
7169
7170   TestIfElementHitsCustomElement(newx, newy, direction);
7171   TestIfPlayerTouchesCustomElement(newx, newy);
7172   TestIfElementTouchesCustomElement(newx, newy);
7173
7174   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7175       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7176     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7177                              MV_DIR_OPPOSITE(direction));
7178 }
7179
7180 int AmoebeNachbarNr(int ax, int ay)
7181 {
7182   int i;
7183   int element = Feld[ax][ay];
7184   int group_nr = 0;
7185   static int xy[4][2] =
7186   {
7187     { 0, -1 },
7188     { -1, 0 },
7189     { +1, 0 },
7190     { 0, +1 }
7191   };
7192
7193   for (i = 0; i < NUM_DIRECTIONS; i++)
7194   {
7195     int x = ax + xy[i][0];
7196     int y = ay + xy[i][1];
7197
7198     if (!IN_LEV_FIELD(x, y))
7199       continue;
7200
7201     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7202       group_nr = AmoebaNr[x][y];
7203   }
7204
7205   return group_nr;
7206 }
7207
7208 void AmoebenVereinigen(int ax, int ay)
7209 {
7210   int i, x, y, xx, yy;
7211   int new_group_nr = AmoebaNr[ax][ay];
7212   static int xy[4][2] =
7213   {
7214     { 0, -1 },
7215     { -1, 0 },
7216     { +1, 0 },
7217     { 0, +1 }
7218   };
7219
7220   if (new_group_nr == 0)
7221     return;
7222
7223   for (i = 0; i < NUM_DIRECTIONS; i++)
7224   {
7225     x = ax + xy[i][0];
7226     y = ay + xy[i][1];
7227
7228     if (!IN_LEV_FIELD(x, y))
7229       continue;
7230
7231     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7232          Feld[x][y] == EL_BD_AMOEBA ||
7233          Feld[x][y] == EL_AMOEBA_DEAD) &&
7234         AmoebaNr[x][y] != new_group_nr)
7235     {
7236       int old_group_nr = AmoebaNr[x][y];
7237
7238       if (old_group_nr == 0)
7239         return;
7240
7241       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7242       AmoebaCnt[old_group_nr] = 0;
7243       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7244       AmoebaCnt2[old_group_nr] = 0;
7245
7246       SCAN_PLAYFIELD(xx, yy)
7247       {
7248         if (AmoebaNr[xx][yy] == old_group_nr)
7249           AmoebaNr[xx][yy] = new_group_nr;
7250       }
7251     }
7252   }
7253 }
7254
7255 void AmoebeUmwandeln(int ax, int ay)
7256 {
7257   int i, x, y;
7258
7259   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7260   {
7261     int group_nr = AmoebaNr[ax][ay];
7262
7263 #ifdef DEBUG
7264     if (group_nr == 0)
7265     {
7266       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7267       printf("AmoebeUmwandeln(): This should never happen!\n");
7268       return;
7269     }
7270 #endif
7271
7272     SCAN_PLAYFIELD(x, y)
7273     {
7274       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7275       {
7276         AmoebaNr[x][y] = 0;
7277         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7278       }
7279     }
7280
7281     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7282                             SND_AMOEBA_TURNING_TO_GEM :
7283                             SND_AMOEBA_TURNING_TO_ROCK));
7284     Bang(ax, ay);
7285   }
7286   else
7287   {
7288     static int xy[4][2] =
7289     {
7290       { 0, -1 },
7291       { -1, 0 },
7292       { +1, 0 },
7293       { 0, +1 }
7294     };
7295
7296     for (i = 0; i < NUM_DIRECTIONS; i++)
7297     {
7298       x = ax + xy[i][0];
7299       y = ay + xy[i][1];
7300
7301       if (!IN_LEV_FIELD(x, y))
7302         continue;
7303
7304       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7305       {
7306         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7307                               SND_AMOEBA_TURNING_TO_GEM :
7308                               SND_AMOEBA_TURNING_TO_ROCK));
7309         Bang(x, y);
7310       }
7311     }
7312   }
7313 }
7314
7315 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7316 {
7317   int x, y;
7318   int group_nr = AmoebaNr[ax][ay];
7319   boolean done = FALSE;
7320
7321 #ifdef DEBUG
7322   if (group_nr == 0)
7323   {
7324     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7325     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7326     return;
7327   }
7328 #endif
7329
7330   SCAN_PLAYFIELD(x, y)
7331   {
7332     if (AmoebaNr[x][y] == group_nr &&
7333         (Feld[x][y] == EL_AMOEBA_DEAD ||
7334          Feld[x][y] == EL_BD_AMOEBA ||
7335          Feld[x][y] == EL_AMOEBA_GROWING))
7336     {
7337       AmoebaNr[x][y] = 0;
7338       Feld[x][y] = new_element;
7339       InitField(x, y, FALSE);
7340       DrawLevelField(x, y);
7341       done = TRUE;
7342     }
7343   }
7344
7345   if (done)
7346     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7347                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7348                             SND_BD_AMOEBA_TURNING_TO_GEM));
7349 }
7350
7351 void AmoebeWaechst(int x, int y)
7352 {
7353   static unsigned long sound_delay = 0;
7354   static unsigned long sound_delay_value = 0;
7355
7356   if (!MovDelay[x][y])          /* start new growing cycle */
7357   {
7358     MovDelay[x][y] = 7;
7359
7360     if (DelayReached(&sound_delay, sound_delay_value))
7361     {
7362       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7363       sound_delay_value = 30;
7364     }
7365   }
7366
7367   if (MovDelay[x][y])           /* wait some time before growing bigger */
7368   {
7369     MovDelay[x][y]--;
7370     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7371     {
7372       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7373                                            6 - MovDelay[x][y]);
7374
7375       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7376     }
7377
7378     if (!MovDelay[x][y])
7379     {
7380       Feld[x][y] = Store[x][y];
7381       Store[x][y] = 0;
7382       DrawLevelField(x, y);
7383     }
7384   }
7385 }
7386
7387 void AmoebaDisappearing(int x, int y)
7388 {
7389   static unsigned long sound_delay = 0;
7390   static unsigned long sound_delay_value = 0;
7391
7392   if (!MovDelay[x][y])          /* start new shrinking cycle */
7393   {
7394     MovDelay[x][y] = 7;
7395
7396     if (DelayReached(&sound_delay, sound_delay_value))
7397       sound_delay_value = 30;
7398   }
7399
7400   if (MovDelay[x][y])           /* wait some time before shrinking */
7401   {
7402     MovDelay[x][y]--;
7403     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7404     {
7405       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7406                                            6 - MovDelay[x][y]);
7407
7408       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7409     }
7410
7411     if (!MovDelay[x][y])
7412     {
7413       Feld[x][y] = EL_EMPTY;
7414       DrawLevelField(x, y);
7415
7416       /* don't let mole enter this field in this cycle;
7417          (give priority to objects falling to this field from above) */
7418       Stop[x][y] = TRUE;
7419     }
7420   }
7421 }
7422
7423 void AmoebeAbleger(int ax, int ay)
7424 {
7425   int i;
7426   int element = Feld[ax][ay];
7427   int graphic = el2img(element);
7428   int newax = ax, neway = ay;
7429   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7430   static int xy[4][2] =
7431   {
7432     { 0, -1 },
7433     { -1, 0 },
7434     { +1, 0 },
7435     { 0, +1 }
7436   };
7437
7438   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7439   {
7440     Feld[ax][ay] = EL_AMOEBA_DEAD;
7441     DrawLevelField(ax, ay);
7442     return;
7443   }
7444
7445   if (IS_ANIMATED(graphic))
7446     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7447
7448   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7449     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7450
7451   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7452   {
7453     MovDelay[ax][ay]--;
7454     if (MovDelay[ax][ay])
7455       return;
7456   }
7457
7458   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7459   {
7460     int start = RND(4);
7461     int x = ax + xy[start][0];
7462     int y = ay + xy[start][1];
7463
7464     if (!IN_LEV_FIELD(x, y))
7465       return;
7466
7467     if (IS_FREE(x, y) ||
7468         CAN_GROW_INTO(Feld[x][y]) ||
7469         Feld[x][y] == EL_QUICKSAND_EMPTY ||
7470         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7471     {
7472       newax = x;
7473       neway = y;
7474     }
7475
7476     if (newax == ax && neway == ay)
7477       return;
7478   }
7479   else                          /* normal or "filled" (BD style) amoeba */
7480   {
7481     int start = RND(4);
7482     boolean waiting_for_player = FALSE;
7483
7484     for (i = 0; i < NUM_DIRECTIONS; i++)
7485     {
7486       int j = (start + i) % 4;
7487       int x = ax + xy[j][0];
7488       int y = ay + xy[j][1];
7489
7490       if (!IN_LEV_FIELD(x, y))
7491         continue;
7492
7493       if (IS_FREE(x, y) ||
7494           CAN_GROW_INTO(Feld[x][y]) ||
7495           Feld[x][y] == EL_QUICKSAND_EMPTY ||
7496           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7497       {
7498         newax = x;
7499         neway = y;
7500         break;
7501       }
7502       else if (IS_PLAYER(x, y))
7503         waiting_for_player = TRUE;
7504     }
7505
7506     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7507     {
7508       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7509       {
7510         Feld[ax][ay] = EL_AMOEBA_DEAD;
7511         DrawLevelField(ax, ay);
7512         AmoebaCnt[AmoebaNr[ax][ay]]--;
7513
7514         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7515         {
7516           if (element == EL_AMOEBA_FULL)
7517             AmoebeUmwandeln(ax, ay);
7518           else if (element == EL_BD_AMOEBA)
7519             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7520         }
7521       }
7522       return;
7523     }
7524     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7525     {
7526       /* amoeba gets larger by growing in some direction */
7527
7528       int new_group_nr = AmoebaNr[ax][ay];
7529
7530 #ifdef DEBUG
7531   if (new_group_nr == 0)
7532   {
7533     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7534     printf("AmoebeAbleger(): This should never happen!\n");
7535     return;
7536   }
7537 #endif
7538
7539       AmoebaNr[newax][neway] = new_group_nr;
7540       AmoebaCnt[new_group_nr]++;
7541       AmoebaCnt2[new_group_nr]++;
7542
7543       /* if amoeba touches other amoeba(s) after growing, unify them */
7544       AmoebenVereinigen(newax, neway);
7545
7546       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7547       {
7548         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7549         return;
7550       }
7551     }
7552   }
7553
7554   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7555       (neway == lev_fieldy - 1 && newax != ax))
7556   {
7557     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7558     Store[newax][neway] = element;
7559   }
7560   else if (neway == ay || element == EL_EMC_DRIPPER)
7561   {
7562     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7563
7564     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7565   }
7566   else
7567   {
7568     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7569     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7570     Store[ax][ay] = EL_AMOEBA_DROP;
7571     ContinueMoving(ax, ay);
7572     return;
7573   }
7574
7575   DrawLevelField(newax, neway);
7576 }
7577
7578 void Life(int ax, int ay)
7579 {
7580   int x1, y1, x2, y2;
7581   int life_time = 40;
7582   int element = Feld[ax][ay];
7583   int graphic = el2img(element);
7584   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7585                          level.biomaze);
7586   boolean changed = FALSE;
7587
7588   if (IS_ANIMATED(graphic))
7589     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7590
7591   if (Stop[ax][ay])
7592     return;
7593
7594   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7595     MovDelay[ax][ay] = life_time;
7596
7597   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7598   {
7599     MovDelay[ax][ay]--;
7600     if (MovDelay[ax][ay])
7601       return;
7602   }
7603
7604   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7605   {
7606     int xx = ax+x1, yy = ay+y1;
7607     int nachbarn = 0;
7608
7609     if (!IN_LEV_FIELD(xx, yy))
7610       continue;
7611
7612     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7613     {
7614       int x = xx+x2, y = yy+y2;
7615
7616       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7617         continue;
7618
7619       if (((Feld[x][y] == element ||
7620             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7621            !Stop[x][y]) ||
7622           (IS_FREE(x, y) && Stop[x][y]))
7623         nachbarn++;
7624     }
7625
7626     if (xx == ax && yy == ay)           /* field in the middle */
7627     {
7628       if (nachbarn < life_parameter[0] ||
7629           nachbarn > life_parameter[1])
7630       {
7631         Feld[xx][yy] = EL_EMPTY;
7632         if (!Stop[xx][yy])
7633           DrawLevelField(xx, yy);
7634         Stop[xx][yy] = TRUE;
7635         changed = TRUE;
7636       }
7637     }
7638     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7639     {                                   /* free border field */
7640       if (nachbarn >= life_parameter[2] &&
7641           nachbarn <= life_parameter[3])
7642       {
7643         Feld[xx][yy] = element;
7644         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7645         if (!Stop[xx][yy])
7646           DrawLevelField(xx, yy);
7647         Stop[xx][yy] = TRUE;
7648         changed = TRUE;
7649       }
7650     }
7651   }
7652
7653   if (changed)
7654     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7655                    SND_GAME_OF_LIFE_GROWING);
7656 }
7657
7658 static void InitRobotWheel(int x, int y)
7659 {
7660   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7661 }
7662
7663 static void RunRobotWheel(int x, int y)
7664 {
7665   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7666 }
7667
7668 static void StopRobotWheel(int x, int y)
7669 {
7670   if (ZX == x && ZY == y)
7671     ZX = ZY = -1;
7672 }
7673
7674 static void InitTimegateWheel(int x, int y)
7675 {
7676   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7677 }
7678
7679 static void RunTimegateWheel(int x, int y)
7680 {
7681   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7682 }
7683
7684 static void InitMagicBallDelay(int x, int y)
7685 {
7686 #if 1
7687   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7688 #else
7689   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7690 #endif
7691 }
7692
7693 static void ActivateMagicBall(int bx, int by)
7694 {
7695   int x, y;
7696
7697   if (level.ball_random)
7698   {
7699     int pos_border = RND(8);    /* select one of the eight border elements */
7700     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7701     int xx = pos_content % 3;
7702     int yy = pos_content / 3;
7703
7704     x = bx - 1 + xx;
7705     y = by - 1 + yy;
7706
7707     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7708       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7709   }
7710   else
7711   {
7712     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7713     {
7714       int xx = x - bx + 1;
7715       int yy = y - by + 1;
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   }
7721
7722   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7723 }
7724
7725 void CheckExit(int x, int y)
7726 {
7727   if (local_player->gems_still_needed > 0 ||
7728       local_player->sokobanfields_still_needed > 0 ||
7729       local_player->lights_still_needed > 0)
7730   {
7731     int element = Feld[x][y];
7732     int graphic = el2img(element);
7733
7734     if (IS_ANIMATED(graphic))
7735       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7736
7737     return;
7738   }
7739
7740   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7741     return;
7742
7743   Feld[x][y] = EL_EXIT_OPENING;
7744
7745   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7746 }
7747
7748 void CheckExitEM(int x, int y)
7749 {
7750   if (local_player->gems_still_needed > 0 ||
7751       local_player->sokobanfields_still_needed > 0 ||
7752       local_player->lights_still_needed > 0)
7753   {
7754     int element = Feld[x][y];
7755     int graphic = el2img(element);
7756
7757     if (IS_ANIMATED(graphic))
7758       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7759
7760     return;
7761   }
7762
7763   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7764     return;
7765
7766   Feld[x][y] = EL_EM_EXIT_OPENING;
7767
7768   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7769 }
7770
7771 void CheckExitSteel(int x, int y)
7772 {
7773   if (local_player->gems_still_needed > 0 ||
7774       local_player->sokobanfields_still_needed > 0 ||
7775       local_player->lights_still_needed > 0)
7776   {
7777     int element = Feld[x][y];
7778     int graphic = el2img(element);
7779
7780     if (IS_ANIMATED(graphic))
7781       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7782
7783     return;
7784   }
7785
7786   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7787     return;
7788
7789   Feld[x][y] = EL_STEEL_EXIT_OPENING;
7790
7791   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7792 }
7793
7794 void CheckExitSteelEM(int x, int y)
7795 {
7796   if (local_player->gems_still_needed > 0 ||
7797       local_player->sokobanfields_still_needed > 0 ||
7798       local_player->lights_still_needed > 0)
7799   {
7800     int element = Feld[x][y];
7801     int graphic = el2img(element);
7802
7803     if (IS_ANIMATED(graphic))
7804       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7805
7806     return;
7807   }
7808
7809   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7810     return;
7811
7812   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7813
7814   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7815 }
7816
7817 void CheckExitSP(int x, int y)
7818 {
7819   if (local_player->gems_still_needed > 0)
7820   {
7821     int element = Feld[x][y];
7822     int graphic = el2img(element);
7823
7824     if (IS_ANIMATED(graphic))
7825       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7826
7827     return;
7828   }
7829
7830   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7831     return;
7832
7833   Feld[x][y] = EL_SP_EXIT_OPENING;
7834
7835   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7836 }
7837
7838 static void CloseAllOpenTimegates()
7839 {
7840   int x, y;
7841
7842   SCAN_PLAYFIELD(x, y)
7843   {
7844     int element = Feld[x][y];
7845
7846     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7847     {
7848       Feld[x][y] = EL_TIMEGATE_CLOSING;
7849
7850       PlayLevelSoundAction(x, y, ACTION_CLOSING);
7851     }
7852   }
7853 }
7854
7855 void DrawTwinkleOnField(int x, int y)
7856 {
7857   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7858     return;
7859
7860   if (Feld[x][y] == EL_BD_DIAMOND)
7861     return;
7862
7863   if (MovDelay[x][y] == 0)      /* next animation frame */
7864     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7865
7866   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7867   {
7868     MovDelay[x][y]--;
7869
7870     if (setup.direct_draw && MovDelay[x][y])
7871       SetDrawtoField(DRAW_BUFFERED);
7872
7873     DrawLevelElementAnimation(x, y, Feld[x][y]);
7874
7875     if (MovDelay[x][y] != 0)
7876     {
7877       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7878                                            10 - MovDelay[x][y]);
7879
7880       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7881
7882       if (setup.direct_draw)
7883       {
7884         int dest_x, dest_y;
7885
7886         dest_x = FX + SCREENX(x) * TILEX;
7887         dest_y = FY + SCREENY(y) * TILEY;
7888
7889         BlitBitmap(drawto_field, window,
7890                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7891         SetDrawtoField(DRAW_DIRECT);
7892       }
7893     }
7894   }
7895 }
7896
7897 void MauerWaechst(int x, int y)
7898 {
7899   int delay = 6;
7900
7901   if (!MovDelay[x][y])          /* next animation frame */
7902     MovDelay[x][y] = 3 * delay;
7903
7904   if (MovDelay[x][y])           /* wait some time before next frame */
7905   {
7906     MovDelay[x][y]--;
7907
7908     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7909     {
7910       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7911       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7912
7913       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7914     }
7915
7916     if (!MovDelay[x][y])
7917     {
7918       if (MovDir[x][y] == MV_LEFT)
7919       {
7920         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7921           DrawLevelField(x - 1, y);
7922       }
7923       else if (MovDir[x][y] == MV_RIGHT)
7924       {
7925         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7926           DrawLevelField(x + 1, y);
7927       }
7928       else if (MovDir[x][y] == MV_UP)
7929       {
7930         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7931           DrawLevelField(x, y - 1);
7932       }
7933       else
7934       {
7935         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7936           DrawLevelField(x, y + 1);
7937       }
7938
7939       Feld[x][y] = Store[x][y];
7940       Store[x][y] = 0;
7941       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7942       DrawLevelField(x, y);
7943     }
7944   }
7945 }
7946
7947 void MauerAbleger(int ax, int ay)
7948 {
7949   int element = Feld[ax][ay];
7950   int graphic = el2img(element);
7951   boolean oben_frei = FALSE, unten_frei = FALSE;
7952   boolean links_frei = FALSE, rechts_frei = FALSE;
7953   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7954   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7955   boolean new_wall = FALSE;
7956
7957   if (IS_ANIMATED(graphic))
7958     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7959
7960   if (!MovDelay[ax][ay])        /* start building new wall */
7961     MovDelay[ax][ay] = 6;
7962
7963   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7964   {
7965     MovDelay[ax][ay]--;
7966     if (MovDelay[ax][ay])
7967       return;
7968   }
7969
7970   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7971     oben_frei = TRUE;
7972   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7973     unten_frei = TRUE;
7974   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7975     links_frei = TRUE;
7976   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7977     rechts_frei = TRUE;
7978
7979   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7980       element == EL_EXPANDABLE_WALL_ANY)
7981   {
7982     if (oben_frei)
7983     {
7984       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7985       Store[ax][ay-1] = element;
7986       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7987       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7988         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7989                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7990       new_wall = TRUE;
7991     }
7992     if (unten_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_DOWN;
7997       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7998         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7999                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8000       new_wall = TRUE;
8001     }
8002   }
8003
8004   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8005       element == EL_EXPANDABLE_WALL_ANY ||
8006       element == EL_EXPANDABLE_WALL ||
8007       element == EL_BD_EXPANDABLE_WALL)
8008   {
8009     if (links_frei)
8010     {
8011       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8012       Store[ax-1][ay] = element;
8013       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8014       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8015         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8016                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8017       new_wall = TRUE;
8018     }
8019
8020     if (rechts_frei)
8021     {
8022       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8023       Store[ax+1][ay] = element;
8024       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8025       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8026         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8027                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8028       new_wall = TRUE;
8029     }
8030   }
8031
8032   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8033     DrawLevelField(ax, ay);
8034
8035   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8036     oben_massiv = TRUE;
8037   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8038     unten_massiv = TRUE;
8039   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8040     links_massiv = TRUE;
8041   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8042     rechts_massiv = TRUE;
8043
8044   if (((oben_massiv && unten_massiv) ||
8045        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8046        element == EL_EXPANDABLE_WALL) &&
8047       ((links_massiv && rechts_massiv) ||
8048        element == EL_EXPANDABLE_WALL_VERTICAL))
8049     Feld[ax][ay] = EL_WALL;
8050
8051   if (new_wall)
8052     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8053 }
8054
8055 void MauerAblegerStahl(int ax, int ay)
8056 {
8057   int element = Feld[ax][ay];
8058   int graphic = el2img(element);
8059   boolean oben_frei = FALSE, unten_frei = FALSE;
8060   boolean links_frei = FALSE, rechts_frei = FALSE;
8061   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8062   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8063   boolean new_wall = FALSE;
8064
8065   if (IS_ANIMATED(graphic))
8066     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8067
8068   if (!MovDelay[ax][ay])        /* start building new wall */
8069     MovDelay[ax][ay] = 6;
8070
8071   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8072   {
8073     MovDelay[ax][ay]--;
8074     if (MovDelay[ax][ay])
8075       return;
8076   }
8077
8078   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8079     oben_frei = TRUE;
8080   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8081     unten_frei = TRUE;
8082   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8083     links_frei = TRUE;
8084   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8085     rechts_frei = TRUE;
8086
8087   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8088       element == EL_EXPANDABLE_STEELWALL_ANY)
8089   {
8090     if (oben_frei)
8091     {
8092       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8093       Store[ax][ay-1] = element;
8094       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8095       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8096         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8097                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8098       new_wall = TRUE;
8099     }
8100     if (unten_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_DOWN;
8105       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8106         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8107                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8108       new_wall = TRUE;
8109     }
8110   }
8111
8112   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8113       element == EL_EXPANDABLE_STEELWALL_ANY)
8114   {
8115     if (links_frei)
8116     {
8117       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8118       Store[ax-1][ay] = element;
8119       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8120       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8121         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8122                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8123       new_wall = TRUE;
8124     }
8125
8126     if (rechts_frei)
8127     {
8128       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8129       Store[ax+1][ay] = element;
8130       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8131       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8132         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8133                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8134       new_wall = TRUE;
8135     }
8136   }
8137
8138   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8139     oben_massiv = TRUE;
8140   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8141     unten_massiv = TRUE;
8142   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8143     links_massiv = TRUE;
8144   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8145     rechts_massiv = TRUE;
8146
8147   if (((oben_massiv && unten_massiv) ||
8148        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8149       ((links_massiv && rechts_massiv) ||
8150        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8151     Feld[ax][ay] = EL_WALL;
8152
8153   if (new_wall)
8154     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8155 }
8156
8157 void CheckForDragon(int x, int y)
8158 {
8159   int i, j;
8160   boolean dragon_found = FALSE;
8161   static int xy[4][2] =
8162   {
8163     { 0, -1 },
8164     { -1, 0 },
8165     { +1, 0 },
8166     { 0, +1 }
8167   };
8168
8169   for (i = 0; i < NUM_DIRECTIONS; i++)
8170   {
8171     for (j = 0; j < 4; j++)
8172     {
8173       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8174
8175       if (IN_LEV_FIELD(xx, yy) &&
8176           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8177       {
8178         if (Feld[xx][yy] == EL_DRAGON)
8179           dragon_found = TRUE;
8180       }
8181       else
8182         break;
8183     }
8184   }
8185
8186   if (!dragon_found)
8187   {
8188     for (i = 0; i < NUM_DIRECTIONS; i++)
8189     {
8190       for (j = 0; j < 3; j++)
8191       {
8192         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8193   
8194         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8195         {
8196           Feld[xx][yy] = EL_EMPTY;
8197           DrawLevelField(xx, yy);
8198         }
8199         else
8200           break;
8201       }
8202     }
8203   }
8204 }
8205
8206 static void InitBuggyBase(int x, int y)
8207 {
8208   int element = Feld[x][y];
8209   int activating_delay = FRAMES_PER_SECOND / 4;
8210
8211   ChangeDelay[x][y] =
8212     (element == EL_SP_BUGGY_BASE ?
8213      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8214      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8215      activating_delay :
8216      element == EL_SP_BUGGY_BASE_ACTIVE ?
8217      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8218 }
8219
8220 static void WarnBuggyBase(int x, int y)
8221 {
8222   int i;
8223   static int xy[4][2] =
8224   {
8225     { 0, -1 },
8226     { -1, 0 },
8227     { +1, 0 },
8228     { 0, +1 }
8229   };
8230
8231   for (i = 0; i < NUM_DIRECTIONS; i++)
8232   {
8233     int xx = x + xy[i][0];
8234     int yy = y + xy[i][1];
8235
8236     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8237     {
8238       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8239
8240       break;
8241     }
8242   }
8243 }
8244
8245 static void InitTrap(int x, int y)
8246 {
8247   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8248 }
8249
8250 static void ActivateTrap(int x, int y)
8251 {
8252   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8253 }
8254
8255 static void ChangeActiveTrap(int x, int y)
8256 {
8257   int graphic = IMG_TRAP_ACTIVE;
8258
8259   /* if new animation frame was drawn, correct crumbled sand border */
8260   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8261     DrawLevelFieldCrumbledSand(x, y);
8262 }
8263
8264 static int getSpecialActionElement(int element, int number, int base_element)
8265 {
8266   return (element != EL_EMPTY ? element :
8267           number != -1 ? base_element + number - 1 :
8268           EL_EMPTY);
8269 }
8270
8271 static int getModifiedActionNumber(int value_old, int operator, int operand,
8272                                    int value_min, int value_max)
8273 {
8274   int value_new = (operator == CA_MODE_SET      ? operand :
8275                    operator == CA_MODE_ADD      ? value_old + operand :
8276                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8277                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8278                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8279                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8280                    value_old);
8281
8282   return (value_new < value_min ? value_min :
8283           value_new > value_max ? value_max :
8284           value_new);
8285 }
8286
8287 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8288 {
8289   struct ElementInfo *ei = &element_info[element];
8290   struct ElementChangeInfo *change = &ei->change_page[page];
8291   int target_element = change->target_element;
8292   int action_type = change->action_type;
8293   int action_mode = change->action_mode;
8294   int action_arg = change->action_arg;
8295   int i;
8296
8297   if (!change->has_action)
8298     return;
8299
8300   /* ---------- determine action paramater values -------------------------- */
8301
8302   int level_time_value =
8303     (level.time > 0 ? TimeLeft :
8304      TimePlayed);
8305
8306   int action_arg_element =
8307     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8308      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8309      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8310      EL_EMPTY);
8311
8312   int action_arg_direction =
8313     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8314      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8315      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8316      change->actual_trigger_side :
8317      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8318      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8319      MV_NONE);
8320
8321   int action_arg_number_min =
8322     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8323      CA_ARG_MIN);
8324
8325   int action_arg_number_max =
8326     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8327      action_type == CA_SET_LEVEL_GEMS ? 999 :
8328      action_type == CA_SET_LEVEL_TIME ? 9999 :
8329      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8330      action_type == CA_SET_CE_VALUE ? 9999 :
8331      action_type == CA_SET_CE_SCORE ? 9999 :
8332      CA_ARG_MAX);
8333
8334   int action_arg_number_reset =
8335     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8336      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8337      action_type == CA_SET_LEVEL_TIME ? level.time :
8338      action_type == CA_SET_LEVEL_SCORE ? 0 :
8339 #if USE_NEW_CUSTOM_VALUE
8340      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8341 #else
8342      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8343 #endif
8344      action_type == CA_SET_CE_SCORE ? 0 :
8345      0);
8346
8347   int action_arg_number =
8348     (action_arg <= CA_ARG_MAX ? action_arg :
8349      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8350      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8351      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8352      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8353      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8354      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8355 #if USE_NEW_CUSTOM_VALUE
8356      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8357 #else
8358      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8359 #endif
8360      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8361      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8362      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8363      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8364      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8365      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8366      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8367      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8368      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8369      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8370      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8371      -1);
8372
8373   int action_arg_number_old =
8374     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8375      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8376      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8377      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8378      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8379      0);
8380
8381   int action_arg_number_new =
8382     getModifiedActionNumber(action_arg_number_old,
8383                             action_mode, action_arg_number,
8384                             action_arg_number_min, action_arg_number_max);
8385
8386   int trigger_player_bits =
8387     (change->actual_trigger_player >= EL_PLAYER_1 &&
8388      change->actual_trigger_player <= EL_PLAYER_4 ?
8389      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8390      PLAYER_BITS_ANY);
8391
8392   int action_arg_player_bits =
8393     (action_arg >= CA_ARG_PLAYER_1 &&
8394      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8395      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8396      PLAYER_BITS_ANY);
8397
8398   /* ---------- execute action  -------------------------------------------- */
8399
8400   switch (action_type)
8401   {
8402     case CA_NO_ACTION:
8403     {
8404       return;
8405     }
8406
8407     /* ---------- level actions  ------------------------------------------- */
8408
8409     case CA_RESTART_LEVEL:
8410     {
8411       game.restart_level = TRUE;
8412
8413       break;
8414     }
8415
8416     case CA_SHOW_ENVELOPE:
8417     {
8418       int element = getSpecialActionElement(action_arg_element,
8419                                             action_arg_number, EL_ENVELOPE_1);
8420
8421       if (IS_ENVELOPE(element))
8422         local_player->show_envelope = element;
8423
8424       break;
8425     }
8426
8427     case CA_SET_LEVEL_TIME:
8428     {
8429       if (level.time > 0)       /* only modify limited time value */
8430       {
8431         TimeLeft = action_arg_number_new;
8432
8433         DrawGameValue_Time(TimeLeft);
8434
8435         if (!TimeLeft && setup.time_limit)
8436           for (i = 0; i < MAX_PLAYERS; i++)
8437             KillPlayer(&stored_player[i]);
8438       }
8439
8440       break;
8441     }
8442
8443     case CA_SET_LEVEL_SCORE:
8444     {
8445       local_player->score = action_arg_number_new;
8446
8447       DrawGameValue_Score(local_player->score);
8448
8449       break;
8450     }
8451
8452     case CA_SET_LEVEL_GEMS:
8453     {
8454       local_player->gems_still_needed = action_arg_number_new;
8455
8456       DrawGameValue_Emeralds(local_player->gems_still_needed);
8457
8458       break;
8459     }
8460
8461 #if !USE_PLAYER_GRAVITY
8462     case CA_SET_LEVEL_GRAVITY:
8463     {
8464       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
8465                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
8466                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8467                       game.gravity);
8468       break;
8469     }
8470 #endif
8471
8472     case CA_SET_LEVEL_WIND:
8473     {
8474       game.wind_direction = action_arg_direction;
8475
8476       break;
8477     }
8478
8479     /* ---------- player actions  ------------------------------------------ */
8480
8481     case CA_MOVE_PLAYER:
8482     {
8483       /* automatically move to the next field in specified direction */
8484       for (i = 0; i < MAX_PLAYERS; i++)
8485         if (trigger_player_bits & (1 << i))
8486           stored_player[i].programmed_action = action_arg_direction;
8487
8488       break;
8489     }
8490
8491     case CA_EXIT_PLAYER:
8492     {
8493       for (i = 0; i < MAX_PLAYERS; i++)
8494         if (action_arg_player_bits & (1 << i))
8495           PlayerWins(&stored_player[i]);
8496
8497       break;
8498     }
8499
8500     case CA_KILL_PLAYER:
8501     {
8502       for (i = 0; i < MAX_PLAYERS; i++)
8503         if (action_arg_player_bits & (1 << i))
8504           KillPlayer(&stored_player[i]);
8505
8506       break;
8507     }
8508
8509     case CA_SET_PLAYER_KEYS:
8510     {
8511       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8512       int element = getSpecialActionElement(action_arg_element,
8513                                             action_arg_number, EL_KEY_1);
8514
8515       if (IS_KEY(element))
8516       {
8517         for (i = 0; i < MAX_PLAYERS; i++)
8518         {
8519           if (trigger_player_bits & (1 << i))
8520           {
8521             stored_player[i].key[KEY_NR(element)] = key_state;
8522
8523             DrawGameDoorValues();
8524           }
8525         }
8526       }
8527
8528       break;
8529     }
8530
8531     case CA_SET_PLAYER_SPEED:
8532     {
8533       for (i = 0; i < MAX_PLAYERS; i++)
8534       {
8535         if (trigger_player_bits & (1 << i))
8536         {
8537           int move_stepsize = TILEX / stored_player[i].move_delay_value;
8538
8539           if (action_arg == CA_ARG_SPEED_FASTER &&
8540               stored_player[i].cannot_move)
8541           {
8542             action_arg_number = STEPSIZE_VERY_SLOW;
8543           }
8544           else if (action_arg == CA_ARG_SPEED_SLOWER ||
8545                    action_arg == CA_ARG_SPEED_FASTER)
8546           {
8547             action_arg_number = 2;
8548             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8549                            CA_MODE_MULTIPLY);
8550           }
8551           else if (action_arg == CA_ARG_NUMBER_RESET)
8552           {
8553             action_arg_number = level.initial_player_stepsize[i];
8554           }
8555
8556           move_stepsize =
8557             getModifiedActionNumber(move_stepsize,
8558                                     action_mode,
8559                                     action_arg_number,
8560                                     action_arg_number_min,
8561                                     action_arg_number_max);
8562
8563           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8564         }
8565       }
8566
8567       break;
8568     }
8569
8570     case CA_SET_PLAYER_SHIELD:
8571     {
8572       for (i = 0; i < MAX_PLAYERS; i++)
8573       {
8574         if (trigger_player_bits & (1 << i))
8575         {
8576           if (action_arg == CA_ARG_SHIELD_OFF)
8577           {
8578             stored_player[i].shield_normal_time_left = 0;
8579             stored_player[i].shield_deadly_time_left = 0;
8580           }
8581           else if (action_arg == CA_ARG_SHIELD_NORMAL)
8582           {
8583             stored_player[i].shield_normal_time_left = 999999;
8584           }
8585           else if (action_arg == CA_ARG_SHIELD_DEADLY)
8586           {
8587             stored_player[i].shield_normal_time_left = 999999;
8588             stored_player[i].shield_deadly_time_left = 999999;
8589           }
8590         }
8591       }
8592
8593       break;
8594     }
8595
8596 #if USE_PLAYER_GRAVITY
8597     case CA_SET_PLAYER_GRAVITY:
8598     {
8599       for (i = 0; i < MAX_PLAYERS; i++)
8600       {
8601         if (trigger_player_bits & (1 << i))
8602         {
8603           stored_player[i].gravity =
8604             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
8605              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
8606              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8607              stored_player[i].gravity);
8608         }
8609       }
8610
8611       break;
8612     }
8613 #endif
8614
8615     case CA_SET_PLAYER_ARTWORK:
8616     {
8617       for (i = 0; i < MAX_PLAYERS; i++)
8618       {
8619         if (trigger_player_bits & (1 << i))
8620         {
8621           int artwork_element = action_arg_element;
8622
8623           if (action_arg == CA_ARG_ELEMENT_RESET)
8624             artwork_element =
8625               (level.use_artwork_element[i] ? level.artwork_element[i] :
8626                stored_player[i].element_nr);
8627
8628 #if USE_GFX_RESET_PLAYER_ARTWORK
8629           if (stored_player[i].artwork_element != artwork_element)
8630             stored_player[i].Frame = 0;
8631 #endif
8632
8633           stored_player[i].artwork_element = artwork_element;
8634
8635           SetPlayerWaiting(&stored_player[i], FALSE);
8636
8637           /* set number of special actions for bored and sleeping animation */
8638           stored_player[i].num_special_action_bored =
8639             get_num_special_action(artwork_element,
8640                                    ACTION_BORING_1, ACTION_BORING_LAST);
8641           stored_player[i].num_special_action_sleeping =
8642             get_num_special_action(artwork_element,
8643                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8644         }
8645       }
8646
8647       break;
8648     }
8649
8650     /* ---------- CE actions  ---------------------------------------------- */
8651
8652     case CA_SET_CE_VALUE:
8653     {
8654 #if USE_NEW_CUSTOM_VALUE
8655       int last_ce_value = CustomValue[x][y];
8656
8657       CustomValue[x][y] = action_arg_number_new;
8658
8659       if (CustomValue[x][y] != last_ce_value)
8660       {
8661         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8662         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8663
8664         if (CustomValue[x][y] == 0)
8665         {
8666           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8667           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8668         }
8669       }
8670 #endif
8671
8672       break;
8673     }
8674
8675     case CA_SET_CE_SCORE:
8676     {
8677 #if USE_NEW_CUSTOM_VALUE
8678       int last_ce_score = ei->collect_score;
8679
8680       ei->collect_score = action_arg_number_new;
8681
8682       if (ei->collect_score != last_ce_score)
8683       {
8684         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8685         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8686
8687         if (ei->collect_score == 0)
8688         {
8689           int xx, yy;
8690
8691           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8692           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8693
8694           /*
8695             This is a very special case that seems to be a mixture between
8696             CheckElementChange() and CheckTriggeredElementChange(): while
8697             the first one only affects single elements that are triggered
8698             directly, the second one affects multiple elements in the playfield
8699             that are triggered indirectly by another element. This is a third
8700             case: Changing the CE score always affects multiple identical CEs,
8701             so every affected CE must be checked, not only the single CE for
8702             which the CE score was changed in the first place (as every instance
8703             of that CE shares the same CE score, and therefore also can change)!
8704           */
8705           SCAN_PLAYFIELD(xx, yy)
8706           {
8707             if (Feld[xx][yy] == element)
8708               CheckElementChange(xx, yy, element, EL_UNDEFINED,
8709                                  CE_SCORE_GETS_ZERO);
8710           }
8711         }
8712       }
8713 #endif
8714
8715       break;
8716     }
8717
8718     /* ---------- engine actions  ------------------------------------------ */
8719
8720     case CA_SET_ENGINE_SCAN_MODE:
8721     {
8722       InitPlayfieldScanMode(action_arg);
8723
8724       break;
8725     }
8726
8727     default:
8728       break;
8729   }
8730 }
8731
8732 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8733 {
8734   int old_element = Feld[x][y];
8735   int new_element = get_element_from_group_element(element);
8736   int previous_move_direction = MovDir[x][y];
8737 #if USE_NEW_CUSTOM_VALUE
8738   int last_ce_value = CustomValue[x][y];
8739 #endif
8740   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8741   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8742   boolean add_player_onto_element = (new_element_is_player &&
8743 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8744                                      /* this breaks SnakeBite when a snake is
8745                                         halfway through a door that closes */
8746                                      /* NOW FIXED AT LEVEL INIT IN files.c */
8747                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
8748 #endif
8749                                      IS_WALKABLE(old_element));
8750
8751 #if 0
8752   /* check if element under the player changes from accessible to unaccessible
8753      (needed for special case of dropping element which then changes) */
8754   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8755       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8756   {
8757     Bang(x, y);
8758
8759     return;
8760   }
8761 #endif
8762
8763   if (!add_player_onto_element)
8764   {
8765     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8766       RemoveMovingField(x, y);
8767     else
8768       RemoveField(x, y);
8769
8770     Feld[x][y] = new_element;
8771
8772 #if !USE_GFX_RESET_GFX_ANIMATION
8773     ResetGfxAnimation(x, y);
8774     ResetRandomAnimationValue(x, y);
8775 #endif
8776
8777     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8778       MovDir[x][y] = previous_move_direction;
8779
8780 #if USE_NEW_CUSTOM_VALUE
8781     if (element_info[new_element].use_last_ce_value)
8782       CustomValue[x][y] = last_ce_value;
8783 #endif
8784
8785     InitField_WithBug1(x, y, FALSE);
8786
8787     new_element = Feld[x][y];   /* element may have changed */
8788
8789 #if USE_GFX_RESET_GFX_ANIMATION
8790     ResetGfxAnimation(x, y);
8791     ResetRandomAnimationValue(x, y);
8792 #endif
8793
8794     DrawLevelField(x, y);
8795
8796     if (GFX_CRUMBLED(new_element))
8797       DrawLevelFieldCrumbledSandNeighbours(x, y);
8798   }
8799
8800 #if 1
8801   /* check if element under the player changes from accessible to unaccessible
8802      (needed for special case of dropping element which then changes) */
8803   /* (must be checked after creating new element for walkable group elements) */
8804 #if USE_FIX_KILLED_BY_NON_WALKABLE
8805   if (IS_PLAYER(x, y) && !player_explosion_protected &&
8806       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8807   {
8808     Bang(x, y);
8809
8810     return;
8811   }
8812 #else
8813   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8814       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8815   {
8816     Bang(x, y);
8817
8818     return;
8819   }
8820 #endif
8821 #endif
8822
8823   /* "ChangeCount" not set yet to allow "entered by player" change one time */
8824   if (new_element_is_player)
8825     RelocatePlayer(x, y, new_element);
8826
8827   if (is_change)
8828     ChangeCount[x][y]++;        /* count number of changes in the same frame */
8829
8830   TestIfBadThingTouchesPlayer(x, y);
8831   TestIfPlayerTouchesCustomElement(x, y);
8832   TestIfElementTouchesCustomElement(x, y);
8833 }
8834
8835 static void CreateField(int x, int y, int element)
8836 {
8837   CreateFieldExt(x, y, element, FALSE);
8838 }
8839
8840 static void CreateElementFromChange(int x, int y, int element)
8841 {
8842   element = GET_VALID_RUNTIME_ELEMENT(element);
8843
8844 #if USE_STOP_CHANGED_ELEMENTS
8845   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8846   {
8847     int old_element = Feld[x][y];
8848
8849     /* prevent changed element from moving in same engine frame
8850        unless both old and new element can either fall or move */
8851     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8852         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8853       Stop[x][y] = TRUE;
8854   }
8855 #endif
8856
8857   CreateFieldExt(x, y, element, TRUE);
8858 }
8859
8860 static boolean ChangeElement(int x, int y, int element, int page)
8861 {
8862   struct ElementInfo *ei = &element_info[element];
8863   struct ElementChangeInfo *change = &ei->change_page[page];
8864   int ce_value = CustomValue[x][y];
8865   int ce_score = ei->collect_score;
8866   int target_element;
8867   int old_element = Feld[x][y];
8868
8869   /* always use default change event to prevent running into a loop */
8870   if (ChangeEvent[x][y] == -1)
8871     ChangeEvent[x][y] = CE_DELAY;
8872
8873   if (ChangeEvent[x][y] == CE_DELAY)
8874   {
8875     /* reset actual trigger element, trigger player and action element */
8876     change->actual_trigger_element = EL_EMPTY;
8877     change->actual_trigger_player = EL_PLAYER_1;
8878     change->actual_trigger_side = CH_SIDE_NONE;
8879     change->actual_trigger_ce_value = 0;
8880     change->actual_trigger_ce_score = 0;
8881   }
8882
8883   /* do not change elements more than a specified maximum number of changes */
8884   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8885     return FALSE;
8886
8887   ChangeCount[x][y]++;          /* count number of changes in the same frame */
8888
8889   if (change->explode)
8890   {
8891     Bang(x, y);
8892
8893     return TRUE;
8894   }
8895
8896   if (change->use_target_content)
8897   {
8898     boolean complete_replace = TRUE;
8899     boolean can_replace[3][3];
8900     int xx, yy;
8901
8902     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8903     {
8904       boolean is_empty;
8905       boolean is_walkable;
8906       boolean is_diggable;
8907       boolean is_collectible;
8908       boolean is_removable;
8909       boolean is_destructible;
8910       int ex = x + xx - 1;
8911       int ey = y + yy - 1;
8912       int content_element = change->target_content.e[xx][yy];
8913       int e;
8914
8915       can_replace[xx][yy] = TRUE;
8916
8917       if (ex == x && ey == y)   /* do not check changing element itself */
8918         continue;
8919
8920       if (content_element == EL_EMPTY_SPACE)
8921       {
8922         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8923
8924         continue;
8925       }
8926
8927       if (!IN_LEV_FIELD(ex, ey))
8928       {
8929         can_replace[xx][yy] = FALSE;
8930         complete_replace = FALSE;
8931
8932         continue;
8933       }
8934
8935       e = Feld[ex][ey];
8936
8937       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8938         e = MovingOrBlocked2Element(ex, ey);
8939
8940       is_empty = (IS_FREE(ex, ey) ||
8941                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8942
8943       is_walkable     = (is_empty || IS_WALKABLE(e));
8944       is_diggable     = (is_empty || IS_DIGGABLE(e));
8945       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8946       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8947       is_removable    = (is_diggable || is_collectible);
8948
8949       can_replace[xx][yy] =
8950         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
8951           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
8952           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
8953           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
8954           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
8955           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8956          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8957
8958       if (!can_replace[xx][yy])
8959         complete_replace = FALSE;
8960     }
8961
8962     if (!change->only_if_complete || complete_replace)
8963     {
8964       boolean something_has_changed = FALSE;
8965
8966       if (change->only_if_complete && change->use_random_replace &&
8967           RND(100) < change->random_percentage)
8968         return FALSE;
8969
8970       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8971       {
8972         int ex = x + xx - 1;
8973         int ey = y + yy - 1;
8974         int content_element;
8975
8976         if (can_replace[xx][yy] && (!change->use_random_replace ||
8977                                     RND(100) < change->random_percentage))
8978         {
8979           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8980             RemoveMovingField(ex, ey);
8981
8982           ChangeEvent[ex][ey] = ChangeEvent[x][y];
8983
8984           content_element = change->target_content.e[xx][yy];
8985           target_element = GET_TARGET_ELEMENT(element, content_element, change,
8986                                               ce_value, ce_score);
8987
8988           CreateElementFromChange(ex, ey, target_element);
8989
8990           something_has_changed = TRUE;
8991
8992           /* for symmetry reasons, freeze newly created border elements */
8993           if (ex != x || ey != y)
8994             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
8995         }
8996       }
8997
8998       if (something_has_changed)
8999       {
9000         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9001         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9002       }
9003     }
9004   }
9005   else
9006   {
9007     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9008                                         ce_value, ce_score);
9009
9010     if (element == EL_DIAGONAL_GROWING ||
9011         element == EL_DIAGONAL_SHRINKING)
9012     {
9013       target_element = Store[x][y];
9014
9015       Store[x][y] = EL_EMPTY;
9016     }
9017
9018     CreateElementFromChange(x, y, target_element);
9019
9020     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9021     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9022   }
9023
9024   /* this uses direct change before indirect change */
9025   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9026
9027   return TRUE;
9028 }
9029
9030 #if USE_NEW_DELAYED_ACTION
9031
9032 static void HandleElementChange(int x, int y, int page)
9033 {
9034   int element = MovingOrBlocked2Element(x, y);
9035   struct ElementInfo *ei = &element_info[element];
9036   struct ElementChangeInfo *change = &ei->change_page[page];
9037
9038 #ifdef DEBUG
9039   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9040       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9041   {
9042     printf("\n\n");
9043     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9044            x, y, element, element_info[element].token_name);
9045     printf("HandleElementChange(): This should never happen!\n");
9046     printf("\n\n");
9047   }
9048 #endif
9049
9050   /* this can happen with classic bombs on walkable, changing elements */
9051   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9052   {
9053 #if 0
9054     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9055       ChangeDelay[x][y] = 0;
9056 #endif
9057
9058     return;
9059   }
9060
9061   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9062   {
9063     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9064
9065     if (change->can_change)
9066     {
9067 #if 0
9068       /* !!! not clear why graphic animation should be reset at all here !!! */
9069 #if USE_GFX_RESET_WHEN_NOT_MOVING
9070       /* when a custom element is about to change (for example by change delay),
9071          do not reset graphic animation when the custom element is moving */
9072       if (IS_MOVING(x, y))
9073 #endif
9074       {
9075         ResetGfxAnimation(x, y);
9076         ResetRandomAnimationValue(x, y);
9077       }
9078 #endif
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 && !local_player->LevelSolved_GameEnd)
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 }