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