rnd-20070306-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #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 int GetElementFromGroupElement(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] = GetElementFromGroupElement(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 = GetElementFromGroupElement(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 1
9068       /* !!! not clear why graphic animation should be reset at all here !!! */
9069       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
9070 #if USE_GFX_RESET_WHEN_NOT_MOVING
9071       /* when a custom element is about to change (for example by change delay),
9072          do not reset graphic animation when the custom element is moving */
9073       if (!IS_MOVING(x, y))
9074 #endif
9075       {
9076         ResetGfxAnimation(x, y);
9077         ResetRandomAnimationValue(x, y);
9078       }
9079 #endif
9080
9081       if (change->pre_change_function)
9082         change->pre_change_function(x, y);
9083     }
9084   }
9085
9086   ChangeDelay[x][y]--;
9087
9088   if (ChangeDelay[x][y] != 0)           /* continue element change */
9089   {
9090     if (change->can_change)
9091     {
9092       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9093
9094       if (IS_ANIMATED(graphic))
9095         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9096
9097       if (change->change_function)
9098         change->change_function(x, y);
9099     }
9100   }
9101   else                                  /* finish element change */
9102   {
9103     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9104     {
9105       page = ChangePage[x][y];
9106       ChangePage[x][y] = -1;
9107
9108       change = &ei->change_page[page];
9109     }
9110
9111     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9112     {
9113       ChangeDelay[x][y] = 1;            /* try change after next move step */
9114       ChangePage[x][y] = page;          /* remember page to use for change */
9115
9116       return;
9117     }
9118
9119     if (change->can_change)
9120     {
9121       if (ChangeElement(x, y, element, page))
9122       {
9123         if (change->post_change_function)
9124           change->post_change_function(x, y);
9125       }
9126     }
9127
9128     if (change->has_action)
9129       ExecuteCustomElementAction(x, y, element, page);
9130   }
9131 }
9132
9133 #else
9134
9135 static void HandleElementChange(int x, int y, int page)
9136 {
9137   int element = MovingOrBlocked2Element(x, y);
9138   struct ElementInfo *ei = &element_info[element];
9139   struct ElementChangeInfo *change = &ei->change_page[page];
9140
9141 #ifdef DEBUG
9142   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9143   {
9144     printf("\n\n");
9145     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9146            x, y, element, element_info[element].token_name);
9147     printf("HandleElementChange(): This should never happen!\n");
9148     printf("\n\n");
9149   }
9150 #endif
9151
9152   /* this can happen with classic bombs on walkable, changing elements */
9153   if (!CAN_CHANGE(element))
9154   {
9155 #if 0
9156     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9157       ChangeDelay[x][y] = 0;
9158 #endif
9159
9160     return;
9161   }
9162
9163   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9164   {
9165     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9166
9167     ResetGfxAnimation(x, y);
9168     ResetRandomAnimationValue(x, y);
9169
9170     if (change->pre_change_function)
9171       change->pre_change_function(x, y);
9172   }
9173
9174   ChangeDelay[x][y]--;
9175
9176   if (ChangeDelay[x][y] != 0)           /* continue element change */
9177   {
9178     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9179
9180     if (IS_ANIMATED(graphic))
9181       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9182
9183     if (change->change_function)
9184       change->change_function(x, y);
9185   }
9186   else                                  /* finish element change */
9187   {
9188     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9189     {
9190       page = ChangePage[x][y];
9191       ChangePage[x][y] = -1;
9192
9193       change = &ei->change_page[page];
9194     }
9195
9196     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9197     {
9198       ChangeDelay[x][y] = 1;            /* try change after next move step */
9199       ChangePage[x][y] = page;          /* remember page to use for change */
9200
9201       return;
9202     }
9203
9204     if (ChangeElement(x, y, element, page))
9205     {
9206       if (change->post_change_function)
9207         change->post_change_function(x, y);
9208     }
9209   }
9210 }
9211
9212 #endif
9213
9214 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9215                                               int trigger_element,
9216                                               int trigger_event,
9217                                               int trigger_player,
9218                                               int trigger_side,
9219                                               int trigger_page)
9220 {
9221   boolean change_done_any = FALSE;
9222   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9223   int i;
9224
9225   if (!(trigger_events[trigger_element][trigger_event]))
9226     return FALSE;
9227
9228 #if 0
9229   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9230          trigger_event, recursion_loop_depth, recursion_loop_detected,
9231          recursion_loop_element, EL_NAME(recursion_loop_element));
9232 #endif
9233
9234   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9235
9236   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9237   {
9238     int element = EL_CUSTOM_START + i;
9239     boolean change_done = FALSE;
9240     int p;
9241
9242     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9243         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9244       continue;
9245
9246     for (p = 0; p < element_info[element].num_change_pages; p++)
9247     {
9248       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9249
9250       if (change->can_change_or_has_action &&
9251           change->has_event[trigger_event] &&
9252           change->trigger_side & trigger_side &&
9253           change->trigger_player & trigger_player &&
9254           change->trigger_page & trigger_page_bits &&
9255           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9256       {
9257         change->actual_trigger_element = trigger_element;
9258         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9259         change->actual_trigger_side = trigger_side;
9260         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9261         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9262
9263         if ((change->can_change && !change_done) || change->has_action)
9264         {
9265           int x, y;
9266
9267           SCAN_PLAYFIELD(x, y)
9268           {
9269             if (Feld[x][y] == element)
9270             {
9271               if (change->can_change && !change_done)
9272               {
9273                 ChangeDelay[x][y] = 1;
9274                 ChangeEvent[x][y] = trigger_event;
9275
9276                 HandleElementChange(x, y, p);
9277               }
9278 #if USE_NEW_DELAYED_ACTION
9279               else if (change->has_action)
9280               {
9281                 ExecuteCustomElementAction(x, y, element, p);
9282                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9283               }
9284 #else
9285               if (change->has_action)
9286               {
9287                 ExecuteCustomElementAction(x, y, element, p);
9288                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9289               }
9290 #endif
9291             }
9292           }
9293
9294           if (change->can_change)
9295           {
9296             change_done = TRUE;
9297             change_done_any = TRUE;
9298           }
9299         }
9300       }
9301     }
9302   }
9303
9304   RECURSION_LOOP_DETECTION_END();
9305
9306   return change_done_any;
9307 }
9308
9309 static boolean CheckElementChangeExt(int x, int y,
9310                                      int element,
9311                                      int trigger_element,
9312                                      int trigger_event,
9313                                      int trigger_player,
9314                                      int trigger_side)
9315 {
9316   boolean change_done = FALSE;
9317   int p;
9318
9319   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9320       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9321     return FALSE;
9322
9323   if (Feld[x][y] == EL_BLOCKED)
9324   {
9325     Blocked2Moving(x, y, &x, &y);
9326     element = Feld[x][y];
9327   }
9328
9329 #if 0
9330   /* check if element has already changed */
9331   if (Feld[x][y] != element)
9332     return FALSE;
9333 #else
9334   /* check if element has already changed or is about to change after moving */
9335   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9336        Feld[x][y] != element) ||
9337
9338       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9339        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9340         ChangePage[x][y] != -1)))
9341     return FALSE;
9342 #endif
9343
9344 #if 0
9345   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9346          trigger_event, recursion_loop_depth, recursion_loop_detected,
9347          recursion_loop_element, EL_NAME(recursion_loop_element));
9348 #endif
9349
9350   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9351
9352   for (p = 0; p < element_info[element].num_change_pages; p++)
9353   {
9354     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9355
9356     /* check trigger element for all events where the element that is checked
9357        for changing interacts with a directly adjacent element -- this is
9358        different to element changes that affect other elements to change on the
9359        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9360     boolean check_trigger_element =
9361       (trigger_event == CE_TOUCHING_X ||
9362        trigger_event == CE_HITTING_X ||
9363        trigger_event == CE_HIT_BY_X ||
9364 #if 1
9365        /* this one was forgotten until 3.2.3 */
9366        trigger_event == CE_DIGGING_X);
9367 #endif
9368
9369     if (change->can_change_or_has_action &&
9370         change->has_event[trigger_event] &&
9371         change->trigger_side & trigger_side &&
9372         change->trigger_player & trigger_player &&
9373         (!check_trigger_element ||
9374          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9375     {
9376       change->actual_trigger_element = trigger_element;
9377       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9378       change->actual_trigger_side = trigger_side;
9379       change->actual_trigger_ce_value = CustomValue[x][y];
9380       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9381
9382       /* special case: trigger element not at (x,y) position for some events */
9383       if (check_trigger_element)
9384       {
9385         static struct
9386         {
9387           int dx, dy;
9388         } move_xy[] =
9389           {
9390             {  0,  0 },
9391             { -1,  0 },
9392             { +1,  0 },
9393             {  0,  0 },
9394             {  0, -1 },
9395             {  0,  0 }, { 0, 0 }, { 0, 0 },
9396             {  0, +1 }
9397           };
9398
9399         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9400         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9401
9402         change->actual_trigger_ce_value = CustomValue[xx][yy];
9403         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9404       }
9405
9406       if (change->can_change && !change_done)
9407       {
9408         ChangeDelay[x][y] = 1;
9409         ChangeEvent[x][y] = trigger_event;
9410
9411         HandleElementChange(x, y, p);
9412
9413         change_done = TRUE;
9414       }
9415 #if USE_NEW_DELAYED_ACTION
9416       else if (change->has_action)
9417       {
9418         ExecuteCustomElementAction(x, y, element, p);
9419         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9420       }
9421 #else
9422       if (change->has_action)
9423       {
9424         ExecuteCustomElementAction(x, y, element, p);
9425         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9426       }
9427 #endif
9428     }
9429   }
9430
9431   RECURSION_LOOP_DETECTION_END();
9432
9433   return change_done;
9434 }
9435
9436 static void PlayPlayerSound(struct PlayerInfo *player)
9437 {
9438   int jx = player->jx, jy = player->jy;
9439   int sound_element = player->artwork_element;
9440   int last_action = player->last_action_waiting;
9441   int action = player->action_waiting;
9442
9443   if (player->is_waiting)
9444   {
9445     if (action != last_action)
9446       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9447     else
9448       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9449   }
9450   else
9451   {
9452     if (action != last_action)
9453       StopSound(element_info[sound_element].sound[last_action]);
9454
9455     if (last_action == ACTION_SLEEPING)
9456       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9457   }
9458 }
9459
9460 static void PlayAllPlayersSound()
9461 {
9462   int i;
9463
9464   for (i = 0; i < MAX_PLAYERS; i++)
9465     if (stored_player[i].active)
9466       PlayPlayerSound(&stored_player[i]);
9467 }
9468
9469 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9470 {
9471   boolean last_waiting = player->is_waiting;
9472   int move_dir = player->MovDir;
9473
9474   player->dir_waiting = move_dir;
9475   player->last_action_waiting = player->action_waiting;
9476
9477   if (is_waiting)
9478   {
9479     if (!last_waiting)          /* not waiting -> waiting */
9480     {
9481       player->is_waiting = TRUE;
9482
9483       player->frame_counter_bored =
9484         FrameCounter +
9485         game.player_boring_delay_fixed +
9486         GetSimpleRandom(game.player_boring_delay_random);
9487       player->frame_counter_sleeping =
9488         FrameCounter +
9489         game.player_sleeping_delay_fixed +
9490         GetSimpleRandom(game.player_sleeping_delay_random);
9491
9492       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9493     }
9494
9495     if (game.player_sleeping_delay_fixed +
9496         game.player_sleeping_delay_random > 0 &&
9497         player->anim_delay_counter == 0 &&
9498         player->post_delay_counter == 0 &&
9499         FrameCounter >= player->frame_counter_sleeping)
9500       player->is_sleeping = TRUE;
9501     else if (game.player_boring_delay_fixed +
9502              game.player_boring_delay_random > 0 &&
9503              FrameCounter >= player->frame_counter_bored)
9504       player->is_bored = TRUE;
9505
9506     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9507                               player->is_bored ? ACTION_BORING :
9508                               ACTION_WAITING);
9509
9510     if (player->is_sleeping && player->use_murphy)
9511     {
9512       /* special case for sleeping Murphy when leaning against non-free tile */
9513
9514       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9515           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9516            !IS_MOVING(player->jx - 1, player->jy)))
9517         move_dir = MV_LEFT;
9518       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9519                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9520                 !IS_MOVING(player->jx + 1, player->jy)))
9521         move_dir = MV_RIGHT;
9522       else
9523         player->is_sleeping = FALSE;
9524
9525       player->dir_waiting = move_dir;
9526     }
9527
9528     if (player->is_sleeping)
9529     {
9530       if (player->num_special_action_sleeping > 0)
9531       {
9532         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9533         {
9534           int last_special_action = player->special_action_sleeping;
9535           int num_special_action = player->num_special_action_sleeping;
9536           int special_action =
9537             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9538              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9539              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9540              last_special_action + 1 : ACTION_SLEEPING);
9541           int special_graphic =
9542             el_act_dir2img(player->artwork_element, special_action, move_dir);
9543
9544           player->anim_delay_counter =
9545             graphic_info[special_graphic].anim_delay_fixed +
9546             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9547           player->post_delay_counter =
9548             graphic_info[special_graphic].post_delay_fixed +
9549             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9550
9551           player->special_action_sleeping = special_action;
9552         }
9553
9554         if (player->anim_delay_counter > 0)
9555         {
9556           player->action_waiting = player->special_action_sleeping;
9557           player->anim_delay_counter--;
9558         }
9559         else if (player->post_delay_counter > 0)
9560         {
9561           player->post_delay_counter--;
9562         }
9563       }
9564     }
9565     else if (player->is_bored)
9566     {
9567       if (player->num_special_action_bored > 0)
9568       {
9569         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9570         {
9571           int special_action =
9572             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9573           int special_graphic =
9574             el_act_dir2img(player->artwork_element, special_action, move_dir);
9575
9576           player->anim_delay_counter =
9577             graphic_info[special_graphic].anim_delay_fixed +
9578             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9579           player->post_delay_counter =
9580             graphic_info[special_graphic].post_delay_fixed +
9581             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9582
9583           player->special_action_bored = special_action;
9584         }
9585
9586         if (player->anim_delay_counter > 0)
9587         {
9588           player->action_waiting = player->special_action_bored;
9589           player->anim_delay_counter--;
9590         }
9591         else if (player->post_delay_counter > 0)
9592         {
9593           player->post_delay_counter--;
9594         }
9595       }
9596     }
9597   }
9598   else if (last_waiting)        /* waiting -> not waiting */
9599   {
9600     player->is_waiting = FALSE;
9601     player->is_bored = FALSE;
9602     player->is_sleeping = FALSE;
9603
9604     player->frame_counter_bored = -1;
9605     player->frame_counter_sleeping = -1;
9606
9607     player->anim_delay_counter = 0;
9608     player->post_delay_counter = 0;
9609
9610     player->dir_waiting = player->MovDir;
9611     player->action_waiting = ACTION_DEFAULT;
9612
9613     player->special_action_bored = ACTION_DEFAULT;
9614     player->special_action_sleeping = ACTION_DEFAULT;
9615   }
9616 }
9617
9618 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9619 {
9620   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9621   int left      = player_action & JOY_LEFT;
9622   int right     = player_action & JOY_RIGHT;
9623   int up        = player_action & JOY_UP;
9624   int down      = player_action & JOY_DOWN;
9625   int button1   = player_action & JOY_BUTTON_1;
9626   int button2   = player_action & JOY_BUTTON_2;
9627   int dx        = (left ? -1 : right ? 1 : 0);
9628   int dy        = (up   ? -1 : down  ? 1 : 0);
9629
9630   if (!player->active || tape.pausing)
9631     return 0;
9632
9633   if (player_action)
9634   {
9635     if (button1)
9636       snapped = SnapField(player, dx, dy);
9637     else
9638     {
9639       if (button2)
9640         dropped = DropElement(player);
9641
9642       moved = MovePlayer(player, dx, dy);
9643     }
9644
9645     if (tape.single_step && tape.recording && !tape.pausing)
9646     {
9647       if (button1 || (dropped && !moved))
9648       {
9649         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9650         SnapField(player, 0, 0);                /* stop snapping */
9651       }
9652     }
9653
9654     SetPlayerWaiting(player, FALSE);
9655
9656     return player_action;
9657   }
9658   else
9659   {
9660     /* no actions for this player (no input at player's configured device) */
9661
9662     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9663     SnapField(player, 0, 0);
9664     CheckGravityMovementWhenNotMoving(player);
9665
9666     if (player->MovPos == 0)
9667       SetPlayerWaiting(player, TRUE);
9668
9669     if (player->MovPos == 0)    /* needed for tape.playing */
9670       player->is_moving = FALSE;
9671
9672     player->is_dropping = FALSE;
9673     player->is_dropping_pressed = FALSE;
9674     player->drop_pressed_delay = 0;
9675
9676     return 0;
9677   }
9678 }
9679
9680 static void CheckLevelTime()
9681 {
9682   int i;
9683
9684   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9685   {
9686     if (level.native_em_level->lev->home == 0)  /* all players at home */
9687     {
9688       PlayerWins(local_player);
9689
9690       AllPlayersGone = TRUE;
9691
9692       level.native_em_level->lev->home = -1;
9693     }
9694
9695     if (level.native_em_level->ply[0]->alive == 0 &&
9696         level.native_em_level->ply[1]->alive == 0 &&
9697         level.native_em_level->ply[2]->alive == 0 &&
9698         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9699       AllPlayersGone = TRUE;
9700   }
9701
9702   if (TimeFrames >= FRAMES_PER_SECOND)
9703   {
9704     TimeFrames = 0;
9705     TapeTime++;
9706
9707     for (i = 0; i < MAX_PLAYERS; i++)
9708     {
9709       struct PlayerInfo *player = &stored_player[i];
9710
9711       if (SHIELD_ON(player))
9712       {
9713         player->shield_normal_time_left--;
9714
9715         if (player->shield_deadly_time_left > 0)
9716           player->shield_deadly_time_left--;
9717       }
9718     }
9719
9720     if (!local_player->LevelSolved && !level.use_step_counter)
9721     {
9722       TimePlayed++;
9723
9724       if (TimeLeft > 0)
9725       {
9726         TimeLeft--;
9727
9728         if (TimeLeft <= 10 && setup.time_limit)
9729           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9730
9731         DrawGameValue_Time(TimeLeft);
9732
9733         if (!TimeLeft && setup.time_limit)
9734         {
9735           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9736             level.native_em_level->lev->killed_out_of_time = TRUE;
9737           else
9738             for (i = 0; i < MAX_PLAYERS; i++)
9739               KillPlayer(&stored_player[i]);
9740         }
9741       }
9742       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9743         DrawGameValue_Time(TimePlayed);
9744
9745       level.native_em_level->lev->time =
9746         (level.time == 0 ? TimePlayed : TimeLeft);
9747     }
9748
9749     if (tape.recording || tape.playing)
9750       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9751   }
9752 }
9753
9754 void AdvanceFrameAndPlayerCounters(int player_nr)
9755 {
9756   int i;
9757
9758   /* advance frame counters (global frame counter and time frame counter) */
9759   FrameCounter++;
9760   TimeFrames++;
9761
9762   /* advance player counters (counters for move delay, move animation etc.) */
9763   for (i = 0; i < MAX_PLAYERS; i++)
9764   {
9765     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9766     int move_delay_value = stored_player[i].move_delay_value;
9767     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9768
9769     if (!advance_player_counters)       /* not all players may be affected */
9770       continue;
9771
9772 #if USE_NEW_PLAYER_ANIM
9773     if (move_frames == 0)       /* less than one move per game frame */
9774     {
9775       int stepsize = TILEX / move_delay_value;
9776       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9777       int count = (stored_player[i].is_moving ?
9778                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9779
9780       if (count % delay == 0)
9781         move_frames = 1;
9782     }
9783 #endif
9784
9785     stored_player[i].Frame += move_frames;
9786
9787     if (stored_player[i].MovPos != 0)
9788       stored_player[i].StepFrame += move_frames;
9789
9790     if (stored_player[i].move_delay > 0)
9791       stored_player[i].move_delay--;
9792
9793     /* due to bugs in previous versions, counter must count up, not down */
9794     if (stored_player[i].push_delay != -1)
9795       stored_player[i].push_delay++;
9796
9797     if (stored_player[i].drop_delay > 0)
9798       stored_player[i].drop_delay--;
9799
9800     if (stored_player[i].is_dropping_pressed)
9801       stored_player[i].drop_pressed_delay++;
9802   }
9803 }
9804
9805 void StartGameActions(boolean init_network_game, boolean record_tape,
9806                       long random_seed)
9807 {
9808   unsigned long new_random_seed = InitRND(random_seed);
9809
9810   if (record_tape)
9811     TapeStartRecording(new_random_seed);
9812
9813 #if defined(NETWORK_AVALIABLE)
9814   if (init_network_game)
9815   {
9816     SendToServer_StartPlaying();
9817
9818     return;
9819   }
9820 #endif
9821
9822   InitGame();
9823 }
9824
9825 void GameActions()
9826 {
9827   static unsigned long game_frame_delay = 0;
9828   unsigned long game_frame_delay_value;
9829   byte *recorded_player_action;
9830   byte summarized_player_action = 0;
9831   byte tape_action[MAX_PLAYERS];
9832   int i;
9833
9834   /* detect endless loops, caused by custom element programming */
9835   if (recursion_loop_detected && recursion_loop_depth == 0)
9836   {
9837     char *message = getStringCat3("Internal Error ! Element ",
9838                                   EL_NAME(recursion_loop_element),
9839                                   " caused endless loop ! Quit the game ?");
9840
9841     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9842           EL_NAME(recursion_loop_element));
9843
9844     RequestQuitGameExt(FALSE, level_editor_test_game, message);
9845
9846     recursion_loop_detected = FALSE;    /* if game should be continued */
9847
9848     free(message);
9849
9850     return;
9851   }
9852
9853   if (game.restart_level)
9854     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9855
9856   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9857   {
9858     if (level.native_em_level->lev->home == 0)  /* all players at home */
9859     {
9860       PlayerWins(local_player);
9861
9862       AllPlayersGone = TRUE;
9863
9864       level.native_em_level->lev->home = -1;
9865     }
9866
9867     if (level.native_em_level->ply[0]->alive == 0 &&
9868         level.native_em_level->ply[1]->alive == 0 &&
9869         level.native_em_level->ply[2]->alive == 0 &&
9870         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9871       AllPlayersGone = TRUE;
9872   }
9873
9874   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
9875     GameWon();
9876
9877   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9878     TapeStop();
9879
9880   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9881     return;
9882
9883   game_frame_delay_value =
9884     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9885
9886   if (tape.playing && tape.warp_forward && !tape.pausing)
9887     game_frame_delay_value = 0;
9888
9889   /* ---------- main game synchronization point ---------- */
9890
9891   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9892
9893   if (network_playing && !network_player_action_received)
9894   {
9895     /* try to get network player actions in time */
9896
9897 #if defined(NETWORK_AVALIABLE)
9898     /* last chance to get network player actions without main loop delay */
9899     HandleNetworking();
9900 #endif
9901
9902     /* game was quit by network peer */
9903     if (game_status != GAME_MODE_PLAYING)
9904       return;
9905
9906     if (!network_player_action_received)
9907       return;           /* failed to get network player actions in time */
9908
9909     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9910   }
9911
9912   if (tape.pausing)
9913     return;
9914
9915   /* at this point we know that we really continue executing the game */
9916
9917   network_player_action_received = FALSE;
9918
9919   /* when playing tape, read previously recorded player input from tape data */
9920   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9921
9922 #if 1
9923   /* TapePlayAction() may return NULL when toggling to "pause before death" */
9924   if (tape.pausing)
9925     return;
9926 #endif
9927
9928   if (tape.set_centered_player)
9929   {
9930     game.centered_player_nr_next = tape.centered_player_nr_next;
9931     game.set_centered_player = TRUE;
9932   }
9933
9934   for (i = 0; i < MAX_PLAYERS; i++)
9935   {
9936     summarized_player_action |= stored_player[i].action;
9937
9938     if (!network_playing)
9939       stored_player[i].effective_action = stored_player[i].action;
9940   }
9941
9942 #if defined(NETWORK_AVALIABLE)
9943   if (network_playing)
9944     SendToServer_MovePlayer(summarized_player_action);
9945 #endif
9946
9947   if (!options.network && !setup.team_mode)
9948     local_player->effective_action = summarized_player_action;
9949
9950   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9951   {
9952     for (i = 0; i < MAX_PLAYERS; i++)
9953       stored_player[i].effective_action =
9954         (i == game.centered_player_nr ? summarized_player_action : 0);
9955   }
9956
9957   if (recorded_player_action != NULL)
9958     for (i = 0; i < MAX_PLAYERS; i++)
9959       stored_player[i].effective_action = recorded_player_action[i];
9960
9961   for (i = 0; i < MAX_PLAYERS; i++)
9962   {
9963     tape_action[i] = stored_player[i].effective_action;
9964
9965     /* (this can only happen in the R'n'D game engine) */
9966     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9967       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9968   }
9969
9970   /* only record actions from input devices, but not programmed actions */
9971   if (tape.recording)
9972     TapeRecordAction(tape_action);
9973
9974   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9975   {
9976     GameActions_EM_Main();
9977   }
9978   else
9979   {
9980     GameActions_RND();
9981   }
9982 }
9983
9984 void GameActions_EM_Main()
9985 {
9986   byte effective_action[MAX_PLAYERS];
9987   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9988   int i;
9989
9990   for (i = 0; i < MAX_PLAYERS; i++)
9991     effective_action[i] = stored_player[i].effective_action;
9992
9993   GameActions_EM(effective_action, warp_mode);
9994
9995   CheckLevelTime();
9996
9997   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9998 }
9999
10000 void GameActions_RND()
10001 {
10002   int magic_wall_x = 0, magic_wall_y = 0;
10003   int i, x, y, element, graphic;
10004
10005   InitPlayfieldScanModeVars();
10006
10007 #if USE_ONE_MORE_CHANGE_PER_FRAME
10008   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10009   {
10010     SCAN_PLAYFIELD(x, y)
10011     {
10012       ChangeCount[x][y] = 0;
10013       ChangeEvent[x][y] = -1;
10014     }
10015   }
10016 #endif
10017
10018   if (game.set_centered_player)
10019   {
10020     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10021
10022     /* switching to "all players" only possible if all players fit to screen */
10023     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10024     {
10025       game.centered_player_nr_next = game.centered_player_nr;
10026       game.set_centered_player = FALSE;
10027     }
10028
10029     /* do not switch focus to non-existing (or non-active) player */
10030     if (game.centered_player_nr_next >= 0 &&
10031         !stored_player[game.centered_player_nr_next].active)
10032     {
10033       game.centered_player_nr_next = game.centered_player_nr;
10034       game.set_centered_player = FALSE;
10035     }
10036   }
10037
10038   if (game.set_centered_player &&
10039       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10040   {
10041     int sx, sy;
10042
10043     if (game.centered_player_nr_next == -1)
10044     {
10045       setScreenCenteredToAllPlayers(&sx, &sy);
10046     }
10047     else
10048     {
10049       sx = stored_player[game.centered_player_nr_next].jx;
10050       sy = stored_player[game.centered_player_nr_next].jy;
10051     }
10052
10053     game.centered_player_nr = game.centered_player_nr_next;
10054     game.set_centered_player = FALSE;
10055
10056     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10057     DrawGameDoorValues();
10058   }
10059
10060   for (i = 0; i < MAX_PLAYERS; i++)
10061   {
10062     int actual_player_action = stored_player[i].effective_action;
10063
10064 #if 1
10065     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10066        - rnd_equinox_tetrachloride 048
10067        - rnd_equinox_tetrachloride_ii 096
10068        - rnd_emanuel_schmieg 002
10069        - doctor_sloan_ww 001, 020
10070     */
10071     if (stored_player[i].MovPos == 0)
10072       CheckGravityMovement(&stored_player[i]);
10073 #endif
10074
10075     /* overwrite programmed action with tape action */
10076     if (stored_player[i].programmed_action)
10077       actual_player_action = stored_player[i].programmed_action;
10078
10079     PlayerActions(&stored_player[i], actual_player_action);
10080
10081     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10082   }
10083
10084   ScrollScreen(NULL, SCROLL_GO_ON);
10085
10086   /* for backwards compatibility, the following code emulates a fixed bug that
10087      occured when pushing elements (causing elements that just made their last
10088      pushing step to already (if possible) make their first falling step in the
10089      same game frame, which is bad); this code is also needed to use the famous
10090      "spring push bug" which is used in older levels and might be wanted to be
10091      used also in newer levels, but in this case the buggy pushing code is only
10092      affecting the "spring" element and no other elements */
10093
10094   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10095   {
10096     for (i = 0; i < MAX_PLAYERS; i++)
10097     {
10098       struct PlayerInfo *player = &stored_player[i];
10099       int x = player->jx;
10100       int y = player->jy;
10101
10102       if (player->active && player->is_pushing && player->is_moving &&
10103           IS_MOVING(x, y) &&
10104           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10105            Feld[x][y] == EL_SPRING))
10106       {
10107         ContinueMoving(x, y);
10108
10109         /* continue moving after pushing (this is actually a bug) */
10110         if (!IS_MOVING(x, y))
10111           Stop[x][y] = FALSE;
10112       }
10113     }
10114   }
10115
10116 #if 0
10117   debug_print_timestamp(0, "start main loop profiling");
10118 #endif
10119
10120   SCAN_PLAYFIELD(x, y)
10121   {
10122     ChangeCount[x][y] = 0;
10123     ChangeEvent[x][y] = -1;
10124
10125     /* this must be handled before main playfield loop */
10126     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10127     {
10128       MovDelay[x][y]--;
10129       if (MovDelay[x][y] <= 0)
10130         RemoveField(x, y);
10131     }
10132
10133 #if USE_NEW_SNAP_DELAY
10134     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10135     {
10136       MovDelay[x][y]--;
10137       if (MovDelay[x][y] <= 0)
10138       {
10139         RemoveField(x, y);
10140         DrawLevelField(x, y);
10141
10142         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10143       }
10144     }
10145 #endif
10146
10147 #if DEBUG
10148     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10149     {
10150       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10151       printf("GameActions(): This should never happen!\n");
10152
10153       ChangePage[x][y] = -1;
10154     }
10155 #endif
10156
10157     Stop[x][y] = FALSE;
10158     if (WasJustMoving[x][y] > 0)
10159       WasJustMoving[x][y]--;
10160     if (WasJustFalling[x][y] > 0)
10161       WasJustFalling[x][y]--;
10162     if (CheckCollision[x][y] > 0)
10163       CheckCollision[x][y]--;
10164     if (CheckImpact[x][y] > 0)
10165       CheckImpact[x][y]--;
10166
10167     GfxFrame[x][y]++;
10168
10169     /* reset finished pushing action (not done in ContinueMoving() to allow
10170        continuous pushing animation for elements with zero push delay) */
10171     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10172     {
10173       ResetGfxAnimation(x, y);
10174       DrawLevelField(x, y);
10175     }
10176
10177 #if DEBUG
10178     if (IS_BLOCKED(x, y))
10179     {
10180       int oldx, oldy;
10181
10182       Blocked2Moving(x, y, &oldx, &oldy);
10183       if (!IS_MOVING(oldx, oldy))
10184       {
10185         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10186         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10187         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10188         printf("GameActions(): This should never happen!\n");
10189       }
10190     }
10191 #endif
10192   }
10193
10194 #if 0
10195   debug_print_timestamp(0, "- time for pre-main loop:");
10196 #endif
10197
10198 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
10199   SCAN_PLAYFIELD(x, y)
10200   {
10201     element = Feld[x][y];
10202     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10203
10204 #if 1
10205     {
10206 #if 1
10207       int element2 = element;
10208       int graphic2 = graphic;
10209 #else
10210       int element2 = Feld[x][y];
10211       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
10212 #endif
10213       int last_gfx_frame = GfxFrame[x][y];
10214
10215       if (graphic_info[graphic2].anim_global_sync)
10216         GfxFrame[x][y] = FrameCounter;
10217       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
10218         GfxFrame[x][y] = CustomValue[x][y];
10219       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
10220         GfxFrame[x][y] = element_info[element2].collect_score;
10221       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
10222         GfxFrame[x][y] = ChangeDelay[x][y];
10223
10224       if (redraw && GfxFrame[x][y] != last_gfx_frame)
10225         DrawLevelGraphicAnimation(x, y, graphic2);
10226     }
10227 #else
10228     ResetGfxFrame(x, y, TRUE);
10229 #endif
10230
10231 #if 1
10232     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10233         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10234       ResetRandomAnimationValue(x, y);
10235 #endif
10236
10237 #if 1
10238     SetRandomAnimationValue(x, y);
10239 #endif
10240
10241 #if 1
10242     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10243 #endif
10244   }
10245 #endif  // -------------------- !!! TEST ONLY !!! --------------------
10246
10247 #if 0
10248   debug_print_timestamp(0, "- time for TEST loop:     -->");
10249 #endif
10250
10251   SCAN_PLAYFIELD(x, y)
10252   {
10253     element = Feld[x][y];
10254     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10255
10256     ResetGfxFrame(x, y, TRUE);
10257
10258     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10259         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10260       ResetRandomAnimationValue(x, y);
10261
10262     SetRandomAnimationValue(x, y);
10263
10264     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10265
10266     if (IS_INACTIVE(element))
10267     {
10268       if (IS_ANIMATED(graphic))
10269         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10270
10271       continue;
10272     }
10273
10274     /* this may take place after moving, so 'element' may have changed */
10275     if (IS_CHANGING(x, y) &&
10276         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10277     {
10278       int page = element_info[element].event_page_nr[CE_DELAY];
10279
10280 #if 1
10281       HandleElementChange(x, y, page);
10282 #else
10283       if (CAN_CHANGE(element))
10284         HandleElementChange(x, y, page);
10285
10286       if (HAS_ACTION(element))
10287         ExecuteCustomElementAction(x, y, element, page);
10288 #endif
10289
10290       element = Feld[x][y];
10291       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10292     }
10293
10294 #if 0   // ---------------------------------------------------------------------
10295
10296     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10297     {
10298       StartMoving(x, y);
10299
10300       element = Feld[x][y];
10301       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10302
10303       if (IS_ANIMATED(graphic) &&
10304           !IS_MOVING(x, y) &&
10305           !Stop[x][y])
10306         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10307
10308       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10309         DrawTwinkleOnField(x, y);
10310     }
10311     else if (IS_MOVING(x, y))
10312       ContinueMoving(x, y);
10313     else
10314     {
10315       switch (element)
10316       {
10317         case EL_ACID:
10318         case EL_EXIT_OPEN:
10319         case EL_EM_EXIT_OPEN:
10320         case EL_SP_EXIT_OPEN:
10321         case EL_STEEL_EXIT_OPEN:
10322         case EL_EM_STEEL_EXIT_OPEN:
10323         case EL_SP_TERMINAL:
10324         case EL_SP_TERMINAL_ACTIVE:
10325         case EL_EXTRA_TIME:
10326         case EL_SHIELD_NORMAL:
10327         case EL_SHIELD_DEADLY:
10328           if (IS_ANIMATED(graphic))
10329             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10330           break;
10331
10332         case EL_DYNAMITE_ACTIVE:
10333         case EL_EM_DYNAMITE_ACTIVE:
10334         case EL_DYNABOMB_PLAYER_1_ACTIVE:
10335         case EL_DYNABOMB_PLAYER_2_ACTIVE:
10336         case EL_DYNABOMB_PLAYER_3_ACTIVE:
10337         case EL_DYNABOMB_PLAYER_4_ACTIVE:
10338         case EL_SP_DISK_RED_ACTIVE:
10339           CheckDynamite(x, y);
10340           break;
10341
10342         case EL_AMOEBA_GROWING:
10343           AmoebeWaechst(x, y);
10344           break;
10345
10346         case EL_AMOEBA_SHRINKING:
10347           AmoebaDisappearing(x, y);
10348           break;
10349
10350 #if !USE_NEW_AMOEBA_CODE
10351         case EL_AMOEBA_WET:
10352         case EL_AMOEBA_DRY:
10353         case EL_AMOEBA_FULL:
10354         case EL_BD_AMOEBA:
10355         case EL_EMC_DRIPPER:
10356           AmoebeAbleger(x, y);
10357           break;
10358 #endif
10359
10360         case EL_GAME_OF_LIFE:
10361         case EL_BIOMAZE:
10362           Life(x, y);
10363           break;
10364
10365         case EL_EXIT_CLOSED:
10366           CheckExit(x, y);
10367           break;
10368
10369         case EL_EM_EXIT_CLOSED:
10370           CheckExitEM(x, y);
10371           break;
10372
10373         case EL_STEEL_EXIT_CLOSED:
10374           CheckExitSteel(x, y);
10375           break;
10376
10377         case EL_EM_STEEL_EXIT_CLOSED:
10378           CheckExitSteelEM(x, y);
10379           break;
10380
10381         case EL_SP_EXIT_CLOSED:
10382           CheckExitSP(x, y);
10383           break;
10384
10385         case EL_EXPANDABLE_WALL_GROWING:
10386         case EL_EXPANDABLE_STEELWALL_GROWING:
10387           MauerWaechst(x, y);
10388           break;
10389
10390         case EL_EXPANDABLE_WALL:
10391         case EL_EXPANDABLE_WALL_HORIZONTAL:
10392         case EL_EXPANDABLE_WALL_VERTICAL:
10393         case EL_EXPANDABLE_WALL_ANY:
10394         case EL_BD_EXPANDABLE_WALL:
10395           MauerAbleger(x, y);
10396           break;
10397
10398         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
10399         case EL_EXPANDABLE_STEELWALL_VERTICAL:
10400         case EL_EXPANDABLE_STEELWALL_ANY:
10401           MauerAblegerStahl(x, y);
10402           break;
10403
10404         case EL_FLAMES:
10405           CheckForDragon(x, y);
10406           break;
10407
10408         case EL_EXPLOSION:
10409           break;
10410
10411         case EL_ELEMENT_SNAPPING:
10412         case EL_DIAGONAL_SHRINKING:
10413         case EL_DIAGONAL_GROWING:
10414         {
10415           graphic =
10416             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10417
10418           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10419           break;
10420         }
10421
10422         default:
10423           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10424             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10425           break;
10426       }
10427     }
10428
10429 #else   // ---------------------------------------------------------------------
10430
10431     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10432     {
10433       StartMoving(x, y);
10434
10435       element = Feld[x][y];
10436       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10437
10438       if (IS_ANIMATED(graphic) &&
10439           !IS_MOVING(x, y) &&
10440           !Stop[x][y])
10441         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10442
10443       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10444         DrawTwinkleOnField(x, y);
10445     }
10446     else if ((element == EL_ACID ||
10447               element == EL_EXIT_OPEN ||
10448               element == EL_EM_EXIT_OPEN ||
10449               element == EL_SP_EXIT_OPEN ||
10450               element == EL_STEEL_EXIT_OPEN ||
10451               element == EL_EM_STEEL_EXIT_OPEN ||
10452               element == EL_SP_TERMINAL ||
10453               element == EL_SP_TERMINAL_ACTIVE ||
10454               element == EL_EXTRA_TIME ||
10455               element == EL_SHIELD_NORMAL ||
10456               element == EL_SHIELD_DEADLY) &&
10457              IS_ANIMATED(graphic))
10458       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10459     else if (IS_MOVING(x, y))
10460       ContinueMoving(x, y);
10461     else if (IS_ACTIVE_BOMB(element))
10462       CheckDynamite(x, y);
10463     else if (element == EL_AMOEBA_GROWING)
10464       AmoebeWaechst(x, y);
10465     else if (element == EL_AMOEBA_SHRINKING)
10466       AmoebaDisappearing(x, y);
10467
10468 #if !USE_NEW_AMOEBA_CODE
10469     else if (IS_AMOEBALIVE(element))
10470       AmoebeAbleger(x, y);
10471 #endif
10472
10473     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10474       Life(x, y);
10475     else if (element == EL_EXIT_CLOSED)
10476       CheckExit(x, y);
10477     else if (element == EL_EM_EXIT_CLOSED)
10478       CheckExitEM(x, y);
10479     else if (element == EL_STEEL_EXIT_CLOSED)
10480       CheckExitSteel(x, y);
10481     else if (element == EL_EM_STEEL_EXIT_CLOSED)
10482       CheckExitSteelEM(x, y);
10483     else if (element == EL_SP_EXIT_CLOSED)
10484       CheckExitSP(x, y);
10485     else if (element == EL_EXPANDABLE_WALL_GROWING ||
10486              element == EL_EXPANDABLE_STEELWALL_GROWING)
10487       MauerWaechst(x, y);
10488     else if (element == EL_EXPANDABLE_WALL ||
10489              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10490              element == EL_EXPANDABLE_WALL_VERTICAL ||
10491              element == EL_EXPANDABLE_WALL_ANY ||
10492              element == EL_BD_EXPANDABLE_WALL)
10493       MauerAbleger(x, y);
10494     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10495              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10496              element == EL_EXPANDABLE_STEELWALL_ANY)
10497       MauerAblegerStahl(x, y);
10498     else if (element == EL_FLAMES)
10499       CheckForDragon(x, y);
10500     else if (element == EL_EXPLOSION)
10501       ; /* drawing of correct explosion animation is handled separately */
10502     else if (element == EL_ELEMENT_SNAPPING ||
10503              element == EL_DIAGONAL_SHRINKING ||
10504              element == EL_DIAGONAL_GROWING)
10505     {
10506       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10507
10508       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10509     }
10510     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10511       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10512
10513 #endif  // ---------------------------------------------------------------------
10514
10515     if (IS_BELT_ACTIVE(element))
10516       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10517
10518     if (game.magic_wall_active)
10519     {
10520       int jx = local_player->jx, jy = local_player->jy;
10521
10522       /* play the element sound at the position nearest to the player */
10523       if ((element == EL_MAGIC_WALL_FULL ||
10524            element == EL_MAGIC_WALL_ACTIVE ||
10525            element == EL_MAGIC_WALL_EMPTYING ||
10526            element == EL_BD_MAGIC_WALL_FULL ||
10527            element == EL_BD_MAGIC_WALL_ACTIVE ||
10528            element == EL_BD_MAGIC_WALL_EMPTYING ||
10529            element == EL_DC_MAGIC_WALL_FULL ||
10530            element == EL_DC_MAGIC_WALL_ACTIVE ||
10531            element == EL_DC_MAGIC_WALL_EMPTYING) &&
10532           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10533       {
10534         magic_wall_x = x;
10535         magic_wall_y = y;
10536       }
10537     }
10538   }
10539
10540 #if 0
10541   debug_print_timestamp(0, "- time for MAIN loop:     -->");
10542 #endif
10543
10544 #if USE_NEW_AMOEBA_CODE
10545   /* new experimental amoeba growth stuff */
10546   if (!(FrameCounter % 8))
10547   {
10548     static unsigned long random = 1684108901;
10549
10550     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10551     {
10552       x = RND(lev_fieldx);
10553       y = RND(lev_fieldy);
10554       element = Feld[x][y];
10555
10556       if (!IS_PLAYER(x,y) &&
10557           (element == EL_EMPTY ||
10558            CAN_GROW_INTO(element) ||
10559            element == EL_QUICKSAND_EMPTY ||
10560            element == EL_QUICKSAND_FAST_EMPTY ||
10561            element == EL_ACID_SPLASH_LEFT ||
10562            element == EL_ACID_SPLASH_RIGHT))
10563       {
10564         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10565             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10566             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10567             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10568           Feld[x][y] = EL_AMOEBA_DROP;
10569       }
10570
10571       random = random * 129 + 1;
10572     }
10573   }
10574 #endif
10575
10576 #if 0
10577   if (game.explosions_delayed)
10578 #endif
10579   {
10580     game.explosions_delayed = FALSE;
10581
10582     SCAN_PLAYFIELD(x, y)
10583     {
10584       element = Feld[x][y];
10585
10586       if (ExplodeField[x][y])
10587         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10588       else if (element == EL_EXPLOSION)
10589         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10590
10591       ExplodeField[x][y] = EX_TYPE_NONE;
10592     }
10593
10594     game.explosions_delayed = TRUE;
10595   }
10596
10597   if (game.magic_wall_active)
10598   {
10599     if (!(game.magic_wall_time_left % 4))
10600     {
10601       int element = Feld[magic_wall_x][magic_wall_y];
10602
10603       if (element == EL_BD_MAGIC_WALL_FULL ||
10604           element == EL_BD_MAGIC_WALL_ACTIVE ||
10605           element == EL_BD_MAGIC_WALL_EMPTYING)
10606         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10607       else if (element == EL_DC_MAGIC_WALL_FULL ||
10608                element == EL_DC_MAGIC_WALL_ACTIVE ||
10609                element == EL_DC_MAGIC_WALL_EMPTYING)
10610         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10611       else
10612         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10613     }
10614
10615     if (game.magic_wall_time_left > 0)
10616     {
10617       game.magic_wall_time_left--;
10618       if (!game.magic_wall_time_left)
10619       {
10620         SCAN_PLAYFIELD(x, y)
10621         {
10622           element = Feld[x][y];
10623
10624           if (element == EL_MAGIC_WALL_ACTIVE ||
10625               element == EL_MAGIC_WALL_FULL)
10626           {
10627             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10628             DrawLevelField(x, y);
10629           }
10630           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10631                    element == EL_BD_MAGIC_WALL_FULL)
10632           {
10633             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10634             DrawLevelField(x, y);
10635           }
10636           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10637                    element == EL_DC_MAGIC_WALL_FULL)
10638           {
10639             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10640             DrawLevelField(x, y);
10641           }
10642         }
10643
10644         game.magic_wall_active = FALSE;
10645       }
10646     }
10647   }
10648
10649   if (game.light_time_left > 0)
10650   {
10651     game.light_time_left--;
10652
10653     if (game.light_time_left == 0)
10654       RedrawAllLightSwitchesAndInvisibleElements();
10655   }
10656
10657   if (game.timegate_time_left > 0)
10658   {
10659     game.timegate_time_left--;
10660
10661     if (game.timegate_time_left == 0)
10662       CloseAllOpenTimegates();
10663   }
10664
10665   if (game.lenses_time_left > 0)
10666   {
10667     game.lenses_time_left--;
10668
10669     if (game.lenses_time_left == 0)
10670       RedrawAllInvisibleElementsForLenses();
10671   }
10672
10673   if (game.magnify_time_left > 0)
10674   {
10675     game.magnify_time_left--;
10676
10677     if (game.magnify_time_left == 0)
10678       RedrawAllInvisibleElementsForMagnifier();
10679   }
10680
10681   for (i = 0; i < MAX_PLAYERS; i++)
10682   {
10683     struct PlayerInfo *player = &stored_player[i];
10684
10685     if (SHIELD_ON(player))
10686     {
10687       if (player->shield_deadly_time_left)
10688         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10689       else if (player->shield_normal_time_left)
10690         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10691     }
10692   }
10693
10694   CheckLevelTime();
10695
10696   DrawAllPlayers();
10697   PlayAllPlayersSound();
10698
10699   if (options.debug)                    /* calculate frames per second */
10700   {
10701     static unsigned long fps_counter = 0;
10702     static int fps_frames = 0;
10703     unsigned long fps_delay_ms = Counter() - fps_counter;
10704
10705     fps_frames++;
10706
10707     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10708     {
10709       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10710
10711       fps_frames = 0;
10712       fps_counter = Counter();
10713     }
10714
10715     redraw_mask |= REDRAW_FPS;
10716   }
10717
10718   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10719
10720   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10721   {
10722     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10723
10724     local_player->show_envelope = 0;
10725   }
10726
10727 #if 0
10728   debug_print_timestamp(0, "stop main loop profiling ");
10729   printf("----------------------------------------------------------\n");
10730 #endif
10731
10732   /* use random number generator in every frame to make it less predictable */
10733   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10734     RND(1);
10735 }
10736
10737 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10738 {
10739   int min_x = x, min_y = y, max_x = x, max_y = y;
10740   int i;
10741
10742   for (i = 0; i < MAX_PLAYERS; i++)
10743   {
10744     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10745
10746     if (!stored_player[i].active || &stored_player[i] == player)
10747       continue;
10748
10749     min_x = MIN(min_x, jx);
10750     min_y = MIN(min_y, jy);
10751     max_x = MAX(max_x, jx);
10752     max_y = MAX(max_y, jy);
10753   }
10754
10755   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10756 }
10757
10758 static boolean AllPlayersInVisibleScreen()
10759 {
10760   int i;
10761
10762   for (i = 0; i < MAX_PLAYERS; i++)
10763   {
10764     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10765
10766     if (!stored_player[i].active)
10767       continue;
10768
10769     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10770       return FALSE;
10771   }
10772
10773   return TRUE;
10774 }
10775
10776 void ScrollLevel(int dx, int dy)
10777 {
10778 #if 1
10779   static Bitmap *bitmap_db_field2 = NULL;
10780   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10781   int x, y;
10782 #else
10783   int i, x, y;
10784 #endif
10785
10786 #if 0
10787   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
10788   /* only horizontal XOR vertical scroll direction allowed */
10789   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
10790     return;
10791 #endif
10792
10793 #if 1
10794   if (bitmap_db_field2 == NULL)
10795     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
10796
10797   /* needed when blitting directly to same bitmap -- should not be needed with
10798      recent SDL libraries, but apparently does not work in 1.2.11 directly */
10799   BlitBitmap(drawto_field, bitmap_db_field2,
10800              FX + TILEX * (dx == -1) - softscroll_offset,
10801              FY + TILEY * (dy == -1) - softscroll_offset,
10802              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10803              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10804              FX + TILEX * (dx == 1) - softscroll_offset,
10805              FY + TILEY * (dy == 1) - softscroll_offset);
10806   BlitBitmap(bitmap_db_field2, drawto_field,
10807              FX + TILEX * (dx == 1) - softscroll_offset,
10808              FY + TILEY * (dy == 1) - softscroll_offset,
10809              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10810              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10811              FX + TILEX * (dx == 1) - softscroll_offset,
10812              FY + TILEY * (dy == 1) - softscroll_offset);
10813
10814 #else
10815
10816 #if 1
10817   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
10818   int xsize = (BX2 - BX1 + 1);
10819   int ysize = (BY2 - BY1 + 1);
10820   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
10821   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
10822   int step  = (start < end ? +1 : -1);
10823
10824   for (i = start; i != end; i += step)
10825   {
10826     BlitBitmap(drawto_field, drawto_field,
10827                FX + TILEX * (dx != 0 ? i + step : 0),
10828                FY + TILEY * (dy != 0 ? i + step : 0),
10829                TILEX * (dx != 0 ? 1 : xsize),
10830                TILEY * (dy != 0 ? 1 : ysize),
10831                FX + TILEX * (dx != 0 ? i : 0),
10832                FY + TILEY * (dy != 0 ? i : 0));
10833   }
10834
10835 #else
10836
10837   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10838
10839   BlitBitmap(drawto_field, drawto_field,
10840              FX + TILEX * (dx == -1) - softscroll_offset,
10841              FY + TILEY * (dy == -1) - softscroll_offset,
10842              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10843              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10844              FX + TILEX * (dx == 1) - softscroll_offset,
10845              FY + TILEY * (dy == 1) - softscroll_offset);
10846 #endif
10847 #endif
10848
10849   if (dx != 0)
10850   {
10851     x = (dx == 1 ? BX1 : BX2);
10852     for (y = BY1; y <= BY2; y++)
10853       DrawScreenField(x, y);
10854   }
10855
10856   if (dy != 0)
10857   {
10858     y = (dy == 1 ? BY1 : BY2);
10859     for (x = BX1; x <= BX2; x++)
10860       DrawScreenField(x, y);
10861   }
10862
10863   redraw_mask |= REDRAW_FIELD;
10864 }
10865
10866 static boolean canFallDown(struct PlayerInfo *player)
10867 {
10868   int jx = player->jx, jy = player->jy;
10869
10870   return (IN_LEV_FIELD(jx, jy + 1) &&
10871           (IS_FREE(jx, jy + 1) ||
10872            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10873           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10874           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10875 }
10876
10877 static boolean canPassField(int x, int y, int move_dir)
10878 {
10879   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10880   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10881   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10882   int nextx = x + dx;
10883   int nexty = y + dy;
10884   int element = Feld[x][y];
10885
10886   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10887           !CAN_MOVE(element) &&
10888           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10889           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10890           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10891 }
10892
10893 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10894 {
10895   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10896   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10897   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10898   int newx = x + dx;
10899   int newy = y + dy;
10900
10901   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10902           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10903           (IS_DIGGABLE(Feld[newx][newy]) ||
10904            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10905            canPassField(newx, newy, move_dir)));
10906 }
10907
10908 static void CheckGravityMovement(struct PlayerInfo *player)
10909 {
10910 #if USE_PLAYER_GRAVITY
10911   if (player->gravity && !player->programmed_action)
10912 #else
10913   if (game.gravity && !player->programmed_action)
10914 #endif
10915   {
10916     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10917     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10918     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10919     int jx = player->jx, jy = player->jy;
10920     boolean player_is_moving_to_valid_field =
10921       (!player_is_snapping &&
10922        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10923         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10924     boolean player_can_fall_down = canFallDown(player);
10925
10926     if (player_can_fall_down &&
10927         !player_is_moving_to_valid_field)
10928       player->programmed_action = MV_DOWN;
10929   }
10930 }
10931
10932 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10933 {
10934   return CheckGravityMovement(player);
10935
10936 #if USE_PLAYER_GRAVITY
10937   if (player->gravity && !player->programmed_action)
10938 #else
10939   if (game.gravity && !player->programmed_action)
10940 #endif
10941   {
10942     int jx = player->jx, jy = player->jy;
10943     boolean field_under_player_is_free =
10944       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10945     boolean player_is_standing_on_valid_field =
10946       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10947        (IS_WALKABLE(Feld[jx][jy]) &&
10948         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10949
10950     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10951       player->programmed_action = MV_DOWN;
10952   }
10953 }
10954
10955 /*
10956   MovePlayerOneStep()
10957   -----------------------------------------------------------------------------
10958   dx, dy:               direction (non-diagonal) to try to move the player to
10959   real_dx, real_dy:     direction as read from input device (can be diagonal)
10960 */
10961
10962 boolean MovePlayerOneStep(struct PlayerInfo *player,
10963                           int dx, int dy, int real_dx, int real_dy)
10964 {
10965   int jx = player->jx, jy = player->jy;
10966   int new_jx = jx + dx, new_jy = jy + dy;
10967 #if !USE_FIXED_DONT_RUN_INTO
10968   int element;
10969 #endif
10970   int can_move;
10971   boolean player_can_move = !player->cannot_move;
10972
10973   if (!player->active || (!dx && !dy))
10974     return MP_NO_ACTION;
10975
10976   player->MovDir = (dx < 0 ? MV_LEFT :
10977                     dx > 0 ? MV_RIGHT :
10978                     dy < 0 ? MV_UP :
10979                     dy > 0 ? MV_DOWN :  MV_NONE);
10980
10981   if (!IN_LEV_FIELD(new_jx, new_jy))
10982     return MP_NO_ACTION;
10983
10984   if (!player_can_move)
10985   {
10986     if (player->MovPos == 0)
10987     {
10988       player->is_moving = FALSE;
10989       player->is_digging = FALSE;
10990       player->is_collecting = FALSE;
10991       player->is_snapping = FALSE;
10992       player->is_pushing = FALSE;
10993     }
10994   }
10995
10996 #if 1
10997   if (!options.network && game.centered_player_nr == -1 &&
10998       !AllPlayersInSight(player, new_jx, new_jy))
10999     return MP_NO_ACTION;
11000 #else
11001   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
11002     return MP_NO_ACTION;
11003 #endif
11004
11005 #if !USE_FIXED_DONT_RUN_INTO
11006   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
11007
11008   /* (moved to DigField()) */
11009   if (player_can_move && DONT_RUN_INTO(element))
11010   {
11011     if (element == EL_ACID && dx == 0 && dy == 1)
11012     {
11013       SplashAcid(new_jx, new_jy);
11014       Feld[jx][jy] = EL_PLAYER_1;
11015       InitMovingField(jx, jy, MV_DOWN);
11016       Store[jx][jy] = EL_ACID;
11017       ContinueMoving(jx, jy);
11018       BuryPlayer(player);
11019     }
11020     else
11021       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11022
11023     return MP_MOVING;
11024   }
11025 #endif
11026
11027   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11028   if (can_move != MP_MOVING)
11029     return can_move;
11030
11031   /* check if DigField() has caused relocation of the player */
11032   if (player->jx != jx || player->jy != jy)
11033     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11034
11035   StorePlayer[jx][jy] = 0;
11036   player->last_jx = jx;
11037   player->last_jy = jy;
11038   player->jx = new_jx;
11039   player->jy = new_jy;
11040   StorePlayer[new_jx][new_jy] = player->element_nr;
11041
11042   if (player->move_delay_value_next != -1)
11043   {
11044     player->move_delay_value = player->move_delay_value_next;
11045     player->move_delay_value_next = -1;
11046   }
11047
11048   player->MovPos =
11049     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11050
11051   player->step_counter++;
11052
11053   PlayerVisit[jx][jy] = FrameCounter;
11054
11055 #if USE_UFAST_PLAYER_EXIT_BUGFIX
11056   player->is_moving = TRUE;
11057 #endif
11058
11059 #if 1
11060   /* should better be called in MovePlayer(), but this breaks some tapes */
11061   ScrollPlayer(player, SCROLL_INIT);
11062 #endif
11063
11064   return MP_MOVING;
11065 }
11066
11067 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11068 {
11069   int jx = player->jx, jy = player->jy;
11070   int old_jx = jx, old_jy = jy;
11071   int moved = MP_NO_ACTION;
11072
11073   if (!player->active)
11074     return FALSE;
11075
11076   if (!dx && !dy)
11077   {
11078     if (player->MovPos == 0)
11079     {
11080       player->is_moving = FALSE;
11081       player->is_digging = FALSE;
11082       player->is_collecting = FALSE;
11083       player->is_snapping = FALSE;
11084       player->is_pushing = FALSE;
11085     }
11086
11087     return FALSE;
11088   }
11089
11090   if (player->move_delay > 0)
11091     return FALSE;
11092
11093   player->move_delay = -1;              /* set to "uninitialized" value */
11094
11095   /* store if player is automatically moved to next field */
11096   player->is_auto_moving = (player->programmed_action != MV_NONE);
11097
11098   /* remove the last programmed player action */
11099   player->programmed_action = 0;
11100
11101   if (player->MovPos)
11102   {
11103     /* should only happen if pre-1.2 tape recordings are played */
11104     /* this is only for backward compatibility */
11105
11106     int original_move_delay_value = player->move_delay_value;
11107
11108 #if DEBUG
11109     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
11110            tape.counter);
11111 #endif
11112
11113     /* scroll remaining steps with finest movement resolution */
11114     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
11115
11116     while (player->MovPos)
11117     {
11118       ScrollPlayer(player, SCROLL_GO_ON);
11119       ScrollScreen(NULL, SCROLL_GO_ON);
11120
11121       AdvanceFrameAndPlayerCounters(player->index_nr);
11122
11123       DrawAllPlayers();
11124       BackToFront();
11125     }
11126
11127     player->move_delay_value = original_move_delay_value;
11128   }
11129
11130   player->is_active = FALSE;
11131
11132   if (player->last_move_dir & MV_HORIZONTAL)
11133   {
11134     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
11135       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
11136   }
11137   else
11138   {
11139     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
11140       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
11141   }
11142
11143 #if USE_FIXED_BORDER_RUNNING_GFX
11144   if (!moved && !player->is_active)
11145   {
11146     player->is_moving = FALSE;
11147     player->is_digging = FALSE;
11148     player->is_collecting = FALSE;
11149     player->is_snapping = FALSE;
11150     player->is_pushing = FALSE;
11151   }
11152 #endif
11153
11154   jx = player->jx;
11155   jy = player->jy;
11156
11157 #if 1
11158   if (moved & MP_MOVING && !ScreenMovPos &&
11159       (player->index_nr == game.centered_player_nr ||
11160        game.centered_player_nr == -1))
11161 #else
11162   if (moved & MP_MOVING && !ScreenMovPos &&
11163       (player == local_player || !options.network))
11164 #endif
11165   {
11166     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11167     int offset = (setup.scroll_delay ? 3 : 0);
11168
11169     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11170     {
11171       /* actual player has left the screen -- scroll in that direction */
11172       if (jx != old_jx)         /* player has moved horizontally */
11173         scroll_x += (jx - old_jx);
11174       else                      /* player has moved vertically */
11175         scroll_y += (jy - old_jy);
11176     }
11177     else
11178     {
11179       if (jx != old_jx)         /* player has moved horizontally */
11180       {
11181         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
11182             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11183           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11184
11185         /* don't scroll over playfield boundaries */
11186         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11187           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11188
11189         /* don't scroll more than one field at a time */
11190         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11191
11192         /* don't scroll against the player's moving direction */
11193         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
11194             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11195           scroll_x = old_scroll_x;
11196       }
11197       else                      /* player has moved vertically */
11198       {
11199         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
11200             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11201           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11202
11203         /* don't scroll over playfield boundaries */
11204         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11205           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11206
11207         /* don't scroll more than one field at a time */
11208         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11209
11210         /* don't scroll against the player's moving direction */
11211         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
11212             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11213           scroll_y = old_scroll_y;
11214       }
11215     }
11216
11217     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11218     {
11219 #if 1
11220       if (!options.network && game.centered_player_nr == -1 &&
11221           !AllPlayersInVisibleScreen())
11222       {
11223         scroll_x = old_scroll_x;
11224         scroll_y = old_scroll_y;
11225       }
11226       else
11227 #else
11228       if (!options.network && !AllPlayersInVisibleScreen())
11229       {
11230         scroll_x = old_scroll_x;
11231         scroll_y = old_scroll_y;
11232       }
11233       else
11234 #endif
11235       {
11236         ScrollScreen(player, SCROLL_INIT);
11237         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11238       }
11239     }
11240   }
11241
11242   player->StepFrame = 0;
11243
11244   if (moved & MP_MOVING)
11245   {
11246     if (old_jx != jx && old_jy == jy)
11247       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11248     else if (old_jx == jx && old_jy != jy)
11249       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11250
11251     DrawLevelField(jx, jy);     /* for "crumbled sand" */
11252
11253     player->last_move_dir = player->MovDir;
11254     player->is_moving = TRUE;
11255     player->is_snapping = FALSE;
11256     player->is_switching = FALSE;
11257     player->is_dropping = FALSE;
11258     player->is_dropping_pressed = FALSE;
11259     player->drop_pressed_delay = 0;
11260
11261 #if 0
11262     /* should better be called here than above, but this breaks some tapes */
11263     ScrollPlayer(player, SCROLL_INIT);
11264 #endif
11265   }
11266   else
11267   {
11268     CheckGravityMovementWhenNotMoving(player);
11269
11270     player->is_moving = FALSE;
11271
11272     /* at this point, the player is allowed to move, but cannot move right now
11273        (e.g. because of something blocking the way) -- ensure that the player
11274        is also allowed to move in the next frame (in old versions before 3.1.1,
11275        the player was forced to wait again for eight frames before next try) */
11276
11277     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11278       player->move_delay = 0;   /* allow direct movement in the next frame */
11279   }
11280
11281   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11282     player->move_delay = player->move_delay_value;
11283
11284   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11285   {
11286     TestIfPlayerTouchesBadThing(jx, jy);
11287     TestIfPlayerTouchesCustomElement(jx, jy);
11288   }
11289
11290   if (!player->active)
11291     RemovePlayer(player);
11292
11293   return moved;
11294 }
11295
11296 void ScrollPlayer(struct PlayerInfo *player, int mode)
11297 {
11298   int jx = player->jx, jy = player->jy;
11299   int last_jx = player->last_jx, last_jy = player->last_jy;
11300   int move_stepsize = TILEX / player->move_delay_value;
11301
11302 #if USE_NEW_PLAYER_SPEED
11303   if (!player->active)
11304     return;
11305
11306   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11307     return;
11308 #else
11309   if (!player->active || player->MovPos == 0)
11310     return;
11311 #endif
11312
11313   if (mode == SCROLL_INIT)
11314   {
11315     player->actual_frame_counter = FrameCounter;
11316     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11317
11318     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11319         Feld[last_jx][last_jy] == EL_EMPTY)
11320     {
11321       int last_field_block_delay = 0;   /* start with no blocking at all */
11322       int block_delay_adjustment = player->block_delay_adjustment;
11323
11324       /* if player blocks last field, add delay for exactly one move */
11325       if (player->block_last_field)
11326       {
11327         last_field_block_delay += player->move_delay_value;
11328
11329         /* when blocking enabled, prevent moving up despite gravity */
11330 #if USE_PLAYER_GRAVITY
11331         if (player->gravity && player->MovDir == MV_UP)
11332           block_delay_adjustment = -1;
11333 #else
11334         if (game.gravity && player->MovDir == MV_UP)
11335           block_delay_adjustment = -1;
11336 #endif
11337       }
11338
11339       /* add block delay adjustment (also possible when not blocking) */
11340       last_field_block_delay += block_delay_adjustment;
11341
11342       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11343       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11344     }
11345
11346 #if USE_NEW_PLAYER_SPEED
11347     if (player->MovPos != 0)    /* player has not yet reached destination */
11348       return;
11349 #else
11350     return;
11351 #endif
11352   }
11353   else if (!FrameReached(&player->actual_frame_counter, 1))
11354     return;
11355
11356 #if USE_NEW_PLAYER_SPEED
11357   if (player->MovPos != 0)
11358   {
11359     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11360     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11361
11362     /* before DrawPlayer() to draw correct player graphic for this case */
11363     if (player->MovPos == 0)
11364       CheckGravityMovement(player);
11365   }
11366 #else
11367   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11368   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11369
11370   /* before DrawPlayer() to draw correct player graphic for this case */
11371   if (player->MovPos == 0)
11372     CheckGravityMovement(player);
11373 #endif
11374
11375   if (player->MovPos == 0)      /* player reached destination field */
11376   {
11377     if (player->move_delay_reset_counter > 0)
11378     {
11379       player->move_delay_reset_counter--;
11380
11381       if (player->move_delay_reset_counter == 0)
11382       {
11383         /* continue with normal speed after quickly moving through gate */
11384         HALVE_PLAYER_SPEED(player);
11385
11386         /* be able to make the next move without delay */
11387         player->move_delay = 0;
11388       }
11389     }
11390
11391     player->last_jx = jx;
11392     player->last_jy = jy;
11393
11394     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11395         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11396         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11397         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11398         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11399         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11400     {
11401       DrawPlayer(player);       /* needed here only to cleanup last field */
11402       RemovePlayer(player);
11403
11404       if (local_player->friends_still_needed == 0 ||
11405           IS_SP_ELEMENT(Feld[jx][jy]))
11406         PlayerWins(player);
11407     }
11408
11409     /* this breaks one level: "machine", level 000 */
11410     {
11411       int move_direction = player->MovDir;
11412       int enter_side = MV_DIR_OPPOSITE(move_direction);
11413       int leave_side = move_direction;
11414       int old_jx = last_jx;
11415       int old_jy = last_jy;
11416       int old_element = Feld[old_jx][old_jy];
11417       int new_element = Feld[jx][jy];
11418
11419       if (IS_CUSTOM_ELEMENT(old_element))
11420         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11421                                    CE_LEFT_BY_PLAYER,
11422                                    player->index_bit, leave_side);
11423
11424       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11425                                           CE_PLAYER_LEAVES_X,
11426                                           player->index_bit, leave_side);
11427
11428       if (IS_CUSTOM_ELEMENT(new_element))
11429         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11430                                    player->index_bit, enter_side);
11431
11432       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11433                                           CE_PLAYER_ENTERS_X,
11434                                           player->index_bit, enter_side);
11435
11436       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11437                                         CE_MOVE_OF_X, move_direction);
11438     }
11439
11440     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11441     {
11442       TestIfPlayerTouchesBadThing(jx, jy);
11443       TestIfPlayerTouchesCustomElement(jx, jy);
11444
11445       /* needed because pushed element has not yet reached its destination,
11446          so it would trigger a change event at its previous field location */
11447       if (!player->is_pushing)
11448         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
11449
11450       if (!player->active)
11451         RemovePlayer(player);
11452     }
11453
11454     if (!local_player->LevelSolved && level.use_step_counter)
11455     {
11456       int i;
11457
11458       TimePlayed++;
11459
11460       if (TimeLeft > 0)
11461       {
11462         TimeLeft--;
11463
11464         if (TimeLeft <= 10 && setup.time_limit)
11465           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11466
11467         DrawGameValue_Time(TimeLeft);
11468
11469         if (!TimeLeft && setup.time_limit)
11470           for (i = 0; i < MAX_PLAYERS; i++)
11471             KillPlayer(&stored_player[i]);
11472       }
11473       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11474         DrawGameValue_Time(TimePlayed);
11475     }
11476
11477     if (tape.single_step && tape.recording && !tape.pausing &&
11478         !player->programmed_action)
11479       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11480   }
11481 }
11482
11483 void ScrollScreen(struct PlayerInfo *player, int mode)
11484 {
11485   static unsigned long screen_frame_counter = 0;
11486
11487   if (mode == SCROLL_INIT)
11488   {
11489     /* set scrolling step size according to actual player's moving speed */
11490     ScrollStepSize = TILEX / player->move_delay_value;
11491
11492     screen_frame_counter = FrameCounter;
11493     ScreenMovDir = player->MovDir;
11494     ScreenMovPos = player->MovPos;
11495     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11496     return;
11497   }
11498   else if (!FrameReached(&screen_frame_counter, 1))
11499     return;
11500
11501   if (ScreenMovPos)
11502   {
11503     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11504     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11505     redraw_mask |= REDRAW_FIELD;
11506   }
11507   else
11508     ScreenMovDir = MV_NONE;
11509 }
11510
11511 void TestIfPlayerTouchesCustomElement(int x, int y)
11512 {
11513   static int xy[4][2] =
11514   {
11515     { 0, -1 },
11516     { -1, 0 },
11517     { +1, 0 },
11518     { 0, +1 }
11519   };
11520   static int trigger_sides[4][2] =
11521   {
11522     /* center side       border side */
11523     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11524     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11525     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11526     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11527   };
11528   static int touch_dir[4] =
11529   {
11530     MV_LEFT | MV_RIGHT,
11531     MV_UP   | MV_DOWN,
11532     MV_UP   | MV_DOWN,
11533     MV_LEFT | MV_RIGHT
11534   };
11535   int center_element = Feld[x][y];      /* should always be non-moving! */
11536   int i;
11537
11538   for (i = 0; i < NUM_DIRECTIONS; i++)
11539   {
11540     int xx = x + xy[i][0];
11541     int yy = y + xy[i][1];
11542     int center_side = trigger_sides[i][0];
11543     int border_side = trigger_sides[i][1];
11544     int border_element;
11545
11546     if (!IN_LEV_FIELD(xx, yy))
11547       continue;
11548
11549     if (IS_PLAYER(x, y))
11550     {
11551       struct PlayerInfo *player = PLAYERINFO(x, y);
11552
11553       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11554         border_element = Feld[xx][yy];          /* may be moving! */
11555       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11556         border_element = Feld[xx][yy];
11557       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11558         border_element = MovingOrBlocked2Element(xx, yy);
11559       else
11560         continue;               /* center and border element do not touch */
11561
11562       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11563                                  player->index_bit, border_side);
11564       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11565                                           CE_PLAYER_TOUCHES_X,
11566                                           player->index_bit, border_side);
11567     }
11568     else if (IS_PLAYER(xx, yy))
11569     {
11570       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11571
11572       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11573       {
11574         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11575           continue;             /* center and border element do not touch */
11576       }
11577
11578       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11579                                  player->index_bit, center_side);
11580       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11581                                           CE_PLAYER_TOUCHES_X,
11582                                           player->index_bit, center_side);
11583       break;
11584     }
11585   }
11586 }
11587
11588 #if USE_ELEMENT_TOUCHING_BUGFIX
11589
11590 void TestIfElementTouchesCustomElement(int x, int y)
11591 {
11592   static int xy[4][2] =
11593   {
11594     { 0, -1 },
11595     { -1, 0 },
11596     { +1, 0 },
11597     { 0, +1 }
11598   };
11599   static int trigger_sides[4][2] =
11600   {
11601     /* center side      border side */
11602     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11603     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11604     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11605     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11606   };
11607   static int touch_dir[4] =
11608   {
11609     MV_LEFT | MV_RIGHT,
11610     MV_UP   | MV_DOWN,
11611     MV_UP   | MV_DOWN,
11612     MV_LEFT | MV_RIGHT
11613   };
11614   boolean change_center_element = FALSE;
11615   int center_element = Feld[x][y];      /* should always be non-moving! */
11616   int border_element_old[NUM_DIRECTIONS];
11617   int i;
11618
11619   for (i = 0; i < NUM_DIRECTIONS; i++)
11620   {
11621     int xx = x + xy[i][0];
11622     int yy = y + xy[i][1];
11623     int border_element;
11624
11625     border_element_old[i] = -1;
11626
11627     if (!IN_LEV_FIELD(xx, yy))
11628       continue;
11629
11630     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11631       border_element = Feld[xx][yy];    /* may be moving! */
11632     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11633       border_element = Feld[xx][yy];
11634     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11635       border_element = MovingOrBlocked2Element(xx, yy);
11636     else
11637       continue;                 /* center and border element do not touch */
11638
11639     border_element_old[i] = border_element;
11640   }
11641
11642   for (i = 0; i < NUM_DIRECTIONS; i++)
11643   {
11644     int xx = x + xy[i][0];
11645     int yy = y + xy[i][1];
11646     int center_side = trigger_sides[i][0];
11647     int border_element = border_element_old[i];
11648
11649     if (border_element == -1)
11650       continue;
11651
11652     /* check for change of border element */
11653     CheckElementChangeBySide(xx, yy, border_element, center_element,
11654                              CE_TOUCHING_X, center_side);
11655   }
11656
11657   for (i = 0; i < NUM_DIRECTIONS; i++)
11658   {
11659     int border_side = trigger_sides[i][1];
11660     int border_element = border_element_old[i];
11661
11662     if (border_element == -1)
11663       continue;
11664
11665     /* check for change of center element (but change it only once) */
11666     if (!change_center_element)
11667       change_center_element =
11668         CheckElementChangeBySide(x, y, center_element, border_element,
11669                                  CE_TOUCHING_X, border_side);
11670   }
11671 }
11672
11673 #else
11674
11675 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11676 {
11677   static int xy[4][2] =
11678   {
11679     { 0, -1 },
11680     { -1, 0 },
11681     { +1, 0 },
11682     { 0, +1 }
11683   };
11684   static int trigger_sides[4][2] =
11685   {
11686     /* center side      border side */
11687     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11688     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11689     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11690     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11691   };
11692   static int touch_dir[4] =
11693   {
11694     MV_LEFT | MV_RIGHT,
11695     MV_UP   | MV_DOWN,
11696     MV_UP   | MV_DOWN,
11697     MV_LEFT | MV_RIGHT
11698   };
11699   boolean change_center_element = FALSE;
11700   int center_element = Feld[x][y];      /* should always be non-moving! */
11701   int i;
11702
11703   for (i = 0; i < NUM_DIRECTIONS; i++)
11704   {
11705     int xx = x + xy[i][0];
11706     int yy = y + xy[i][1];
11707     int center_side = trigger_sides[i][0];
11708     int border_side = trigger_sides[i][1];
11709     int border_element;
11710
11711     if (!IN_LEV_FIELD(xx, yy))
11712       continue;
11713
11714     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11715       border_element = Feld[xx][yy];    /* may be moving! */
11716     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11717       border_element = Feld[xx][yy];
11718     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11719       border_element = MovingOrBlocked2Element(xx, yy);
11720     else
11721       continue;                 /* center and border element do not touch */
11722
11723     /* check for change of center element (but change it only once) */
11724     if (!change_center_element)
11725       change_center_element =
11726         CheckElementChangeBySide(x, y, center_element, border_element,
11727                                  CE_TOUCHING_X, border_side);
11728
11729     /* check for change of border element */
11730     CheckElementChangeBySide(xx, yy, border_element, center_element,
11731                              CE_TOUCHING_X, center_side);
11732   }
11733 }
11734
11735 #endif
11736
11737 void TestIfElementHitsCustomElement(int x, int y, int direction)
11738 {
11739   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11740   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11741   int hitx = x + dx, hity = y + dy;
11742   int hitting_element = Feld[x][y];
11743   int touched_element;
11744
11745   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11746     return;
11747
11748   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11749                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11750
11751   if (IN_LEV_FIELD(hitx, hity))
11752   {
11753     int opposite_direction = MV_DIR_OPPOSITE(direction);
11754     int hitting_side = direction;
11755     int touched_side = opposite_direction;
11756     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11757                           MovDir[hitx][hity] != direction ||
11758                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11759
11760     object_hit = TRUE;
11761
11762     if (object_hit)
11763     {
11764       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11765                                CE_HITTING_X, touched_side);
11766
11767       CheckElementChangeBySide(hitx, hity, touched_element,
11768                                hitting_element, CE_HIT_BY_X, hitting_side);
11769
11770       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11771                                CE_HIT_BY_SOMETHING, opposite_direction);
11772     }
11773   }
11774
11775   /* "hitting something" is also true when hitting the playfield border */
11776   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11777                            CE_HITTING_SOMETHING, direction);
11778 }
11779
11780 #if 0
11781 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11782 {
11783   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11784   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11785   int hitx = x + dx, hity = y + dy;
11786   int hitting_element = Feld[x][y];
11787   int touched_element;
11788 #if 0
11789   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11790                         !IS_FREE(hitx, hity) &&
11791                         (!IS_MOVING(hitx, hity) ||
11792                          MovDir[hitx][hity] != direction ||
11793                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11794 #endif
11795
11796   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11797     return;
11798
11799 #if 0
11800   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11801     return;
11802 #endif
11803
11804   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11805                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11806
11807   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11808                            EP_CAN_SMASH_EVERYTHING, direction);
11809
11810   if (IN_LEV_FIELD(hitx, hity))
11811   {
11812     int opposite_direction = MV_DIR_OPPOSITE(direction);
11813     int hitting_side = direction;
11814     int touched_side = opposite_direction;
11815 #if 0
11816     int touched_element = MovingOrBlocked2Element(hitx, hity);
11817 #endif
11818 #if 1
11819     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11820                           MovDir[hitx][hity] != direction ||
11821                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11822
11823     object_hit = TRUE;
11824 #endif
11825
11826     if (object_hit)
11827     {
11828       int i;
11829
11830       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11831                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11832
11833       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11834                                CE_OTHER_IS_SMASHING, touched_side);
11835
11836       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11837                                CE_OTHER_GETS_SMASHED, hitting_side);
11838     }
11839   }
11840 }
11841 #endif
11842
11843 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11844 {
11845   int i, kill_x = -1, kill_y = -1;
11846
11847   int bad_element = -1;
11848   static int test_xy[4][2] =
11849   {
11850     { 0, -1 },
11851     { -1, 0 },
11852     { +1, 0 },
11853     { 0, +1 }
11854   };
11855   static int test_dir[4] =
11856   {
11857     MV_UP,
11858     MV_LEFT,
11859     MV_RIGHT,
11860     MV_DOWN
11861   };
11862
11863   for (i = 0; i < NUM_DIRECTIONS; i++)
11864   {
11865     int test_x, test_y, test_move_dir, test_element;
11866
11867     test_x = good_x + test_xy[i][0];
11868     test_y = good_y + test_xy[i][1];
11869
11870     if (!IN_LEV_FIELD(test_x, test_y))
11871       continue;
11872
11873     test_move_dir =
11874       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11875
11876     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11877
11878     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11879        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11880     */
11881     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11882         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11883     {
11884       kill_x = test_x;
11885       kill_y = test_y;
11886       bad_element = test_element;
11887
11888       break;
11889     }
11890   }
11891
11892   if (kill_x != -1 || kill_y != -1)
11893   {
11894     if (IS_PLAYER(good_x, good_y))
11895     {
11896       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11897
11898       if (player->shield_deadly_time_left > 0 &&
11899           !IS_INDESTRUCTIBLE(bad_element))
11900         Bang(kill_x, kill_y);
11901       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11902         KillPlayer(player);
11903     }
11904     else
11905       Bang(good_x, good_y);
11906   }
11907 }
11908
11909 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11910 {
11911   int i, kill_x = -1, kill_y = -1;
11912   int bad_element = Feld[bad_x][bad_y];
11913   static int test_xy[4][2] =
11914   {
11915     { 0, -1 },
11916     { -1, 0 },
11917     { +1, 0 },
11918     { 0, +1 }
11919   };
11920   static int touch_dir[4] =
11921   {
11922     MV_LEFT | MV_RIGHT,
11923     MV_UP   | MV_DOWN,
11924     MV_UP   | MV_DOWN,
11925     MV_LEFT | MV_RIGHT
11926   };
11927   static int test_dir[4] =
11928   {
11929     MV_UP,
11930     MV_LEFT,
11931     MV_RIGHT,
11932     MV_DOWN
11933   };
11934
11935   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11936     return;
11937
11938   for (i = 0; i < NUM_DIRECTIONS; i++)
11939   {
11940     int test_x, test_y, test_move_dir, test_element;
11941
11942     test_x = bad_x + test_xy[i][0];
11943     test_y = bad_y + test_xy[i][1];
11944     if (!IN_LEV_FIELD(test_x, test_y))
11945       continue;
11946
11947     test_move_dir =
11948       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11949
11950     test_element = Feld[test_x][test_y];
11951
11952     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11953        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11954     */
11955     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11956         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11957     {
11958       /* good thing is player or penguin that does not move away */
11959       if (IS_PLAYER(test_x, test_y))
11960       {
11961         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11962
11963         if (bad_element == EL_ROBOT && player->is_moving)
11964           continue;     /* robot does not kill player if he is moving */
11965
11966         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11967         {
11968           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11969             continue;           /* center and border element do not touch */
11970         }
11971
11972         kill_x = test_x;
11973         kill_y = test_y;
11974         break;
11975       }
11976       else if (test_element == EL_PENGUIN)
11977       {
11978         kill_x = test_x;
11979         kill_y = test_y;
11980         break;
11981       }
11982     }
11983   }
11984
11985   if (kill_x != -1 || kill_y != -1)
11986   {
11987     if (IS_PLAYER(kill_x, kill_y))
11988     {
11989       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11990
11991       if (player->shield_deadly_time_left > 0 &&
11992           !IS_INDESTRUCTIBLE(bad_element))
11993         Bang(bad_x, bad_y);
11994       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11995         KillPlayer(player);
11996     }
11997     else
11998       Bang(kill_x, kill_y);
11999   }
12000 }
12001
12002 void TestIfPlayerTouchesBadThing(int x, int y)
12003 {
12004   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12005 }
12006
12007 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12008 {
12009   TestIfGoodThingHitsBadThing(x, y, move_dir);
12010 }
12011
12012 void TestIfBadThingTouchesPlayer(int x, int y)
12013 {
12014   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12015 }
12016
12017 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12018 {
12019   TestIfBadThingHitsGoodThing(x, y, move_dir);
12020 }
12021
12022 void TestIfFriendTouchesBadThing(int x, int y)
12023 {
12024   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12025 }
12026
12027 void TestIfBadThingTouchesFriend(int x, int y)
12028 {
12029   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12030 }
12031
12032 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12033 {
12034   int i, kill_x = bad_x, kill_y = bad_y;
12035   static int xy[4][2] =
12036   {
12037     { 0, -1 },
12038     { -1, 0 },
12039     { +1, 0 },
12040     { 0, +1 }
12041   };
12042
12043   for (i = 0; i < NUM_DIRECTIONS; i++)
12044   {
12045     int x, y, element;
12046
12047     x = bad_x + xy[i][0];
12048     y = bad_y + xy[i][1];
12049     if (!IN_LEV_FIELD(x, y))
12050       continue;
12051
12052     element = Feld[x][y];
12053     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12054         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12055     {
12056       kill_x = x;
12057       kill_y = y;
12058       break;
12059     }
12060   }
12061
12062   if (kill_x != bad_x || kill_y != bad_y)
12063     Bang(bad_x, bad_y);
12064 }
12065
12066 void KillPlayer(struct PlayerInfo *player)
12067 {
12068   int jx = player->jx, jy = player->jy;
12069
12070   if (!player->active)
12071     return;
12072
12073   /* the following code was introduced to prevent an infinite loop when calling
12074      -> Bang()
12075      -> CheckTriggeredElementChangeExt()
12076      -> ExecuteCustomElementAction()
12077      -> KillPlayer()
12078      -> (infinitely repeating the above sequence of function calls)
12079      which occurs when killing the player while having a CE with the setting
12080      "kill player X when explosion of <player X>"; the solution using a new
12081      field "player->killed" was chosen for backwards compatibility, although
12082      clever use of the fields "player->active" etc. would probably also work */
12083 #if 1
12084   if (player->killed)
12085     return;
12086 #endif
12087
12088   player->killed = TRUE;
12089
12090   /* remove accessible field at the player's position */
12091   Feld[jx][jy] = EL_EMPTY;
12092
12093   /* deactivate shield (else Bang()/Explode() would not work right) */
12094   player->shield_normal_time_left = 0;
12095   player->shield_deadly_time_left = 0;
12096
12097   Bang(jx, jy);
12098   BuryPlayer(player);
12099 }
12100
12101 static void KillPlayerUnlessEnemyProtected(int x, int y)
12102 {
12103   if (!PLAYER_ENEMY_PROTECTED(x, y))
12104     KillPlayer(PLAYERINFO(x, y));
12105 }
12106
12107 static void KillPlayerUnlessExplosionProtected(int x, int y)
12108 {
12109   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
12110     KillPlayer(PLAYERINFO(x, y));
12111 }
12112
12113 void BuryPlayer(struct PlayerInfo *player)
12114 {
12115   int jx = player->jx, jy = player->jy;
12116
12117   if (!player->active)
12118     return;
12119
12120   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
12121   PlayLevelSound(jx, jy, SND_GAME_LOSING);
12122
12123   player->GameOver = TRUE;
12124   RemovePlayer(player);
12125 }
12126
12127 void RemovePlayer(struct PlayerInfo *player)
12128 {
12129   int jx = player->jx, jy = player->jy;
12130   int i, found = FALSE;
12131
12132   player->present = FALSE;
12133   player->active = FALSE;
12134
12135   if (!ExplodeField[jx][jy])
12136     StorePlayer[jx][jy] = 0;
12137
12138   if (player->is_moving)
12139     DrawLevelField(player->last_jx, player->last_jy);
12140
12141   for (i = 0; i < MAX_PLAYERS; i++)
12142     if (stored_player[i].active)
12143       found = TRUE;
12144
12145   if (!found)
12146     AllPlayersGone = TRUE;
12147
12148   ExitX = ZX = jx;
12149   ExitY = ZY = jy;
12150 }
12151
12152 #if USE_NEW_SNAP_DELAY
12153 static void setFieldForSnapping(int x, int y, int element, int direction)
12154 {
12155   struct ElementInfo *ei = &element_info[element];
12156   int direction_bit = MV_DIR_TO_BIT(direction);
12157   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
12158   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
12159                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
12160
12161   Feld[x][y] = EL_ELEMENT_SNAPPING;
12162   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
12163
12164   ResetGfxAnimation(x, y);
12165
12166   GfxElement[x][y] = element;
12167   GfxAction[x][y] = action;
12168   GfxDir[x][y] = direction;
12169   GfxFrame[x][y] = -1;
12170 }
12171 #endif
12172
12173 /*
12174   =============================================================================
12175   checkDiagonalPushing()
12176   -----------------------------------------------------------------------------
12177   check if diagonal input device direction results in pushing of object
12178   (by checking if the alternative direction is walkable, diggable, ...)
12179   =============================================================================
12180 */
12181
12182 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12183                                     int x, int y, int real_dx, int real_dy)
12184 {
12185   int jx, jy, dx, dy, xx, yy;
12186
12187   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
12188     return TRUE;
12189
12190   /* diagonal direction: check alternative direction */
12191   jx = player->jx;
12192   jy = player->jy;
12193   dx = x - jx;
12194   dy = y - jy;
12195   xx = jx + (dx == 0 ? real_dx : 0);
12196   yy = jy + (dy == 0 ? real_dy : 0);
12197
12198   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12199 }
12200
12201 /*
12202   =============================================================================
12203   DigField()
12204   -----------------------------------------------------------------------------
12205   x, y:                 field next to player (non-diagonal) to try to dig to
12206   real_dx, real_dy:     direction as read from input device (can be diagonal)
12207   =============================================================================
12208 */
12209
12210 int DigField(struct PlayerInfo *player,
12211              int oldx, int oldy, int x, int y,
12212              int real_dx, int real_dy, int mode)
12213 {
12214   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12215   boolean player_was_pushing = player->is_pushing;
12216   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12217   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12218   int jx = oldx, jy = oldy;
12219   int dx = x - jx, dy = y - jy;
12220   int nextx = x + dx, nexty = y + dy;
12221   int move_direction = (dx == -1 ? MV_LEFT  :
12222                         dx == +1 ? MV_RIGHT :
12223                         dy == -1 ? MV_UP    :
12224                         dy == +1 ? MV_DOWN  : MV_NONE);
12225   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12226   int dig_side = MV_DIR_OPPOSITE(move_direction);
12227   int old_element = Feld[jx][jy];
12228 #if USE_FIXED_DONT_RUN_INTO
12229   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12230 #else
12231   int element;
12232 #endif
12233   int collect_count;
12234
12235   if (is_player)                /* function can also be called by EL_PENGUIN */
12236   {
12237     if (player->MovPos == 0)
12238     {
12239       player->is_digging = FALSE;
12240       player->is_collecting = FALSE;
12241     }
12242
12243     if (player->MovPos == 0)    /* last pushing move finished */
12244       player->is_pushing = FALSE;
12245
12246     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
12247     {
12248       player->is_switching = FALSE;
12249       player->push_delay = -1;
12250
12251       return MP_NO_ACTION;
12252     }
12253   }
12254
12255 #if !USE_FIXED_DONT_RUN_INTO
12256   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12257     return MP_NO_ACTION;
12258 #endif
12259
12260   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12261     old_element = Back[jx][jy];
12262
12263   /* in case of element dropped at player position, check background */
12264   else if (Back[jx][jy] != EL_EMPTY &&
12265            game.engine_version >= VERSION_IDENT(2,2,0,0))
12266     old_element = Back[jx][jy];
12267
12268   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12269     return MP_NO_ACTION;        /* field has no opening in this direction */
12270
12271   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12272     return MP_NO_ACTION;        /* field has no opening in this direction */
12273
12274 #if USE_FIXED_DONT_RUN_INTO
12275   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12276   {
12277     SplashAcid(x, y);
12278
12279     Feld[jx][jy] = player->artwork_element;
12280     InitMovingField(jx, jy, MV_DOWN);
12281     Store[jx][jy] = EL_ACID;
12282     ContinueMoving(jx, jy);
12283     BuryPlayer(player);
12284
12285     return MP_DONT_RUN_INTO;
12286   }
12287 #endif
12288
12289 #if USE_FIXED_DONT_RUN_INTO
12290   if (player_can_move && DONT_RUN_INTO(element))
12291   {
12292     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12293
12294     return MP_DONT_RUN_INTO;
12295   }
12296 #endif
12297
12298 #if USE_FIXED_DONT_RUN_INTO
12299   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12300     return MP_NO_ACTION;
12301 #endif
12302
12303 #if !USE_FIXED_DONT_RUN_INTO
12304   element = Feld[x][y];
12305 #endif
12306
12307   collect_count = element_info[element].collect_count_initial;
12308
12309   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12310     return MP_NO_ACTION;
12311
12312   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12313     player_can_move = player_can_move_or_snap;
12314
12315   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12316       game.engine_version >= VERSION_IDENT(2,2,0,0))
12317   {
12318     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12319                                player->index_bit, dig_side);
12320     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12321                                         player->index_bit, dig_side);
12322
12323     if (element == EL_DC_LANDMINE)
12324       Bang(x, y);
12325
12326     if (Feld[x][y] != element)          /* field changed by snapping */
12327       return MP_ACTION;
12328
12329     return MP_NO_ACTION;
12330   }
12331
12332 #if USE_PLAYER_GRAVITY
12333   if (player->gravity && is_player && !player->is_auto_moving &&
12334       canFallDown(player) && move_direction != MV_DOWN &&
12335       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12336     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12337 #else
12338   if (game.gravity && is_player && !player->is_auto_moving &&
12339       canFallDown(player) && move_direction != MV_DOWN &&
12340       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12341     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12342 #endif
12343
12344   if (player_can_move &&
12345       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12346   {
12347     int sound_element = SND_ELEMENT(element);
12348     int sound_action = ACTION_WALKING;
12349
12350     if (IS_RND_GATE(element))
12351     {
12352       if (!player->key[RND_GATE_NR(element)])
12353         return MP_NO_ACTION;
12354     }
12355     else if (IS_RND_GATE_GRAY(element))
12356     {
12357       if (!player->key[RND_GATE_GRAY_NR(element)])
12358         return MP_NO_ACTION;
12359     }
12360     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12361     {
12362       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12363         return MP_NO_ACTION;
12364     }
12365     else if (element == EL_EXIT_OPEN ||
12366              element == EL_EM_EXIT_OPEN ||
12367              element == EL_STEEL_EXIT_OPEN ||
12368              element == EL_EM_STEEL_EXIT_OPEN ||
12369              element == EL_SP_EXIT_OPEN ||
12370              element == EL_SP_EXIT_OPENING)
12371     {
12372       sound_action = ACTION_PASSING;    /* player is passing exit */
12373     }
12374     else if (element == EL_EMPTY)
12375     {
12376       sound_action = ACTION_MOVING;             /* nothing to walk on */
12377     }
12378
12379     /* play sound from background or player, whatever is available */
12380     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12381       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12382     else
12383       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12384   }
12385   else if (player_can_move &&
12386            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12387   {
12388     if (!ACCESS_FROM(element, opposite_direction))
12389       return MP_NO_ACTION;      /* field not accessible from this direction */
12390
12391     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12392       return MP_NO_ACTION;
12393
12394     if (IS_EM_GATE(element))
12395     {
12396       if (!player->key[EM_GATE_NR(element)])
12397         return MP_NO_ACTION;
12398     }
12399     else if (IS_EM_GATE_GRAY(element))
12400     {
12401       if (!player->key[EM_GATE_GRAY_NR(element)])
12402         return MP_NO_ACTION;
12403     }
12404     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12405     {
12406       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12407         return MP_NO_ACTION;
12408     }
12409     else if (IS_EMC_GATE(element))
12410     {
12411       if (!player->key[EMC_GATE_NR(element)])
12412         return MP_NO_ACTION;
12413     }
12414     else if (IS_EMC_GATE_GRAY(element))
12415     {
12416       if (!player->key[EMC_GATE_GRAY_NR(element)])
12417         return MP_NO_ACTION;
12418     }
12419     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12420     {
12421       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12422         return MP_NO_ACTION;
12423     }
12424     else if (element == EL_DC_GATE_WHITE ||
12425              element == EL_DC_GATE_WHITE_GRAY ||
12426              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12427     {
12428       if (player->num_white_keys == 0)
12429         return MP_NO_ACTION;
12430
12431       player->num_white_keys--;
12432     }
12433     else if (IS_SP_PORT(element))
12434     {
12435       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12436           element == EL_SP_GRAVITY_PORT_RIGHT ||
12437           element == EL_SP_GRAVITY_PORT_UP ||
12438           element == EL_SP_GRAVITY_PORT_DOWN)
12439 #if USE_PLAYER_GRAVITY
12440         player->gravity = !player->gravity;
12441 #else
12442         game.gravity = !game.gravity;
12443 #endif
12444       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12445                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12446                element == EL_SP_GRAVITY_ON_PORT_UP ||
12447                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12448 #if USE_PLAYER_GRAVITY
12449         player->gravity = TRUE;
12450 #else
12451         game.gravity = TRUE;
12452 #endif
12453       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12454                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12455                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12456                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12457 #if USE_PLAYER_GRAVITY
12458         player->gravity = FALSE;
12459 #else
12460         game.gravity = FALSE;
12461 #endif
12462     }
12463
12464     /* automatically move to the next field with double speed */
12465     player->programmed_action = move_direction;
12466
12467     if (player->move_delay_reset_counter == 0)
12468     {
12469       player->move_delay_reset_counter = 2;     /* two double speed steps */
12470
12471       DOUBLE_PLAYER_SPEED(player);
12472     }
12473
12474     PlayLevelSoundAction(x, y, ACTION_PASSING);
12475   }
12476   else if (player_can_move_or_snap && IS_DIGGABLE(element))
12477   {
12478     RemoveField(x, y);
12479
12480     if (mode != DF_SNAP)
12481     {
12482       GfxElement[x][y] = GFX_ELEMENT(element);
12483       player->is_digging = TRUE;
12484     }
12485
12486     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12487
12488     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12489                                         player->index_bit, dig_side);
12490
12491     if (mode == DF_SNAP)
12492     {
12493 #if USE_NEW_SNAP_DELAY
12494       if (level.block_snap_field)
12495         setFieldForSnapping(x, y, element, move_direction);
12496       else
12497         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12498 #else
12499       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12500 #endif
12501
12502       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12503                                           player->index_bit, dig_side);
12504     }
12505   }
12506   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12507   {
12508     RemoveField(x, y);
12509
12510     if (is_player && mode != DF_SNAP)
12511     {
12512       GfxElement[x][y] = element;
12513       player->is_collecting = TRUE;
12514     }
12515
12516     if (element == EL_SPEED_PILL)
12517     {
12518       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12519     }
12520     else if (element == EL_EXTRA_TIME && level.time > 0)
12521     {
12522       TimeLeft += level.extra_time;
12523       DrawGameValue_Time(TimeLeft);
12524     }
12525     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12526     {
12527       player->shield_normal_time_left += level.shield_normal_time;
12528       if (element == EL_SHIELD_DEADLY)
12529         player->shield_deadly_time_left += level.shield_deadly_time;
12530     }
12531     else if (element == EL_DYNAMITE ||
12532              element == EL_EM_DYNAMITE ||
12533              element == EL_SP_DISK_RED)
12534     {
12535       if (player->inventory_size < MAX_INVENTORY_SIZE)
12536         player->inventory_element[player->inventory_size++] = element;
12537
12538       DrawGameDoorValues();
12539     }
12540     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12541     {
12542       player->dynabomb_count++;
12543       player->dynabombs_left++;
12544     }
12545     else if (element == EL_DYNABOMB_INCREASE_SIZE)
12546     {
12547       player->dynabomb_size++;
12548     }
12549     else if (element == EL_DYNABOMB_INCREASE_POWER)
12550     {
12551       player->dynabomb_xl = TRUE;
12552     }
12553     else if (IS_KEY(element))
12554     {
12555       player->key[KEY_NR(element)] = TRUE;
12556
12557       DrawGameDoorValues();
12558     }
12559     else if (element == EL_DC_KEY_WHITE)
12560     {
12561       player->num_white_keys++;
12562
12563       /* display white keys? */
12564       /* DrawGameDoorValues(); */
12565     }
12566     else if (IS_ENVELOPE(element))
12567     {
12568       player->show_envelope = element;
12569     }
12570     else if (element == EL_EMC_LENSES)
12571     {
12572       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12573
12574       RedrawAllInvisibleElementsForLenses();
12575     }
12576     else if (element == EL_EMC_MAGNIFIER)
12577     {
12578       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12579
12580       RedrawAllInvisibleElementsForMagnifier();
12581     }
12582     else if (IS_DROPPABLE(element) ||
12583              IS_THROWABLE(element))     /* can be collected and dropped */
12584     {
12585       int i;
12586
12587       if (collect_count == 0)
12588         player->inventory_infinite_element = element;
12589       else
12590         for (i = 0; i < collect_count; i++)
12591           if (player->inventory_size < MAX_INVENTORY_SIZE)
12592             player->inventory_element[player->inventory_size++] = element;
12593
12594       DrawGameDoorValues();
12595     }
12596     else if (collect_count > 0)
12597     {
12598       local_player->gems_still_needed -= collect_count;
12599       if (local_player->gems_still_needed < 0)
12600         local_player->gems_still_needed = 0;
12601
12602       DrawGameValue_Emeralds(local_player->gems_still_needed);
12603     }
12604
12605     RaiseScoreElement(element);
12606     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12607
12608     if (is_player)
12609       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12610                                           player->index_bit, dig_side);
12611
12612     if (mode == DF_SNAP)
12613     {
12614 #if USE_NEW_SNAP_DELAY
12615       if (level.block_snap_field)
12616         setFieldForSnapping(x, y, element, move_direction);
12617       else
12618         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12619 #else
12620       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12621 #endif
12622
12623       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12624                                           player->index_bit, dig_side);
12625     }
12626   }
12627   else if (player_can_move_or_snap && IS_PUSHABLE(element))
12628   {
12629     if (mode == DF_SNAP && element != EL_BD_ROCK)
12630       return MP_NO_ACTION;
12631
12632     if (CAN_FALL(element) && dy)
12633       return MP_NO_ACTION;
12634
12635     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12636         !(element == EL_SPRING && level.use_spring_bug))
12637       return MP_NO_ACTION;
12638
12639     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12640         ((move_direction & MV_VERTICAL &&
12641           ((element_info[element].move_pattern & MV_LEFT &&
12642             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12643            (element_info[element].move_pattern & MV_RIGHT &&
12644             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12645          (move_direction & MV_HORIZONTAL &&
12646           ((element_info[element].move_pattern & MV_UP &&
12647             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12648            (element_info[element].move_pattern & MV_DOWN &&
12649             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12650       return MP_NO_ACTION;
12651
12652     /* do not push elements already moving away faster than player */
12653     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12654         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12655       return MP_NO_ACTION;
12656
12657     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12658     {
12659       if (player->push_delay_value == -1 || !player_was_pushing)
12660         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12661     }
12662     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12663     {
12664       if (player->push_delay_value == -1)
12665         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12666     }
12667     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12668     {
12669       if (!player->is_pushing)
12670         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12671     }
12672
12673     player->is_pushing = TRUE;
12674     player->is_active = TRUE;
12675
12676     if (!(IN_LEV_FIELD(nextx, nexty) &&
12677           (IS_FREE(nextx, nexty) ||
12678            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12679             IS_SB_ELEMENT(element)))))
12680       return MP_NO_ACTION;
12681
12682     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12683       return MP_NO_ACTION;
12684
12685     if (player->push_delay == -1)       /* new pushing; restart delay */
12686       player->push_delay = 0;
12687
12688     if (player->push_delay < player->push_delay_value &&
12689         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12690         element != EL_SPRING && element != EL_BALLOON)
12691     {
12692       /* make sure that there is no move delay before next try to push */
12693       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12694         player->move_delay = 0;
12695
12696       return MP_NO_ACTION;
12697     }
12698
12699     if (IS_SB_ELEMENT(element))
12700     {
12701       if (element == EL_SOKOBAN_FIELD_FULL)
12702       {
12703         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12704         local_player->sokobanfields_still_needed++;
12705       }
12706
12707       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12708       {
12709         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12710         local_player->sokobanfields_still_needed--;
12711       }
12712
12713       Feld[x][y] = EL_SOKOBAN_OBJECT;
12714
12715       if (Back[x][y] == Back[nextx][nexty])
12716         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12717       else if (Back[x][y] != 0)
12718         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12719                                     ACTION_EMPTYING);
12720       else
12721         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12722                                     ACTION_FILLING);
12723
12724       if (local_player->sokobanfields_still_needed == 0 &&
12725           game.emulation == EMU_SOKOBAN)
12726       {
12727         PlayerWins(player);
12728
12729         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12730       }
12731     }
12732     else
12733       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12734
12735     InitMovingField(x, y, move_direction);
12736     GfxAction[x][y] = ACTION_PUSHING;
12737
12738     if (mode == DF_SNAP)
12739       ContinueMoving(x, y);
12740     else
12741       MovPos[x][y] = (dx != 0 ? dx : dy);
12742
12743     Pushed[x][y] = TRUE;
12744     Pushed[nextx][nexty] = TRUE;
12745
12746     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12747       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12748     else
12749       player->push_delay_value = -1;    /* get new value later */
12750
12751     /* check for element change _after_ element has been pushed */
12752     if (game.use_change_when_pushing_bug)
12753     {
12754       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12755                                  player->index_bit, dig_side);
12756       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12757                                           player->index_bit, dig_side);
12758     }
12759   }
12760   else if (IS_SWITCHABLE(element))
12761   {
12762     if (PLAYER_SWITCHING(player, x, y))
12763     {
12764       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12765                                           player->index_bit, dig_side);
12766
12767       return MP_ACTION;
12768     }
12769
12770     player->is_switching = TRUE;
12771     player->switch_x = x;
12772     player->switch_y = y;
12773
12774     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12775
12776     if (element == EL_ROBOT_WHEEL)
12777     {
12778       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12779       ZX = x;
12780       ZY = y;
12781
12782       DrawLevelField(x, y);
12783     }
12784     else if (element == EL_SP_TERMINAL)
12785     {
12786       int xx, yy;
12787
12788       SCAN_PLAYFIELD(xx, yy)
12789       {
12790         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12791           Bang(xx, yy);
12792         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12793           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12794       }
12795     }
12796     else if (IS_BELT_SWITCH(element))
12797     {
12798       ToggleBeltSwitch(x, y);
12799     }
12800     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12801              element == EL_SWITCHGATE_SWITCH_DOWN ||
12802              element == EL_DC_SWITCHGATE_SWITCH_UP ||
12803              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12804     {
12805       ToggleSwitchgateSwitch(x, y);
12806     }
12807     else if (element == EL_LIGHT_SWITCH ||
12808              element == EL_LIGHT_SWITCH_ACTIVE)
12809     {
12810       ToggleLightSwitch(x, y);
12811     }
12812     else if (element == EL_TIMEGATE_SWITCH ||
12813              element == EL_DC_TIMEGATE_SWITCH)
12814     {
12815       ActivateTimegateSwitch(x, y);
12816     }
12817     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12818              element == EL_BALLOON_SWITCH_RIGHT ||
12819              element == EL_BALLOON_SWITCH_UP    ||
12820              element == EL_BALLOON_SWITCH_DOWN  ||
12821              element == EL_BALLOON_SWITCH_NONE  ||
12822              element == EL_BALLOON_SWITCH_ANY)
12823     {
12824       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12825                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12826                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12827                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12828                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12829                              move_direction);
12830     }
12831     else if (element == EL_LAMP)
12832     {
12833       Feld[x][y] = EL_LAMP_ACTIVE;
12834       local_player->lights_still_needed--;
12835
12836       ResetGfxAnimation(x, y);
12837       DrawLevelField(x, y);
12838     }
12839     else if (element == EL_TIME_ORB_FULL)
12840     {
12841       Feld[x][y] = EL_TIME_ORB_EMPTY;
12842
12843       if (level.time > 0 || level.use_time_orb_bug)
12844       {
12845         TimeLeft += level.time_orb_time;
12846         DrawGameValue_Time(TimeLeft);
12847       }
12848
12849       ResetGfxAnimation(x, y);
12850       DrawLevelField(x, y);
12851     }
12852     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12853              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12854     {
12855       int xx, yy;
12856
12857       game.ball_state = !game.ball_state;
12858
12859       SCAN_PLAYFIELD(xx, yy)
12860       {
12861         int e = Feld[xx][yy];
12862
12863         if (game.ball_state)
12864         {
12865           if (e == EL_EMC_MAGIC_BALL)
12866             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12867           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12868             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12869         }
12870         else
12871         {
12872           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12873             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12874           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12875             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12876         }
12877       }
12878     }
12879
12880     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12881                                         player->index_bit, dig_side);
12882
12883     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12884                                         player->index_bit, dig_side);
12885
12886     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12887                                         player->index_bit, dig_side);
12888
12889     return MP_ACTION;
12890   }
12891   else
12892   {
12893     if (!PLAYER_SWITCHING(player, x, y))
12894     {
12895       player->is_switching = TRUE;
12896       player->switch_x = x;
12897       player->switch_y = y;
12898
12899       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12900                                  player->index_bit, dig_side);
12901       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12902                                           player->index_bit, dig_side);
12903
12904       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12905                                  player->index_bit, dig_side);
12906       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12907                                           player->index_bit, dig_side);
12908     }
12909
12910     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12911                                player->index_bit, dig_side);
12912     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12913                                         player->index_bit, dig_side);
12914
12915     return MP_NO_ACTION;
12916   }
12917
12918   player->push_delay = -1;
12919
12920   if (is_player)                /* function can also be called by EL_PENGUIN */
12921   {
12922     if (Feld[x][y] != element)          /* really digged/collected something */
12923     {
12924       player->is_collecting = !player->is_digging;
12925       player->is_active = TRUE;
12926     }
12927   }
12928
12929   return MP_MOVING;
12930 }
12931
12932 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12933 {
12934   int jx = player->jx, jy = player->jy;
12935   int x = jx + dx, y = jy + dy;
12936   int snap_direction = (dx == -1 ? MV_LEFT  :
12937                         dx == +1 ? MV_RIGHT :
12938                         dy == -1 ? MV_UP    :
12939                         dy == +1 ? MV_DOWN  : MV_NONE);
12940   boolean can_continue_snapping = (level.continuous_snapping &&
12941                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12942
12943   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12944     return FALSE;
12945
12946   if (!player->active || !IN_LEV_FIELD(x, y))
12947     return FALSE;
12948
12949   if (dx && dy)
12950     return FALSE;
12951
12952   if (!dx && !dy)
12953   {
12954     if (player->MovPos == 0)
12955       player->is_pushing = FALSE;
12956
12957     player->is_snapping = FALSE;
12958
12959     if (player->MovPos == 0)
12960     {
12961       player->is_moving = FALSE;
12962       player->is_digging = FALSE;
12963       player->is_collecting = FALSE;
12964     }
12965
12966     return FALSE;
12967   }
12968
12969 #if USE_NEW_CONTINUOUS_SNAPPING
12970   /* prevent snapping with already pressed snap key when not allowed */
12971   if (player->is_snapping && !can_continue_snapping)
12972     return FALSE;
12973 #else
12974   if (player->is_snapping)
12975     return FALSE;
12976 #endif
12977
12978   player->MovDir = snap_direction;
12979
12980   if (player->MovPos == 0)
12981   {
12982     player->is_moving = FALSE;
12983     player->is_digging = FALSE;
12984     player->is_collecting = FALSE;
12985   }
12986
12987   player->is_dropping = FALSE;
12988   player->is_dropping_pressed = FALSE;
12989   player->drop_pressed_delay = 0;
12990
12991   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12992     return FALSE;
12993
12994   player->is_snapping = TRUE;
12995   player->is_active = TRUE;
12996
12997   if (player->MovPos == 0)
12998   {
12999     player->is_moving = FALSE;
13000     player->is_digging = FALSE;
13001     player->is_collecting = FALSE;
13002   }
13003
13004   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13005     DrawLevelField(player->last_jx, player->last_jy);
13006
13007   DrawLevelField(x, y);
13008
13009   return TRUE;
13010 }
13011
13012 boolean DropElement(struct PlayerInfo *player)
13013 {
13014   int old_element, new_element;
13015   int dropx = player->jx, dropy = player->jy;
13016   int drop_direction = player->MovDir;
13017   int drop_side = drop_direction;
13018   int drop_element = (player->inventory_size > 0 ?
13019                       player->inventory_element[player->inventory_size - 1] :
13020                       player->inventory_infinite_element != EL_UNDEFINED ?
13021                       player->inventory_infinite_element :
13022                       player->dynabombs_left > 0 ?
13023                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
13024                       EL_UNDEFINED);
13025
13026   player->is_dropping_pressed = TRUE;
13027
13028   /* do not drop an element on top of another element; when holding drop key
13029      pressed without moving, dropped element must move away before the next
13030      element can be dropped (this is especially important if the next element
13031      is dynamite, which can be placed on background for historical reasons) */
13032   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13033     return MP_ACTION;
13034
13035   if (IS_THROWABLE(drop_element))
13036   {
13037     dropx += GET_DX_FROM_DIR(drop_direction);
13038     dropy += GET_DY_FROM_DIR(drop_direction);
13039
13040     if (!IN_LEV_FIELD(dropx, dropy))
13041       return FALSE;
13042   }
13043
13044   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13045   new_element = drop_element;           /* default: no change when dropping */
13046
13047   /* check if player is active, not moving and ready to drop */
13048   if (!player->active || player->MovPos || player->drop_delay > 0)
13049     return FALSE;
13050
13051   /* check if player has anything that can be dropped */
13052   if (new_element == EL_UNDEFINED)
13053     return FALSE;
13054
13055   /* check if drop key was pressed long enough for EM style dynamite */
13056   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13057     return FALSE;
13058
13059   /* check if anything can be dropped at the current position */
13060   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13061     return FALSE;
13062
13063   /* collected custom elements can only be dropped on empty fields */
13064   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13065     return FALSE;
13066
13067   if (old_element != EL_EMPTY)
13068     Back[dropx][dropy] = old_element;   /* store old element on this field */
13069
13070   ResetGfxAnimation(dropx, dropy);
13071   ResetRandomAnimationValue(dropx, dropy);
13072
13073   if (player->inventory_size > 0 ||
13074       player->inventory_infinite_element != EL_UNDEFINED)
13075   {
13076     if (player->inventory_size > 0)
13077     {
13078       player->inventory_size--;
13079
13080       DrawGameDoorValues();
13081
13082       if (new_element == EL_DYNAMITE)
13083         new_element = EL_DYNAMITE_ACTIVE;
13084       else if (new_element == EL_EM_DYNAMITE)
13085         new_element = EL_EM_DYNAMITE_ACTIVE;
13086       else if (new_element == EL_SP_DISK_RED)
13087         new_element = EL_SP_DISK_RED_ACTIVE;
13088     }
13089
13090     Feld[dropx][dropy] = new_element;
13091
13092     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13093       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13094                           el2img(Feld[dropx][dropy]), 0);
13095
13096     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13097
13098     /* needed if previous element just changed to "empty" in the last frame */
13099     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13100
13101     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13102                                player->index_bit, drop_side);
13103     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13104                                         CE_PLAYER_DROPS_X,
13105                                         player->index_bit, drop_side);
13106
13107     TestIfElementTouchesCustomElement(dropx, dropy);
13108   }
13109   else          /* player is dropping a dyna bomb */
13110   {
13111     player->dynabombs_left--;
13112
13113     Feld[dropx][dropy] = new_element;
13114
13115     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13116       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13117                           el2img(Feld[dropx][dropy]), 0);
13118
13119     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13120   }
13121
13122   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13123     InitField_WithBug1(dropx, dropy, FALSE);
13124
13125   new_element = Feld[dropx][dropy];     /* element might have changed */
13126
13127   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13128       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13129   {
13130     int move_direction, nextx, nexty;
13131
13132     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13133       MovDir[dropx][dropy] = drop_direction;
13134
13135     move_direction = MovDir[dropx][dropy];
13136     nextx = dropx + GET_DX_FROM_DIR(move_direction);
13137     nexty = dropy + GET_DY_FROM_DIR(move_direction);
13138
13139     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13140
13141 #if USE_FIX_IMPACT_COLLISION
13142     /* do not cause impact style collision by dropping elements that can fall */
13143     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13144 #else
13145     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13146 #endif
13147   }
13148
13149   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13150   player->is_dropping = TRUE;
13151
13152   player->drop_pressed_delay = 0;
13153   player->is_dropping_pressed = FALSE;
13154
13155   player->drop_x = dropx;
13156   player->drop_y = dropy;
13157
13158   return TRUE;
13159 }
13160
13161 /* ------------------------------------------------------------------------- */
13162 /* game sound playing functions                                              */
13163 /* ------------------------------------------------------------------------- */
13164
13165 static int *loop_sound_frame = NULL;
13166 static int *loop_sound_volume = NULL;
13167
13168 void InitPlayLevelSound()
13169 {
13170   int num_sounds = getSoundListSize();
13171
13172   checked_free(loop_sound_frame);
13173   checked_free(loop_sound_volume);
13174
13175   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
13176   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13177 }
13178
13179 static void PlayLevelSound(int x, int y, int nr)
13180 {
13181   int sx = SCREENX(x), sy = SCREENY(y);
13182   int volume, stereo_position;
13183   int max_distance = 8;
13184   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13185
13186   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13187       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13188     return;
13189
13190   if (!IN_LEV_FIELD(x, y) ||
13191       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13192       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13193     return;
13194
13195   volume = SOUND_MAX_VOLUME;
13196
13197   if (!IN_SCR_FIELD(sx, sy))
13198   {
13199     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13200     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13201
13202     volume -= volume * (dx > dy ? dx : dy) / max_distance;
13203   }
13204
13205   stereo_position = (SOUND_MAX_LEFT +
13206                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13207                      (SCR_FIELDX + 2 * max_distance));
13208
13209   if (IS_LOOP_SOUND(nr))
13210   {
13211     /* This assures that quieter loop sounds do not overwrite louder ones,
13212        while restarting sound volume comparison with each new game frame. */
13213
13214     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13215       return;
13216
13217     loop_sound_volume[nr] = volume;
13218     loop_sound_frame[nr] = FrameCounter;
13219   }
13220
13221   PlaySoundExt(nr, volume, stereo_position, type);
13222 }
13223
13224 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13225 {
13226   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13227                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13228                  y < LEVELY(BY1) ? LEVELY(BY1) :
13229                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13230                  sound_action);
13231 }
13232
13233 static void PlayLevelSoundAction(int x, int y, int action)
13234 {
13235   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13236 }
13237
13238 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13239 {
13240   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13241
13242   if (sound_effect != SND_UNDEFINED)
13243     PlayLevelSound(x, y, sound_effect);
13244 }
13245
13246 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13247                                               int action)
13248 {
13249   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13250
13251   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13252     PlayLevelSound(x, y, sound_effect);
13253 }
13254
13255 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13256 {
13257   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13258
13259   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13260     PlayLevelSound(x, y, sound_effect);
13261 }
13262
13263 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13264 {
13265   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13266
13267   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13268     StopSound(sound_effect);
13269 }
13270
13271 static void PlayLevelMusic()
13272 {
13273   if (levelset.music[level_nr] != MUS_UNDEFINED)
13274     PlayMusic(levelset.music[level_nr]);        /* from config file */
13275   else
13276     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13277 }
13278
13279 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13280 {
13281   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13282   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13283   int x = xx - 1 - offset;
13284   int y = yy - 1 - offset;
13285
13286   switch (sample)
13287   {
13288     case SAMPLE_blank:
13289       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13290       break;
13291
13292     case SAMPLE_roll:
13293       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13294       break;
13295
13296     case SAMPLE_stone:
13297       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13298       break;
13299
13300     case SAMPLE_nut:
13301       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13302       break;
13303
13304     case SAMPLE_crack:
13305       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13306       break;
13307
13308     case SAMPLE_bug:
13309       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13310       break;
13311
13312     case SAMPLE_tank:
13313       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13314       break;
13315
13316     case SAMPLE_android_clone:
13317       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13318       break;
13319
13320     case SAMPLE_android_move:
13321       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13322       break;
13323
13324     case SAMPLE_spring:
13325       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13326       break;
13327
13328     case SAMPLE_slurp:
13329       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13330       break;
13331
13332     case SAMPLE_eater:
13333       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13334       break;
13335
13336     case SAMPLE_eater_eat:
13337       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13338       break;
13339
13340     case SAMPLE_alien:
13341       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13342       break;
13343
13344     case SAMPLE_collect:
13345       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13346       break;
13347
13348     case SAMPLE_diamond:
13349       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13350       break;
13351
13352     case SAMPLE_squash:
13353       /* !!! CHECK THIS !!! */
13354 #if 1
13355       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13356 #else
13357       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13358 #endif
13359       break;
13360
13361     case SAMPLE_wonderfall:
13362       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13363       break;
13364
13365     case SAMPLE_drip:
13366       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13367       break;
13368
13369     case SAMPLE_push:
13370       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13371       break;
13372
13373     case SAMPLE_dirt:
13374       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13375       break;
13376
13377     case SAMPLE_acid:
13378       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13379       break;
13380
13381     case SAMPLE_ball:
13382       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13383       break;
13384
13385     case SAMPLE_grow:
13386       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13387       break;
13388
13389     case SAMPLE_wonder:
13390       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13391       break;
13392
13393     case SAMPLE_door:
13394       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13395       break;
13396
13397     case SAMPLE_exit_open:
13398       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13399       break;
13400
13401     case SAMPLE_exit_leave:
13402       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13403       break;
13404
13405     case SAMPLE_dynamite:
13406       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13407       break;
13408
13409     case SAMPLE_tick:
13410       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13411       break;
13412
13413     case SAMPLE_press:
13414       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13415       break;
13416
13417     case SAMPLE_wheel:
13418       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13419       break;
13420
13421     case SAMPLE_boom:
13422       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13423       break;
13424
13425     case SAMPLE_die:
13426       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13427       break;
13428
13429     case SAMPLE_time:
13430       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13431       break;
13432
13433     default:
13434       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13435       break;
13436   }
13437 }
13438
13439 #if 0
13440 void ChangeTime(int value)
13441 {
13442   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13443
13444   *time += value;
13445
13446   /* EMC game engine uses value from time counter of RND game engine */
13447   level.native_em_level->lev->time = *time;
13448
13449   DrawGameValue_Time(*time);
13450 }
13451
13452 void RaiseScore(int value)
13453 {
13454   /* EMC game engine and RND game engine have separate score counters */
13455   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13456                 &level.native_em_level->lev->score : &local_player->score);
13457
13458   *score += value;
13459
13460   DrawGameValue_Score(*score);
13461 }
13462 #endif
13463
13464 void RaiseScore(int value)
13465 {
13466   local_player->score += value;
13467
13468   DrawGameValue_Score(local_player->score);
13469 }
13470
13471 void RaiseScoreElement(int element)
13472 {
13473   switch (element)
13474   {
13475     case EL_EMERALD:
13476     case EL_BD_DIAMOND:
13477     case EL_EMERALD_YELLOW:
13478     case EL_EMERALD_RED:
13479     case EL_EMERALD_PURPLE:
13480     case EL_SP_INFOTRON:
13481       RaiseScore(level.score[SC_EMERALD]);
13482       break;
13483     case EL_DIAMOND:
13484       RaiseScore(level.score[SC_DIAMOND]);
13485       break;
13486     case EL_CRYSTAL:
13487       RaiseScore(level.score[SC_CRYSTAL]);
13488       break;
13489     case EL_PEARL:
13490       RaiseScore(level.score[SC_PEARL]);
13491       break;
13492     case EL_BUG:
13493     case EL_BD_BUTTERFLY:
13494     case EL_SP_ELECTRON:
13495       RaiseScore(level.score[SC_BUG]);
13496       break;
13497     case EL_SPACESHIP:
13498     case EL_BD_FIREFLY:
13499     case EL_SP_SNIKSNAK:
13500       RaiseScore(level.score[SC_SPACESHIP]);
13501       break;
13502     case EL_YAMYAM:
13503     case EL_DARK_YAMYAM:
13504       RaiseScore(level.score[SC_YAMYAM]);
13505       break;
13506     case EL_ROBOT:
13507       RaiseScore(level.score[SC_ROBOT]);
13508       break;
13509     case EL_PACMAN:
13510       RaiseScore(level.score[SC_PACMAN]);
13511       break;
13512     case EL_NUT:
13513       RaiseScore(level.score[SC_NUT]);
13514       break;
13515     case EL_DYNAMITE:
13516     case EL_EM_DYNAMITE:
13517     case EL_SP_DISK_RED:
13518     case EL_DYNABOMB_INCREASE_NUMBER:
13519     case EL_DYNABOMB_INCREASE_SIZE:
13520     case EL_DYNABOMB_INCREASE_POWER:
13521       RaiseScore(level.score[SC_DYNAMITE]);
13522       break;
13523     case EL_SHIELD_NORMAL:
13524     case EL_SHIELD_DEADLY:
13525       RaiseScore(level.score[SC_SHIELD]);
13526       break;
13527     case EL_EXTRA_TIME:
13528       RaiseScore(level.extra_time_score);
13529       break;
13530     case EL_KEY_1:
13531     case EL_KEY_2:
13532     case EL_KEY_3:
13533     case EL_KEY_4:
13534     case EL_EM_KEY_1:
13535     case EL_EM_KEY_2:
13536     case EL_EM_KEY_3:
13537     case EL_EM_KEY_4:
13538     case EL_EMC_KEY_5:
13539     case EL_EMC_KEY_6:
13540     case EL_EMC_KEY_7:
13541     case EL_EMC_KEY_8:
13542     case EL_DC_KEY_WHITE:
13543       RaiseScore(level.score[SC_KEY]);
13544       break;
13545     default:
13546       RaiseScore(element_info[element].collect_score);
13547       break;
13548   }
13549 }
13550
13551 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13552 {
13553   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13554   {
13555 #if defined(NETWORK_AVALIABLE)
13556     if (options.network)
13557       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13558     else
13559 #endif
13560     {
13561       if (quick_quit)
13562       {
13563         game_status = GAME_MODE_MAIN;
13564
13565         DrawMainMenu();
13566       }
13567       else
13568       {
13569         FadeOut(REDRAW_FIELD);
13570
13571         game_status = GAME_MODE_MAIN;
13572
13573         DrawAndFadeInMainMenu(REDRAW_FIELD);
13574       }
13575     }
13576   }
13577   else          /* continue playing the game */
13578   {
13579     if (tape.playing && tape.deactivate_display)
13580       TapeDeactivateDisplayOff(TRUE);
13581
13582     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13583
13584     if (tape.playing && tape.deactivate_display)
13585       TapeDeactivateDisplayOn();
13586   }
13587 }
13588
13589 void RequestQuitGame(boolean ask_if_really_quit)
13590 {
13591   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13592   boolean skip_request = AllPlayersGone || quick_quit;
13593
13594   RequestQuitGameExt(skip_request, quick_quit,
13595                      "Do you really want to quit the game ?");
13596 }
13597
13598
13599 /* ------------------------------------------------------------------------- */
13600 /* random generator functions                                                */
13601 /* ------------------------------------------------------------------------- */
13602
13603 unsigned int InitEngineRandom_RND(long seed)
13604 {
13605   game.num_random_calls = 0;
13606
13607 #if 0
13608   unsigned int rnd_seed = InitEngineRandom(seed);
13609
13610   printf("::: START RND: %d\n", rnd_seed);
13611
13612   return rnd_seed;
13613 #else
13614
13615   return InitEngineRandom(seed);
13616
13617 #endif
13618
13619 }
13620
13621 unsigned int RND(int max)
13622 {
13623   if (max > 0)
13624   {
13625     game.num_random_calls++;
13626
13627     return GetEngineRandom(max);
13628   }
13629
13630   return 0;
13631 }
13632
13633
13634 /* ------------------------------------------------------------------------- */
13635 /* game engine snapshot handling functions                                   */
13636 /* ------------------------------------------------------------------------- */
13637
13638 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
13639
13640 struct EngineSnapshotInfo
13641 {
13642   /* runtime values for custom element collect score */
13643   int collect_score[NUM_CUSTOM_ELEMENTS];
13644
13645   /* runtime values for group element choice position */
13646   int choice_pos[NUM_GROUP_ELEMENTS];
13647
13648   /* runtime values for belt position animations */
13649   int belt_graphic[4 * NUM_BELT_PARTS];
13650   int belt_anim_mode[4 * NUM_BELT_PARTS];
13651 };
13652
13653 struct EngineSnapshotNodeInfo
13654 {
13655   void *buffer_orig;
13656   void *buffer_copy;
13657   int size;
13658 };
13659
13660 static struct EngineSnapshotInfo engine_snapshot_rnd;
13661 static ListNode *engine_snapshot_list = NULL;
13662 static char *snapshot_level_identifier = NULL;
13663 static int snapshot_level_nr = -1;
13664
13665 void FreeEngineSnapshot()
13666 {
13667   while (engine_snapshot_list != NULL)
13668     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13669                        checked_free);
13670
13671   setString(&snapshot_level_identifier, NULL);
13672   snapshot_level_nr = -1;
13673 }
13674
13675 static void SaveEngineSnapshotValues_RND()
13676 {
13677   static int belt_base_active_element[4] =
13678   {
13679     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13680     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13681     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13682     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13683   };
13684   int i, j;
13685
13686   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13687   {
13688     int element = EL_CUSTOM_START + i;
13689
13690     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13691   }
13692
13693   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13694   {
13695     int element = EL_GROUP_START + i;
13696
13697     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13698   }
13699
13700   for (i = 0; i < 4; i++)
13701   {
13702     for (j = 0; j < NUM_BELT_PARTS; j++)
13703     {
13704       int element = belt_base_active_element[i] + j;
13705       int graphic = el2img(element);
13706       int anim_mode = graphic_info[graphic].anim_mode;
13707
13708       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13709       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13710     }
13711   }
13712 }
13713
13714 static void LoadEngineSnapshotValues_RND()
13715 {
13716   unsigned long num_random_calls = game.num_random_calls;
13717   int i, j;
13718
13719   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13720   {
13721     int element = EL_CUSTOM_START + i;
13722
13723     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13724   }
13725
13726   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13727   {
13728     int element = EL_GROUP_START + i;
13729
13730     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13731   }
13732
13733   for (i = 0; i < 4; i++)
13734   {
13735     for (j = 0; j < NUM_BELT_PARTS; j++)
13736     {
13737       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13738       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13739
13740       graphic_info[graphic].anim_mode = anim_mode;
13741     }
13742   }
13743
13744   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13745   {
13746     InitRND(tape.random_seed);
13747     for (i = 0; i < num_random_calls; i++)
13748       RND(1);
13749   }
13750
13751   if (game.num_random_calls != num_random_calls)
13752   {
13753     Error(ERR_INFO, "number of random calls out of sync");
13754     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
13755     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
13756     Error(ERR_EXIT, "this should not happen -- please debug");
13757   }
13758 }
13759
13760 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13761 {
13762   struct EngineSnapshotNodeInfo *bi =
13763     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13764
13765   bi->buffer_orig = buffer;
13766   bi->buffer_copy = checked_malloc(size);
13767   bi->size = size;
13768
13769   memcpy(bi->buffer_copy, buffer, size);
13770
13771   addNodeToList(&engine_snapshot_list, NULL, bi);
13772 }
13773
13774 void SaveEngineSnapshot()
13775 {
13776   FreeEngineSnapshot();         /* free previous snapshot, if needed */
13777
13778   if (level_editor_test_game)   /* do not save snapshots from editor */
13779     return;
13780
13781   /* copy some special values to a structure better suited for the snapshot */
13782
13783   SaveEngineSnapshotValues_RND();
13784   SaveEngineSnapshotValues_EM();
13785
13786   /* save values stored in special snapshot structure */
13787
13788   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13789   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13790
13791   /* save further RND engine values */
13792
13793   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13794   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13795   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13796
13797   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13798   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13799   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13800   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13801
13802   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13803   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13804   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13805   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13806   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13807
13808   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13809   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13810   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13811
13812   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13813
13814   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13815
13816   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13817   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13818
13819   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13820   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13821   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13822   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13823   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13824   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13825   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13826   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13827   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13828   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13829   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13830   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13831   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13832   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13833   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13834   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13835   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13836   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13837
13838   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13839   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13840
13841   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13842   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13843   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13844
13845   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13846   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13847
13848   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13849   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13850   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13851   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13852   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13853
13854   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13855   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13856
13857   /* save level identification information */
13858
13859   setString(&snapshot_level_identifier, leveldir_current->identifier);
13860   snapshot_level_nr = level_nr;
13861
13862 #if 0
13863   ListNode *node = engine_snapshot_list;
13864   int num_bytes = 0;
13865
13866   while (node != NULL)
13867   {
13868     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13869
13870     node = node->next;
13871   }
13872
13873   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13874 #endif
13875 }
13876
13877 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13878 {
13879   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13880 }
13881
13882 void LoadEngineSnapshot()
13883 {
13884   ListNode *node = engine_snapshot_list;
13885
13886   if (engine_snapshot_list == NULL)
13887     return;
13888
13889   while (node != NULL)
13890   {
13891     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13892
13893     node = node->next;
13894   }
13895
13896   /* restore special values from snapshot structure */
13897
13898   LoadEngineSnapshotValues_RND();
13899   LoadEngineSnapshotValues_EM();
13900 }
13901
13902 boolean CheckEngineSnapshot()
13903 {
13904   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13905           snapshot_level_nr == level_nr);
13906 }
13907
13908
13909 /* ---------- new game button stuff ---------------------------------------- */
13910
13911 /* graphic position values for game buttons */
13912 #define GAME_BUTTON_XSIZE       30
13913 #define GAME_BUTTON_YSIZE       30
13914 #define GAME_BUTTON_XPOS        5
13915 #define GAME_BUTTON_YPOS        215
13916 #define SOUND_BUTTON_XPOS       5
13917 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13918
13919 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13920 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13921 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13922 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13923 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13924 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13925
13926 static struct
13927 {
13928   int x, y;
13929   int gadget_id;
13930   char *infotext;
13931 } gamebutton_info[NUM_GAME_BUTTONS] =
13932 {
13933   {
13934     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13935     GAME_CTRL_ID_STOP,
13936     "stop game"
13937   },
13938   {
13939     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13940     GAME_CTRL_ID_PAUSE,
13941     "pause game"
13942   },
13943   {
13944     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13945     GAME_CTRL_ID_PLAY,
13946     "play game"
13947   },
13948   {
13949     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13950     SOUND_CTRL_ID_MUSIC,
13951     "background music on/off"
13952   },
13953   {
13954     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13955     SOUND_CTRL_ID_LOOPS,
13956     "sound loops on/off"
13957   },
13958   {
13959     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13960     SOUND_CTRL_ID_SIMPLE,
13961     "normal sounds on/off"
13962   }
13963 };
13964
13965 void CreateGameButtons()
13966 {
13967   int i;
13968
13969   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13970   {
13971     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13972     struct GadgetInfo *gi;
13973     int button_type;
13974     boolean checked;
13975     unsigned long event_mask;
13976     int gd_xoffset, gd_yoffset;
13977     int gd_x1, gd_x2, gd_y1, gd_y2;
13978     int id = i;
13979
13980     gd_xoffset = gamebutton_info[i].x;
13981     gd_yoffset = gamebutton_info[i].y;
13982     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13983     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13984
13985     if (id == GAME_CTRL_ID_STOP ||
13986         id == GAME_CTRL_ID_PAUSE ||
13987         id == GAME_CTRL_ID_PLAY)
13988     {
13989       button_type = GD_TYPE_NORMAL_BUTTON;
13990       checked = FALSE;
13991       event_mask = GD_EVENT_RELEASED;
13992       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13993       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13994     }
13995     else
13996     {
13997       button_type = GD_TYPE_CHECK_BUTTON;
13998       checked =
13999         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14000          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14001          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14002       event_mask = GD_EVENT_PRESSED;
14003       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
14004       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
14005     }
14006
14007     gi = CreateGadget(GDI_CUSTOM_ID, id,
14008                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14009                       GDI_X, DX + gd_xoffset,
14010                       GDI_Y, DY + gd_yoffset,
14011                       GDI_WIDTH, GAME_BUTTON_XSIZE,
14012                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
14013                       GDI_TYPE, button_type,
14014                       GDI_STATE, GD_BUTTON_UNPRESSED,
14015                       GDI_CHECKED, checked,
14016                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
14017                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
14018                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
14019                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
14020                       GDI_EVENT_MASK, event_mask,
14021                       GDI_CALLBACK_ACTION, HandleGameButtons,
14022                       GDI_END);
14023
14024     if (gi == NULL)
14025       Error(ERR_EXIT, "cannot create gadget");
14026
14027     game_gadget[id] = gi;
14028   }
14029 }
14030
14031 void FreeGameButtons()
14032 {
14033   int i;
14034
14035   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14036     FreeGadget(game_gadget[i]);
14037 }
14038
14039 static void MapGameButtons()
14040 {
14041   int i;
14042
14043   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14044     MapGadget(game_gadget[i]);
14045 }
14046
14047 void UnmapGameButtons()
14048 {
14049   int i;
14050
14051   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14052     UnmapGadget(game_gadget[i]);
14053 }
14054
14055 static void HandleGameButtons(struct GadgetInfo *gi)
14056 {
14057   int id = gi->custom_id;
14058
14059   if (game_status != GAME_MODE_PLAYING)
14060     return;
14061
14062   switch (id)
14063   {
14064     case GAME_CTRL_ID_STOP:
14065       if (tape.playing)
14066         TapeStop();
14067       else
14068         RequestQuitGame(TRUE);
14069       break;
14070
14071     case GAME_CTRL_ID_PAUSE:
14072       if (options.network)
14073       {
14074 #if defined(NETWORK_AVALIABLE)
14075         if (tape.pausing)
14076           SendToServer_ContinuePlaying();
14077         else
14078           SendToServer_PausePlaying();
14079 #endif
14080       }
14081       else
14082         TapeTogglePause(TAPE_TOGGLE_MANUAL);
14083       break;
14084
14085     case GAME_CTRL_ID_PLAY:
14086       if (tape.pausing)
14087       {
14088 #if defined(NETWORK_AVALIABLE)
14089         if (options.network)
14090           SendToServer_ContinuePlaying();
14091         else
14092 #endif
14093         {
14094           tape.pausing = FALSE;
14095           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
14096         }
14097       }
14098       break;
14099
14100     case SOUND_CTRL_ID_MUSIC:
14101       if (setup.sound_music)
14102       { 
14103         setup.sound_music = FALSE;
14104         FadeMusic();
14105       }
14106       else if (audio.music_available)
14107       { 
14108         setup.sound = setup.sound_music = TRUE;
14109
14110         SetAudioMode(setup.sound);
14111
14112         PlayLevelMusic();
14113       }
14114       break;
14115
14116     case SOUND_CTRL_ID_LOOPS:
14117       if (setup.sound_loops)
14118         setup.sound_loops = FALSE;
14119       else if (audio.loops_available)
14120       {
14121         setup.sound = setup.sound_loops = TRUE;
14122         SetAudioMode(setup.sound);
14123       }
14124       break;
14125
14126     case SOUND_CTRL_ID_SIMPLE:
14127       if (setup.sound_simple)
14128         setup.sound_simple = FALSE;
14129       else if (audio.sound_available)
14130       {
14131         setup.sound = setup.sound_simple = TRUE;
14132         SetAudioMode(setup.sound);
14133       }
14134       break;
14135
14136     default:
14137       break;
14138   }
14139 }