rnd-20070207-2-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62
63 /* for DigField() */
64 #define DF_NO_PUSH              0
65 #define DF_DIG                  1
66 #define DF_SNAP                 2
67
68 /* for MovePlayer() */
69 #define MP_NO_ACTION            0
70 #define MP_MOVING               1
71 #define MP_ACTION               2
72 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
73
74 /* for ScrollPlayer() */
75 #define SCROLL_INIT             0
76 #define SCROLL_GO_ON            1
77
78 /* for Bang()/Explode() */
79 #define EX_PHASE_START          0
80 #define EX_TYPE_NONE            0
81 #define EX_TYPE_NORMAL          (1 << 0)
82 #define EX_TYPE_CENTER          (1 << 1)
83 #define EX_TYPE_BORDER          (1 << 2)
84 #define EX_TYPE_CROSS           (1 << 3)
85 #define EX_TYPE_DYNA            (1 << 4)
86 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
87
88 #if 1
89 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
90 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
91 #define PANEL_XPOS(p)           (DX + ALIGNED_MENU_XPOS(p))
92 #define PANEL_YPOS(p)           (DY + ALIGNED_MENU_YPOS(p))
93 #else
94 #define PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
95 #define PANEL_XPOS(p)           (ALIGNED_XPOS((p).x, (p).width, (p).align))
96 #define PANEL_YPOS(p)           ((p).y)
97 #endif
98
99 /* special positions in the game control window (relative to control window) */
100 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
101 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
102 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
103 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
104 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
105 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
106 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
107 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
108 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
109 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
110 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
111 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
112 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
113 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
114 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
115 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
116
117 /* special positions in the game control window (relative to main window) */
118 #define DX_LEVEL1               (DX + XX_LEVEL1)
119 #define DX_LEVEL2               (DX + XX_LEVEL2)
120 #define DX_LEVEL                (DX + XX_LEVEL)
121 #define DY_LEVEL                (DY + YY_LEVEL)
122 #define DX_EMERALDS             (DX + XX_EMERALDS)
123 #define DY_EMERALDS             (DY + YY_EMERALDS)
124 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
125 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
126 #define DX_KEYS                 (DX + XX_KEYS)
127 #define DY_KEYS                 (DY + YY_KEYS)
128 #define DX_SCORE                (DX + XX_SCORE)
129 #define DY_SCORE                (DY + YY_SCORE)
130 #define DX_TIME1                (DX + XX_TIME1)
131 #define DX_TIME2                (DX + XX_TIME2)
132 #define DX_TIME                 (DX + XX_TIME)
133 #define DY_TIME                 (DY + YY_TIME)
134
135 /* values for delayed check of falling and moving elements and for collision */
136 #define CHECK_DELAY_MOVING      3
137 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
138 #define CHECK_DELAY_COLLISION   2
139 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
140
141 /* values for initial player move delay (initial delay counter value) */
142 #define INITIAL_MOVE_DELAY_OFF  -1
143 #define INITIAL_MOVE_DELAY_ON   0
144
145 /* values for player movement speed (which is in fact a delay value) */
146 #define MOVE_DELAY_MIN_SPEED    32
147 #define MOVE_DELAY_NORMAL_SPEED 8
148 #define MOVE_DELAY_HIGH_SPEED   4
149 #define MOVE_DELAY_MAX_SPEED    1
150
151 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
152 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
153
154 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
155 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
156
157 /* values for other actions */
158 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
159 #define MOVE_STEPSIZE_MIN       (1)
160 #define MOVE_STEPSIZE_MAX       (TILEX)
161
162 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
163 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
164
165 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
166
167 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
168                                  RND(element_info[e].push_delay_random))
169 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
170                                  RND(element_info[e].drop_delay_random))
171 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
172                                  RND(element_info[e].move_delay_random))
173 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
174                                     (element_info[e].move_delay_random))
175 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
176                                  RND(element_info[e].ce_value_random_initial))
177 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
178 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
179                                  RND((c)->delay_random * (c)->delay_frames))
180 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
181                                  RND((c)->delay_random))
182
183
184 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
185          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
186
187 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
188         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
189          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
190          (be) + (e) - EL_SELF)
191
192 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
193         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
194          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
195          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
196          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
197          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
198          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
199          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
200          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
201          (e))
202
203 #define CAN_GROW_INTO(e)                                                \
204         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
205
206 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
207                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
208                                         (condition)))
209
210 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
211                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
212                                         (CAN_MOVE_INTO_ACID(e) &&       \
213                                          Feld[x][y] == EL_ACID) ||      \
214                                         (condition)))
215
216 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
217                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
218                                         (CAN_MOVE_INTO_ACID(e) &&       \
219                                          Feld[x][y] == EL_ACID) ||      \
220                                         (condition)))
221
222 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
223                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
224                                         (condition) ||                  \
225                                         (CAN_MOVE_INTO_ACID(e) &&       \
226                                          Feld[x][y] == EL_ACID) ||      \
227                                         (DONT_COLLIDE_WITH(e) &&        \
228                                          IS_PLAYER(x, y) &&             \
229                                          !PLAYER_ENEMY_PROTECTED(x, y))))
230
231 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
232         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
233
234 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
235         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
236
237 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
238         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
239
240 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
241         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
242                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
243
244 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
245         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
246
247 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
248         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
249
250 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
251         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
252
253 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
254         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
255
256 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
257         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
258
259 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
260         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
261                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
262                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
263                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
264                                                  IS_FOOD_PENGUIN(Feld[x][y])))
265 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
266         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
267
268 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
269         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
270
271 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
272         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
273
274 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
275         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
276                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
277
278 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
279
280 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
281                 (!IS_PLAYER(x, y) &&                                    \
282                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
283
284 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
285         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
286
287 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
288 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
289
290 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
291 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
292 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
293 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
294
295 /* game button identifiers */
296 #define GAME_CTRL_ID_STOP               0
297 #define GAME_CTRL_ID_PAUSE              1
298 #define GAME_CTRL_ID_PLAY               2
299 #define SOUND_CTRL_ID_MUSIC             3
300 #define SOUND_CTRL_ID_LOOPS             4
301 #define SOUND_CTRL_ID_SIMPLE            5
302
303 #define NUM_GAME_BUTTONS                6
304
305
306 /* forward declaration for internal use */
307
308 static void CreateField(int, int, int);
309
310 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
311 static void AdvanceFrameAndPlayerCounters(int);
312
313 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
314 static boolean MovePlayer(struct PlayerInfo *, int, int);
315 static void ScrollPlayer(struct PlayerInfo *, int);
316 static void ScrollScreen(struct PlayerInfo *, int);
317
318 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
319
320 static void InitBeltMovement(void);
321 static void CloseAllOpenTimegates(void);
322 static void CheckGravityMovement(struct PlayerInfo *);
323 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
324 static void KillPlayerUnlessEnemyProtected(int, int);
325 static void KillPlayerUnlessExplosionProtected(int, int);
326
327 static void TestIfPlayerTouchesCustomElement(int, int);
328 static void TestIfElementTouchesCustomElement(int, int);
329 static void TestIfElementHitsCustomElement(int, int, int);
330 #if 0
331 static void TestIfElementSmashesCustomElement(int, int, int);
332 #endif
333
334 static void HandleElementChange(int, int, int);
335 static void ExecuteCustomElementAction(int, int, int, int);
336 static boolean ChangeElement(int, int, int, int);
337
338 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
339 #define CheckTriggeredElementChange(x, y, e, ev)                        \
340         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
341 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
342         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
343 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
344         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
345 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
346         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
347
348 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
349 #define CheckElementChange(x, y, e, te, ev)                             \
350         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
351 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
352         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
353 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
354         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
355
356 static void PlayLevelSound(int, int, int);
357 static void PlayLevelSoundNearest(int, int, int);
358 static void PlayLevelSoundAction(int, int, int);
359 static void PlayLevelSoundElementAction(int, int, int, int);
360 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
361 static void PlayLevelSoundActionIfLoop(int, int, int);
362 static void StopLevelSoundActionIfLoop(int, int, int);
363 static void PlayLevelMusic();
364
365 static void MapGameButtons();
366 static void HandleGameButtons(struct GadgetInfo *);
367
368 int AmoebeNachbarNr(int, int);
369 void AmoebeUmwandeln(int, int);
370 void ContinueMoving(int, int);
371 void Bang(int, int);
372 void InitMovDir(int, int);
373 void InitAmoebaNr(int, int);
374 int NewHiScore(void);
375
376 void TestIfGoodThingHitsBadThing(int, int, int);
377 void TestIfBadThingHitsGoodThing(int, int, int);
378 void TestIfPlayerTouchesBadThing(int, int);
379 void TestIfPlayerRunsIntoBadThing(int, int, int);
380 void TestIfBadThingTouchesPlayer(int, int);
381 void TestIfBadThingRunsIntoPlayer(int, int, int);
382 void TestIfFriendTouchesBadThing(int, int);
383 void TestIfBadThingTouchesFriend(int, int);
384 void TestIfBadThingTouchesOtherBadThing(int, int);
385
386 void KillPlayer(struct PlayerInfo *);
387 void BuryPlayer(struct PlayerInfo *);
388 void RemovePlayer(struct PlayerInfo *);
389
390 boolean SnapField(struct PlayerInfo *, int, int);
391 boolean DropElement(struct PlayerInfo *);
392
393 static int getInvisibleActiveFromInvisibleElement(int);
394 static int getInvisibleFromInvisibleActiveElement(int);
395
396 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
397
398 /* for detection of endless loops, caused by custom element programming */
399 /* (using maximal playfield width x 10 is just a rough approximation) */
400 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
401
402 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
403 {                                                                       \
404   if (recursion_loop_detected)                                          \
405     return (rc);                                                        \
406                                                                         \
407   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
408   {                                                                     \
409     recursion_loop_detected = TRUE;                                     \
410     recursion_loop_element = (e);                                       \
411   }                                                                     \
412                                                                         \
413   recursion_loop_depth++;                                               \
414 }
415
416 #define RECURSION_LOOP_DETECTION_END()                                  \
417 {                                                                       \
418   recursion_loop_depth--;                                               \
419 }
420
421 static int recursion_loop_depth;
422 static boolean recursion_loop_detected;
423 static boolean recursion_loop_element;
424
425
426 /* ------------------------------------------------------------------------- */
427 /* definition of elements that automatically change to other elements after  */
428 /* a specified time, eventually calling a function when changing             */
429 /* ------------------------------------------------------------------------- */
430
431 /* forward declaration for changer functions */
432 static void InitBuggyBase(int, int);
433 static void WarnBuggyBase(int, int);
434
435 static void InitTrap(int, int);
436 static void ActivateTrap(int, int);
437 static void ChangeActiveTrap(int, int);
438
439 static void InitRobotWheel(int, int);
440 static void RunRobotWheel(int, int);
441 static void StopRobotWheel(int, int);
442
443 static void InitTimegateWheel(int, int);
444 static void RunTimegateWheel(int, int);
445
446 static void InitMagicBallDelay(int, int);
447 static void ActivateMagicBall(int, int);
448
449 struct ChangingElementInfo
450 {
451   int element;
452   int target_element;
453   int change_delay;
454   void (*pre_change_function)(int x, int y);
455   void (*change_function)(int x, int y);
456   void (*post_change_function)(int x, int y);
457 };
458
459 static struct ChangingElementInfo change_delay_list[] =
460 {
461   {
462     EL_NUT_BREAKING,
463     EL_EMERALD,
464     6,
465     NULL,
466     NULL,
467     NULL
468   },
469   {
470     EL_PEARL_BREAKING,
471     EL_EMPTY,
472     8,
473     NULL,
474     NULL,
475     NULL
476   },
477   {
478     EL_EXIT_OPENING,
479     EL_EXIT_OPEN,
480     29,
481     NULL,
482     NULL,
483     NULL
484   },
485   {
486     EL_EXIT_CLOSING,
487     EL_EXIT_CLOSED,
488     29,
489     NULL,
490     NULL,
491     NULL
492   },
493   {
494     EL_STEEL_EXIT_OPENING,
495     EL_STEEL_EXIT_OPEN,
496     29,
497     NULL,
498     NULL,
499     NULL
500   },
501   {
502     EL_STEEL_EXIT_CLOSING,
503     EL_STEEL_EXIT_CLOSED,
504     29,
505     NULL,
506     NULL,
507     NULL
508   },
509   {
510     EL_EM_EXIT_OPENING,
511     EL_EM_EXIT_OPEN,
512     29,
513     NULL,
514     NULL,
515     NULL
516   },
517   {
518     EL_EM_EXIT_CLOSING,
519 #if 1
520     EL_EMPTY,
521 #else
522     EL_EM_EXIT_CLOSED,
523 #endif
524     29,
525     NULL,
526     NULL,
527     NULL
528   },
529   {
530     EL_EM_STEEL_EXIT_OPENING,
531     EL_EM_STEEL_EXIT_OPEN,
532     29,
533     NULL,
534     NULL,
535     NULL
536   },
537   {
538     EL_EM_STEEL_EXIT_CLOSING,
539 #if 1
540     EL_STEELWALL,
541 #else
542     EL_EM_STEEL_EXIT_CLOSED,
543 #endif
544     29,
545     NULL,
546     NULL,
547     NULL
548   },
549   {
550     EL_SP_EXIT_OPENING,
551     EL_SP_EXIT_OPEN,
552     29,
553     NULL,
554     NULL,
555     NULL
556   },
557   {
558     EL_SP_EXIT_CLOSING,
559     EL_SP_EXIT_CLOSED,
560     29,
561     NULL,
562     NULL,
563     NULL
564   },
565   {
566     EL_SWITCHGATE_OPENING,
567     EL_SWITCHGATE_OPEN,
568     29,
569     NULL,
570     NULL,
571     NULL
572   },
573   {
574     EL_SWITCHGATE_CLOSING,
575     EL_SWITCHGATE_CLOSED,
576     29,
577     NULL,
578     NULL,
579     NULL
580   },
581   {
582     EL_TIMEGATE_OPENING,
583     EL_TIMEGATE_OPEN,
584     29,
585     NULL,
586     NULL,
587     NULL
588   },
589   {
590     EL_TIMEGATE_CLOSING,
591     EL_TIMEGATE_CLOSED,
592     29,
593     NULL,
594     NULL,
595     NULL
596   },
597
598   {
599     EL_ACID_SPLASH_LEFT,
600     EL_EMPTY,
601     8,
602     NULL,
603     NULL,
604     NULL
605   },
606   {
607     EL_ACID_SPLASH_RIGHT,
608     EL_EMPTY,
609     8,
610     NULL,
611     NULL,
612     NULL
613   },
614   {
615     EL_SP_BUGGY_BASE,
616     EL_SP_BUGGY_BASE_ACTIVATING,
617     0,
618     InitBuggyBase,
619     NULL,
620     NULL
621   },
622   {
623     EL_SP_BUGGY_BASE_ACTIVATING,
624     EL_SP_BUGGY_BASE_ACTIVE,
625     0,
626     InitBuggyBase,
627     NULL,
628     NULL
629   },
630   {
631     EL_SP_BUGGY_BASE_ACTIVE,
632     EL_SP_BUGGY_BASE,
633     0,
634     InitBuggyBase,
635     WarnBuggyBase,
636     NULL
637   },
638   {
639     EL_TRAP,
640     EL_TRAP_ACTIVE,
641     0,
642     InitTrap,
643     NULL,
644     ActivateTrap
645   },
646   {
647     EL_TRAP_ACTIVE,
648     EL_TRAP,
649     31,
650     NULL,
651     ChangeActiveTrap,
652     NULL
653   },
654   {
655     EL_ROBOT_WHEEL_ACTIVE,
656     EL_ROBOT_WHEEL,
657     0,
658     InitRobotWheel,
659     RunRobotWheel,
660     StopRobotWheel
661   },
662   {
663     EL_TIMEGATE_SWITCH_ACTIVE,
664     EL_TIMEGATE_SWITCH,
665     0,
666     InitTimegateWheel,
667     RunTimegateWheel,
668     NULL
669   },
670   {
671     EL_DC_TIMEGATE_SWITCH_ACTIVE,
672     EL_DC_TIMEGATE_SWITCH,
673     0,
674     InitTimegateWheel,
675     RunTimegateWheel,
676     NULL
677   },
678   {
679     EL_EMC_MAGIC_BALL_ACTIVE,
680     EL_EMC_MAGIC_BALL_ACTIVE,
681     0,
682     InitMagicBallDelay,
683     NULL,
684     ActivateMagicBall
685   },
686   {
687     EL_EMC_SPRING_BUMPER_ACTIVE,
688     EL_EMC_SPRING_BUMPER,
689     8,
690     NULL,
691     NULL,
692     NULL
693   },
694   {
695     EL_DIAGONAL_SHRINKING,
696     EL_UNDEFINED,
697     0,
698     NULL,
699     NULL,
700     NULL
701   },
702   {
703     EL_DIAGONAL_GROWING,
704     EL_UNDEFINED,
705     0,
706     NULL,
707     NULL,
708     NULL,
709   },
710
711   {
712     EL_UNDEFINED,
713     EL_UNDEFINED,
714     -1,
715     NULL,
716     NULL,
717     NULL
718   }
719 };
720
721 struct
722 {
723   int element;
724   int push_delay_fixed, push_delay_random;
725 }
726 push_delay_list[] =
727 {
728   { EL_SPRING,                  0, 0 },
729   { EL_BALLOON,                 0, 0 },
730
731   { EL_SOKOBAN_OBJECT,          2, 0 },
732   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
733   { EL_SATELLITE,               2, 0 },
734   { EL_SP_DISK_YELLOW,          2, 0 },
735
736   { EL_UNDEFINED,               0, 0 },
737 };
738
739 struct
740 {
741   int element;
742   int move_stepsize;
743 }
744 move_stepsize_list[] =
745 {
746   { EL_AMOEBA_DROP,             2 },
747   { EL_AMOEBA_DROPPING,         2 },
748   { EL_QUICKSAND_FILLING,       1 },
749   { EL_QUICKSAND_EMPTYING,      1 },
750   { EL_QUICKSAND_FAST_FILLING,  2 },
751   { EL_QUICKSAND_FAST_EMPTYING, 2 },
752   { EL_MAGIC_WALL_FILLING,      2 },
753   { EL_MAGIC_WALL_EMPTYING,     2 },
754   { EL_BD_MAGIC_WALL_FILLING,   2 },
755   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
756   { EL_DC_MAGIC_WALL_FILLING,   2 },
757   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
758
759   { EL_UNDEFINED,               0 },
760 };
761
762 struct
763 {
764   int element;
765   int count;
766 }
767 collect_count_list[] =
768 {
769   { EL_EMERALD,                 1 },
770   { EL_BD_DIAMOND,              1 },
771   { EL_EMERALD_YELLOW,          1 },
772   { EL_EMERALD_RED,             1 },
773   { EL_EMERALD_PURPLE,          1 },
774   { EL_DIAMOND,                 3 },
775   { EL_SP_INFOTRON,             1 },
776   { EL_PEARL,                   5 },
777   { EL_CRYSTAL,                 8 },
778
779   { EL_UNDEFINED,               0 },
780 };
781
782 struct
783 {
784   int element;
785   int direction;
786 }
787 access_direction_list[] =
788 {
789   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
790   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
791   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
792   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
793   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
794   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
795   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
796   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
797   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
798   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
799   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
800
801   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
802   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
803   { EL_SP_PORT_UP,                                                   MV_DOWN },
804   { EL_SP_PORT_DOWN,                                         MV_UP           },
805   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
806   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
807   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
808   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
809   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
810   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
811   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
812   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
813   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
814   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
815   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
816   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
817   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
818   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
819   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
820
821   { EL_UNDEFINED,                       MV_NONE                              }
822 };
823
824 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
825
826 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
827 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
828 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
829                                  IS_JUST_CHANGING(x, y))
830
831 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
832
833 /* static variables for playfield scan mode (scanning forward or backward) */
834 static int playfield_scan_start_x = 0;
835 static int playfield_scan_start_y = 0;
836 static int playfield_scan_delta_x = 1;
837 static int playfield_scan_delta_y = 1;
838
839 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
840                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
841                                      (y) += playfield_scan_delta_y)     \
842                                 for ((x) = playfield_scan_start_x;      \
843                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
844                                      (x) += playfield_scan_delta_x)     \
845
846 #ifdef DEBUG
847 void DEBUG_SetMaximumDynamite()
848 {
849   int i;
850
851   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
852     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
853       local_player->inventory_element[local_player->inventory_size++] =
854         EL_DYNAMITE;
855 }
856 #endif
857
858 static void InitPlayfieldScanModeVars()
859 {
860   if (game.use_reverse_scan_direction)
861   {
862     playfield_scan_start_x = lev_fieldx - 1;
863     playfield_scan_start_y = lev_fieldy - 1;
864
865     playfield_scan_delta_x = -1;
866     playfield_scan_delta_y = -1;
867   }
868   else
869   {
870     playfield_scan_start_x = 0;
871     playfield_scan_start_y = 0;
872
873     playfield_scan_delta_x = 1;
874     playfield_scan_delta_y = 1;
875   }
876 }
877
878 static void InitPlayfieldScanMode(int mode)
879 {
880   game.use_reverse_scan_direction =
881     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
882
883   InitPlayfieldScanModeVars();
884 }
885
886 static int get_move_delay_from_stepsize(int move_stepsize)
887 {
888   move_stepsize =
889     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
890
891   /* make sure that stepsize value is always a power of 2 */
892   move_stepsize = (1 << log_2(move_stepsize));
893
894   return TILEX / move_stepsize;
895 }
896
897 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
898                                boolean init_game)
899 {
900   int player_nr = player->index_nr;
901   int move_delay = get_move_delay_from_stepsize(move_stepsize);
902   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
903
904   /* do no immediately change move delay -- the player might just be moving */
905   player->move_delay_value_next = move_delay;
906
907   /* information if player can move must be set separately */
908   player->cannot_move = cannot_move;
909
910   if (init_game)
911   {
912     player->move_delay       = game.initial_move_delay[player_nr];
913     player->move_delay_value = game.initial_move_delay_value[player_nr];
914
915     player->move_delay_value_next = -1;
916
917     player->move_delay_reset_counter = 0;
918   }
919 }
920
921 void GetPlayerConfig()
922 {
923   GameFrameDelay = setup.game_frame_delay;
924
925   if (!audio.sound_available)
926     setup.sound_simple = FALSE;
927
928   if (!audio.loops_available)
929     setup.sound_loops = FALSE;
930
931   if (!audio.music_available)
932     setup.sound_music = FALSE;
933
934   if (!video.fullscreen_available)
935     setup.fullscreen = FALSE;
936
937   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
938
939   SetAudioMode(setup.sound);
940   InitJoysticks();
941 }
942
943 static int getBeltNrFromBeltElement(int element)
944 {
945   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
946           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
947           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
948 }
949
950 static int getBeltNrFromBeltActiveElement(int element)
951 {
952   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
953           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
954           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
955 }
956
957 static int getBeltNrFromBeltSwitchElement(int element)
958 {
959   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
960           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
961           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
962 }
963
964 static int getBeltDirNrFromBeltSwitchElement(int element)
965 {
966   static int belt_base_element[4] =
967   {
968     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
969     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
970     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
971     EL_CONVEYOR_BELT_4_SWITCH_LEFT
972   };
973
974   int belt_nr = getBeltNrFromBeltSwitchElement(element);
975   int belt_dir_nr = element - belt_base_element[belt_nr];
976
977   return (belt_dir_nr % 3);
978 }
979
980 static int getBeltDirFromBeltSwitchElement(int element)
981 {
982   static int belt_move_dir[3] =
983   {
984     MV_LEFT,
985     MV_NONE,
986     MV_RIGHT
987   };
988
989   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
990
991   return belt_move_dir[belt_dir_nr];
992 }
993
994 static int get_element_from_group_element(int element)
995 {
996   if (IS_GROUP_ELEMENT(element))
997   {
998     struct ElementGroupInfo *group = element_info[element].group;
999     int last_anim_random_frame = gfx.anim_random_frame;
1000     int element_pos;
1001
1002     if (group->choice_mode == ANIM_RANDOM)
1003       gfx.anim_random_frame = RND(group->num_elements_resolved);
1004
1005     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1006                                     group->choice_mode, 0,
1007                                     group->choice_pos);
1008
1009     if (group->choice_mode == ANIM_RANDOM)
1010       gfx.anim_random_frame = last_anim_random_frame;
1011
1012     group->choice_pos++;
1013
1014     element = group->element_resolved[element_pos];
1015   }
1016
1017   return element;
1018 }
1019
1020 static void InitPlayerField(int x, int y, int element, boolean init_game)
1021 {
1022   if (element == EL_SP_MURPHY)
1023   {
1024     if (init_game)
1025     {
1026       if (stored_player[0].present)
1027       {
1028         Feld[x][y] = EL_SP_MURPHY_CLONE;
1029
1030         return;
1031       }
1032       else
1033       {
1034         stored_player[0].use_murphy = TRUE;
1035
1036         if (!level.use_artwork_element[0])
1037           stored_player[0].artwork_element = EL_SP_MURPHY;
1038       }
1039
1040       Feld[x][y] = EL_PLAYER_1;
1041     }
1042   }
1043
1044   if (init_game)
1045   {
1046     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1047     int jx = player->jx, jy = player->jy;
1048
1049     player->present = TRUE;
1050
1051     player->block_last_field = (element == EL_SP_MURPHY ?
1052                                 level.sp_block_last_field :
1053                                 level.block_last_field);
1054
1055     /* ---------- initialize player's last field block delay --------------- */
1056
1057     /* always start with reliable default value (no adjustment needed) */
1058     player->block_delay_adjustment = 0;
1059
1060     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1061     if (player->block_last_field && element == EL_SP_MURPHY)
1062       player->block_delay_adjustment = 1;
1063
1064     /* special case 2: in game engines before 3.1.1, blocking was different */
1065     if (game.use_block_last_field_bug)
1066       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1067
1068     if (!options.network || player->connected)
1069     {
1070       player->active = TRUE;
1071
1072       /* remove potentially duplicate players */
1073       if (StorePlayer[jx][jy] == Feld[x][y])
1074         StorePlayer[jx][jy] = 0;
1075
1076       StorePlayer[x][y] = Feld[x][y];
1077
1078       if (options.debug)
1079       {
1080         printf("Player %d activated.\n", player->element_nr);
1081         printf("[Local player is %d and currently %s.]\n",
1082                local_player->element_nr,
1083                local_player->active ? "active" : "not active");
1084       }
1085     }
1086
1087     Feld[x][y] = EL_EMPTY;
1088
1089     player->jx = player->last_jx = x;
1090     player->jy = player->last_jy = y;
1091   }
1092 }
1093
1094 static void InitField(int x, int y, boolean init_game)
1095 {
1096   int element = Feld[x][y];
1097
1098   switch (element)
1099   {
1100     case EL_SP_MURPHY:
1101     case EL_PLAYER_1:
1102     case EL_PLAYER_2:
1103     case EL_PLAYER_3:
1104     case EL_PLAYER_4:
1105       InitPlayerField(x, y, element, init_game);
1106       break;
1107
1108     case EL_SOKOBAN_FIELD_PLAYER:
1109       element = Feld[x][y] = EL_PLAYER_1;
1110       InitField(x, y, init_game);
1111
1112       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1113       InitField(x, y, init_game);
1114       break;
1115
1116     case EL_SOKOBAN_FIELD_EMPTY:
1117       local_player->sokobanfields_still_needed++;
1118       break;
1119
1120     case EL_STONEBLOCK:
1121       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1122         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1123       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1124         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1125       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1126         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1127       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1128         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1129       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1130         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1131       break;
1132
1133     case EL_BUG:
1134     case EL_BUG_RIGHT:
1135     case EL_BUG_UP:
1136     case EL_BUG_LEFT:
1137     case EL_BUG_DOWN:
1138     case EL_SPACESHIP:
1139     case EL_SPACESHIP_RIGHT:
1140     case EL_SPACESHIP_UP:
1141     case EL_SPACESHIP_LEFT:
1142     case EL_SPACESHIP_DOWN:
1143     case EL_BD_BUTTERFLY:
1144     case EL_BD_BUTTERFLY_RIGHT:
1145     case EL_BD_BUTTERFLY_UP:
1146     case EL_BD_BUTTERFLY_LEFT:
1147     case EL_BD_BUTTERFLY_DOWN:
1148     case EL_BD_FIREFLY:
1149     case EL_BD_FIREFLY_RIGHT:
1150     case EL_BD_FIREFLY_UP:
1151     case EL_BD_FIREFLY_LEFT:
1152     case EL_BD_FIREFLY_DOWN:
1153     case EL_PACMAN_RIGHT:
1154     case EL_PACMAN_UP:
1155     case EL_PACMAN_LEFT:
1156     case EL_PACMAN_DOWN:
1157     case EL_YAMYAM:
1158     case EL_YAMYAM_LEFT:
1159     case EL_YAMYAM_RIGHT:
1160     case EL_YAMYAM_UP:
1161     case EL_YAMYAM_DOWN:
1162     case EL_DARK_YAMYAM:
1163     case EL_ROBOT:
1164     case EL_PACMAN:
1165     case EL_SP_SNIKSNAK:
1166     case EL_SP_ELECTRON:
1167     case EL_MOLE:
1168     case EL_MOLE_LEFT:
1169     case EL_MOLE_RIGHT:
1170     case EL_MOLE_UP:
1171     case EL_MOLE_DOWN:
1172       InitMovDir(x, y);
1173       break;
1174
1175     case EL_AMOEBA_FULL:
1176     case EL_BD_AMOEBA:
1177       InitAmoebaNr(x, y);
1178       break;
1179
1180     case EL_AMOEBA_DROP:
1181       if (y == lev_fieldy - 1)
1182       {
1183         Feld[x][y] = EL_AMOEBA_GROWING;
1184         Store[x][y] = EL_AMOEBA_WET;
1185       }
1186       break;
1187
1188     case EL_DYNAMITE_ACTIVE:
1189     case EL_SP_DISK_RED_ACTIVE:
1190     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1191     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1192     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1193     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1194       MovDelay[x][y] = 96;
1195       break;
1196
1197     case EL_EM_DYNAMITE_ACTIVE:
1198       MovDelay[x][y] = 32;
1199       break;
1200
1201     case EL_LAMP:
1202       local_player->lights_still_needed++;
1203       break;
1204
1205     case EL_PENGUIN:
1206       local_player->friends_still_needed++;
1207       break;
1208
1209     case EL_PIG:
1210     case EL_DRAGON:
1211       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1212       break;
1213
1214     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1215     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1216     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1217     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1218     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1219     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1220     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1221     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1222     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1223     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1224     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1225     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1226       if (init_game)
1227       {
1228         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1229         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1230         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1231
1232         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1233         {
1234           game.belt_dir[belt_nr] = belt_dir;
1235           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1236         }
1237         else    /* more than one switch -- set it like the first switch */
1238         {
1239           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1240         }
1241       }
1242       break;
1243
1244 #if !USE_BOTH_SWITCHGATE_SWITCHES
1245     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1246       if (init_game)
1247         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1248       break;
1249
1250     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1251       if (init_game)
1252         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1253       break;
1254 #endif
1255
1256     case EL_LIGHT_SWITCH_ACTIVE:
1257       if (init_game)
1258         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1259       break;
1260
1261     case EL_INVISIBLE_STEELWALL:
1262     case EL_INVISIBLE_WALL:
1263     case EL_INVISIBLE_SAND:
1264       if (game.light_time_left > 0 ||
1265           game.lenses_time_left > 0)
1266         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1267       break;
1268
1269     case EL_EMC_MAGIC_BALL:
1270       if (game.ball_state)
1271         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1272       break;
1273
1274     case EL_EMC_MAGIC_BALL_SWITCH:
1275       if (game.ball_state)
1276         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1277       break;
1278
1279     default:
1280       if (IS_CUSTOM_ELEMENT(element))
1281       {
1282         if (CAN_MOVE(element))
1283           InitMovDir(x, y);
1284
1285 #if USE_NEW_CUSTOM_VALUE
1286         if (!element_info[element].use_last_ce_value || init_game)
1287           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1288 #endif
1289       }
1290       else if (IS_GROUP_ELEMENT(element))
1291       {
1292         Feld[x][y] = get_element_from_group_element(element);
1293
1294         InitField(x, y, init_game);
1295       }
1296
1297       break;
1298   }
1299
1300   if (!init_game)
1301     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1302 }
1303
1304 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1305 {
1306   InitField(x, y, init_game);
1307
1308   /* not needed to call InitMovDir() -- already done by InitField()! */
1309   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1310       CAN_MOVE(Feld[x][y]))
1311     InitMovDir(x, y);
1312 }
1313
1314 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1315 {
1316   int old_element = Feld[x][y];
1317
1318   InitField(x, y, init_game);
1319
1320   /* not needed to call InitMovDir() -- already done by InitField()! */
1321   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1322       CAN_MOVE(old_element) &&
1323       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1324     InitMovDir(x, y);
1325
1326   /* this case is in fact a combination of not less than three bugs:
1327      first, it calls InitMovDir() for elements that can move, although this is
1328      already done by InitField(); then, it checks the element that was at this
1329      field _before_ the call to InitField() (which can change it); lastly, it
1330      was not called for "mole with direction" elements, which were treated as
1331      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1332   */
1333 }
1334
1335 #if 1
1336
1337 void DrawGameValue_Emeralds(int value)
1338 {
1339   struct TextPosInfo *pos = &game.panel.gems;
1340   int font_nr = FONT_TEXT_2;
1341   int font_width = getFontWidth(font_nr);
1342   int digits = pos->chars;
1343
1344   if (PANEL_DEACTIVATED(pos))
1345     return;
1346
1347   pos->width = digits * font_width;
1348
1349   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1350 }
1351
1352 void DrawGameValue_Dynamite(int value)
1353 {
1354   struct TextPosInfo *pos = &game.panel.inventory;
1355   int font_nr = FONT_TEXT_2;
1356   int font_width = getFontWidth(font_nr);
1357   int digits = pos->chars;
1358
1359   if (PANEL_DEACTIVATED(pos))
1360     return;
1361
1362   pos->width = digits * font_width;
1363
1364   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1365 }
1366
1367 void DrawGameValue_Score(int value)
1368 {
1369   struct TextPosInfo *pos = &game.panel.score;
1370   int font_nr = FONT_TEXT_2;
1371   int font_width = getFontWidth(font_nr);
1372   int digits = pos->chars;
1373
1374   if (PANEL_DEACTIVATED(pos))
1375     return;
1376
1377   pos->width = digits * font_width;
1378
1379   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1380 }
1381
1382 void DrawGameValue_Time(int value)
1383 {
1384   struct TextPosInfo *pos = &game.panel.time;
1385   static int last_value = -1;
1386   int digits1 = 3;
1387   int digits2 = 4;
1388   int digits = pos->chars;
1389   int font1_nr = FONT_TEXT_2;
1390   int font2_nr = FONT_TEXT_1;
1391   int font_nr = font1_nr;
1392   boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1393
1394   if (PANEL_DEACTIVATED(pos))
1395     return;
1396
1397   if (use_dynamic_digits)               /* use dynamic number of digits */
1398   {
1399     digits  = (value < 1000 ? digits1  : digits2);
1400     font_nr = (value < 1000 ? font1_nr : font2_nr);
1401   }
1402
1403   /* clear background if value just changed its size (dynamic digits only) */
1404   if (use_dynamic_digits && (last_value < 1000) != (value < 1000))
1405   {
1406     int width1 = digits1 * getFontWidth(font1_nr);
1407     int width2 = digits2 * getFontWidth(font2_nr);
1408     int max_width = MAX(width1, width2);
1409     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1410
1411     pos->width = max_width;
1412
1413     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1414                                max_width, max_height);
1415   }
1416
1417   pos->width = digits * getFontWidth(font_nr);
1418
1419   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1420
1421   last_value = value;
1422 }
1423
1424 void DrawGameValue_Level(int value)
1425 {
1426   struct TextPosInfo *pos = &game.panel.level;
1427   int digits1 = 2;
1428   int digits2 = 3;
1429   int digits = pos->chars;
1430   int font1_nr = FONT_TEXT_2;
1431   int font2_nr = FONT_TEXT_1;
1432   int font_nr = font1_nr;
1433   boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1434
1435   if (PANEL_DEACTIVATED(pos))
1436     return;
1437
1438   if (use_dynamic_digits)               /* use dynamic number of digits */
1439   {
1440     digits  = (level_nr < 100 ? digits1  : digits2);
1441     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1442   }
1443
1444   pos->width = digits * getFontWidth(font_nr);
1445
1446   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1447 }
1448
1449 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1450 {
1451   struct TextPosInfo *pos = &game.panel.keys;
1452   int base_key_graphic = EL_KEY_1;
1453   int i;
1454
1455   if (PANEL_DEACTIVATED(pos))
1456     return;
1457
1458   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1459     base_key_graphic = EL_EM_KEY_1;
1460
1461   pos->width = 4 * MINI_TILEX;
1462
1463   /* currently only 4 of 8 possible keys are displayed */
1464   for (i = 0; i < STD_NUM_KEYS; i++)
1465   {
1466     int src_x = DOOR_GFX_PAGEX5 + 18;
1467     int src_y = DOOR_GFX_PAGEY1 + 123;
1468     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1469     int dst_y = PANEL_YPOS(pos);
1470
1471     if (key[i])
1472       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1473     else
1474       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1475                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1476   }
1477 }
1478
1479 #else
1480
1481 void DrawGameValue_Emeralds(int value)
1482 {
1483   int font_nr = FONT_TEXT_2;
1484   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1485
1486   if (PANEL_DEACTIVATED(game.panel.gems))
1487     return;
1488
1489   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1490 }
1491
1492 void DrawGameValue_Dynamite(int value)
1493 {
1494   int font_nr = FONT_TEXT_2;
1495   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1496
1497   if (PANEL_DEACTIVATED(game.panel.inventory))
1498     return;
1499
1500   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1501 }
1502
1503 void DrawGameValue_Score(int value)
1504 {
1505   int font_nr = FONT_TEXT_2;
1506   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1507
1508   if (PANEL_DEACTIVATED(game.panel.score))
1509     return;
1510
1511   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1512 }
1513
1514 void DrawGameValue_Time(int value)
1515 {
1516   int font1_nr = FONT_TEXT_2;
1517 #if 1
1518   int font2_nr = FONT_TEXT_1;
1519 #else
1520   int font2_nr = FONT_LEVEL_NUMBER;
1521 #endif
1522   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1523   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1524
1525   if (PANEL_DEACTIVATED(game.panel.time))
1526     return;
1527
1528   /* clear background if value just changed its size */
1529   if (value == 999 || value == 1000)
1530     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1531
1532   if (value < 1000)
1533     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1534   else
1535     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1536 }
1537
1538 void DrawGameValue_Level(int value)
1539 {
1540   int font1_nr = FONT_TEXT_2;
1541 #if 1
1542   int font2_nr = FONT_TEXT_1;
1543 #else
1544   int font2_nr = FONT_LEVEL_NUMBER;
1545 #endif
1546
1547   if (PANEL_DEACTIVATED(game.panel.level))
1548     return;
1549
1550   if (level_nr < 100)
1551     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1552   else
1553     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1554 }
1555
1556 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1557 {
1558   int base_key_graphic = EL_KEY_1;
1559   int i;
1560
1561   if (PANEL_DEACTIVATED(game.panel.keys))
1562     return;
1563
1564   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1565     base_key_graphic = EL_EM_KEY_1;
1566
1567   /* currently only 4 of 8 possible keys are displayed */
1568   for (i = 0; i < STD_NUM_KEYS; i++)
1569   {
1570     int x = XX_KEYS + i * MINI_TILEX;
1571     int y = YY_KEYS;
1572
1573     if (key[i])
1574       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1575     else
1576       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1577                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1578   }
1579 }
1580
1581 #endif
1582
1583 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1584                        int key_bits)
1585 {
1586   int key[MAX_NUM_KEYS];
1587   int i;
1588
1589   /* prevent EM engine from updating time/score values parallel to GameWon() */
1590   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1591       local_player->LevelSolved)
1592     return;
1593
1594   for (i = 0; i < MAX_NUM_KEYS; i++)
1595     key[i] = key_bits & (1 << i);
1596
1597   DrawGameValue_Level(level_nr);
1598
1599   DrawGameValue_Emeralds(emeralds);
1600   DrawGameValue_Dynamite(dynamite);
1601   DrawGameValue_Score(score);
1602   DrawGameValue_Time(time);
1603
1604   DrawGameValue_Keys(key);
1605 }
1606
1607 void DrawGameDoorValues()
1608 {
1609   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1610   int dynamite_value = 0;
1611   int score_value = (local_player->LevelSolved ? local_player->score_final :
1612                      local_player->score);
1613   int gems_value = local_player->gems_still_needed;
1614   int key_bits = 0;
1615   int i, j;
1616
1617   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1618   {
1619     DrawGameDoorValues_EM();
1620
1621     return;
1622   }
1623
1624   if (game.centered_player_nr == -1)
1625   {
1626     for (i = 0; i < MAX_PLAYERS; i++)
1627     {
1628       for (j = 0; j < MAX_NUM_KEYS; j++)
1629         if (stored_player[i].key[j])
1630           key_bits |= (1 << j);
1631
1632       dynamite_value += stored_player[i].inventory_size;
1633     }
1634   }
1635   else
1636   {
1637     int player_nr = game.centered_player_nr;
1638
1639     for (i = 0; i < MAX_NUM_KEYS; i++)
1640       if (stored_player[player_nr].key[i])
1641         key_bits |= (1 << i);
1642
1643     dynamite_value = stored_player[player_nr].inventory_size;
1644   }
1645
1646   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1647                     key_bits);
1648 }
1649
1650
1651 /*
1652   =============================================================================
1653   InitGameEngine()
1654   -----------------------------------------------------------------------------
1655   initialize game engine due to level / tape version number
1656   =============================================================================
1657 */
1658
1659 static void InitGameEngine()
1660 {
1661   int i, j, k, l, x, y;
1662
1663   /* set game engine from tape file when re-playing, else from level file */
1664   game.engine_version = (tape.playing ? tape.engine_version :
1665                          level.game_version);
1666
1667   /* ---------------------------------------------------------------------- */
1668   /* set flags for bugs and changes according to active game engine version */
1669   /* ---------------------------------------------------------------------- */
1670
1671   /*
1672     Summary of bugfix/change:
1673     Fixed handling for custom elements that change when pushed by the player.
1674
1675     Fixed/changed in version:
1676     3.1.0
1677
1678     Description:
1679     Before 3.1.0, custom elements that "change when pushing" changed directly
1680     after the player started pushing them (until then handled in "DigField()").
1681     Since 3.1.0, these custom elements are not changed until the "pushing"
1682     move of the element is finished (now handled in "ContinueMoving()").
1683
1684     Affected levels/tapes:
1685     The first condition is generally needed for all levels/tapes before version
1686     3.1.0, which might use the old behaviour before it was changed; known tapes
1687     that are affected are some tapes from the level set "Walpurgis Gardens" by
1688     Jamie Cullen.
1689     The second condition is an exception from the above case and is needed for
1690     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1691     above (including some development versions of 3.1.0), but before it was
1692     known that this change would break tapes like the above and was fixed in
1693     3.1.1, so that the changed behaviour was active although the engine version
1694     while recording maybe was before 3.1.0. There is at least one tape that is
1695     affected by this exception, which is the tape for the one-level set "Bug
1696     Machine" by Juergen Bonhagen.
1697   */
1698
1699   game.use_change_when_pushing_bug =
1700     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1701      !(tape.playing &&
1702        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1703        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1704
1705   /*
1706     Summary of bugfix/change:
1707     Fixed handling for blocking the field the player leaves when moving.
1708
1709     Fixed/changed in version:
1710     3.1.1
1711
1712     Description:
1713     Before 3.1.1, when "block last field when moving" was enabled, the field
1714     the player is leaving when moving was blocked for the time of the move,
1715     and was directly unblocked afterwards. This resulted in the last field
1716     being blocked for exactly one less than the number of frames of one player
1717     move. Additionally, even when blocking was disabled, the last field was
1718     blocked for exactly one frame.
1719     Since 3.1.1, due to changes in player movement handling, the last field
1720     is not blocked at all when blocking is disabled. When blocking is enabled,
1721     the last field is blocked for exactly the number of frames of one player
1722     move. Additionally, if the player is Murphy, the hero of Supaplex, the
1723     last field is blocked for exactly one more than the number of frames of
1724     one player move.
1725
1726     Affected levels/tapes:
1727     (!!! yet to be determined -- probably many !!!)
1728   */
1729
1730   game.use_block_last_field_bug =
1731     (game.engine_version < VERSION_IDENT(3,1,1,0));
1732
1733   /*
1734     Summary of bugfix/change:
1735     Changed behaviour of CE changes with multiple changes per single frame.
1736
1737     Fixed/changed in version:
1738     3.2.0-6
1739
1740     Description:
1741     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1742     This resulted in race conditions where CEs seem to behave strange in some
1743     situations (where triggered CE changes were just skipped because there was
1744     already a CE change on that tile in the playfield in that engine frame).
1745     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1746     (The number of changes per frame must be limited in any case, because else
1747     it is easily possible to define CE changes that would result in an infinite
1748     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1749     should be set large enough so that it would only be reached in cases where
1750     the corresponding CE change conditions run into a loop. Therefore, it seems
1751     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1752     maximal number of change pages for custom elements.)
1753
1754     Affected levels/tapes:
1755     Probably many.
1756   */
1757
1758 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1759   game.max_num_changes_per_frame = 1;
1760 #else
1761   game.max_num_changes_per_frame =
1762     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1763 #endif
1764
1765   /* ---------------------------------------------------------------------- */
1766
1767   /* default scan direction: scan playfield from top/left to bottom/right */
1768   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1769
1770   /* dynamically adjust element properties according to game engine version */
1771   InitElementPropertiesEngine(game.engine_version);
1772
1773 #if 0
1774   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1775   printf("          tape version == %06d [%s] [file: %06d]\n",
1776          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1777          tape.file_version);
1778   printf("       => game.engine_version == %06d\n", game.engine_version);
1779 #endif
1780
1781   /* ---------- initialize player's initial move delay --------------------- */
1782
1783   /* dynamically adjust player properties according to level information */
1784   for (i = 0; i < MAX_PLAYERS; i++)
1785     game.initial_move_delay_value[i] =
1786       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1787
1788   /* dynamically adjust player properties according to game engine version */
1789   for (i = 0; i < MAX_PLAYERS; i++)
1790     game.initial_move_delay[i] =
1791       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1792        game.initial_move_delay_value[i] : 0);
1793
1794   /* ---------- initialize player's initial push delay --------------------- */
1795
1796   /* dynamically adjust player properties according to game engine version */
1797   game.initial_push_delay_value =
1798     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1799
1800   /* ---------- initialize changing elements ------------------------------- */
1801
1802   /* initialize changing elements information */
1803   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1804   {
1805     struct ElementInfo *ei = &element_info[i];
1806
1807     /* this pointer might have been changed in the level editor */
1808     ei->change = &ei->change_page[0];
1809
1810     if (!IS_CUSTOM_ELEMENT(i))
1811     {
1812       ei->change->target_element = EL_EMPTY_SPACE;
1813       ei->change->delay_fixed = 0;
1814       ei->change->delay_random = 0;
1815       ei->change->delay_frames = 1;
1816     }
1817
1818     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1819     {
1820       ei->has_change_event[j] = FALSE;
1821
1822       ei->event_page_nr[j] = 0;
1823       ei->event_page[j] = &ei->change_page[0];
1824     }
1825   }
1826
1827   /* add changing elements from pre-defined list */
1828   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1829   {
1830     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1831     struct ElementInfo *ei = &element_info[ch_delay->element];
1832
1833     ei->change->target_element       = ch_delay->target_element;
1834     ei->change->delay_fixed          = ch_delay->change_delay;
1835
1836     ei->change->pre_change_function  = ch_delay->pre_change_function;
1837     ei->change->change_function      = ch_delay->change_function;
1838     ei->change->post_change_function = ch_delay->post_change_function;
1839
1840     ei->change->can_change = TRUE;
1841     ei->change->can_change_or_has_action = TRUE;
1842
1843     ei->has_change_event[CE_DELAY] = TRUE;
1844
1845     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1846     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1847   }
1848
1849   /* ---------- initialize internal run-time variables ------------- */
1850
1851   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1852   {
1853     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1854
1855     for (j = 0; j < ei->num_change_pages; j++)
1856     {
1857       ei->change_page[j].can_change_or_has_action =
1858         (ei->change_page[j].can_change |
1859          ei->change_page[j].has_action);
1860     }
1861   }
1862
1863   /* add change events from custom element configuration */
1864   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1865   {
1866     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1867
1868     for (j = 0; j < ei->num_change_pages; j++)
1869     {
1870       if (!ei->change_page[j].can_change_or_has_action)
1871         continue;
1872
1873       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1874       {
1875         /* only add event page for the first page found with this event */
1876         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1877         {
1878           ei->has_change_event[k] = TRUE;
1879
1880           ei->event_page_nr[k] = j;
1881           ei->event_page[k] = &ei->change_page[j];
1882         }
1883       }
1884     }
1885   }
1886
1887   /* ---------- initialize run-time trigger player and element ------------- */
1888
1889   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1890   {
1891     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1892
1893     for (j = 0; j < ei->num_change_pages; j++)
1894     {
1895       ei->change_page[j].actual_trigger_element = EL_EMPTY;
1896       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1897       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1898       ei->change_page[j].actual_trigger_ce_value = 0;
1899       ei->change_page[j].actual_trigger_ce_score = 0;
1900     }
1901   }
1902
1903   /* ---------- initialize trigger events ---------------------------------- */
1904
1905   /* initialize trigger events information */
1906   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1907     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1908       trigger_events[i][j] = FALSE;
1909
1910   /* add trigger events from element change event properties */
1911   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1912   {
1913     struct ElementInfo *ei = &element_info[i];
1914
1915     for (j = 0; j < ei->num_change_pages; j++)
1916     {
1917       if (!ei->change_page[j].can_change_or_has_action)
1918         continue;
1919
1920       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1921       {
1922         int trigger_element = ei->change_page[j].trigger_element;
1923
1924         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1925         {
1926           if (ei->change_page[j].has_event[k])
1927           {
1928             if (IS_GROUP_ELEMENT(trigger_element))
1929             {
1930               struct ElementGroupInfo *group =
1931                 element_info[trigger_element].group;
1932
1933               for (l = 0; l < group->num_elements_resolved; l++)
1934                 trigger_events[group->element_resolved[l]][k] = TRUE;
1935             }
1936             else if (trigger_element == EL_ANY_ELEMENT)
1937               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1938                 trigger_events[l][k] = TRUE;
1939             else
1940               trigger_events[trigger_element][k] = TRUE;
1941           }
1942         }
1943       }
1944     }
1945   }
1946
1947   /* ---------- initialize push delay -------------------------------------- */
1948
1949   /* initialize push delay values to default */
1950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1951   {
1952     if (!IS_CUSTOM_ELEMENT(i))
1953     {
1954       /* set default push delay values (corrected since version 3.0.7-1) */
1955       if (game.engine_version < VERSION_IDENT(3,0,7,1))
1956       {
1957         element_info[i].push_delay_fixed = 2;
1958         element_info[i].push_delay_random = 8;
1959       }
1960       else
1961       {
1962         element_info[i].push_delay_fixed = 8;
1963         element_info[i].push_delay_random = 8;
1964       }
1965     }
1966   }
1967
1968   /* set push delay value for certain elements from pre-defined list */
1969   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1970   {
1971     int e = push_delay_list[i].element;
1972
1973     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1974     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1975   }
1976
1977   /* set push delay value for Supaplex elements for newer engine versions */
1978   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1979   {
1980     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1981     {
1982       if (IS_SP_ELEMENT(i))
1983       {
1984         /* set SP push delay to just enough to push under a falling zonk */
1985         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1986
1987         element_info[i].push_delay_fixed  = delay;
1988         element_info[i].push_delay_random = 0;
1989       }
1990     }
1991   }
1992
1993   /* ---------- initialize move stepsize ----------------------------------- */
1994
1995   /* initialize move stepsize values to default */
1996   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1997     if (!IS_CUSTOM_ELEMENT(i))
1998       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1999
2000   /* set move stepsize value for certain elements from pre-defined list */
2001   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2002   {
2003     int e = move_stepsize_list[i].element;
2004
2005     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2006   }
2007
2008   /* ---------- initialize collect score ----------------------------------- */
2009
2010   /* initialize collect score values for custom elements from initial value */
2011   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2012     if (IS_CUSTOM_ELEMENT(i))
2013       element_info[i].collect_score = element_info[i].collect_score_initial;
2014
2015   /* ---------- initialize collect count ----------------------------------- */
2016
2017   /* initialize collect count values for non-custom elements */
2018   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2019     if (!IS_CUSTOM_ELEMENT(i))
2020       element_info[i].collect_count_initial = 0;
2021
2022   /* add collect count values for all elements from pre-defined list */
2023   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2024     element_info[collect_count_list[i].element].collect_count_initial =
2025       collect_count_list[i].count;
2026
2027   /* ---------- initialize access direction -------------------------------- */
2028
2029   /* initialize access direction values to default (access from every side) */
2030   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2031     if (!IS_CUSTOM_ELEMENT(i))
2032       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2033
2034   /* set access direction value for certain elements from pre-defined list */
2035   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2036     element_info[access_direction_list[i].element].access_direction =
2037       access_direction_list[i].direction;
2038
2039   /* ---------- initialize explosion content ------------------------------- */
2040   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2041   {
2042     if (IS_CUSTOM_ELEMENT(i))
2043       continue;
2044
2045     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2046     {
2047       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2048
2049       element_info[i].content.e[x][y] =
2050         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2051          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2052          i == EL_PLAYER_3 ? EL_EMERALD :
2053          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2054          i == EL_MOLE ? EL_EMERALD_RED :
2055          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2056          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2057          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2058          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2059          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2060          i == EL_WALL_EMERALD ? EL_EMERALD :
2061          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2062          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2063          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2064          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2065          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2066          i == EL_WALL_PEARL ? EL_PEARL :
2067          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2068          EL_EMPTY);
2069     }
2070   }
2071
2072   /* ---------- initialize recursion detection ------------------------------ */
2073   recursion_loop_depth = 0;
2074   recursion_loop_detected = FALSE;
2075   recursion_loop_element = EL_UNDEFINED;
2076 }
2077
2078 int get_num_special_action(int element, int action_first, int action_last)
2079 {
2080   int num_special_action = 0;
2081   int i, j;
2082
2083   for (i = action_first; i <= action_last; i++)
2084   {
2085     boolean found = FALSE;
2086
2087     for (j = 0; j < NUM_DIRECTIONS; j++)
2088       if (el_act_dir2img(element, i, j) !=
2089           el_act_dir2img(element, ACTION_DEFAULT, j))
2090         found = TRUE;
2091
2092     if (found)
2093       num_special_action++;
2094     else
2095       break;
2096   }
2097
2098   return num_special_action;
2099 }
2100
2101
2102 /*
2103   =============================================================================
2104   InitGame()
2105   -----------------------------------------------------------------------------
2106   initialize and start new game
2107   =============================================================================
2108 */
2109
2110 void InitGame()
2111 {
2112   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2113   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2114   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2115   boolean do_fading = (game_status == GAME_MODE_MAIN);
2116   int i, j, x, y;
2117
2118   game_status = GAME_MODE_PLAYING;
2119
2120   InitGameEngine();
2121
2122   /* don't play tapes over network */
2123   network_playing = (options.network && !tape.playing);
2124
2125   for (i = 0; i < MAX_PLAYERS; i++)
2126   {
2127     struct PlayerInfo *player = &stored_player[i];
2128
2129     player->index_nr = i;
2130     player->index_bit = (1 << i);
2131     player->element_nr = EL_PLAYER_1 + i;
2132
2133     player->present = FALSE;
2134     player->active = FALSE;
2135     player->killed = FALSE;
2136
2137     player->action = 0;
2138     player->effective_action = 0;
2139     player->programmed_action = 0;
2140
2141     player->score = 0;
2142     player->score_final = 0;
2143
2144     player->gems_still_needed = level.gems_needed;
2145     player->sokobanfields_still_needed = 0;
2146     player->lights_still_needed = 0;
2147     player->friends_still_needed = 0;
2148
2149     for (j = 0; j < MAX_NUM_KEYS; j++)
2150       player->key[j] = FALSE;
2151
2152     player->num_white_keys = 0;
2153
2154     player->dynabomb_count = 0;
2155     player->dynabomb_size = 1;
2156     player->dynabombs_left = 0;
2157     player->dynabomb_xl = FALSE;
2158
2159     player->MovDir = MV_NONE;
2160     player->MovPos = 0;
2161     player->GfxPos = 0;
2162     player->GfxDir = MV_NONE;
2163     player->GfxAction = ACTION_DEFAULT;
2164     player->Frame = 0;
2165     player->StepFrame = 0;
2166
2167     player->use_murphy = FALSE;
2168     player->artwork_element =
2169       (level.use_artwork_element[i] ? level.artwork_element[i] :
2170        player->element_nr);
2171
2172     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2173     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2174
2175     player->gravity = level.initial_player_gravity[i];
2176
2177     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2178
2179     player->actual_frame_counter = 0;
2180
2181     player->step_counter = 0;
2182
2183     player->last_move_dir = MV_NONE;
2184
2185     player->is_active = FALSE;
2186
2187     player->is_waiting = FALSE;
2188     player->is_moving = FALSE;
2189     player->is_auto_moving = FALSE;
2190     player->is_digging = FALSE;
2191     player->is_snapping = FALSE;
2192     player->is_collecting = FALSE;
2193     player->is_pushing = FALSE;
2194     player->is_switching = FALSE;
2195     player->is_dropping = FALSE;
2196     player->is_dropping_pressed = FALSE;
2197
2198     player->is_bored = FALSE;
2199     player->is_sleeping = FALSE;
2200
2201     player->frame_counter_bored = -1;
2202     player->frame_counter_sleeping = -1;
2203
2204     player->anim_delay_counter = 0;
2205     player->post_delay_counter = 0;
2206
2207     player->dir_waiting = MV_NONE;
2208     player->action_waiting = ACTION_DEFAULT;
2209     player->last_action_waiting = ACTION_DEFAULT;
2210     player->special_action_bored = ACTION_DEFAULT;
2211     player->special_action_sleeping = ACTION_DEFAULT;
2212
2213     player->switch_x = -1;
2214     player->switch_y = -1;
2215
2216     player->drop_x = -1;
2217     player->drop_y = -1;
2218
2219     player->show_envelope = 0;
2220
2221     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2222
2223     player->push_delay       = -1;      /* initialized when pushing starts */
2224     player->push_delay_value = game.initial_push_delay_value;
2225
2226     player->drop_delay = 0;
2227     player->drop_pressed_delay = 0;
2228
2229     player->last_jx = -1;
2230     player->last_jy = -1;
2231     player->jx = -1;
2232     player->jy = -1;
2233
2234     player->shield_normal_time_left = 0;
2235     player->shield_deadly_time_left = 0;
2236
2237     player->inventory_infinite_element = EL_UNDEFINED;
2238     player->inventory_size = 0;
2239
2240     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2241     SnapField(player, 0, 0);
2242
2243     player->LevelSolved = FALSE;
2244     player->GameOver = FALSE;
2245
2246     player->LevelSolved_GameWon = FALSE;
2247     player->LevelSolved_GameEnd = FALSE;
2248     player->LevelSolved_PanelOff = FALSE;
2249     player->LevelSolved_SaveTape = FALSE;
2250     player->LevelSolved_SaveScore = FALSE;
2251   }
2252
2253   network_player_action_received = FALSE;
2254
2255 #if defined(NETWORK_AVALIABLE)
2256   /* initial null action */
2257   if (network_playing)
2258     SendToServer_MovePlayer(MV_NONE);
2259 #endif
2260
2261   ZX = ZY = -1;
2262   ExitX = ExitY = -1;
2263
2264   FrameCounter = 0;
2265   TimeFrames = 0;
2266   TimePlayed = 0;
2267   TimeLeft = level.time;
2268   TapeTime = 0;
2269
2270   ScreenMovDir = MV_NONE;
2271   ScreenMovPos = 0;
2272   ScreenGfxPos = 0;
2273
2274   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2275
2276   AllPlayersGone = FALSE;
2277
2278   game.yamyam_content_nr = 0;
2279   game.magic_wall_active = FALSE;
2280   game.magic_wall_time_left = 0;
2281   game.light_time_left = 0;
2282   game.timegate_time_left = 0;
2283   game.switchgate_pos = 0;
2284   game.wind_direction = level.wind_direction_initial;
2285
2286 #if !USE_PLAYER_GRAVITY
2287   game.gravity = FALSE;
2288   game.explosions_delayed = TRUE;
2289 #endif
2290
2291   game.lenses_time_left = 0;
2292   game.magnify_time_left = 0;
2293
2294   game.ball_state = level.ball_state_initial;
2295   game.ball_content_nr = 0;
2296
2297   game.envelope_active = FALSE;
2298
2299   /* set focus to local player for network games, else to all players */
2300   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2301   game.centered_player_nr_next = game.centered_player_nr;
2302   game.set_centered_player = FALSE;
2303
2304   if (network_playing && tape.recording)
2305   {
2306     /* store client dependent player focus when recording network games */
2307     tape.centered_player_nr_next = game.centered_player_nr_next;
2308     tape.set_centered_player = TRUE;
2309   }
2310
2311   for (i = 0; i < NUM_BELTS; i++)
2312   {
2313     game.belt_dir[i] = MV_NONE;
2314     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2315   }
2316
2317   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2318     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2319
2320   SCAN_PLAYFIELD(x, y)
2321   {
2322     Feld[x][y] = level.field[x][y];
2323     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2324     ChangeDelay[x][y] = 0;
2325     ChangePage[x][y] = -1;
2326 #if USE_NEW_CUSTOM_VALUE
2327     CustomValue[x][y] = 0;              /* initialized in InitField() */
2328 #endif
2329     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2330     AmoebaNr[x][y] = 0;
2331     WasJustMoving[x][y] = 0;
2332     WasJustFalling[x][y] = 0;
2333     CheckCollision[x][y] = 0;
2334     CheckImpact[x][y] = 0;
2335     Stop[x][y] = FALSE;
2336     Pushed[x][y] = FALSE;
2337
2338     ChangeCount[x][y] = 0;
2339     ChangeEvent[x][y] = -1;
2340
2341     ExplodePhase[x][y] = 0;
2342     ExplodeDelay[x][y] = 0;
2343     ExplodeField[x][y] = EX_TYPE_NONE;
2344
2345     RunnerVisit[x][y] = 0;
2346     PlayerVisit[x][y] = 0;
2347
2348     GfxFrame[x][y] = 0;
2349     GfxRandom[x][y] = INIT_GFX_RANDOM();
2350     GfxElement[x][y] = EL_UNDEFINED;
2351     GfxAction[x][y] = ACTION_DEFAULT;
2352     GfxDir[x][y] = MV_NONE;
2353   }
2354
2355   SCAN_PLAYFIELD(x, y)
2356   {
2357     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2358       emulate_bd = FALSE;
2359     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2360       emulate_sb = FALSE;
2361     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2362       emulate_sp = FALSE;
2363
2364     InitField(x, y, TRUE);
2365   }
2366
2367   InitBeltMovement();
2368
2369   for (i = 0; i < MAX_PLAYERS; i++)
2370   {
2371     struct PlayerInfo *player = &stored_player[i];
2372
2373     /* set number of special actions for bored and sleeping animation */
2374     player->num_special_action_bored =
2375       get_num_special_action(player->artwork_element,
2376                              ACTION_BORING_1, ACTION_BORING_LAST);
2377     player->num_special_action_sleeping =
2378       get_num_special_action(player->artwork_element,
2379                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2380   }
2381
2382   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2383                     emulate_sb ? EMU_SOKOBAN :
2384                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2385
2386 #if USE_NEW_ALL_SLIPPERY
2387   /* initialize type of slippery elements */
2388   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2389   {
2390     if (!IS_CUSTOM_ELEMENT(i))
2391     {
2392       /* default: elements slip down either to the left or right randomly */
2393       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2394
2395       /* SP style elements prefer to slip down on the left side */
2396       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2397         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2398
2399       /* BD style elements prefer to slip down on the left side */
2400       if (game.emulation == EMU_BOULDERDASH)
2401         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2402     }
2403   }
2404 #endif
2405
2406   /* initialize explosion and ignition delay */
2407   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2408   {
2409     if (!IS_CUSTOM_ELEMENT(i))
2410     {
2411       int num_phase = 8;
2412       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2413                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2414                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2415       int last_phase = (num_phase + 1) * delay;
2416       int half_phase = (num_phase / 2) * delay;
2417
2418       element_info[i].explosion_delay = last_phase - 1;
2419       element_info[i].ignition_delay = half_phase;
2420
2421       if (i == EL_BLACK_ORB)
2422         element_info[i].ignition_delay = 1;
2423     }
2424
2425 #if 0
2426     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2427       element_info[i].explosion_delay = 1;
2428
2429     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2430       element_info[i].ignition_delay = 1;
2431 #endif
2432   }
2433
2434   /* correct non-moving belts to start moving left */
2435   for (i = 0; i < NUM_BELTS; i++)
2436     if (game.belt_dir[i] == MV_NONE)
2437       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2438
2439   /* check if any connected player was not found in playfield */
2440   for (i = 0; i < MAX_PLAYERS; i++)
2441   {
2442     struct PlayerInfo *player = &stored_player[i];
2443
2444     if (player->connected && !player->present)
2445     {
2446       for (j = 0; j < MAX_PLAYERS; j++)
2447       {
2448         struct PlayerInfo *some_player = &stored_player[j];
2449         int jx = some_player->jx, jy = some_player->jy;
2450
2451         /* assign first free player found that is present in the playfield */
2452         if (some_player->present && !some_player->connected)
2453         {
2454           player->present = TRUE;
2455           player->active = TRUE;
2456
2457           some_player->present = FALSE;
2458           some_player->active = FALSE;
2459
2460           player->artwork_element = some_player->artwork_element;
2461
2462           player->block_last_field       = some_player->block_last_field;
2463           player->block_delay_adjustment = some_player->block_delay_adjustment;
2464
2465           StorePlayer[jx][jy] = player->element_nr;
2466           player->jx = player->last_jx = jx;
2467           player->jy = player->last_jy = jy;
2468
2469           break;
2470         }
2471       }
2472     }
2473   }
2474
2475   if (tape.playing)
2476   {
2477     /* when playing a tape, eliminate all players who do not participate */
2478
2479     for (i = 0; i < MAX_PLAYERS; i++)
2480     {
2481       if (stored_player[i].active && !tape.player_participates[i])
2482       {
2483         struct PlayerInfo *player = &stored_player[i];
2484         int jx = player->jx, jy = player->jy;
2485
2486         player->active = FALSE;
2487         StorePlayer[jx][jy] = 0;
2488         Feld[jx][jy] = EL_EMPTY;
2489       }
2490     }
2491   }
2492   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2493   {
2494     /* when in single player mode, eliminate all but the first active player */
2495
2496     for (i = 0; i < MAX_PLAYERS; i++)
2497     {
2498       if (stored_player[i].active)
2499       {
2500         for (j = i + 1; j < MAX_PLAYERS; j++)
2501         {
2502           if (stored_player[j].active)
2503           {
2504             struct PlayerInfo *player = &stored_player[j];
2505             int jx = player->jx, jy = player->jy;
2506
2507             player->active = FALSE;
2508             player->present = FALSE;
2509
2510             StorePlayer[jx][jy] = 0;
2511             Feld[jx][jy] = EL_EMPTY;
2512           }
2513         }
2514       }
2515     }
2516   }
2517
2518   /* when recording the game, store which players take part in the game */
2519   if (tape.recording)
2520   {
2521     for (i = 0; i < MAX_PLAYERS; i++)
2522       if (stored_player[i].active)
2523         tape.player_participates[i] = TRUE;
2524   }
2525
2526   if (options.debug)
2527   {
2528     for (i = 0; i < MAX_PLAYERS; i++)
2529     {
2530       struct PlayerInfo *player = &stored_player[i];
2531
2532       printf("Player %d: present == %d, connected == %d, active == %d.\n",
2533              i+1,
2534              player->present,
2535              player->connected,
2536              player->active);
2537       if (local_player == player)
2538         printf("Player  %d is local player.\n", i+1);
2539     }
2540   }
2541
2542   if (BorderElement == EL_EMPTY)
2543   {
2544     SBX_Left = 0;
2545     SBX_Right = lev_fieldx - SCR_FIELDX;
2546     SBY_Upper = 0;
2547     SBY_Lower = lev_fieldy - SCR_FIELDY;
2548   }
2549   else
2550   {
2551     SBX_Left = -1;
2552     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2553     SBY_Upper = -1;
2554     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2555   }
2556
2557   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2558     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2559
2560   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2561     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2562
2563   /* if local player not found, look for custom element that might create
2564      the player (make some assumptions about the right custom element) */
2565   if (!local_player->present)
2566   {
2567     int start_x = 0, start_y = 0;
2568     int found_rating = 0;
2569     int found_element = EL_UNDEFINED;
2570     int player_nr = local_player->index_nr;
2571
2572     SCAN_PLAYFIELD(x, y)
2573     {
2574       int element = Feld[x][y];
2575       int content;
2576       int xx, yy;
2577       boolean is_player;
2578
2579       if (level.use_start_element[player_nr] &&
2580           level.start_element[player_nr] == element &&
2581           found_rating < 4)
2582       {
2583         start_x = x;
2584         start_y = y;
2585
2586         found_rating = 4;
2587         found_element = element;
2588       }
2589
2590       if (!IS_CUSTOM_ELEMENT(element))
2591         continue;
2592
2593       if (CAN_CHANGE(element))
2594       {
2595         for (i = 0; i < element_info[element].num_change_pages; i++)
2596         {
2597           /* check for player created from custom element as single target */
2598           content = element_info[element].change_page[i].target_element;
2599           is_player = ELEM_IS_PLAYER(content);
2600
2601           if (is_player && (found_rating < 3 || element < found_element))
2602           {
2603             start_x = x;
2604             start_y = y;
2605
2606             found_rating = 3;
2607             found_element = element;
2608           }
2609         }
2610       }
2611
2612       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2613       {
2614         /* check for player created from custom element as explosion content */
2615         content = element_info[element].content.e[xx][yy];
2616         is_player = ELEM_IS_PLAYER(content);
2617
2618         if (is_player && (found_rating < 2 || element < found_element))
2619         {
2620           start_x = x + xx - 1;
2621           start_y = y + yy - 1;
2622
2623           found_rating = 2;
2624           found_element = element;
2625         }
2626
2627         if (!CAN_CHANGE(element))
2628           continue;
2629
2630         for (i = 0; i < element_info[element].num_change_pages; i++)
2631         {
2632           /* check for player created from custom element as extended target */
2633           content =
2634             element_info[element].change_page[i].target_content.e[xx][yy];
2635
2636           is_player = ELEM_IS_PLAYER(content);
2637
2638           if (is_player && (found_rating < 1 || element < found_element))
2639           {
2640             start_x = x + xx - 1;
2641             start_y = y + yy - 1;
2642
2643             found_rating = 1;
2644             found_element = element;
2645           }
2646         }
2647       }
2648     }
2649
2650     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
2651                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2652                 start_x - MIDPOSX);
2653
2654     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2655                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2656                 start_y - MIDPOSY);
2657   }
2658   else
2659   {
2660     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2661                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2662                 local_player->jx - MIDPOSX);
2663
2664     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2665                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2666                 local_player->jy - MIDPOSY);
2667   }
2668
2669   StopAnimation();
2670
2671   if (!game.restart_level)
2672     CloseDoor(DOOR_CLOSE_1);
2673
2674   if (do_fading)
2675     FadeOut(REDRAW_FIELD);
2676
2677   /* !!! FIX THIS (START) !!! */
2678   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2679   {
2680     InitGameEngine_EM();
2681
2682     /* blit playfield from scroll buffer to normal back buffer for fading in */
2683     BlitScreenToBitmap_EM(backbuffer);
2684   }
2685   else
2686   {
2687     DrawLevel();
2688     DrawAllPlayers();
2689
2690     /* after drawing the level, correct some elements */
2691     if (game.timegate_time_left == 0)
2692       CloseAllOpenTimegates();
2693
2694     /* blit playfield from scroll buffer to normal back buffer for fading in */
2695     if (setup.soft_scrolling)
2696       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2697
2698     redraw_mask |= REDRAW_FROM_BACKBUFFER;
2699   }
2700   /* !!! FIX THIS (END) !!! */
2701
2702   if (do_fading)
2703     FadeIn(REDRAW_FIELD);
2704
2705   BackToFront();
2706
2707   if (!game.restart_level)
2708   {
2709     /* copy default game door content to main double buffer */
2710     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2711                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2712   }
2713
2714   SetPanelBackground();
2715   SetDrawBackgroundMask(REDRAW_DOOR_1);
2716
2717   DrawGameDoorValues();
2718
2719   if (!game.restart_level)
2720   {
2721     UnmapGameButtons();
2722     UnmapTapeButtons();
2723     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2724     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2725     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2726     MapGameButtons();
2727     MapTapeButtons();
2728
2729     /* copy actual game door content to door double buffer for OpenDoor() */
2730     BlitBitmap(drawto, bitmap_db_door,
2731                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2732
2733     OpenDoor(DOOR_OPEN_ALL);
2734
2735     PlaySound(SND_GAME_STARTING);
2736
2737     if (setup.sound_music)
2738       PlayLevelMusic();
2739
2740     KeyboardAutoRepeatOffUnlessAutoplay();
2741
2742     if (options.debug)
2743     {
2744       for (i = 0; i < MAX_PLAYERS; i++)
2745         printf("Player %d %sactive.\n",
2746                i + 1, (stored_player[i].active ? "" : "not "));
2747     }
2748   }
2749
2750 #if 1
2751   UnmapAllGadgets();
2752
2753   MapGameButtons();
2754   MapTapeButtons();
2755 #endif
2756
2757   game.restart_level = FALSE;
2758 }
2759
2760 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2761 {
2762   /* this is used for non-R'n'D game engines to update certain engine values */
2763
2764   /* needed to determine if sounds are played within the visible screen area */
2765   scroll_x = actual_scroll_x;
2766   scroll_y = actual_scroll_y;
2767 }
2768
2769 void InitMovDir(int x, int y)
2770 {
2771   int i, element = Feld[x][y];
2772   static int xy[4][2] =
2773   {
2774     {  0, +1 },
2775     { +1,  0 },
2776     {  0, -1 },
2777     { -1,  0 }
2778   };
2779   static int direction[3][4] =
2780   {
2781     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
2782     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
2783     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
2784   };
2785
2786   switch (element)
2787   {
2788     case EL_BUG_RIGHT:
2789     case EL_BUG_UP:
2790     case EL_BUG_LEFT:
2791     case EL_BUG_DOWN:
2792       Feld[x][y] = EL_BUG;
2793       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2794       break;
2795
2796     case EL_SPACESHIP_RIGHT:
2797     case EL_SPACESHIP_UP:
2798     case EL_SPACESHIP_LEFT:
2799     case EL_SPACESHIP_DOWN:
2800       Feld[x][y] = EL_SPACESHIP;
2801       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2802       break;
2803
2804     case EL_BD_BUTTERFLY_RIGHT:
2805     case EL_BD_BUTTERFLY_UP:
2806     case EL_BD_BUTTERFLY_LEFT:
2807     case EL_BD_BUTTERFLY_DOWN:
2808       Feld[x][y] = EL_BD_BUTTERFLY;
2809       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2810       break;
2811
2812     case EL_BD_FIREFLY_RIGHT:
2813     case EL_BD_FIREFLY_UP:
2814     case EL_BD_FIREFLY_LEFT:
2815     case EL_BD_FIREFLY_DOWN:
2816       Feld[x][y] = EL_BD_FIREFLY;
2817       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2818       break;
2819
2820     case EL_PACMAN_RIGHT:
2821     case EL_PACMAN_UP:
2822     case EL_PACMAN_LEFT:
2823     case EL_PACMAN_DOWN:
2824       Feld[x][y] = EL_PACMAN;
2825       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2826       break;
2827
2828     case EL_YAMYAM_LEFT:
2829     case EL_YAMYAM_RIGHT:
2830     case EL_YAMYAM_UP:
2831     case EL_YAMYAM_DOWN:
2832       Feld[x][y] = EL_YAMYAM;
2833       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2834       break;
2835
2836     case EL_SP_SNIKSNAK:
2837       MovDir[x][y] = MV_UP;
2838       break;
2839
2840     case EL_SP_ELECTRON:
2841       MovDir[x][y] = MV_LEFT;
2842       break;
2843
2844     case EL_MOLE_LEFT:
2845     case EL_MOLE_RIGHT:
2846     case EL_MOLE_UP:
2847     case EL_MOLE_DOWN:
2848       Feld[x][y] = EL_MOLE;
2849       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2850       break;
2851
2852     default:
2853       if (IS_CUSTOM_ELEMENT(element))
2854       {
2855         struct ElementInfo *ei = &element_info[element];
2856         int move_direction_initial = ei->move_direction_initial;
2857         int move_pattern = ei->move_pattern;
2858
2859         if (move_direction_initial == MV_START_PREVIOUS)
2860         {
2861           if (MovDir[x][y] != MV_NONE)
2862             return;
2863
2864           move_direction_initial = MV_START_AUTOMATIC;
2865         }
2866
2867         if (move_direction_initial == MV_START_RANDOM)
2868           MovDir[x][y] = 1 << RND(4);
2869         else if (move_direction_initial & MV_ANY_DIRECTION)
2870           MovDir[x][y] = move_direction_initial;
2871         else if (move_pattern == MV_ALL_DIRECTIONS ||
2872                  move_pattern == MV_TURNING_LEFT ||
2873                  move_pattern == MV_TURNING_RIGHT ||
2874                  move_pattern == MV_TURNING_LEFT_RIGHT ||
2875                  move_pattern == MV_TURNING_RIGHT_LEFT ||
2876                  move_pattern == MV_TURNING_RANDOM)
2877           MovDir[x][y] = 1 << RND(4);
2878         else if (move_pattern == MV_HORIZONTAL)
2879           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2880         else if (move_pattern == MV_VERTICAL)
2881           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2882         else if (move_pattern & MV_ANY_DIRECTION)
2883           MovDir[x][y] = element_info[element].move_pattern;
2884         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2885                  move_pattern == MV_ALONG_RIGHT_SIDE)
2886         {
2887           /* use random direction as default start direction */
2888           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2889             MovDir[x][y] = 1 << RND(4);
2890
2891           for (i = 0; i < NUM_DIRECTIONS; i++)
2892           {
2893             int x1 = x + xy[i][0];
2894             int y1 = y + xy[i][1];
2895
2896             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2897             {
2898               if (move_pattern == MV_ALONG_RIGHT_SIDE)
2899                 MovDir[x][y] = direction[0][i];
2900               else
2901                 MovDir[x][y] = direction[1][i];
2902
2903               break;
2904             }
2905           }
2906         }                
2907       }
2908       else
2909       {
2910         MovDir[x][y] = 1 << RND(4);
2911
2912         if (element != EL_BUG &&
2913             element != EL_SPACESHIP &&
2914             element != EL_BD_BUTTERFLY &&
2915             element != EL_BD_FIREFLY)
2916           break;
2917
2918         for (i = 0; i < NUM_DIRECTIONS; i++)
2919         {
2920           int x1 = x + xy[i][0];
2921           int y1 = y + xy[i][1];
2922
2923           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2924           {
2925             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2926             {
2927               MovDir[x][y] = direction[0][i];
2928               break;
2929             }
2930             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2931                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2932             {
2933               MovDir[x][y] = direction[1][i];
2934               break;
2935             }
2936           }
2937         }
2938       }
2939       break;
2940   }
2941
2942   GfxDir[x][y] = MovDir[x][y];
2943 }
2944
2945 void InitAmoebaNr(int x, int y)
2946 {
2947   int i;
2948   int group_nr = AmoebeNachbarNr(x, y);
2949
2950   if (group_nr == 0)
2951   {
2952     for (i = 1; i < MAX_NUM_AMOEBA; i++)
2953     {
2954       if (AmoebaCnt[i] == 0)
2955       {
2956         group_nr = i;
2957         break;
2958       }
2959     }
2960   }
2961
2962   AmoebaNr[x][y] = group_nr;
2963   AmoebaCnt[group_nr]++;
2964   AmoebaCnt2[group_nr]++;
2965 }
2966
2967 static void PlayerWins(struct PlayerInfo *player)
2968 {
2969   player->LevelSolved = TRUE;
2970   player->GameOver = TRUE;
2971
2972   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2973                          level.native_em_level->lev->score : player->score);
2974 }
2975
2976 void GameWon()
2977 {
2978   static int time, time_final;
2979   static int score, score_final;
2980   static int game_over_delay_1 = 0;
2981   static int game_over_delay_2 = 0;
2982   int game_over_delay_value_1 = 50;
2983   int game_over_delay_value_2 = 50;
2984
2985   if (!local_player->LevelSolved_GameWon)
2986   {
2987     int i;
2988
2989     /* do not start end game actions before the player stops moving (to exit) */
2990     if (local_player->MovPos)
2991       return;
2992
2993     local_player->LevelSolved_GameWon = TRUE;
2994     local_player->LevelSolved_SaveTape = tape.recording;
2995     local_player->LevelSolved_SaveScore = !tape.playing;
2996
2997     if (tape.auto_play)         /* tape might already be stopped here */
2998       tape.auto_play_level_solved = TRUE;
2999
3000 #if 1
3001     TapeStop();
3002 #endif
3003
3004     game_over_delay_1 = game_over_delay_value_1;
3005     game_over_delay_2 = game_over_delay_value_2;
3006
3007     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3008     score = score_final = local_player->score_final;
3009
3010     if (TimeLeft > 0)
3011     {
3012       time_final = 0;
3013       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3014     }
3015     else if (level.time == 0 && TimePlayed < 999)
3016     {
3017       time_final = 999;
3018       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3019     }
3020
3021     local_player->score_final = score_final;
3022
3023     if (level_editor_test_game)
3024     {
3025       time = time_final;
3026       score = score_final;
3027
3028       DrawGameValue_Time(time);
3029       DrawGameValue_Score(score);
3030     }
3031
3032     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3033     {
3034       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3035       {
3036         /* close exit door after last player */
3037         if ((AllPlayersGone &&
3038              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3039               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3040               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3041             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3042             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3043         {
3044           int element = Feld[ExitX][ExitY];
3045
3046 #if 0
3047           if (element == EL_EM_EXIT_OPEN ||
3048               element == EL_EM_STEEL_EXIT_OPEN)
3049           {
3050             Bang(ExitX, ExitY);
3051           }
3052           else
3053 #endif
3054           {
3055             Feld[ExitX][ExitY] =
3056               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3057                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3058                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3059                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3060                EL_EM_STEEL_EXIT_CLOSING);
3061
3062             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3063           }
3064         }
3065
3066         /* player disappears */
3067         DrawLevelField(ExitX, ExitY);
3068       }
3069
3070       for (i = 0; i < MAX_PLAYERS; i++)
3071       {
3072         struct PlayerInfo *player = &stored_player[i];
3073
3074         if (player->present)
3075         {
3076           RemovePlayer(player);
3077
3078           /* player disappears */
3079           DrawLevelField(player->jx, player->jy);
3080         }
3081       }
3082     }
3083
3084     PlaySound(SND_GAME_WINNING);
3085   }
3086
3087   if (game_over_delay_1 > 0)
3088   {
3089     game_over_delay_1--;
3090
3091     return;
3092   }
3093
3094   if (time != time_final)
3095   {
3096     int time_to_go = ABS(time_final - time);
3097     int time_count_dir = (time < time_final ? +1 : -1);
3098     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3099
3100     time  += time_count_steps * time_count_dir;
3101     score += time_count_steps * level.score[SC_TIME_BONUS];
3102
3103     DrawGameValue_Time(time);
3104     DrawGameValue_Score(score);
3105
3106     if (time == time_final)
3107       StopSound(SND_GAME_LEVELTIME_BONUS);
3108     else if (setup.sound_loops)
3109       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3110     else
3111       PlaySound(SND_GAME_LEVELTIME_BONUS);
3112
3113     return;
3114   }
3115
3116   local_player->LevelSolved_PanelOff = TRUE;
3117
3118   if (game_over_delay_2 > 0)
3119   {
3120     game_over_delay_2--;
3121
3122     return;
3123   }
3124
3125 #if 1
3126   GameEnd();
3127 #endif
3128 }
3129
3130 void GameEnd()
3131 {
3132   int hi_pos;
3133   boolean raise_level = FALSE;
3134
3135   local_player->LevelSolved_GameEnd = TRUE;
3136
3137   CloseDoor(DOOR_CLOSE_1);
3138
3139   if (local_player->LevelSolved_SaveTape)
3140   {
3141 #if 0
3142     TapeStop();
3143 #endif
3144
3145 #if 1
3146     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3147 #else
3148     SaveTape(tape.level_nr);            /* ask to save tape */
3149 #endif
3150   }
3151
3152   if (level_editor_test_game)
3153   {
3154     game_status = GAME_MODE_MAIN;
3155
3156     DrawMainMenu();
3157
3158     return;
3159   }
3160
3161   if (!local_player->LevelSolved_SaveScore)
3162   {
3163     FadeOut(REDRAW_FIELD);
3164
3165     game_status = GAME_MODE_MAIN;
3166
3167     DrawAndFadeInMainMenu(REDRAW_FIELD);
3168
3169     return;
3170   }
3171
3172   if (level_nr == leveldir_current->handicap_level)
3173   {
3174     leveldir_current->handicap_level++;
3175     SaveLevelSetup_SeriesInfo();
3176   }
3177
3178   if (level_nr < leveldir_current->last_level)
3179     raise_level = TRUE;                 /* advance to next level */
3180
3181   if ((hi_pos = NewHiScore()) >= 0) 
3182   {
3183     game_status = GAME_MODE_SCORES;
3184
3185     DrawHallOfFame(hi_pos);
3186
3187     if (raise_level)
3188     {
3189       level_nr++;
3190       TapeErase();
3191     }
3192   }
3193   else
3194   {
3195     FadeOut(REDRAW_FIELD);
3196
3197     game_status = GAME_MODE_MAIN;
3198
3199     if (raise_level)
3200     {
3201       level_nr++;
3202       TapeErase();
3203     }
3204
3205     DrawAndFadeInMainMenu(REDRAW_FIELD);
3206   }
3207 }
3208
3209 int NewHiScore()
3210 {
3211   int k, l;
3212   int position = -1;
3213
3214   LoadScore(level_nr);
3215
3216   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3217       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3218     return -1;
3219
3220   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3221   {
3222     if (local_player->score_final > highscore[k].Score)
3223     {
3224       /* player has made it to the hall of fame */
3225
3226       if (k < MAX_SCORE_ENTRIES - 1)
3227       {
3228         int m = MAX_SCORE_ENTRIES - 1;
3229
3230 #ifdef ONE_PER_NAME
3231         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3232           if (strEqual(setup.player_name, highscore[l].Name))
3233             m = l;
3234         if (m == k)     /* player's new highscore overwrites his old one */
3235           goto put_into_list;
3236 #endif
3237
3238         for (l = m; l > k; l--)
3239         {
3240           strcpy(highscore[l].Name, highscore[l - 1].Name);
3241           highscore[l].Score = highscore[l - 1].Score;
3242         }
3243       }
3244
3245 #ifdef ONE_PER_NAME
3246       put_into_list:
3247 #endif
3248       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3249       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3250       highscore[k].Score = local_player->score_final; 
3251       position = k;
3252       break;
3253     }
3254
3255 #ifdef ONE_PER_NAME
3256     else if (!strncmp(setup.player_name, highscore[k].Name,
3257                       MAX_PLAYER_NAME_LEN))
3258       break;    /* player already there with a higher score */
3259 #endif
3260
3261   }
3262
3263   if (position >= 0) 
3264     SaveScore(level_nr);
3265
3266   return position;
3267 }
3268
3269 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3270 {
3271   int element = Feld[x][y];
3272   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3273   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3274   int horiz_move = (dx != 0);
3275   int sign = (horiz_move ? dx : dy);
3276   int step = sign * element_info[element].move_stepsize;
3277
3278   /* special values for move stepsize for spring and things on conveyor belt */
3279   if (horiz_move)
3280   {
3281     if (CAN_FALL(element) &&
3282         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3283       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3284     else if (element == EL_SPRING)
3285       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3286   }
3287
3288   return step;
3289 }
3290
3291 inline static int getElementMoveStepsize(int x, int y)
3292 {
3293   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3294 }
3295
3296 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3297 {
3298   if (player->GfxAction != action || player->GfxDir != dir)
3299   {
3300 #if 0
3301     printf("Player frame reset! (%d => %d, %d => %d)\n",
3302            player->GfxAction, action, player->GfxDir, dir);
3303 #endif
3304
3305     player->GfxAction = action;
3306     player->GfxDir = dir;
3307     player->Frame = 0;
3308     player->StepFrame = 0;
3309   }
3310 }
3311
3312 #if USE_GFX_RESET_GFX_ANIMATION
3313 static void ResetGfxFrame(int x, int y, boolean redraw)
3314 {
3315   int element = Feld[x][y];
3316   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3317   int last_gfx_frame = GfxFrame[x][y];
3318
3319   if (graphic_info[graphic].anim_global_sync)
3320     GfxFrame[x][y] = FrameCounter;
3321   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3322     GfxFrame[x][y] = CustomValue[x][y];
3323   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3324     GfxFrame[x][y] = element_info[element].collect_score;
3325   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3326     GfxFrame[x][y] = ChangeDelay[x][y];
3327
3328   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3329     DrawLevelGraphicAnimation(x, y, graphic);
3330 }
3331 #endif
3332
3333 static void ResetGfxAnimation(int x, int y)
3334 {
3335   GfxAction[x][y] = ACTION_DEFAULT;
3336   GfxDir[x][y] = MovDir[x][y];
3337   GfxFrame[x][y] = 0;
3338
3339 #if USE_GFX_RESET_GFX_ANIMATION
3340   ResetGfxFrame(x, y, FALSE);
3341 #endif
3342 }
3343
3344 static void ResetRandomAnimationValue(int x, int y)
3345 {
3346   GfxRandom[x][y] = INIT_GFX_RANDOM();
3347 }
3348
3349 void InitMovingField(int x, int y, int direction)
3350 {
3351   int element = Feld[x][y];
3352   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3353   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3354   int newx = x + dx;
3355   int newy = y + dy;
3356   boolean is_moving_before, is_moving_after;
3357 #if 0
3358   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3359 #endif
3360
3361   /* check if element was/is moving or being moved before/after mode change */
3362 #if 1
3363   is_moving_before = WasJustMoving[x][y];
3364 #else
3365   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3366 #endif
3367   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
3368
3369   /* reset animation only for moving elements which change direction of moving
3370      or which just started or stopped moving
3371      (else CEs with property "can move" / "not moving" are reset each frame) */
3372 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3373 #if 1
3374   if (is_moving_before != is_moving_after ||
3375       direction != MovDir[x][y])
3376     ResetGfxAnimation(x, y);
3377 #else
3378   if ((is_moving_before || is_moving_after) && !continues_moving)
3379     ResetGfxAnimation(x, y);
3380 #endif
3381 #else
3382   if (!continues_moving)
3383     ResetGfxAnimation(x, y);
3384 #endif
3385
3386   MovDir[x][y] = direction;
3387   GfxDir[x][y] = direction;
3388
3389 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3390   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3391                      direction == MV_DOWN && CAN_FALL(element) ?
3392                      ACTION_FALLING : ACTION_MOVING);
3393 #else
3394   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3395                      ACTION_FALLING : ACTION_MOVING);
3396 #endif
3397
3398   /* this is needed for CEs with property "can move" / "not moving" */
3399
3400   if (is_moving_after)
3401   {
3402     if (Feld[newx][newy] == EL_EMPTY)
3403       Feld[newx][newy] = EL_BLOCKED;
3404
3405     MovDir[newx][newy] = MovDir[x][y];
3406
3407 #if USE_NEW_CUSTOM_VALUE
3408     CustomValue[newx][newy] = CustomValue[x][y];
3409 #endif
3410
3411     GfxFrame[newx][newy] = GfxFrame[x][y];
3412     GfxRandom[newx][newy] = GfxRandom[x][y];
3413     GfxAction[newx][newy] = GfxAction[x][y];
3414     GfxDir[newx][newy] = GfxDir[x][y];
3415   }
3416 }
3417
3418 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3419 {
3420   int direction = MovDir[x][y];
3421   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3422   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3423
3424   *goes_to_x = newx;
3425   *goes_to_y = newy;
3426 }
3427
3428 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3429 {
3430   int oldx = x, oldy = y;
3431   int direction = MovDir[x][y];
3432
3433   if (direction == MV_LEFT)
3434     oldx++;
3435   else if (direction == MV_RIGHT)
3436     oldx--;
3437   else if (direction == MV_UP)
3438     oldy++;
3439   else if (direction == MV_DOWN)
3440     oldy--;
3441
3442   *comes_from_x = oldx;
3443   *comes_from_y = oldy;
3444 }
3445
3446 int MovingOrBlocked2Element(int x, int y)
3447 {
3448   int element = Feld[x][y];
3449
3450   if (element == EL_BLOCKED)
3451   {
3452     int oldx, oldy;
3453
3454     Blocked2Moving(x, y, &oldx, &oldy);
3455     return Feld[oldx][oldy];
3456   }
3457   else
3458     return element;
3459 }
3460
3461 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3462 {
3463   /* like MovingOrBlocked2Element(), but if element is moving
3464      and (x,y) is the field the moving element is just leaving,
3465      return EL_BLOCKED instead of the element value */
3466   int element = Feld[x][y];
3467
3468   if (IS_MOVING(x, y))
3469   {
3470     if (element == EL_BLOCKED)
3471     {
3472       int oldx, oldy;
3473
3474       Blocked2Moving(x, y, &oldx, &oldy);
3475       return Feld[oldx][oldy];
3476     }
3477     else
3478       return EL_BLOCKED;
3479   }
3480   else
3481     return element;
3482 }
3483
3484 static void RemoveField(int x, int y)
3485 {
3486   Feld[x][y] = EL_EMPTY;
3487
3488   MovPos[x][y] = 0;
3489   MovDir[x][y] = 0;
3490   MovDelay[x][y] = 0;
3491
3492 #if USE_NEW_CUSTOM_VALUE
3493   CustomValue[x][y] = 0;
3494 #endif
3495
3496   AmoebaNr[x][y] = 0;
3497   ChangeDelay[x][y] = 0;
3498   ChangePage[x][y] = -1;
3499   Pushed[x][y] = FALSE;
3500
3501 #if 0
3502   ExplodeField[x][y] = EX_TYPE_NONE;
3503 #endif
3504
3505   GfxElement[x][y] = EL_UNDEFINED;
3506   GfxAction[x][y] = ACTION_DEFAULT;
3507   GfxDir[x][y] = MV_NONE;
3508 }
3509
3510 void RemoveMovingField(int x, int y)
3511 {
3512   int oldx = x, oldy = y, newx = x, newy = y;
3513   int element = Feld[x][y];
3514   int next_element = EL_UNDEFINED;
3515
3516   if (element != EL_BLOCKED && !IS_MOVING(x, y))
3517     return;
3518
3519   if (IS_MOVING(x, y))
3520   {
3521     Moving2Blocked(x, y, &newx, &newy);
3522
3523     if (Feld[newx][newy] != EL_BLOCKED)
3524     {
3525       /* element is moving, but target field is not free (blocked), but
3526          already occupied by something different (example: acid pool);
3527          in this case, only remove the moving field, but not the target */
3528
3529       RemoveField(oldx, oldy);
3530
3531       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3532
3533       DrawLevelField(oldx, oldy);
3534
3535       return;
3536     }
3537   }
3538   else if (element == EL_BLOCKED)
3539   {
3540     Blocked2Moving(x, y, &oldx, &oldy);
3541     if (!IS_MOVING(oldx, oldy))
3542       return;
3543   }
3544
3545   if (element == EL_BLOCKED &&
3546       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3547        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3548        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3549        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3550        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3551        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3552     next_element = get_next_element(Feld[oldx][oldy]);
3553
3554   RemoveField(oldx, oldy);
3555   RemoveField(newx, newy);
3556
3557   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3558
3559   if (next_element != EL_UNDEFINED)
3560     Feld[oldx][oldy] = next_element;
3561
3562   DrawLevelField(oldx, oldy);
3563   DrawLevelField(newx, newy);
3564 }
3565
3566 void DrawDynamite(int x, int y)
3567 {
3568   int sx = SCREENX(x), sy = SCREENY(y);
3569   int graphic = el2img(Feld[x][y]);
3570   int frame;
3571
3572   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3573     return;
3574
3575   if (IS_WALKABLE_INSIDE(Back[x][y]))
3576     return;
3577
3578   if (Back[x][y])
3579     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3580   else if (Store[x][y])
3581     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3582
3583   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3584
3585   if (Back[x][y] || Store[x][y])
3586     DrawGraphicThruMask(sx, sy, graphic, frame);
3587   else
3588     DrawGraphic(sx, sy, graphic, frame);
3589 }
3590
3591 void CheckDynamite(int x, int y)
3592 {
3593   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
3594   {
3595     MovDelay[x][y]--;
3596
3597     if (MovDelay[x][y] != 0)
3598     {
3599       DrawDynamite(x, y);
3600       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3601
3602       return;
3603     }
3604   }
3605
3606   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3607
3608   Bang(x, y);
3609 }
3610
3611 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3612 {
3613   boolean num_checked_players = 0;
3614   int i;
3615
3616   for (i = 0; i < MAX_PLAYERS; i++)
3617   {
3618     if (stored_player[i].active)
3619     {
3620       int sx = stored_player[i].jx;
3621       int sy = stored_player[i].jy;
3622
3623       if (num_checked_players == 0)
3624       {
3625         *sx1 = *sx2 = sx;
3626         *sy1 = *sy2 = sy;
3627       }
3628       else
3629       {
3630         *sx1 = MIN(*sx1, sx);
3631         *sy1 = MIN(*sy1, sy);
3632         *sx2 = MAX(*sx2, sx);
3633         *sy2 = MAX(*sy2, sy);
3634       }
3635
3636       num_checked_players++;
3637     }
3638   }
3639 }
3640
3641 static boolean checkIfAllPlayersFitToScreen_RND()
3642 {
3643   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3644
3645   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3646
3647   return (sx2 - sx1 < SCR_FIELDX &&
3648           sy2 - sy1 < SCR_FIELDY);
3649 }
3650
3651 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3652 {
3653   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3654
3655   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3656
3657   *sx = (sx1 + sx2) / 2;
3658   *sy = (sy1 + sy2) / 2;
3659 }
3660
3661 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3662                         boolean center_screen, boolean quick_relocation)
3663 {
3664   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3665   boolean no_delay = (tape.warp_forward);
3666   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3667   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3668
3669   if (quick_relocation)
3670   {
3671     int offset = (setup.scroll_delay ? 3 : 0);
3672
3673     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3674     {
3675       if (center_screen)
3676       {
3677         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3678                     x > SBX_Right + MIDPOSX ? SBX_Right :
3679                     x - MIDPOSX);
3680
3681         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3682                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
3683                     y - MIDPOSY);
3684       }
3685       else
3686       {
3687         /* quick relocation (without scrolling), but do not center screen */
3688
3689         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
3690                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
3691                                old_x - MIDPOSX);
3692
3693         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3694                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3695                                old_y - MIDPOSY);
3696
3697         int offset_x = x + (scroll_x - center_scroll_x);
3698         int offset_y = y + (scroll_y - center_scroll_y);
3699
3700         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
3701                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3702                     offset_x - MIDPOSX);
3703
3704         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3705                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3706                     offset_y - MIDPOSY);
3707       }
3708     }
3709     else
3710     {
3711       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
3712           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3713         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3714
3715       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
3716           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3717         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3718
3719       /* don't scroll over playfield boundaries */
3720       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3721         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3722
3723       /* don't scroll over playfield boundaries */
3724       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3725         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3726     }
3727
3728     RedrawPlayfield(TRUE, 0,0,0,0);
3729   }
3730   else
3731   {
3732     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3733                      x > SBX_Right + MIDPOSX ? SBX_Right :
3734                      x - MIDPOSX);
3735
3736     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3737                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
3738                      y - MIDPOSY);
3739
3740     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3741
3742     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3743     {
3744       int dx = 0, dy = 0;
3745       int fx = FX, fy = FY;
3746
3747       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3748       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3749
3750       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3751         break;
3752
3753       scroll_x -= dx;
3754       scroll_y -= dy;
3755
3756       fx += dx * TILEX / 2;
3757       fy += dy * TILEY / 2;
3758
3759       ScrollLevel(dx, dy);
3760       DrawAllPlayers();
3761
3762       /* scroll in two steps of half tile size to make things smoother */
3763       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3764       FlushDisplay();
3765       Delay(wait_delay_value);
3766
3767       /* scroll second step to align at full tile size */
3768       BackToFront();
3769       Delay(wait_delay_value);
3770     }
3771
3772     DrawAllPlayers();
3773     BackToFront();
3774     Delay(wait_delay_value);
3775   }
3776 }
3777
3778 void RelocatePlayer(int jx, int jy, int el_player_raw)
3779 {
3780   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3781   int player_nr = GET_PLAYER_NR(el_player);
3782   struct PlayerInfo *player = &stored_player[player_nr];
3783   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3784   boolean no_delay = (tape.warp_forward);
3785   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3786   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3787   int old_jx = player->jx;
3788   int old_jy = player->jy;
3789   int old_element = Feld[old_jx][old_jy];
3790   int element = Feld[jx][jy];
3791   boolean player_relocated = (old_jx != jx || old_jy != jy);
3792
3793   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3794   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
3795   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3796   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
3797   int leave_side_horiz = move_dir_horiz;
3798   int leave_side_vert  = move_dir_vert;
3799   int enter_side = enter_side_horiz | enter_side_vert;
3800   int leave_side = leave_side_horiz | leave_side_vert;
3801
3802   if (player->GameOver)         /* do not reanimate dead player */
3803     return;
3804
3805   if (!player_relocated)        /* no need to relocate the player */
3806     return;
3807
3808   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
3809   {
3810     RemoveField(jx, jy);        /* temporarily remove newly placed player */
3811     DrawLevelField(jx, jy);
3812   }
3813
3814   if (player->present)
3815   {
3816     while (player->MovPos)
3817     {
3818       ScrollPlayer(player, SCROLL_GO_ON);
3819       ScrollScreen(NULL, SCROLL_GO_ON);
3820
3821       AdvanceFrameAndPlayerCounters(player->index_nr);
3822
3823       DrawPlayer(player);
3824
3825       BackToFront();
3826       Delay(wait_delay_value);
3827     }
3828
3829     DrawPlayer(player);         /* needed here only to cleanup last field */
3830     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
3831
3832     player->is_moving = FALSE;
3833   }
3834
3835   if (IS_CUSTOM_ELEMENT(old_element))
3836     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3837                                CE_LEFT_BY_PLAYER,
3838                                player->index_bit, leave_side);
3839
3840   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3841                                       CE_PLAYER_LEAVES_X,
3842                                       player->index_bit, leave_side);
3843
3844   Feld[jx][jy] = el_player;
3845   InitPlayerField(jx, jy, el_player, TRUE);
3846
3847   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3848   {
3849     Feld[jx][jy] = element;
3850     InitField(jx, jy, FALSE);
3851   }
3852
3853   /* only visually relocate centered player */
3854   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3855                      FALSE, level.instant_relocation);
3856
3857   TestIfPlayerTouchesBadThing(jx, jy);
3858   TestIfPlayerTouchesCustomElement(jx, jy);
3859
3860   if (IS_CUSTOM_ELEMENT(element))
3861     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3862                                player->index_bit, enter_side);
3863
3864   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3865                                       player->index_bit, enter_side);
3866 }
3867
3868 void Explode(int ex, int ey, int phase, int mode)
3869 {
3870   int x, y;
3871   int last_phase;
3872   int border_element;
3873
3874   /* !!! eliminate this variable !!! */
3875   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3876
3877   if (game.explosions_delayed)
3878   {
3879     ExplodeField[ex][ey] = mode;
3880     return;
3881   }
3882
3883   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
3884   {
3885     int center_element = Feld[ex][ey];
3886     int artwork_element, explosion_element;     /* set these values later */
3887
3888 #if 0
3889     /* --- This is only really needed (and now handled) in "Impact()". --- */
3890     /* do not explode moving elements that left the explode field in time */
3891     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3892         center_element == EL_EMPTY &&
3893         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3894       return;
3895 #endif
3896
3897 #if 0
3898     /* !!! at this place, the center element may be EL_BLOCKED !!! */
3899     if (mode == EX_TYPE_NORMAL ||
3900         mode == EX_TYPE_CENTER ||
3901         mode == EX_TYPE_CROSS)
3902       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3903 #endif
3904
3905     /* remove things displayed in background while burning dynamite */
3906     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3907       Back[ex][ey] = 0;
3908
3909     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3910     {
3911       /* put moving element to center field (and let it explode there) */
3912       center_element = MovingOrBlocked2Element(ex, ey);
3913       RemoveMovingField(ex, ey);
3914       Feld[ex][ey] = center_element;
3915     }
3916
3917     /* now "center_element" is finally determined -- set related values now */
3918     artwork_element = center_element;           /* for custom player artwork */
3919     explosion_element = center_element;         /* for custom player artwork */
3920
3921     if (IS_PLAYER(ex, ey))
3922     {
3923       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3924
3925       artwork_element = stored_player[player_nr].artwork_element;
3926
3927       if (level.use_explosion_element[player_nr])
3928       {
3929         explosion_element = level.explosion_element[player_nr];
3930         artwork_element = explosion_element;
3931       }
3932     }
3933
3934 #if 1
3935     if (mode == EX_TYPE_NORMAL ||
3936         mode == EX_TYPE_CENTER ||
3937         mode == EX_TYPE_CROSS)
3938       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3939 #endif
3940
3941     last_phase = element_info[explosion_element].explosion_delay + 1;
3942
3943     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3944     {
3945       int xx = x - ex + 1;
3946       int yy = y - ey + 1;
3947       int element;
3948
3949       if (!IN_LEV_FIELD(x, y) ||
3950           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3951           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
3952         continue;
3953
3954       element = Feld[x][y];
3955
3956       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3957       {
3958         element = MovingOrBlocked2Element(x, y);
3959
3960         if (!IS_EXPLOSION_PROOF(element))
3961           RemoveMovingField(x, y);
3962       }
3963
3964       /* indestructible elements can only explode in center (but not flames) */
3965       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3966                                            mode == EX_TYPE_BORDER)) ||
3967           element == EL_FLAMES)
3968         continue;
3969
3970       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3971          behaviour, for example when touching a yamyam that explodes to rocks
3972          with active deadly shield, a rock is created under the player !!! */
3973       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3974 #if 0
3975       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3976           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3977            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3978 #else
3979       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3980 #endif
3981       {
3982         if (IS_ACTIVE_BOMB(element))
3983         {
3984           /* re-activate things under the bomb like gate or penguin */
3985           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3986           Back[x][y] = 0;
3987         }
3988
3989         continue;
3990       }
3991
3992       /* save walkable background elements while explosion on same tile */
3993       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3994           (x != ex || y != ey || mode == EX_TYPE_BORDER))
3995         Back[x][y] = element;
3996
3997       /* ignite explodable elements reached by other explosion */
3998       if (element == EL_EXPLOSION)
3999         element = Store2[x][y];
4000
4001       if (AmoebaNr[x][y] &&
4002           (element == EL_AMOEBA_FULL ||
4003            element == EL_BD_AMOEBA ||
4004            element == EL_AMOEBA_GROWING))
4005       {
4006         AmoebaCnt[AmoebaNr[x][y]]--;
4007         AmoebaCnt2[AmoebaNr[x][y]]--;
4008       }
4009
4010       RemoveField(x, y);
4011
4012       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4013       {
4014         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4015
4016         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4017
4018         if (PLAYERINFO(ex, ey)->use_murphy)
4019           Store[x][y] = EL_EMPTY;
4020       }
4021
4022       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4023          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4024       else if (ELEM_IS_PLAYER(center_element))
4025         Store[x][y] = EL_EMPTY;
4026       else if (center_element == EL_YAMYAM)
4027         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4028       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4029         Store[x][y] = element_info[center_element].content.e[xx][yy];
4030 #if 1
4031       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4032          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4033          otherwise) -- FIX THIS !!! */
4034       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4035         Store[x][y] = element_info[element].content.e[1][1];
4036 #else
4037       else if (!CAN_EXPLODE(element))
4038         Store[x][y] = element_info[element].content.e[1][1];
4039 #endif
4040       else
4041         Store[x][y] = EL_EMPTY;
4042
4043       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4044           center_element == EL_AMOEBA_TO_DIAMOND)
4045         Store2[x][y] = element;
4046
4047       Feld[x][y] = EL_EXPLOSION;
4048       GfxElement[x][y] = artwork_element;
4049
4050       ExplodePhase[x][y] = 1;
4051       ExplodeDelay[x][y] = last_phase;
4052
4053       Stop[x][y] = TRUE;
4054     }
4055
4056     if (center_element == EL_YAMYAM)
4057       game.yamyam_content_nr =
4058         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4059
4060     return;
4061   }
4062
4063   if (Stop[ex][ey])
4064     return;
4065
4066   x = ex;
4067   y = ey;
4068
4069   if (phase == 1)
4070     GfxFrame[x][y] = 0;         /* restart explosion animation */
4071
4072   last_phase = ExplodeDelay[x][y];
4073
4074   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4075
4076 #ifdef DEBUG
4077
4078   /* activate this even in non-DEBUG version until cause for crash in
4079      getGraphicAnimationFrame() (see below) is found and eliminated */
4080
4081 #endif
4082 #if 1
4083
4084 #if 1
4085   /* this can happen if the player leaves an explosion just in time */
4086   if (GfxElement[x][y] == EL_UNDEFINED)
4087     GfxElement[x][y] = EL_EMPTY;
4088 #else
4089   if (GfxElement[x][y] == EL_UNDEFINED)
4090   {
4091     printf("\n\n");
4092     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4093     printf("Explode(): This should never happen!\n");
4094     printf("\n\n");
4095
4096     GfxElement[x][y] = EL_EMPTY;
4097   }
4098 #endif
4099
4100 #endif
4101
4102   border_element = Store2[x][y];
4103   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4104     border_element = StorePlayer[x][y];
4105
4106   if (phase == element_info[border_element].ignition_delay ||
4107       phase == last_phase)
4108   {
4109     boolean border_explosion = FALSE;
4110
4111     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4112         !PLAYER_EXPLOSION_PROTECTED(x, y))
4113     {
4114       KillPlayerUnlessExplosionProtected(x, y);
4115       border_explosion = TRUE;
4116     }
4117     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4118     {
4119       Feld[x][y] = Store2[x][y];
4120       Store2[x][y] = 0;
4121       Bang(x, y);
4122       border_explosion = TRUE;
4123     }
4124     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4125     {
4126       AmoebeUmwandeln(x, y);
4127       Store2[x][y] = 0;
4128       border_explosion = TRUE;
4129     }
4130
4131     /* if an element just explodes due to another explosion (chain-reaction),
4132        do not immediately end the new explosion when it was the last frame of
4133        the explosion (as it would be done in the following "if"-statement!) */
4134     if (border_explosion && phase == last_phase)
4135       return;
4136   }
4137
4138   if (phase == last_phase)
4139   {
4140     int element;
4141
4142     element = Feld[x][y] = Store[x][y];
4143     Store[x][y] = Store2[x][y] = 0;
4144     GfxElement[x][y] = EL_UNDEFINED;
4145
4146     /* player can escape from explosions and might therefore be still alive */
4147     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4148         element <= EL_PLAYER_IS_EXPLODING_4)
4149     {
4150       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4151       int explosion_element = EL_PLAYER_1 + player_nr;
4152       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4153       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4154
4155       if (level.use_explosion_element[player_nr])
4156         explosion_element = level.explosion_element[player_nr];
4157
4158       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4159                     element_info[explosion_element].content.e[xx][yy]);
4160     }
4161
4162     /* restore probably existing indestructible background element */
4163     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4164       element = Feld[x][y] = Back[x][y];
4165     Back[x][y] = 0;
4166
4167     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4168     GfxDir[x][y] = MV_NONE;
4169     ChangeDelay[x][y] = 0;
4170     ChangePage[x][y] = -1;
4171
4172 #if USE_NEW_CUSTOM_VALUE
4173     CustomValue[x][y] = 0;
4174 #endif
4175
4176     InitField_WithBug2(x, y, FALSE);
4177
4178     DrawLevelField(x, y);
4179
4180     TestIfElementTouchesCustomElement(x, y);
4181
4182     if (GFX_CRUMBLED(element))
4183       DrawLevelFieldCrumbledSandNeighbours(x, y);
4184
4185     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4186       StorePlayer[x][y] = 0;
4187
4188     if (ELEM_IS_PLAYER(element))
4189       RelocatePlayer(x, y, element);
4190   }
4191   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4192   {
4193     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4194     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4195
4196     if (phase == delay)
4197       DrawLevelFieldCrumbledSand(x, y);
4198
4199     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4200     {
4201       DrawLevelElement(x, y, Back[x][y]);
4202       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4203     }
4204     else if (IS_WALKABLE_UNDER(Back[x][y]))
4205     {
4206       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4207       DrawLevelElementThruMask(x, y, Back[x][y]);
4208     }
4209     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4210       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4211   }
4212 }
4213
4214 void DynaExplode(int ex, int ey)
4215 {
4216   int i, j;
4217   int dynabomb_element = Feld[ex][ey];
4218   int dynabomb_size = 1;
4219   boolean dynabomb_xl = FALSE;
4220   struct PlayerInfo *player;
4221   static int xy[4][2] =
4222   {
4223     { 0, -1 },
4224     { -1, 0 },
4225     { +1, 0 },
4226     { 0, +1 }
4227   };
4228
4229   if (IS_ACTIVE_BOMB(dynabomb_element))
4230   {
4231     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4232     dynabomb_size = player->dynabomb_size;
4233     dynabomb_xl = player->dynabomb_xl;
4234     player->dynabombs_left++;
4235   }
4236
4237   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4238
4239   for (i = 0; i < NUM_DIRECTIONS; i++)
4240   {
4241     for (j = 1; j <= dynabomb_size; j++)
4242     {
4243       int x = ex + j * xy[i][0];
4244       int y = ey + j * xy[i][1];
4245       int element;
4246
4247       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4248         break;
4249
4250       element = Feld[x][y];
4251
4252       /* do not restart explosions of fields with active bombs */
4253       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4254         continue;
4255
4256       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4257
4258       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4259           !IS_DIGGABLE(element) && !dynabomb_xl)
4260         break;
4261     }
4262   }
4263 }
4264
4265 void Bang(int x, int y)
4266 {
4267   int element = MovingOrBlocked2Element(x, y);
4268   int explosion_type = EX_TYPE_NORMAL;
4269
4270   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4271   {
4272     struct PlayerInfo *player = PLAYERINFO(x, y);
4273
4274     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4275                             player->element_nr);
4276
4277     if (level.use_explosion_element[player->index_nr])
4278     {
4279       int explosion_element = level.explosion_element[player->index_nr];
4280
4281       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4282         explosion_type = EX_TYPE_CROSS;
4283       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4284         explosion_type = EX_TYPE_CENTER;
4285     }
4286   }
4287
4288   switch (element)
4289   {
4290     case EL_BUG:
4291     case EL_SPACESHIP:
4292     case EL_BD_BUTTERFLY:
4293     case EL_BD_FIREFLY:
4294     case EL_YAMYAM:
4295     case EL_DARK_YAMYAM:
4296     case EL_ROBOT:
4297     case EL_PACMAN:
4298     case EL_MOLE:
4299       RaiseScoreElement(element);
4300       break;
4301
4302     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4303     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4304     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4305     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4306     case EL_DYNABOMB_INCREASE_NUMBER:
4307     case EL_DYNABOMB_INCREASE_SIZE:
4308     case EL_DYNABOMB_INCREASE_POWER:
4309       explosion_type = EX_TYPE_DYNA;
4310       break;
4311
4312     case EL_DC_LANDMINE:
4313 #if 0
4314     case EL_EM_EXIT_OPEN:
4315     case EL_EM_STEEL_EXIT_OPEN:
4316 #endif
4317       explosion_type = EX_TYPE_CENTER;
4318       break;
4319
4320     case EL_PENGUIN:
4321     case EL_LAMP:
4322     case EL_LAMP_ACTIVE:
4323     case EL_AMOEBA_TO_DIAMOND:
4324       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4325         explosion_type = EX_TYPE_CENTER;
4326       break;
4327
4328     default:
4329       if (element_info[element].explosion_type == EXPLODES_CROSS)
4330         explosion_type = EX_TYPE_CROSS;
4331       else if (element_info[element].explosion_type == EXPLODES_1X1)
4332         explosion_type = EX_TYPE_CENTER;
4333       break;
4334   }
4335
4336   if (explosion_type == EX_TYPE_DYNA)
4337     DynaExplode(x, y);
4338   else
4339     Explode(x, y, EX_PHASE_START, explosion_type);
4340
4341   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4342 }
4343
4344 void SplashAcid(int x, int y)
4345 {
4346   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4347       (!IN_LEV_FIELD(x - 1, y - 2) ||
4348        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4349     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4350
4351   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4352       (!IN_LEV_FIELD(x + 1, y - 2) ||
4353        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4354     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4355
4356   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4357 }
4358
4359 static void InitBeltMovement()
4360 {
4361   static int belt_base_element[4] =
4362   {
4363     EL_CONVEYOR_BELT_1_LEFT,
4364     EL_CONVEYOR_BELT_2_LEFT,
4365     EL_CONVEYOR_BELT_3_LEFT,
4366     EL_CONVEYOR_BELT_4_LEFT
4367   };
4368   static int belt_base_active_element[4] =
4369   {
4370     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4371     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4372     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4373     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4374   };
4375
4376   int x, y, i, j;
4377
4378   /* set frame order for belt animation graphic according to belt direction */
4379   for (i = 0; i < NUM_BELTS; i++)
4380   {
4381     int belt_nr = i;
4382
4383     for (j = 0; j < NUM_BELT_PARTS; j++)
4384     {
4385       int element = belt_base_active_element[belt_nr] + j;
4386       int graphic = el2img(element);
4387
4388       if (game.belt_dir[i] == MV_LEFT)
4389         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4390       else
4391         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4392     }
4393   }
4394
4395   SCAN_PLAYFIELD(x, y)
4396   {
4397     int element = Feld[x][y];
4398
4399     for (i = 0; i < NUM_BELTS; i++)
4400     {
4401       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4402       {
4403         int e_belt_nr = getBeltNrFromBeltElement(element);
4404         int belt_nr = i;
4405
4406         if (e_belt_nr == belt_nr)
4407         {
4408           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4409
4410           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4411         }
4412       }
4413     }
4414   }
4415 }
4416
4417 static void ToggleBeltSwitch(int x, int y)
4418 {
4419   static int belt_base_element[4] =
4420   {
4421     EL_CONVEYOR_BELT_1_LEFT,
4422     EL_CONVEYOR_BELT_2_LEFT,
4423     EL_CONVEYOR_BELT_3_LEFT,
4424     EL_CONVEYOR_BELT_4_LEFT
4425   };
4426   static int belt_base_active_element[4] =
4427   {
4428     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4429     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4430     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4431     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4432   };
4433   static int belt_base_switch_element[4] =
4434   {
4435     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4436     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4437     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4438     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4439   };
4440   static int belt_move_dir[4] =
4441   {
4442     MV_LEFT,
4443     MV_NONE,
4444     MV_RIGHT,
4445     MV_NONE,
4446   };
4447
4448   int element = Feld[x][y];
4449   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4450   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4451   int belt_dir = belt_move_dir[belt_dir_nr];
4452   int xx, yy, i;
4453
4454   if (!IS_BELT_SWITCH(element))
4455     return;
4456
4457   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4458   game.belt_dir[belt_nr] = belt_dir;
4459
4460   if (belt_dir_nr == 3)
4461     belt_dir_nr = 1;
4462
4463   /* set frame order for belt animation graphic according to belt direction */
4464   for (i = 0; i < NUM_BELT_PARTS; i++)
4465   {
4466     int element = belt_base_active_element[belt_nr] + i;
4467     int graphic = el2img(element);
4468
4469     if (belt_dir == MV_LEFT)
4470       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4471     else
4472       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4473   }
4474
4475   SCAN_PLAYFIELD(xx, yy)
4476   {
4477     int element = Feld[xx][yy];
4478
4479     if (IS_BELT_SWITCH(element))
4480     {
4481       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4482
4483       if (e_belt_nr == belt_nr)
4484       {
4485         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4486         DrawLevelField(xx, yy);
4487       }
4488     }
4489     else if (IS_BELT(element) && belt_dir != MV_NONE)
4490     {
4491       int e_belt_nr = getBeltNrFromBeltElement(element);
4492
4493       if (e_belt_nr == belt_nr)
4494       {
4495         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4496
4497         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4498         DrawLevelField(xx, yy);
4499       }
4500     }
4501     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4502     {
4503       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4504
4505       if (e_belt_nr == belt_nr)
4506       {
4507         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4508
4509         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4510         DrawLevelField(xx, yy);
4511       }
4512     }
4513   }
4514 }
4515
4516 static void ToggleSwitchgateSwitch(int x, int y)
4517 {
4518   int xx, yy;
4519
4520   game.switchgate_pos = !game.switchgate_pos;
4521
4522   SCAN_PLAYFIELD(xx, yy)
4523   {
4524     int element = Feld[xx][yy];
4525
4526 #if !USE_BOTH_SWITCHGATE_SWITCHES
4527     if (element == EL_SWITCHGATE_SWITCH_UP ||
4528         element == EL_SWITCHGATE_SWITCH_DOWN)
4529     {
4530       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4531       DrawLevelField(xx, yy);
4532     }
4533     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4534              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4535     {
4536       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4537       DrawLevelField(xx, yy);
4538     }
4539 #else
4540     if (element == EL_SWITCHGATE_SWITCH_UP)
4541     {
4542       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4543       DrawLevelField(xx, yy);
4544     }
4545     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4546     {
4547       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4548       DrawLevelField(xx, yy);
4549     }
4550     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4551     {
4552       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4553       DrawLevelField(xx, yy);
4554     }
4555     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4556     {
4557       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4558       DrawLevelField(xx, yy);
4559     }
4560 #endif
4561     else if (element == EL_SWITCHGATE_OPEN ||
4562              element == EL_SWITCHGATE_OPENING)
4563     {
4564       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4565
4566       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4567     }
4568     else if (element == EL_SWITCHGATE_CLOSED ||
4569              element == EL_SWITCHGATE_CLOSING)
4570     {
4571       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4572
4573       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4574     }
4575   }
4576 }
4577
4578 static int getInvisibleActiveFromInvisibleElement(int element)
4579 {
4580   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4581           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
4582           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
4583           element);
4584 }
4585
4586 static int getInvisibleFromInvisibleActiveElement(int element)
4587 {
4588   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4589           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
4590           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
4591           element);
4592 }
4593
4594 static void RedrawAllLightSwitchesAndInvisibleElements()
4595 {
4596   int x, y;
4597
4598   SCAN_PLAYFIELD(x, y)
4599   {
4600     int element = Feld[x][y];
4601
4602     if (element == EL_LIGHT_SWITCH &&
4603         game.light_time_left > 0)
4604     {
4605       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4606       DrawLevelField(x, y);
4607     }
4608     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4609              game.light_time_left == 0)
4610     {
4611       Feld[x][y] = EL_LIGHT_SWITCH;
4612       DrawLevelField(x, y);
4613     }
4614     else if (element == EL_EMC_DRIPPER &&
4615              game.light_time_left > 0)
4616     {
4617       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4618       DrawLevelField(x, y);
4619     }
4620     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4621              game.light_time_left == 0)
4622     {
4623       Feld[x][y] = EL_EMC_DRIPPER;
4624       DrawLevelField(x, y);
4625     }
4626     else if (element == EL_INVISIBLE_STEELWALL ||
4627              element == EL_INVISIBLE_WALL ||
4628              element == EL_INVISIBLE_SAND)
4629     {
4630       if (game.light_time_left > 0)
4631         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4632
4633       DrawLevelField(x, y);
4634
4635       /* uncrumble neighbour fields, if needed */
4636       if (element == EL_INVISIBLE_SAND)
4637         DrawLevelFieldCrumbledSandNeighbours(x, y);
4638     }
4639     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4640              element == EL_INVISIBLE_WALL_ACTIVE ||
4641              element == EL_INVISIBLE_SAND_ACTIVE)
4642     {
4643       if (game.light_time_left == 0)
4644         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4645
4646       DrawLevelField(x, y);
4647
4648       /* re-crumble neighbour fields, if needed */
4649       if (element == EL_INVISIBLE_SAND)
4650         DrawLevelFieldCrumbledSandNeighbours(x, y);
4651     }
4652   }
4653 }
4654
4655 static void RedrawAllInvisibleElementsForLenses()
4656 {
4657   int x, y;
4658
4659   SCAN_PLAYFIELD(x, y)
4660   {
4661     int element = Feld[x][y];
4662
4663     if (element == EL_EMC_DRIPPER &&
4664         game.lenses_time_left > 0)
4665     {
4666       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4667       DrawLevelField(x, y);
4668     }
4669     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4670              game.lenses_time_left == 0)
4671     {
4672       Feld[x][y] = EL_EMC_DRIPPER;
4673       DrawLevelField(x, y);
4674     }
4675     else if (element == EL_INVISIBLE_STEELWALL ||
4676              element == EL_INVISIBLE_WALL ||
4677              element == EL_INVISIBLE_SAND)
4678     {
4679       if (game.lenses_time_left > 0)
4680         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4681
4682       DrawLevelField(x, y);
4683
4684       /* uncrumble neighbour fields, if needed */
4685       if (element == EL_INVISIBLE_SAND)
4686         DrawLevelFieldCrumbledSandNeighbours(x, y);
4687     }
4688     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4689              element == EL_INVISIBLE_WALL_ACTIVE ||
4690              element == EL_INVISIBLE_SAND_ACTIVE)
4691     {
4692       if (game.lenses_time_left == 0)
4693         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4694
4695       DrawLevelField(x, y);
4696
4697       /* re-crumble neighbour fields, if needed */
4698       if (element == EL_INVISIBLE_SAND)
4699         DrawLevelFieldCrumbledSandNeighbours(x, y);
4700     }
4701   }
4702 }
4703
4704 static void RedrawAllInvisibleElementsForMagnifier()
4705 {
4706   int x, y;
4707
4708   SCAN_PLAYFIELD(x, y)
4709   {
4710     int element = Feld[x][y];
4711
4712     if (element == EL_EMC_FAKE_GRASS &&
4713         game.magnify_time_left > 0)
4714     {
4715       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4716       DrawLevelField(x, y);
4717     }
4718     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4719              game.magnify_time_left == 0)
4720     {
4721       Feld[x][y] = EL_EMC_FAKE_GRASS;
4722       DrawLevelField(x, y);
4723     }
4724     else if (IS_GATE_GRAY(element) &&
4725              game.magnify_time_left > 0)
4726     {
4727       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4728                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4729                     IS_EM_GATE_GRAY(element) ?
4730                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4731                     IS_EMC_GATE_GRAY(element) ?
4732                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4733                     element);
4734       DrawLevelField(x, y);
4735     }
4736     else if (IS_GATE_GRAY_ACTIVE(element) &&
4737              game.magnify_time_left == 0)
4738     {
4739       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4740                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4741                     IS_EM_GATE_GRAY_ACTIVE(element) ?
4742                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4743                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
4744                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4745                     element);
4746       DrawLevelField(x, y);
4747     }
4748   }
4749 }
4750
4751 static void ToggleLightSwitch(int x, int y)
4752 {
4753   int element = Feld[x][y];
4754
4755   game.light_time_left =
4756     (element == EL_LIGHT_SWITCH ?
4757      level.time_light * FRAMES_PER_SECOND : 0);
4758
4759   RedrawAllLightSwitchesAndInvisibleElements();
4760 }
4761
4762 static void ActivateTimegateSwitch(int x, int y)
4763 {
4764   int xx, yy;
4765
4766   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4767
4768   SCAN_PLAYFIELD(xx, yy)
4769   {
4770     int element = Feld[xx][yy];
4771
4772     if (element == EL_TIMEGATE_CLOSED ||
4773         element == EL_TIMEGATE_CLOSING)
4774     {
4775       Feld[xx][yy] = EL_TIMEGATE_OPENING;
4776       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4777     }
4778
4779     /*
4780     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4781     {
4782       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4783       DrawLevelField(xx, yy);
4784     }
4785     */
4786
4787   }
4788
4789 #if 1
4790   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4791                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4792 #else
4793   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4794 #endif
4795 }
4796
4797 void Impact(int x, int y)
4798 {
4799   boolean last_line = (y == lev_fieldy - 1);
4800   boolean object_hit = FALSE;
4801   boolean impact = (last_line || object_hit);
4802   int element = Feld[x][y];
4803   int smashed = EL_STEELWALL;
4804
4805   if (!last_line)       /* check if element below was hit */
4806   {
4807     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4808       return;
4809
4810     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4811                                          MovDir[x][y + 1] != MV_DOWN ||
4812                                          MovPos[x][y + 1] <= TILEY / 2));
4813
4814     /* do not smash moving elements that left the smashed field in time */
4815     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4816         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4817       object_hit = FALSE;
4818
4819 #if USE_QUICKSAND_IMPACT_BUGFIX
4820     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4821     {
4822       RemoveMovingField(x, y + 1);
4823       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4824       Feld[x][y + 2] = EL_ROCK;
4825       DrawLevelField(x, y + 2);
4826
4827       object_hit = TRUE;
4828     }
4829
4830     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
4831     {
4832       RemoveMovingField(x, y + 1);
4833       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
4834       Feld[x][y + 2] = EL_ROCK;
4835       DrawLevelField(x, y + 2);
4836
4837       object_hit = TRUE;
4838     }
4839 #endif
4840
4841     if (object_hit)
4842       smashed = MovingOrBlocked2Element(x, y + 1);
4843
4844     impact = (last_line || object_hit);
4845   }
4846
4847   if (!last_line && smashed == EL_ACID) /* element falls into acid */
4848   {
4849     SplashAcid(x, y + 1);
4850     return;
4851   }
4852
4853   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4854   /* only reset graphic animation if graphic really changes after impact */
4855   if (impact &&
4856       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4857   {
4858     ResetGfxAnimation(x, y);
4859     DrawLevelField(x, y);
4860   }
4861
4862   if (impact && CAN_EXPLODE_IMPACT(element))
4863   {
4864     Bang(x, y);
4865     return;
4866   }
4867   else if (impact && element == EL_PEARL &&
4868            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
4869   {
4870     ResetGfxAnimation(x, y);
4871
4872     Feld[x][y] = EL_PEARL_BREAKING;
4873     PlayLevelSound(x, y, SND_PEARL_BREAKING);
4874     return;
4875   }
4876   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4877   {
4878     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4879
4880     return;
4881   }
4882
4883   if (impact && element == EL_AMOEBA_DROP)
4884   {
4885     if (object_hit && IS_PLAYER(x, y + 1))
4886       KillPlayerUnlessEnemyProtected(x, y + 1);
4887     else if (object_hit && smashed == EL_PENGUIN)
4888       Bang(x, y + 1);
4889     else
4890     {
4891       Feld[x][y] = EL_AMOEBA_GROWING;
4892       Store[x][y] = EL_AMOEBA_WET;
4893
4894       ResetRandomAnimationValue(x, y);
4895     }
4896     return;
4897   }
4898
4899   if (object_hit)               /* check which object was hit */
4900   {
4901     if ((CAN_PASS_MAGIC_WALL(element) && 
4902          (smashed == EL_MAGIC_WALL ||
4903           smashed == EL_BD_MAGIC_WALL)) ||
4904         (CAN_PASS_DC_MAGIC_WALL(element) &&
4905          smashed == EL_DC_MAGIC_WALL))
4906     {
4907       int xx, yy;
4908       int activated_magic_wall =
4909         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4910          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
4911          EL_DC_MAGIC_WALL_ACTIVE);
4912
4913       /* activate magic wall / mill */
4914       SCAN_PLAYFIELD(xx, yy)
4915       {
4916         if (Feld[xx][yy] == smashed)
4917           Feld[xx][yy] = activated_magic_wall;
4918       }
4919
4920       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4921       game.magic_wall_active = TRUE;
4922
4923       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4924                             SND_MAGIC_WALL_ACTIVATING :
4925                             smashed == EL_BD_MAGIC_WALL ?
4926                             SND_BD_MAGIC_WALL_ACTIVATING :
4927                             SND_DC_MAGIC_WALL_ACTIVATING));
4928     }
4929
4930     if (IS_PLAYER(x, y + 1))
4931     {
4932       if (CAN_SMASH_PLAYER(element))
4933       {
4934         KillPlayerUnlessEnemyProtected(x, y + 1);
4935         return;
4936       }
4937     }
4938     else if (smashed == EL_PENGUIN)
4939     {
4940       if (CAN_SMASH_PLAYER(element))
4941       {
4942         Bang(x, y + 1);
4943         return;
4944       }
4945     }
4946     else if (element == EL_BD_DIAMOND)
4947     {
4948       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4949       {
4950         Bang(x, y + 1);
4951         return;
4952       }
4953     }
4954     else if (((element == EL_SP_INFOTRON ||
4955                element == EL_SP_ZONK) &&
4956               (smashed == EL_SP_SNIKSNAK ||
4957                smashed == EL_SP_ELECTRON ||
4958                smashed == EL_SP_DISK_ORANGE)) ||
4959              (element == EL_SP_INFOTRON &&
4960               smashed == EL_SP_DISK_YELLOW))
4961     {
4962       Bang(x, y + 1);
4963       return;
4964     }
4965     else if (CAN_SMASH_EVERYTHING(element))
4966     {
4967       if (IS_CLASSIC_ENEMY(smashed) ||
4968           CAN_EXPLODE_SMASHED(smashed))
4969       {
4970         Bang(x, y + 1);
4971         return;
4972       }
4973       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4974       {
4975         if (smashed == EL_LAMP ||
4976             smashed == EL_LAMP_ACTIVE)
4977         {
4978           Bang(x, y + 1);
4979           return;
4980         }
4981         else if (smashed == EL_NUT)
4982         {
4983           Feld[x][y + 1] = EL_NUT_BREAKING;
4984           PlayLevelSound(x, y, SND_NUT_BREAKING);
4985           RaiseScoreElement(EL_NUT);
4986           return;
4987         }
4988         else if (smashed == EL_PEARL)
4989         {
4990           ResetGfxAnimation(x, y);
4991
4992           Feld[x][y + 1] = EL_PEARL_BREAKING;
4993           PlayLevelSound(x, y, SND_PEARL_BREAKING);
4994           return;
4995         }
4996         else if (smashed == EL_DIAMOND)
4997         {
4998           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4999           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5000           return;
5001         }
5002         else if (IS_BELT_SWITCH(smashed))
5003         {
5004           ToggleBeltSwitch(x, y + 1);
5005         }
5006         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5007                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
5008                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
5009                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
5010         {
5011           ToggleSwitchgateSwitch(x, y + 1);
5012         }
5013         else if (smashed == EL_LIGHT_SWITCH ||
5014                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5015         {
5016           ToggleLightSwitch(x, y + 1);
5017         }
5018         else
5019         {
5020 #if 0
5021           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5022 #endif
5023
5024           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5025
5026           CheckElementChangeBySide(x, y + 1, smashed, element,
5027                                    CE_SWITCHED, CH_SIDE_TOP);
5028           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5029                                             CH_SIDE_TOP);
5030         }
5031       }
5032       else
5033       {
5034         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5035       }
5036     }
5037   }
5038
5039   /* play sound of magic wall / mill */
5040   if (!last_line &&
5041       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5042        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5043        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5044   {
5045     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5046       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5047     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5048       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5049     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5050       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5051
5052     return;
5053   }
5054
5055   /* play sound of object that hits the ground */
5056   if (last_line || object_hit)
5057     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5058 }
5059
5060 inline static void TurnRoundExt(int x, int y)
5061 {
5062   static struct
5063   {
5064     int dx, dy;
5065   } move_xy[] =
5066   {
5067     {  0,  0 },
5068     { -1,  0 },
5069     { +1,  0 },
5070     {  0,  0 },
5071     {  0, -1 },
5072     {  0,  0 }, { 0, 0 }, { 0, 0 },
5073     {  0, +1 }
5074   };
5075   static struct
5076   {
5077     int left, right, back;
5078   } turn[] =
5079   {
5080     { 0,        0,              0        },
5081     { MV_DOWN,  MV_UP,          MV_RIGHT },
5082     { MV_UP,    MV_DOWN,        MV_LEFT  },
5083     { 0,        0,              0        },
5084     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5085     { 0,        0,              0        },
5086     { 0,        0,              0        },
5087     { 0,        0,              0        },
5088     { MV_RIGHT, MV_LEFT,        MV_UP    }
5089   };
5090
5091   int element = Feld[x][y];
5092   int move_pattern = element_info[element].move_pattern;
5093
5094   int old_move_dir = MovDir[x][y];
5095   int left_dir  = turn[old_move_dir].left;
5096   int right_dir = turn[old_move_dir].right;
5097   int back_dir  = turn[old_move_dir].back;
5098
5099   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5100   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5101   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5102   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5103
5104   int left_x  = x + left_dx,  left_y  = y + left_dy;
5105   int right_x = x + right_dx, right_y = y + right_dy;
5106   int move_x  = x + move_dx,  move_y  = y + move_dy;
5107
5108   int xx, yy;
5109
5110   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5111   {
5112     TestIfBadThingTouchesOtherBadThing(x, y);
5113
5114     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5115       MovDir[x][y] = right_dir;
5116     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5117       MovDir[x][y] = left_dir;
5118
5119     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5120       MovDelay[x][y] = 9;
5121     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5122       MovDelay[x][y] = 1;
5123   }
5124   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5125   {
5126     TestIfBadThingTouchesOtherBadThing(x, y);
5127
5128     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5129       MovDir[x][y] = left_dir;
5130     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5131       MovDir[x][y] = right_dir;
5132
5133     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5134       MovDelay[x][y] = 9;
5135     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5136       MovDelay[x][y] = 1;
5137   }
5138   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5139   {
5140     TestIfBadThingTouchesOtherBadThing(x, y);
5141
5142     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5143       MovDir[x][y] = left_dir;
5144     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5145       MovDir[x][y] = right_dir;
5146
5147     if (MovDir[x][y] != old_move_dir)
5148       MovDelay[x][y] = 9;
5149   }
5150   else if (element == EL_YAMYAM)
5151   {
5152     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5153     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5154
5155     if (can_turn_left && can_turn_right)
5156       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5157     else if (can_turn_left)
5158       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5159     else if (can_turn_right)
5160       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5161     else
5162       MovDir[x][y] = back_dir;
5163
5164     MovDelay[x][y] = 16 + 16 * RND(3);
5165   }
5166   else if (element == EL_DARK_YAMYAM)
5167   {
5168     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5169                                                          left_x, left_y);
5170     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5171                                                          right_x, right_y);
5172
5173     if (can_turn_left && can_turn_right)
5174       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5175     else if (can_turn_left)
5176       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5177     else if (can_turn_right)
5178       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5179     else
5180       MovDir[x][y] = back_dir;
5181
5182     MovDelay[x][y] = 16 + 16 * RND(3);
5183   }
5184   else if (element == EL_PACMAN)
5185   {
5186     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5187     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5188
5189     if (can_turn_left && can_turn_right)
5190       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5191     else if (can_turn_left)
5192       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5193     else if (can_turn_right)
5194       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5195     else
5196       MovDir[x][y] = back_dir;
5197
5198     MovDelay[x][y] = 6 + RND(40);
5199   }
5200   else if (element == EL_PIG)
5201   {
5202     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5203     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5204     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5205     boolean should_turn_left, should_turn_right, should_move_on;
5206     int rnd_value = 24;
5207     int rnd = RND(rnd_value);
5208
5209     should_turn_left = (can_turn_left &&
5210                         (!can_move_on ||
5211                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5212                                                    y + back_dy + left_dy)));
5213     should_turn_right = (can_turn_right &&
5214                          (!can_move_on ||
5215                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5216                                                     y + back_dy + right_dy)));
5217     should_move_on = (can_move_on &&
5218                       (!can_turn_left ||
5219                        !can_turn_right ||
5220                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5221                                                  y + move_dy + left_dy) ||
5222                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5223                                                  y + move_dy + right_dy)));
5224
5225     if (should_turn_left || should_turn_right || should_move_on)
5226     {
5227       if (should_turn_left && should_turn_right && should_move_on)
5228         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5229                         rnd < 2 * rnd_value / 3 ? right_dir :
5230                         old_move_dir);
5231       else if (should_turn_left && should_turn_right)
5232         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5233       else if (should_turn_left && should_move_on)
5234         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5235       else if (should_turn_right && should_move_on)
5236         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5237       else if (should_turn_left)
5238         MovDir[x][y] = left_dir;
5239       else if (should_turn_right)
5240         MovDir[x][y] = right_dir;
5241       else if (should_move_on)
5242         MovDir[x][y] = old_move_dir;
5243     }
5244     else if (can_move_on && rnd > rnd_value / 8)
5245       MovDir[x][y] = old_move_dir;
5246     else if (can_turn_left && can_turn_right)
5247       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5248     else if (can_turn_left && rnd > rnd_value / 8)
5249       MovDir[x][y] = left_dir;
5250     else if (can_turn_right && rnd > rnd_value/8)
5251       MovDir[x][y] = right_dir;
5252     else
5253       MovDir[x][y] = back_dir;
5254
5255     xx = x + move_xy[MovDir[x][y]].dx;
5256     yy = y + move_xy[MovDir[x][y]].dy;
5257
5258     if (!IN_LEV_FIELD(xx, yy) ||
5259         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5260       MovDir[x][y] = old_move_dir;
5261
5262     MovDelay[x][y] = 0;
5263   }
5264   else if (element == EL_DRAGON)
5265   {
5266     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5267     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5268     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5269     int rnd_value = 24;
5270     int rnd = RND(rnd_value);
5271
5272     if (can_move_on && rnd > rnd_value / 8)
5273       MovDir[x][y] = old_move_dir;
5274     else if (can_turn_left && can_turn_right)
5275       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5276     else if (can_turn_left && rnd > rnd_value / 8)
5277       MovDir[x][y] = left_dir;
5278     else if (can_turn_right && rnd > rnd_value / 8)
5279       MovDir[x][y] = right_dir;
5280     else
5281       MovDir[x][y] = back_dir;
5282
5283     xx = x + move_xy[MovDir[x][y]].dx;
5284     yy = y + move_xy[MovDir[x][y]].dy;
5285
5286     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5287       MovDir[x][y] = old_move_dir;
5288
5289     MovDelay[x][y] = 0;
5290   }
5291   else if (element == EL_MOLE)
5292   {
5293     boolean can_move_on =
5294       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5295                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5296                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5297     if (!can_move_on)
5298     {
5299       boolean can_turn_left =
5300         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5301                               IS_AMOEBOID(Feld[left_x][left_y])));
5302
5303       boolean can_turn_right =
5304         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5305                               IS_AMOEBOID(Feld[right_x][right_y])));
5306
5307       if (can_turn_left && can_turn_right)
5308         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5309       else if (can_turn_left)
5310         MovDir[x][y] = left_dir;
5311       else
5312         MovDir[x][y] = right_dir;
5313     }
5314
5315     if (MovDir[x][y] != old_move_dir)
5316       MovDelay[x][y] = 9;
5317   }
5318   else if (element == EL_BALLOON)
5319   {
5320     MovDir[x][y] = game.wind_direction;
5321     MovDelay[x][y] = 0;
5322   }
5323   else if (element == EL_SPRING)
5324   {
5325 #if USE_NEW_SPRING_BUMPER
5326     if (MovDir[x][y] & MV_HORIZONTAL)
5327     {
5328       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5329           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5330       {
5331         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5332         ResetGfxAnimation(move_x, move_y);
5333         DrawLevelField(move_x, move_y);
5334
5335         MovDir[x][y] = back_dir;
5336       }
5337       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5338                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5339         MovDir[x][y] = MV_NONE;
5340     }
5341 #else
5342     if (MovDir[x][y] & MV_HORIZONTAL &&
5343         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5344          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5345       MovDir[x][y] = MV_NONE;
5346 #endif
5347
5348     MovDelay[x][y] = 0;
5349   }
5350   else if (element == EL_ROBOT ||
5351            element == EL_SATELLITE ||
5352            element == EL_PENGUIN ||
5353            element == EL_EMC_ANDROID)
5354   {
5355     int attr_x = -1, attr_y = -1;
5356
5357     if (AllPlayersGone)
5358     {
5359       attr_x = ExitX;
5360       attr_y = ExitY;
5361     }
5362     else
5363     {
5364       int i;
5365
5366       for (i = 0; i < MAX_PLAYERS; i++)
5367       {
5368         struct PlayerInfo *player = &stored_player[i];
5369         int jx = player->jx, jy = player->jy;
5370
5371         if (!player->active)
5372           continue;
5373
5374         if (attr_x == -1 ||
5375             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5376         {
5377           attr_x = jx;
5378           attr_y = jy;
5379         }
5380       }
5381     }
5382
5383     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5384         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5385          game.engine_version < VERSION_IDENT(3,1,0,0)))
5386     {
5387       attr_x = ZX;
5388       attr_y = ZY;
5389     }
5390
5391     if (element == EL_PENGUIN)
5392     {
5393       int i;
5394       static int xy[4][2] =
5395       {
5396         { 0, -1 },
5397         { -1, 0 },
5398         { +1, 0 },
5399         { 0, +1 }
5400       };
5401
5402       for (i = 0; i < NUM_DIRECTIONS; i++)
5403       {
5404         int ex = x + xy[i][0];
5405         int ey = y + xy[i][1];
5406
5407         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5408                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5409                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5410                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5411         {
5412           attr_x = ex;
5413           attr_y = ey;
5414           break;
5415         }
5416       }
5417     }
5418
5419     MovDir[x][y] = MV_NONE;
5420     if (attr_x < x)
5421       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5422     else if (attr_x > x)
5423       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5424     if (attr_y < y)
5425       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5426     else if (attr_y > y)
5427       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5428
5429     if (element == EL_ROBOT)
5430     {
5431       int newx, newy;
5432
5433       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5434         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5435       Moving2Blocked(x, y, &newx, &newy);
5436
5437       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5438         MovDelay[x][y] = 8 + 8 * !RND(3);
5439       else
5440         MovDelay[x][y] = 16;
5441     }
5442     else if (element == EL_PENGUIN)
5443     {
5444       int newx, newy;
5445
5446       MovDelay[x][y] = 1;
5447
5448       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5449       {
5450         boolean first_horiz = RND(2);
5451         int new_move_dir = MovDir[x][y];
5452
5453         MovDir[x][y] =
5454           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5455         Moving2Blocked(x, y, &newx, &newy);
5456
5457         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5458           return;
5459
5460         MovDir[x][y] =
5461           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5462         Moving2Blocked(x, y, &newx, &newy);
5463
5464         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5465           return;
5466
5467         MovDir[x][y] = old_move_dir;
5468         return;
5469       }
5470     }
5471     else if (element == EL_SATELLITE)
5472     {
5473       int newx, newy;
5474
5475       MovDelay[x][y] = 1;
5476
5477       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5478       {
5479         boolean first_horiz = RND(2);
5480         int new_move_dir = MovDir[x][y];
5481
5482         MovDir[x][y] =
5483           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5484         Moving2Blocked(x, y, &newx, &newy);
5485
5486         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5487           return;
5488
5489         MovDir[x][y] =
5490           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5491         Moving2Blocked(x, y, &newx, &newy);
5492
5493         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5494           return;
5495
5496         MovDir[x][y] = old_move_dir;
5497         return;
5498       }
5499     }
5500     else if (element == EL_EMC_ANDROID)
5501     {
5502       static int check_pos[16] =
5503       {
5504         -1,             /*  0 => (invalid)          */
5505         7,              /*  1 => MV_LEFT            */
5506         3,              /*  2 => MV_RIGHT           */
5507         -1,             /*  3 => (invalid)          */
5508         1,              /*  4 =>            MV_UP   */
5509         0,              /*  5 => MV_LEFT  | MV_UP   */
5510         2,              /*  6 => MV_RIGHT | MV_UP   */
5511         -1,             /*  7 => (invalid)          */
5512         5,              /*  8 =>            MV_DOWN */
5513         6,              /*  9 => MV_LEFT  | MV_DOWN */
5514         4,              /* 10 => MV_RIGHT | MV_DOWN */
5515         -1,             /* 11 => (invalid)          */
5516         -1,             /* 12 => (invalid)          */
5517         -1,             /* 13 => (invalid)          */
5518         -1,             /* 14 => (invalid)          */
5519         -1,             /* 15 => (invalid)          */
5520       };
5521       static struct
5522       {
5523         int dx, dy;
5524         int dir;
5525       } check_xy[8] =
5526       {
5527         { -1, -1,       MV_LEFT  | MV_UP   },
5528         {  0, -1,                  MV_UP   },
5529         { +1, -1,       MV_RIGHT | MV_UP   },
5530         { +1,  0,       MV_RIGHT           },
5531         { +1, +1,       MV_RIGHT | MV_DOWN },
5532         {  0, +1,                  MV_DOWN },
5533         { -1, +1,       MV_LEFT  | MV_DOWN },
5534         { -1,  0,       MV_LEFT            },
5535       };
5536       int start_pos, check_order;
5537       boolean can_clone = FALSE;
5538       int i;
5539
5540       /* check if there is any free field around current position */
5541       for (i = 0; i < 8; i++)
5542       {
5543         int newx = x + check_xy[i].dx;
5544         int newy = y + check_xy[i].dy;
5545
5546         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5547         {
5548           can_clone = TRUE;
5549
5550           break;
5551         }
5552       }
5553
5554       if (can_clone)            /* randomly find an element to clone */
5555       {
5556         can_clone = FALSE;
5557
5558         start_pos = check_pos[RND(8)];
5559         check_order = (RND(2) ? -1 : +1);
5560
5561         for (i = 0; i < 8; i++)
5562         {
5563           int pos_raw = start_pos + i * check_order;
5564           int pos = (pos_raw + 8) % 8;
5565           int newx = x + check_xy[pos].dx;
5566           int newy = y + check_xy[pos].dy;
5567
5568           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5569           {
5570             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5571             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5572
5573             Store[x][y] = Feld[newx][newy];
5574
5575             can_clone = TRUE;
5576
5577             break;
5578           }
5579         }
5580       }
5581
5582       if (can_clone)            /* randomly find a direction to move */
5583       {
5584         can_clone = FALSE;
5585
5586         start_pos = check_pos[RND(8)];
5587         check_order = (RND(2) ? -1 : +1);
5588
5589         for (i = 0; i < 8; i++)
5590         {
5591           int pos_raw = start_pos + i * check_order;
5592           int pos = (pos_raw + 8) % 8;
5593           int newx = x + check_xy[pos].dx;
5594           int newy = y + check_xy[pos].dy;
5595           int new_move_dir = check_xy[pos].dir;
5596
5597           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5598           {
5599             MovDir[x][y] = new_move_dir;
5600             MovDelay[x][y] = level.android_clone_time * 8 + 1;
5601
5602             can_clone = TRUE;
5603
5604             break;
5605           }
5606         }
5607       }
5608
5609       if (can_clone)            /* cloning and moving successful */
5610         return;
5611
5612       /* cannot clone -- try to move towards player */
5613
5614       start_pos = check_pos[MovDir[x][y] & 0x0f];
5615       check_order = (RND(2) ? -1 : +1);
5616
5617       for (i = 0; i < 3; i++)
5618       {
5619         /* first check start_pos, then previous/next or (next/previous) pos */
5620         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5621         int pos = (pos_raw + 8) % 8;
5622         int newx = x + check_xy[pos].dx;
5623         int newy = y + check_xy[pos].dy;
5624         int new_move_dir = check_xy[pos].dir;
5625
5626         if (IS_PLAYER(newx, newy))
5627           break;
5628
5629         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5630         {
5631           MovDir[x][y] = new_move_dir;
5632           MovDelay[x][y] = level.android_move_time * 8 + 1;
5633
5634           break;
5635         }
5636       }
5637     }
5638   }
5639   else if (move_pattern == MV_TURNING_LEFT ||
5640            move_pattern == MV_TURNING_RIGHT ||
5641            move_pattern == MV_TURNING_LEFT_RIGHT ||
5642            move_pattern == MV_TURNING_RIGHT_LEFT ||
5643            move_pattern == MV_TURNING_RANDOM ||
5644            move_pattern == MV_ALL_DIRECTIONS)
5645   {
5646     boolean can_turn_left =
5647       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5648     boolean can_turn_right =
5649       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5650
5651     if (element_info[element].move_stepsize == 0)       /* "not moving" */
5652       return;
5653
5654     if (move_pattern == MV_TURNING_LEFT)
5655       MovDir[x][y] = left_dir;
5656     else if (move_pattern == MV_TURNING_RIGHT)
5657       MovDir[x][y] = right_dir;
5658     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5659       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5660     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5661       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5662     else if (move_pattern == MV_TURNING_RANDOM)
5663       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5664                       can_turn_right && !can_turn_left ? right_dir :
5665                       RND(2) ? left_dir : right_dir);
5666     else if (can_turn_left && can_turn_right)
5667       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5668     else if (can_turn_left)
5669       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5670     else if (can_turn_right)
5671       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5672     else
5673       MovDir[x][y] = back_dir;
5674
5675     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5676   }
5677   else if (move_pattern == MV_HORIZONTAL ||
5678            move_pattern == MV_VERTICAL)
5679   {
5680     if (move_pattern & old_move_dir)
5681       MovDir[x][y] = back_dir;
5682     else if (move_pattern == MV_HORIZONTAL)
5683       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5684     else if (move_pattern == MV_VERTICAL)
5685       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5686
5687     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5688   }
5689   else if (move_pattern & MV_ANY_DIRECTION)
5690   {
5691     MovDir[x][y] = move_pattern;
5692     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5693   }
5694   else if (move_pattern & MV_WIND_DIRECTION)
5695   {
5696     MovDir[x][y] = game.wind_direction;
5697     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5698   }
5699   else if (move_pattern == MV_ALONG_LEFT_SIDE)
5700   {
5701     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5702       MovDir[x][y] = left_dir;
5703     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5704       MovDir[x][y] = right_dir;
5705
5706     if (MovDir[x][y] != old_move_dir)
5707       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5708   }
5709   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5710   {
5711     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5712       MovDir[x][y] = right_dir;
5713     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5714       MovDir[x][y] = left_dir;
5715
5716     if (MovDir[x][y] != old_move_dir)
5717       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5718   }
5719   else if (move_pattern == MV_TOWARDS_PLAYER ||
5720            move_pattern == MV_AWAY_FROM_PLAYER)
5721   {
5722     int attr_x = -1, attr_y = -1;
5723     int newx, newy;
5724     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5725
5726     if (AllPlayersGone)
5727     {
5728       attr_x = ExitX;
5729       attr_y = ExitY;
5730     }
5731     else
5732     {
5733       int i;
5734
5735       for (i = 0; i < MAX_PLAYERS; i++)
5736       {
5737         struct PlayerInfo *player = &stored_player[i];
5738         int jx = player->jx, jy = player->jy;
5739
5740         if (!player->active)
5741           continue;
5742
5743         if (attr_x == -1 ||
5744             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5745         {
5746           attr_x = jx;
5747           attr_y = jy;
5748         }
5749       }
5750     }
5751
5752     MovDir[x][y] = MV_NONE;
5753     if (attr_x < x)
5754       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5755     else if (attr_x > x)
5756       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5757     if (attr_y < y)
5758       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5759     else if (attr_y > y)
5760       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5761
5762     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5763
5764     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5765     {
5766       boolean first_horiz = RND(2);
5767       int new_move_dir = MovDir[x][y];
5768
5769       if (element_info[element].move_stepsize == 0)     /* "not moving" */
5770       {
5771         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5772         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5773
5774         return;
5775       }
5776
5777       MovDir[x][y] =
5778         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5779       Moving2Blocked(x, y, &newx, &newy);
5780
5781       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5782         return;
5783
5784       MovDir[x][y] =
5785         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5786       Moving2Blocked(x, y, &newx, &newy);
5787
5788       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5789         return;
5790
5791       MovDir[x][y] = old_move_dir;
5792     }
5793   }
5794   else if (move_pattern == MV_WHEN_PUSHED ||
5795            move_pattern == MV_WHEN_DROPPED)
5796   {
5797     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5798       MovDir[x][y] = MV_NONE;
5799
5800     MovDelay[x][y] = 0;
5801   }
5802   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5803   {
5804     static int test_xy[7][2] =
5805     {
5806       { 0, -1 },
5807       { -1, 0 },
5808       { +1, 0 },
5809       { 0, +1 },
5810       { 0, -1 },
5811       { -1, 0 },
5812       { +1, 0 },
5813     };
5814     static int test_dir[7] =
5815     {
5816       MV_UP,
5817       MV_LEFT,
5818       MV_RIGHT,
5819       MV_DOWN,
5820       MV_UP,
5821       MV_LEFT,
5822       MV_RIGHT,
5823     };
5824     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5825     int move_preference = -1000000;     /* start with very low preference */
5826     int new_move_dir = MV_NONE;
5827     int start_test = RND(4);
5828     int i;
5829
5830     for (i = 0; i < NUM_DIRECTIONS; i++)
5831     {
5832       int move_dir = test_dir[start_test + i];
5833       int move_dir_preference;
5834
5835       xx = x + test_xy[start_test + i][0];
5836       yy = y + test_xy[start_test + i][1];
5837
5838       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5839           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5840       {
5841         new_move_dir = move_dir;
5842
5843         break;
5844       }
5845
5846       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5847         continue;
5848
5849       move_dir_preference = -1 * RunnerVisit[xx][yy];
5850       if (hunter_mode && PlayerVisit[xx][yy] > 0)
5851         move_dir_preference = PlayerVisit[xx][yy];
5852
5853       if (move_dir_preference > move_preference)
5854       {
5855         /* prefer field that has not been visited for the longest time */
5856         move_preference = move_dir_preference;
5857         new_move_dir = move_dir;
5858       }
5859       else if (move_dir_preference == move_preference &&
5860                move_dir == old_move_dir)
5861       {
5862         /* prefer last direction when all directions are preferred equally */
5863         move_preference = move_dir_preference;
5864         new_move_dir = move_dir;
5865       }
5866     }
5867
5868     MovDir[x][y] = new_move_dir;
5869     if (old_move_dir != new_move_dir)
5870       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5871   }
5872 }
5873
5874 static void TurnRound(int x, int y)
5875 {
5876   int direction = MovDir[x][y];
5877
5878   TurnRoundExt(x, y);
5879
5880   GfxDir[x][y] = MovDir[x][y];
5881
5882   if (direction != MovDir[x][y])
5883     GfxFrame[x][y] = 0;
5884
5885   if (MovDelay[x][y])
5886     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5887
5888   ResetGfxFrame(x, y, FALSE);
5889 }
5890
5891 static boolean JustBeingPushed(int x, int y)
5892 {
5893   int i;
5894
5895   for (i = 0; i < MAX_PLAYERS; i++)
5896   {
5897     struct PlayerInfo *player = &stored_player[i];
5898
5899     if (player->active && player->is_pushing && player->MovPos)
5900     {
5901       int next_jx = player->jx + (player->jx - player->last_jx);
5902       int next_jy = player->jy + (player->jy - player->last_jy);
5903
5904       if (x == next_jx && y == next_jy)
5905         return TRUE;
5906     }
5907   }
5908
5909   return FALSE;
5910 }
5911
5912 void StartMoving(int x, int y)
5913 {
5914   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
5915   int element = Feld[x][y];
5916
5917   if (Stop[x][y])
5918     return;
5919
5920   if (MovDelay[x][y] == 0)
5921     GfxAction[x][y] = ACTION_DEFAULT;
5922
5923   if (CAN_FALL(element) && y < lev_fieldy - 1)
5924   {
5925     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5926         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5927       if (JustBeingPushed(x, y))
5928         return;
5929
5930     if (element == EL_QUICKSAND_FULL)
5931     {
5932       if (IS_FREE(x, y + 1))
5933       {
5934         InitMovingField(x, y, MV_DOWN);
5935         started_moving = TRUE;
5936
5937         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5938 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5939         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5940           Store[x][y] = EL_ROCK;
5941 #else
5942         Store[x][y] = EL_ROCK;
5943 #endif
5944
5945         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5946       }
5947       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5948       {
5949         if (!MovDelay[x][y])
5950           MovDelay[x][y] = TILEY + 1;
5951
5952         if (MovDelay[x][y])
5953         {
5954           MovDelay[x][y]--;
5955           if (MovDelay[x][y])
5956             return;
5957         }
5958
5959         Feld[x][y] = EL_QUICKSAND_EMPTY;
5960         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5961         Store[x][y + 1] = Store[x][y];
5962         Store[x][y] = 0;
5963
5964         PlayLevelSoundAction(x, y, ACTION_FILLING);
5965       }
5966     }
5967     else if (element == EL_QUICKSAND_FAST_FULL)
5968     {
5969       if (IS_FREE(x, y + 1))
5970       {
5971         InitMovingField(x, y, MV_DOWN);
5972         started_moving = TRUE;
5973
5974         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
5975 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5976         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5977           Store[x][y] = EL_ROCK;
5978 #else
5979         Store[x][y] = EL_ROCK;
5980 #endif
5981
5982         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5983       }
5984       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5985       {
5986         if (!MovDelay[x][y])
5987           MovDelay[x][y] = TILEY + 1;
5988
5989         if (MovDelay[x][y])
5990         {
5991           MovDelay[x][y]--;
5992           if (MovDelay[x][y])
5993             return;
5994         }
5995
5996         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
5997         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
5998         Store[x][y + 1] = Store[x][y];
5999         Store[x][y] = 0;
6000
6001         PlayLevelSoundAction(x, y, ACTION_FILLING);
6002       }
6003     }
6004     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6005              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6006     {
6007       InitMovingField(x, y, MV_DOWN);
6008       started_moving = TRUE;
6009
6010       Feld[x][y] = EL_QUICKSAND_FILLING;
6011       Store[x][y] = element;
6012
6013       PlayLevelSoundAction(x, y, ACTION_FILLING);
6014     }
6015     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6016              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
6017     {
6018       InitMovingField(x, y, MV_DOWN);
6019       started_moving = TRUE;
6020
6021       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
6022       Store[x][y] = element;
6023
6024       PlayLevelSoundAction(x, y, ACTION_FILLING);
6025     }
6026     else if (element == EL_MAGIC_WALL_FULL)
6027     {
6028       if (IS_FREE(x, y + 1))
6029       {
6030         InitMovingField(x, y, MV_DOWN);
6031         started_moving = TRUE;
6032
6033         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6034         Store[x][y] = EL_CHANGED(Store[x][y]);
6035       }
6036       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6037       {
6038         if (!MovDelay[x][y])
6039           MovDelay[x][y] = TILEY/4 + 1;
6040
6041         if (MovDelay[x][y])
6042         {
6043           MovDelay[x][y]--;
6044           if (MovDelay[x][y])
6045             return;
6046         }
6047
6048         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6049         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6050         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6051         Store[x][y] = 0;
6052       }
6053     }
6054     else if (element == EL_BD_MAGIC_WALL_FULL)
6055     {
6056       if (IS_FREE(x, y + 1))
6057       {
6058         InitMovingField(x, y, MV_DOWN);
6059         started_moving = TRUE;
6060
6061         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6062         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6063       }
6064       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6065       {
6066         if (!MovDelay[x][y])
6067           MovDelay[x][y] = TILEY/4 + 1;
6068
6069         if (MovDelay[x][y])
6070         {
6071           MovDelay[x][y]--;
6072           if (MovDelay[x][y])
6073             return;
6074         }
6075
6076         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6077         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6078         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6079         Store[x][y] = 0;
6080       }
6081     }
6082     else if (element == EL_DC_MAGIC_WALL_FULL)
6083     {
6084       if (IS_FREE(x, y + 1))
6085       {
6086         InitMovingField(x, y, MV_DOWN);
6087         started_moving = TRUE;
6088
6089         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6090         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6091       }
6092       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6093       {
6094         if (!MovDelay[x][y])
6095           MovDelay[x][y] = TILEY/4 + 1;
6096
6097         if (MovDelay[x][y])
6098         {
6099           MovDelay[x][y]--;
6100           if (MovDelay[x][y])
6101             return;
6102         }
6103
6104         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6105         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6106         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6107         Store[x][y] = 0;
6108       }
6109     }
6110     else if ((CAN_PASS_MAGIC_WALL(element) &&
6111               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6112                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6113              (CAN_PASS_DC_MAGIC_WALL(element) &&
6114               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6115
6116     {
6117       InitMovingField(x, y, MV_DOWN);
6118       started_moving = TRUE;
6119
6120       Feld[x][y] =
6121         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6122          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6123          EL_DC_MAGIC_WALL_FILLING);
6124       Store[x][y] = element;
6125     }
6126     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6127     {
6128       SplashAcid(x, y + 1);
6129
6130       InitMovingField(x, y, MV_DOWN);
6131       started_moving = TRUE;
6132
6133       Store[x][y] = EL_ACID;
6134     }
6135     else if (
6136 #if USE_FIX_IMPACT_COLLISION
6137              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6138               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6139 #else
6140              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6141               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6142 #endif
6143              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6144               CAN_FALL(element) && WasJustFalling[x][y] &&
6145               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6146
6147              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6148               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6149               (Feld[x][y + 1] == EL_BLOCKED)))
6150     {
6151       /* this is needed for a special case not covered by calling "Impact()"
6152          from "ContinueMoving()": if an element moves to a tile directly below
6153          another element which was just falling on that tile (which was empty
6154          in the previous frame), the falling element above would just stop
6155          instead of smashing the element below (in previous version, the above
6156          element was just checked for "moving" instead of "falling", resulting
6157          in incorrect smashes caused by horizontal movement of the above
6158          element; also, the case of the player being the element to smash was
6159          simply not covered here... :-/ ) */
6160
6161       CheckCollision[x][y] = 0;
6162       CheckImpact[x][y] = 0;
6163
6164       Impact(x, y);
6165     }
6166     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6167     {
6168       if (MovDir[x][y] == MV_NONE)
6169       {
6170         InitMovingField(x, y, MV_DOWN);
6171         started_moving = TRUE;
6172       }
6173     }
6174     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6175     {
6176       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6177         MovDir[x][y] = MV_DOWN;
6178
6179       InitMovingField(x, y, MV_DOWN);
6180       started_moving = TRUE;
6181     }
6182     else if (element == EL_AMOEBA_DROP)
6183     {
6184       Feld[x][y] = EL_AMOEBA_GROWING;
6185       Store[x][y] = EL_AMOEBA_WET;
6186     }
6187     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6188               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6189              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6190              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6191     {
6192       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6193                                 (IS_FREE(x - 1, y + 1) ||
6194                                  Feld[x - 1][y + 1] == EL_ACID));
6195       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6196                                 (IS_FREE(x + 1, y + 1) ||
6197                                  Feld[x + 1][y + 1] == EL_ACID));
6198       boolean can_fall_any  = (can_fall_left || can_fall_right);
6199       boolean can_fall_both = (can_fall_left && can_fall_right);
6200       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6201
6202 #if USE_NEW_ALL_SLIPPERY
6203       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6204       {
6205         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6206           can_fall_right = FALSE;
6207         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6208           can_fall_left = FALSE;
6209         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6210           can_fall_right = FALSE;
6211         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6212           can_fall_left = FALSE;
6213
6214         can_fall_any  = (can_fall_left || can_fall_right);
6215         can_fall_both = FALSE;
6216       }
6217 #else
6218       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6219       {
6220         if (slippery_type == SLIPPERY_ONLY_LEFT)
6221           can_fall_right = FALSE;
6222         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6223           can_fall_left = FALSE;
6224         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6225           can_fall_right = FALSE;
6226         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6227           can_fall_left = FALSE;
6228
6229         can_fall_any  = (can_fall_left || can_fall_right);
6230         can_fall_both = (can_fall_left && can_fall_right);
6231       }
6232 #endif
6233
6234 #if USE_NEW_ALL_SLIPPERY
6235 #else
6236 #if USE_NEW_SP_SLIPPERY
6237       /* !!! better use the same properties as for custom elements here !!! */
6238       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6239                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6240       {
6241         can_fall_right = FALSE;         /* slip down on left side */
6242         can_fall_both = FALSE;
6243       }
6244 #endif
6245 #endif
6246
6247 #if USE_NEW_ALL_SLIPPERY
6248       if (can_fall_both)
6249       {
6250         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6251           can_fall_right = FALSE;       /* slip down on left side */
6252         else
6253           can_fall_left = !(can_fall_right = RND(2));
6254
6255         can_fall_both = FALSE;
6256       }
6257 #else
6258       if (can_fall_both)
6259       {
6260         if (game.emulation == EMU_BOULDERDASH ||
6261             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6262           can_fall_right = FALSE;       /* slip down on left side */
6263         else
6264           can_fall_left = !(can_fall_right = RND(2));
6265
6266         can_fall_both = FALSE;
6267       }
6268 #endif
6269
6270       if (can_fall_any)
6271       {
6272         /* if not determined otherwise, prefer left side for slipping down */
6273         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6274         started_moving = TRUE;
6275       }
6276     }
6277 #if 0
6278     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6279 #else
6280     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6281 #endif
6282     {
6283       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6284       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6285       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6286       int belt_dir = game.belt_dir[belt_nr];
6287
6288       if ((belt_dir == MV_LEFT  && left_is_free) ||
6289           (belt_dir == MV_RIGHT && right_is_free))
6290       {
6291         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6292
6293         InitMovingField(x, y, belt_dir);
6294         started_moving = TRUE;
6295
6296         Pushed[x][y] = TRUE;
6297         Pushed[nextx][y] = TRUE;
6298
6299         GfxAction[x][y] = ACTION_DEFAULT;
6300       }
6301       else
6302       {
6303         MovDir[x][y] = 0;       /* if element was moving, stop it */
6304       }
6305     }
6306   }
6307
6308   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6309 #if 0
6310   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6311 #else
6312   if (CAN_MOVE(element) && !started_moving)
6313 #endif
6314   {
6315     int move_pattern = element_info[element].move_pattern;
6316     int newx, newy;
6317
6318 #if 0
6319 #if DEBUG
6320     if (MovDir[x][y] == MV_NONE)
6321     {
6322       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6323              x, y, element, element_info[element].token_name);
6324       printf("StartMoving(): This should never happen!\n");
6325     }
6326 #endif
6327 #endif
6328
6329     Moving2Blocked(x, y, &newx, &newy);
6330
6331     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6332       return;
6333
6334     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6335         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6336     {
6337       WasJustMoving[x][y] = 0;
6338       CheckCollision[x][y] = 0;
6339
6340       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6341
6342       if (Feld[x][y] != element)        /* element has changed */
6343         return;
6344     }
6345
6346     if (!MovDelay[x][y])        /* start new movement phase */
6347     {
6348       /* all objects that can change their move direction after each step
6349          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6350
6351       if (element != EL_YAMYAM &&
6352           element != EL_DARK_YAMYAM &&
6353           element != EL_PACMAN &&
6354           !(move_pattern & MV_ANY_DIRECTION) &&
6355           move_pattern != MV_TURNING_LEFT &&
6356           move_pattern != MV_TURNING_RIGHT &&
6357           move_pattern != MV_TURNING_LEFT_RIGHT &&
6358           move_pattern != MV_TURNING_RIGHT_LEFT &&
6359           move_pattern != MV_TURNING_RANDOM)
6360       {
6361         TurnRound(x, y);
6362
6363         if (MovDelay[x][y] && (element == EL_BUG ||
6364                                element == EL_SPACESHIP ||
6365                                element == EL_SP_SNIKSNAK ||
6366                                element == EL_SP_ELECTRON ||
6367                                element == EL_MOLE))
6368           DrawLevelField(x, y);
6369       }
6370     }
6371
6372     if (MovDelay[x][y])         /* wait some time before next movement */
6373     {
6374       MovDelay[x][y]--;
6375
6376       if (element == EL_ROBOT ||
6377           element == EL_YAMYAM ||
6378           element == EL_DARK_YAMYAM)
6379       {
6380         DrawLevelElementAnimationIfNeeded(x, y, element);
6381         PlayLevelSoundAction(x, y, ACTION_WAITING);
6382       }
6383       else if (element == EL_SP_ELECTRON)
6384         DrawLevelElementAnimationIfNeeded(x, y, element);
6385       else if (element == EL_DRAGON)
6386       {
6387         int i;
6388         int dir = MovDir[x][y];
6389         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6390         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6391         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6392                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6393                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6394                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6395         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6396
6397         GfxAction[x][y] = ACTION_ATTACKING;
6398
6399         if (IS_PLAYER(x, y))
6400           DrawPlayerField(x, y);
6401         else
6402           DrawLevelField(x, y);
6403
6404         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6405
6406         for (i = 1; i <= 3; i++)
6407         {
6408           int xx = x + i * dx;
6409           int yy = y + i * dy;
6410           int sx = SCREENX(xx);
6411           int sy = SCREENY(yy);
6412           int flame_graphic = graphic + (i - 1);
6413
6414           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6415             break;
6416
6417           if (MovDelay[x][y])
6418           {
6419             int flamed = MovingOrBlocked2Element(xx, yy);
6420
6421             /* !!! */
6422 #if 0
6423             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6424               Bang(xx, yy);
6425             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6426               RemoveMovingField(xx, yy);
6427             else
6428               RemoveField(xx, yy);
6429 #else
6430             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6431               Bang(xx, yy);
6432             else
6433               RemoveMovingField(xx, yy);
6434 #endif
6435
6436             ChangeDelay[xx][yy] = 0;
6437
6438             Feld[xx][yy] = EL_FLAMES;
6439
6440             if (IN_SCR_FIELD(sx, sy))
6441             {
6442               DrawLevelFieldCrumbledSand(xx, yy);
6443               DrawGraphic(sx, sy, flame_graphic, frame);
6444             }
6445           }
6446           else
6447           {
6448             if (Feld[xx][yy] == EL_FLAMES)
6449               Feld[xx][yy] = EL_EMPTY;
6450             DrawLevelField(xx, yy);
6451           }
6452         }
6453       }
6454
6455       if (MovDelay[x][y])       /* element still has to wait some time */
6456       {
6457         PlayLevelSoundAction(x, y, ACTION_WAITING);
6458
6459         return;
6460       }
6461     }
6462
6463     /* now make next step */
6464
6465     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6466
6467     if (DONT_COLLIDE_WITH(element) &&
6468         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6469         !PLAYER_ENEMY_PROTECTED(newx, newy))
6470     {
6471       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6472
6473       return;
6474     }
6475
6476     else if (CAN_MOVE_INTO_ACID(element) &&
6477              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6478              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6479              (MovDir[x][y] == MV_DOWN ||
6480               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6481     {
6482       SplashAcid(newx, newy);
6483       Store[x][y] = EL_ACID;
6484     }
6485     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6486     {
6487       if (Feld[newx][newy] == EL_EXIT_OPEN ||
6488           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6489           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6490           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6491       {
6492         RemoveField(x, y);
6493         DrawLevelField(x, y);
6494
6495         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6496         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6497           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6498
6499         local_player->friends_still_needed--;
6500         if (!local_player->friends_still_needed &&
6501             !local_player->GameOver && AllPlayersGone)
6502           PlayerWins(local_player);
6503
6504         return;
6505       }
6506       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6507       {
6508         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6509           DrawLevelField(newx, newy);
6510         else
6511           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6512       }
6513       else if (!IS_FREE(newx, newy))
6514       {
6515         GfxAction[x][y] = ACTION_WAITING;
6516
6517         if (IS_PLAYER(x, y))
6518           DrawPlayerField(x, y);
6519         else
6520           DrawLevelField(x, y);
6521
6522         return;
6523       }
6524     }
6525     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6526     {
6527       if (IS_FOOD_PIG(Feld[newx][newy]))
6528       {
6529         if (IS_MOVING(newx, newy))
6530           RemoveMovingField(newx, newy);
6531         else
6532         {
6533           Feld[newx][newy] = EL_EMPTY;
6534           DrawLevelField(newx, newy);
6535         }
6536
6537         PlayLevelSound(x, y, SND_PIG_DIGGING);
6538       }
6539       else if (!IS_FREE(newx, newy))
6540       {
6541         if (IS_PLAYER(x, y))
6542           DrawPlayerField(x, y);
6543         else
6544           DrawLevelField(x, y);
6545
6546         return;
6547       }
6548     }
6549     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6550     {
6551       if (Store[x][y] != EL_EMPTY)
6552       {
6553         boolean can_clone = FALSE;
6554         int xx, yy;
6555
6556         /* check if element to clone is still there */
6557         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6558         {
6559           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6560           {
6561             can_clone = TRUE;
6562
6563             break;
6564           }
6565         }
6566
6567         /* cannot clone or target field not free anymore -- do not clone */
6568         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6569           Store[x][y] = EL_EMPTY;
6570       }
6571
6572       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6573       {
6574         if (IS_MV_DIAGONAL(MovDir[x][y]))
6575         {
6576           int diagonal_move_dir = MovDir[x][y];
6577           int stored = Store[x][y];
6578           int change_delay = 8;
6579           int graphic;
6580
6581           /* android is moving diagonally */
6582
6583           CreateField(x, y, EL_DIAGONAL_SHRINKING);
6584
6585           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6586           GfxElement[x][y] = EL_EMC_ANDROID;
6587           GfxAction[x][y] = ACTION_SHRINKING;
6588           GfxDir[x][y] = diagonal_move_dir;
6589           ChangeDelay[x][y] = change_delay;
6590
6591           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6592                                    GfxDir[x][y]);
6593
6594           DrawLevelGraphicAnimation(x, y, graphic);
6595           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6596
6597           if (Feld[newx][newy] == EL_ACID)
6598           {
6599             SplashAcid(newx, newy);
6600
6601             return;
6602           }
6603
6604           CreateField(newx, newy, EL_DIAGONAL_GROWING);
6605
6606           Store[newx][newy] = EL_EMC_ANDROID;
6607           GfxElement[newx][newy] = EL_EMC_ANDROID;
6608           GfxAction[newx][newy] = ACTION_GROWING;
6609           GfxDir[newx][newy] = diagonal_move_dir;
6610           ChangeDelay[newx][newy] = change_delay;
6611
6612           graphic = el_act_dir2img(GfxElement[newx][newy],
6613                                    GfxAction[newx][newy], GfxDir[newx][newy]);
6614
6615           DrawLevelGraphicAnimation(newx, newy, graphic);
6616           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6617
6618           return;
6619         }
6620         else
6621         {
6622           Feld[newx][newy] = EL_EMPTY;
6623           DrawLevelField(newx, newy);
6624
6625           PlayLevelSoundAction(x, y, ACTION_DIGGING);
6626         }
6627       }
6628       else if (!IS_FREE(newx, newy))
6629       {
6630 #if 0
6631         if (IS_PLAYER(x, y))
6632           DrawPlayerField(x, y);
6633         else
6634           DrawLevelField(x, y);
6635 #endif
6636
6637         return;
6638       }
6639     }
6640     else if (IS_CUSTOM_ELEMENT(element) &&
6641              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6642     {
6643       int new_element = Feld[newx][newy];
6644
6645       if (!IS_FREE(newx, newy))
6646       {
6647         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6648                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6649                       ACTION_BREAKING);
6650
6651         /* no element can dig solid indestructible elements */
6652         if (IS_INDESTRUCTIBLE(new_element) &&
6653             !IS_DIGGABLE(new_element) &&
6654             !IS_COLLECTIBLE(new_element))
6655           return;
6656
6657         if (AmoebaNr[newx][newy] &&
6658             (new_element == EL_AMOEBA_FULL ||
6659              new_element == EL_BD_AMOEBA ||
6660              new_element == EL_AMOEBA_GROWING))
6661         {
6662           AmoebaCnt[AmoebaNr[newx][newy]]--;
6663           AmoebaCnt2[AmoebaNr[newx][newy]]--;
6664         }
6665
6666         if (IS_MOVING(newx, newy))
6667           RemoveMovingField(newx, newy);
6668         else
6669         {
6670           RemoveField(newx, newy);
6671           DrawLevelField(newx, newy);
6672         }
6673
6674         /* if digged element was about to explode, prevent the explosion */
6675         ExplodeField[newx][newy] = EX_TYPE_NONE;
6676
6677         PlayLevelSoundAction(x, y, action);
6678       }
6679
6680       Store[newx][newy] = EL_EMPTY;
6681 #if 1
6682       /* this makes it possible to leave the removed element again */
6683       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6684         Store[newx][newy] = new_element;
6685 #else
6686       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6687       {
6688         int move_leave_element = element_info[element].move_leave_element;
6689
6690         /* this makes it possible to leave the removed element again */
6691         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6692                              new_element : move_leave_element);
6693       }
6694 #endif
6695
6696       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6697       {
6698         RunnerVisit[x][y] = FrameCounter;
6699         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6700       }
6701     }
6702     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6703     {
6704       if (!IS_FREE(newx, newy))
6705       {
6706         if (IS_PLAYER(x, y))
6707           DrawPlayerField(x, y);
6708         else
6709           DrawLevelField(x, y);
6710
6711         return;
6712       }
6713       else
6714       {
6715         boolean wanna_flame = !RND(10);
6716         int dx = newx - x, dy = newy - y;
6717         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6718         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6719         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6720                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6721         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6722                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6723
6724         if ((wanna_flame ||
6725              IS_CLASSIC_ENEMY(element1) ||
6726              IS_CLASSIC_ENEMY(element2)) &&
6727             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6728             element1 != EL_FLAMES && element2 != EL_FLAMES)
6729         {
6730           ResetGfxAnimation(x, y);
6731           GfxAction[x][y] = ACTION_ATTACKING;
6732
6733           if (IS_PLAYER(x, y))
6734             DrawPlayerField(x, y);
6735           else
6736             DrawLevelField(x, y);
6737
6738           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6739
6740           MovDelay[x][y] = 50;
6741
6742           /* !!! */
6743 #if 0
6744           RemoveField(newx, newy);
6745 #endif
6746           Feld[newx][newy] = EL_FLAMES;
6747           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6748           {
6749 #if 0
6750             RemoveField(newx1, newy1);
6751 #endif
6752             Feld[newx1][newy1] = EL_FLAMES;
6753           }
6754           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6755           {
6756 #if 0
6757             RemoveField(newx2, newy2);
6758 #endif
6759             Feld[newx2][newy2] = EL_FLAMES;
6760           }
6761
6762           return;
6763         }
6764       }
6765     }
6766     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6767              Feld[newx][newy] == EL_DIAMOND)
6768     {
6769       if (IS_MOVING(newx, newy))
6770         RemoveMovingField(newx, newy);
6771       else
6772       {
6773         Feld[newx][newy] = EL_EMPTY;
6774         DrawLevelField(newx, newy);
6775       }
6776
6777       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6778     }
6779     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6780              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6781     {
6782       if (AmoebaNr[newx][newy])
6783       {
6784         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6785         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6786             Feld[newx][newy] == EL_BD_AMOEBA)
6787           AmoebaCnt[AmoebaNr[newx][newy]]--;
6788       }
6789
6790 #if 0
6791       /* !!! test !!! */
6792       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6793       {
6794         RemoveMovingField(newx, newy);
6795       }
6796 #else
6797       if (IS_MOVING(newx, newy))
6798       {
6799         RemoveMovingField(newx, newy);
6800       }
6801 #endif
6802       else
6803       {
6804         Feld[newx][newy] = EL_EMPTY;
6805         DrawLevelField(newx, newy);
6806       }
6807
6808       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6809     }
6810     else if ((element == EL_PACMAN || element == EL_MOLE)
6811              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6812     {
6813       if (AmoebaNr[newx][newy])
6814       {
6815         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6816         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6817             Feld[newx][newy] == EL_BD_AMOEBA)
6818           AmoebaCnt[AmoebaNr[newx][newy]]--;
6819       }
6820
6821       if (element == EL_MOLE)
6822       {
6823         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6824         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6825
6826         ResetGfxAnimation(x, y);
6827         GfxAction[x][y] = ACTION_DIGGING;
6828         DrawLevelField(x, y);
6829
6830         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6831
6832         return;                         /* wait for shrinking amoeba */
6833       }
6834       else      /* element == EL_PACMAN */
6835       {
6836         Feld[newx][newy] = EL_EMPTY;
6837         DrawLevelField(newx, newy);
6838         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6839       }
6840     }
6841     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6842              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6843               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6844     {
6845       /* wait for shrinking amoeba to completely disappear */
6846       return;
6847     }
6848     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6849     {
6850       /* object was running against a wall */
6851
6852       TurnRound(x, y);
6853
6854 #if 0
6855       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6856       if (move_pattern & MV_ANY_DIRECTION &&
6857           move_pattern == MovDir[x][y])
6858       {
6859         int blocking_element =
6860           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6861
6862         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6863                                  MovDir[x][y]);
6864
6865         element = Feld[x][y];   /* element might have changed */
6866       }
6867 #endif
6868
6869       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
6870         DrawLevelElementAnimation(x, y, element);
6871
6872       if (DONT_TOUCH(element))
6873         TestIfBadThingTouchesPlayer(x, y);
6874
6875       return;
6876     }
6877
6878     InitMovingField(x, y, MovDir[x][y]);
6879
6880     PlayLevelSoundAction(x, y, ACTION_MOVING);
6881   }
6882
6883   if (MovDir[x][y])
6884     ContinueMoving(x, y);
6885 }
6886
6887 void ContinueMoving(int x, int y)
6888 {
6889   int element = Feld[x][y];
6890   struct ElementInfo *ei = &element_info[element];
6891   int direction = MovDir[x][y];
6892   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6893   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
6894   int newx = x + dx, newy = y + dy;
6895   int stored = Store[x][y];
6896   int stored_new = Store[newx][newy];
6897   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
6898   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6899   boolean last_line = (newy == lev_fieldy - 1);
6900
6901   MovPos[x][y] += getElementMoveStepsize(x, y);
6902
6903   if (pushed_by_player) /* special case: moving object pushed by player */
6904     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6905
6906   if (ABS(MovPos[x][y]) < TILEX)
6907   {
6908     DrawLevelField(x, y);
6909
6910     return;     /* element is still moving */
6911   }
6912
6913   /* element reached destination field */
6914
6915   Feld[x][y] = EL_EMPTY;
6916   Feld[newx][newy] = element;
6917   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6918
6919   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6920   {
6921     element = Feld[newx][newy] = EL_ACID;
6922   }
6923   else if (element == EL_MOLE)
6924   {
6925     Feld[x][y] = EL_SAND;
6926
6927     DrawLevelFieldCrumbledSandNeighbours(x, y);
6928   }
6929   else if (element == EL_QUICKSAND_FILLING)
6930   {
6931     element = Feld[newx][newy] = get_next_element(element);
6932     Store[newx][newy] = Store[x][y];
6933   }
6934   else if (element == EL_QUICKSAND_EMPTYING)
6935   {
6936     Feld[x][y] = get_next_element(element);
6937     element = Feld[newx][newy] = Store[x][y];
6938   }
6939   else if (element == EL_QUICKSAND_FAST_FILLING)
6940   {
6941     element = Feld[newx][newy] = get_next_element(element);
6942     Store[newx][newy] = Store[x][y];
6943   }
6944   else if (element == EL_QUICKSAND_FAST_EMPTYING)
6945   {
6946     Feld[x][y] = get_next_element(element);
6947     element = Feld[newx][newy] = Store[x][y];
6948   }
6949   else if (element == EL_MAGIC_WALL_FILLING)
6950   {
6951     element = Feld[newx][newy] = get_next_element(element);
6952     if (!game.magic_wall_active)
6953       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6954     Store[newx][newy] = Store[x][y];
6955   }
6956   else if (element == EL_MAGIC_WALL_EMPTYING)
6957   {
6958     Feld[x][y] = get_next_element(element);
6959     if (!game.magic_wall_active)
6960       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6961     element = Feld[newx][newy] = Store[x][y];
6962
6963 #if USE_NEW_CUSTOM_VALUE
6964     InitField(newx, newy, FALSE);
6965 #endif
6966   }
6967   else if (element == EL_BD_MAGIC_WALL_FILLING)
6968   {
6969     element = Feld[newx][newy] = get_next_element(element);
6970     if (!game.magic_wall_active)
6971       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6972     Store[newx][newy] = Store[x][y];
6973   }
6974   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6975   {
6976     Feld[x][y] = get_next_element(element);
6977     if (!game.magic_wall_active)
6978       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6979     element = Feld[newx][newy] = Store[x][y];
6980
6981 #if USE_NEW_CUSTOM_VALUE
6982     InitField(newx, newy, FALSE);
6983 #endif
6984   }
6985   else if (element == EL_DC_MAGIC_WALL_FILLING)
6986   {
6987     element = Feld[newx][newy] = get_next_element(element);
6988     if (!game.magic_wall_active)
6989       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
6990     Store[newx][newy] = Store[x][y];
6991   }
6992   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
6993   {
6994     Feld[x][y] = get_next_element(element);
6995     if (!game.magic_wall_active)
6996       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
6997     element = Feld[newx][newy] = Store[x][y];
6998
6999 #if USE_NEW_CUSTOM_VALUE
7000     InitField(newx, newy, FALSE);
7001 #endif
7002   }
7003   else if (element == EL_AMOEBA_DROPPING)
7004   {
7005     Feld[x][y] = get_next_element(element);
7006     element = Feld[newx][newy] = Store[x][y];
7007   }
7008   else if (element == EL_SOKOBAN_OBJECT)
7009   {
7010     if (Back[x][y])
7011       Feld[x][y] = Back[x][y];
7012
7013     if (Back[newx][newy])
7014       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7015
7016     Back[x][y] = Back[newx][newy] = 0;
7017   }
7018
7019   Store[x][y] = EL_EMPTY;
7020   MovPos[x][y] = 0;
7021   MovDir[x][y] = 0;
7022   MovDelay[x][y] = 0;
7023
7024   MovDelay[newx][newy] = 0;
7025
7026   if (CAN_CHANGE_OR_HAS_ACTION(element))
7027   {
7028     /* copy element change control values to new field */
7029     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7030     ChangePage[newx][newy]  = ChangePage[x][y];
7031     ChangeCount[newx][newy] = ChangeCount[x][y];
7032     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7033   }
7034
7035 #if USE_NEW_CUSTOM_VALUE
7036     CustomValue[newx][newy] = CustomValue[x][y];
7037 #endif
7038
7039   ChangeDelay[x][y] = 0;
7040   ChangePage[x][y] = -1;
7041   ChangeCount[x][y] = 0;
7042   ChangeEvent[x][y] = -1;
7043
7044 #if USE_NEW_CUSTOM_VALUE
7045   CustomValue[x][y] = 0;
7046 #endif
7047
7048   /* copy animation control values to new field */
7049   GfxFrame[newx][newy]  = GfxFrame[x][y];
7050   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7051   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7052   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7053
7054   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7055
7056   /* some elements can leave other elements behind after moving */
7057 #if 1
7058   if (ei->move_leave_element != EL_EMPTY &&
7059       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7060       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7061 #else
7062   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7063       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7064       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7065 #endif
7066   {
7067     int move_leave_element = ei->move_leave_element;
7068
7069 #if 1
7070 #if 1
7071     /* this makes it possible to leave the removed element again */
7072     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7073       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7074 #else
7075     /* this makes it possible to leave the removed element again */
7076     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7077       move_leave_element = stored;
7078 #endif
7079 #else
7080     /* this makes it possible to leave the removed element again */
7081     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7082         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7083       move_leave_element = stored;
7084 #endif
7085
7086     Feld[x][y] = move_leave_element;
7087
7088     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7089       MovDir[x][y] = direction;
7090
7091     InitField(x, y, FALSE);
7092
7093     if (GFX_CRUMBLED(Feld[x][y]))
7094       DrawLevelFieldCrumbledSandNeighbours(x, y);
7095
7096     if (ELEM_IS_PLAYER(move_leave_element))
7097       RelocatePlayer(x, y, move_leave_element);
7098   }
7099
7100   /* do this after checking for left-behind element */
7101   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7102
7103   if (!CAN_MOVE(element) ||
7104       (CAN_FALL(element) && direction == MV_DOWN &&
7105        (element == EL_SPRING ||
7106         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7107         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7108     GfxDir[x][y] = MovDir[newx][newy] = 0;
7109
7110   DrawLevelField(x, y);
7111   DrawLevelField(newx, newy);
7112
7113   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7114
7115   /* prevent pushed element from moving on in pushed direction */
7116   if (pushed_by_player && CAN_MOVE(element) &&
7117       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7118       !(element_info[element].move_pattern & direction))
7119     TurnRound(newx, newy);
7120
7121   /* prevent elements on conveyor belt from moving on in last direction */
7122   if (pushed_by_conveyor && CAN_FALL(element) &&
7123       direction & MV_HORIZONTAL)
7124     MovDir[newx][newy] = 0;
7125
7126   if (!pushed_by_player)
7127   {
7128     int nextx = newx + dx, nexty = newy + dy;
7129     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7130
7131     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7132
7133     if (CAN_FALL(element) && direction == MV_DOWN)
7134       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7135
7136     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7137       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7138
7139 #if USE_FIX_IMPACT_COLLISION
7140     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7141       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7142 #endif
7143   }
7144
7145   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7146   {
7147     TestIfBadThingTouchesPlayer(newx, newy);
7148     TestIfBadThingTouchesFriend(newx, newy);
7149
7150     if (!IS_CUSTOM_ELEMENT(element))
7151       TestIfBadThingTouchesOtherBadThing(newx, newy);
7152   }
7153   else if (element == EL_PENGUIN)
7154     TestIfFriendTouchesBadThing(newx, newy);
7155
7156   /* give the player one last chance (one more frame) to move away */
7157   if (CAN_FALL(element) && direction == MV_DOWN &&
7158       (last_line || (!IS_FREE(x, newy + 1) &&
7159                      (!IS_PLAYER(x, newy + 1) ||
7160                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7161     Impact(x, newy);
7162
7163   if (pushed_by_player && !game.use_change_when_pushing_bug)
7164   {
7165     int push_side = MV_DIR_OPPOSITE(direction);
7166     struct PlayerInfo *player = PLAYERINFO(x, y);
7167
7168     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7169                                player->index_bit, push_side);
7170     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7171                                         player->index_bit, push_side);
7172   }
7173
7174   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7175     MovDelay[newx][newy] = 1;
7176
7177   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7178
7179   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7180
7181 #if 0
7182   if (ChangePage[newx][newy] != -1)             /* delayed change */
7183   {
7184     int page = ChangePage[newx][newy];
7185     struct ElementChangeInfo *change = &ei->change_page[page];
7186
7187     ChangePage[newx][newy] = -1;
7188
7189     if (change->can_change)
7190     {
7191       if (ChangeElement(newx, newy, element, page))
7192       {
7193         if (change->post_change_function)
7194           change->post_change_function(newx, newy);
7195       }
7196     }
7197
7198     if (change->has_action)
7199       ExecuteCustomElementAction(newx, newy, element, page);
7200   }
7201 #endif
7202
7203   TestIfElementHitsCustomElement(newx, newy, direction);
7204   TestIfPlayerTouchesCustomElement(newx, newy);
7205   TestIfElementTouchesCustomElement(newx, newy);
7206
7207   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7208       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7209     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7210                              MV_DIR_OPPOSITE(direction));
7211 }
7212
7213 int AmoebeNachbarNr(int ax, int ay)
7214 {
7215   int i;
7216   int element = Feld[ax][ay];
7217   int group_nr = 0;
7218   static int xy[4][2] =
7219   {
7220     { 0, -1 },
7221     { -1, 0 },
7222     { +1, 0 },
7223     { 0, +1 }
7224   };
7225
7226   for (i = 0; i < NUM_DIRECTIONS; i++)
7227   {
7228     int x = ax + xy[i][0];
7229     int y = ay + xy[i][1];
7230
7231     if (!IN_LEV_FIELD(x, y))
7232       continue;
7233
7234     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7235       group_nr = AmoebaNr[x][y];
7236   }
7237
7238   return group_nr;
7239 }
7240
7241 void AmoebenVereinigen(int ax, int ay)
7242 {
7243   int i, x, y, xx, yy;
7244   int new_group_nr = AmoebaNr[ax][ay];
7245   static int xy[4][2] =
7246   {
7247     { 0, -1 },
7248     { -1, 0 },
7249     { +1, 0 },
7250     { 0, +1 }
7251   };
7252
7253   if (new_group_nr == 0)
7254     return;
7255
7256   for (i = 0; i < NUM_DIRECTIONS; i++)
7257   {
7258     x = ax + xy[i][0];
7259     y = ay + xy[i][1];
7260
7261     if (!IN_LEV_FIELD(x, y))
7262       continue;
7263
7264     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7265          Feld[x][y] == EL_BD_AMOEBA ||
7266          Feld[x][y] == EL_AMOEBA_DEAD) &&
7267         AmoebaNr[x][y] != new_group_nr)
7268     {
7269       int old_group_nr = AmoebaNr[x][y];
7270
7271       if (old_group_nr == 0)
7272         return;
7273
7274       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7275       AmoebaCnt[old_group_nr] = 0;
7276       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7277       AmoebaCnt2[old_group_nr] = 0;
7278
7279       SCAN_PLAYFIELD(xx, yy)
7280       {
7281         if (AmoebaNr[xx][yy] == old_group_nr)
7282           AmoebaNr[xx][yy] = new_group_nr;
7283       }
7284     }
7285   }
7286 }
7287
7288 void AmoebeUmwandeln(int ax, int ay)
7289 {
7290   int i, x, y;
7291
7292   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7293   {
7294     int group_nr = AmoebaNr[ax][ay];
7295
7296 #ifdef DEBUG
7297     if (group_nr == 0)
7298     {
7299       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7300       printf("AmoebeUmwandeln(): This should never happen!\n");
7301       return;
7302     }
7303 #endif
7304
7305     SCAN_PLAYFIELD(x, y)
7306     {
7307       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7308       {
7309         AmoebaNr[x][y] = 0;
7310         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7311       }
7312     }
7313
7314     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7315                             SND_AMOEBA_TURNING_TO_GEM :
7316                             SND_AMOEBA_TURNING_TO_ROCK));
7317     Bang(ax, ay);
7318   }
7319   else
7320   {
7321     static int xy[4][2] =
7322     {
7323       { 0, -1 },
7324       { -1, 0 },
7325       { +1, 0 },
7326       { 0, +1 }
7327     };
7328
7329     for (i = 0; i < NUM_DIRECTIONS; i++)
7330     {
7331       x = ax + xy[i][0];
7332       y = ay + xy[i][1];
7333
7334       if (!IN_LEV_FIELD(x, y))
7335         continue;
7336
7337       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7338       {
7339         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7340                               SND_AMOEBA_TURNING_TO_GEM :
7341                               SND_AMOEBA_TURNING_TO_ROCK));
7342         Bang(x, y);
7343       }
7344     }
7345   }
7346 }
7347
7348 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7349 {
7350   int x, y;
7351   int group_nr = AmoebaNr[ax][ay];
7352   boolean done = FALSE;
7353
7354 #ifdef DEBUG
7355   if (group_nr == 0)
7356   {
7357     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7358     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7359     return;
7360   }
7361 #endif
7362
7363   SCAN_PLAYFIELD(x, y)
7364   {
7365     if (AmoebaNr[x][y] == group_nr &&
7366         (Feld[x][y] == EL_AMOEBA_DEAD ||
7367          Feld[x][y] == EL_BD_AMOEBA ||
7368          Feld[x][y] == EL_AMOEBA_GROWING))
7369     {
7370       AmoebaNr[x][y] = 0;
7371       Feld[x][y] = new_element;
7372       InitField(x, y, FALSE);
7373       DrawLevelField(x, y);
7374       done = TRUE;
7375     }
7376   }
7377
7378   if (done)
7379     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7380                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7381                             SND_BD_AMOEBA_TURNING_TO_GEM));
7382 }
7383
7384 void AmoebeWaechst(int x, int y)
7385 {
7386   static unsigned long sound_delay = 0;
7387   static unsigned long sound_delay_value = 0;
7388
7389   if (!MovDelay[x][y])          /* start new growing cycle */
7390   {
7391     MovDelay[x][y] = 7;
7392
7393     if (DelayReached(&sound_delay, sound_delay_value))
7394     {
7395       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7396       sound_delay_value = 30;
7397     }
7398   }
7399
7400   if (MovDelay[x][y])           /* wait some time before growing bigger */
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_GROWING,
7406                                            6 - MovDelay[x][y]);
7407
7408       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7409     }
7410
7411     if (!MovDelay[x][y])
7412     {
7413       Feld[x][y] = Store[x][y];
7414       Store[x][y] = 0;
7415       DrawLevelField(x, y);
7416     }
7417   }
7418 }
7419
7420 void AmoebaDisappearing(int x, int y)
7421 {
7422   static unsigned long sound_delay = 0;
7423   static unsigned long sound_delay_value = 0;
7424
7425   if (!MovDelay[x][y])          /* start new shrinking cycle */
7426   {
7427     MovDelay[x][y] = 7;
7428
7429     if (DelayReached(&sound_delay, sound_delay_value))
7430       sound_delay_value = 30;
7431   }
7432
7433   if (MovDelay[x][y])           /* wait some time before shrinking */
7434   {
7435     MovDelay[x][y]--;
7436     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7437     {
7438       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7439                                            6 - MovDelay[x][y]);
7440
7441       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7442     }
7443
7444     if (!MovDelay[x][y])
7445     {
7446       Feld[x][y] = EL_EMPTY;
7447       DrawLevelField(x, y);
7448
7449       /* don't let mole enter this field in this cycle;
7450          (give priority to objects falling to this field from above) */
7451       Stop[x][y] = TRUE;
7452     }
7453   }
7454 }
7455
7456 void AmoebeAbleger(int ax, int ay)
7457 {
7458   int i;
7459   int element = Feld[ax][ay];
7460   int graphic = el2img(element);
7461   int newax = ax, neway = ay;
7462   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7463   static int xy[4][2] =
7464   {
7465     { 0, -1 },
7466     { -1, 0 },
7467     { +1, 0 },
7468     { 0, +1 }
7469   };
7470
7471   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7472   {
7473     Feld[ax][ay] = EL_AMOEBA_DEAD;
7474     DrawLevelField(ax, ay);
7475     return;
7476   }
7477
7478   if (IS_ANIMATED(graphic))
7479     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7480
7481   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7482     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7483
7484   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7485   {
7486     MovDelay[ax][ay]--;
7487     if (MovDelay[ax][ay])
7488       return;
7489   }
7490
7491   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7492   {
7493     int start = RND(4);
7494     int x = ax + xy[start][0];
7495     int y = ay + xy[start][1];
7496
7497     if (!IN_LEV_FIELD(x, y))
7498       return;
7499
7500     if (IS_FREE(x, y) ||
7501         CAN_GROW_INTO(Feld[x][y]) ||
7502         Feld[x][y] == EL_QUICKSAND_EMPTY ||
7503         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7504     {
7505       newax = x;
7506       neway = y;
7507     }
7508
7509     if (newax == ax && neway == ay)
7510       return;
7511   }
7512   else                          /* normal or "filled" (BD style) amoeba */
7513   {
7514     int start = RND(4);
7515     boolean waiting_for_player = FALSE;
7516
7517     for (i = 0; i < NUM_DIRECTIONS; i++)
7518     {
7519       int j = (start + i) % 4;
7520       int x = ax + xy[j][0];
7521       int y = ay + xy[j][1];
7522
7523       if (!IN_LEV_FIELD(x, y))
7524         continue;
7525
7526       if (IS_FREE(x, y) ||
7527           CAN_GROW_INTO(Feld[x][y]) ||
7528           Feld[x][y] == EL_QUICKSAND_EMPTY ||
7529           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7530       {
7531         newax = x;
7532         neway = y;
7533         break;
7534       }
7535       else if (IS_PLAYER(x, y))
7536         waiting_for_player = TRUE;
7537     }
7538
7539     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7540     {
7541       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7542       {
7543         Feld[ax][ay] = EL_AMOEBA_DEAD;
7544         DrawLevelField(ax, ay);
7545         AmoebaCnt[AmoebaNr[ax][ay]]--;
7546
7547         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7548         {
7549           if (element == EL_AMOEBA_FULL)
7550             AmoebeUmwandeln(ax, ay);
7551           else if (element == EL_BD_AMOEBA)
7552             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7553         }
7554       }
7555       return;
7556     }
7557     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7558     {
7559       /* amoeba gets larger by growing in some direction */
7560
7561       int new_group_nr = AmoebaNr[ax][ay];
7562
7563 #ifdef DEBUG
7564   if (new_group_nr == 0)
7565   {
7566     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7567     printf("AmoebeAbleger(): This should never happen!\n");
7568     return;
7569   }
7570 #endif
7571
7572       AmoebaNr[newax][neway] = new_group_nr;
7573       AmoebaCnt[new_group_nr]++;
7574       AmoebaCnt2[new_group_nr]++;
7575
7576       /* if amoeba touches other amoeba(s) after growing, unify them */
7577       AmoebenVereinigen(newax, neway);
7578
7579       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7580       {
7581         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7582         return;
7583       }
7584     }
7585   }
7586
7587   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7588       (neway == lev_fieldy - 1 && newax != ax))
7589   {
7590     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7591     Store[newax][neway] = element;
7592   }
7593   else if (neway == ay || element == EL_EMC_DRIPPER)
7594   {
7595     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7596
7597     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7598   }
7599   else
7600   {
7601     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7602     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7603     Store[ax][ay] = EL_AMOEBA_DROP;
7604     ContinueMoving(ax, ay);
7605     return;
7606   }
7607
7608   DrawLevelField(newax, neway);
7609 }
7610
7611 void Life(int ax, int ay)
7612 {
7613   int x1, y1, x2, y2;
7614   int life_time = 40;
7615   int element = Feld[ax][ay];
7616   int graphic = el2img(element);
7617   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7618                          level.biomaze);
7619   boolean changed = FALSE;
7620
7621   if (IS_ANIMATED(graphic))
7622     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7623
7624   if (Stop[ax][ay])
7625     return;
7626
7627   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7628     MovDelay[ax][ay] = life_time;
7629
7630   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7631   {
7632     MovDelay[ax][ay]--;
7633     if (MovDelay[ax][ay])
7634       return;
7635   }
7636
7637   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7638   {
7639     int xx = ax+x1, yy = ay+y1;
7640     int nachbarn = 0;
7641
7642     if (!IN_LEV_FIELD(xx, yy))
7643       continue;
7644
7645     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7646     {
7647       int x = xx+x2, y = yy+y2;
7648
7649       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7650         continue;
7651
7652       if (((Feld[x][y] == element ||
7653             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7654            !Stop[x][y]) ||
7655           (IS_FREE(x, y) && Stop[x][y]))
7656         nachbarn++;
7657     }
7658
7659     if (xx == ax && yy == ay)           /* field in the middle */
7660     {
7661       if (nachbarn < life_parameter[0] ||
7662           nachbarn > life_parameter[1])
7663       {
7664         Feld[xx][yy] = EL_EMPTY;
7665         if (!Stop[xx][yy])
7666           DrawLevelField(xx, yy);
7667         Stop[xx][yy] = TRUE;
7668         changed = TRUE;
7669       }
7670     }
7671     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7672     {                                   /* free border field */
7673       if (nachbarn >= life_parameter[2] &&
7674           nachbarn <= life_parameter[3])
7675       {
7676         Feld[xx][yy] = element;
7677         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7678         if (!Stop[xx][yy])
7679           DrawLevelField(xx, yy);
7680         Stop[xx][yy] = TRUE;
7681         changed = TRUE;
7682       }
7683     }
7684   }
7685
7686   if (changed)
7687     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7688                    SND_GAME_OF_LIFE_GROWING);
7689 }
7690
7691 static void InitRobotWheel(int x, int y)
7692 {
7693   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7694 }
7695
7696 static void RunRobotWheel(int x, int y)
7697 {
7698   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7699 }
7700
7701 static void StopRobotWheel(int x, int y)
7702 {
7703   if (ZX == x && ZY == y)
7704     ZX = ZY = -1;
7705 }
7706
7707 static void InitTimegateWheel(int x, int y)
7708 {
7709   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7710 }
7711
7712 static void RunTimegateWheel(int x, int y)
7713 {
7714   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7715 }
7716
7717 static void InitMagicBallDelay(int x, int y)
7718 {
7719 #if 1
7720   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7721 #else
7722   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7723 #endif
7724 }
7725
7726 static void ActivateMagicBall(int bx, int by)
7727 {
7728   int x, y;
7729
7730   if (level.ball_random)
7731   {
7732     int pos_border = RND(8);    /* select one of the eight border elements */
7733     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7734     int xx = pos_content % 3;
7735     int yy = pos_content / 3;
7736
7737     x = bx - 1 + xx;
7738     y = by - 1 + yy;
7739
7740     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7741       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7742   }
7743   else
7744   {
7745     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7746     {
7747       int xx = x - bx + 1;
7748       int yy = y - by + 1;
7749
7750       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7751         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7752     }
7753   }
7754
7755   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7756 }
7757
7758 void CheckExit(int x, int y)
7759 {
7760   if (local_player->gems_still_needed > 0 ||
7761       local_player->sokobanfields_still_needed > 0 ||
7762       local_player->lights_still_needed > 0)
7763   {
7764     int element = Feld[x][y];
7765     int graphic = el2img(element);
7766
7767     if (IS_ANIMATED(graphic))
7768       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7769
7770     return;
7771   }
7772
7773   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7774     return;
7775
7776   Feld[x][y] = EL_EXIT_OPENING;
7777
7778   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7779 }
7780
7781 void CheckExitEM(int x, int y)
7782 {
7783   if (local_player->gems_still_needed > 0 ||
7784       local_player->sokobanfields_still_needed > 0 ||
7785       local_player->lights_still_needed > 0)
7786   {
7787     int element = Feld[x][y];
7788     int graphic = el2img(element);
7789
7790     if (IS_ANIMATED(graphic))
7791       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7792
7793     return;
7794   }
7795
7796   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7797     return;
7798
7799   Feld[x][y] = EL_EM_EXIT_OPENING;
7800
7801   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7802 }
7803
7804 void CheckExitSteel(int x, int y)
7805 {
7806   if (local_player->gems_still_needed > 0 ||
7807       local_player->sokobanfields_still_needed > 0 ||
7808       local_player->lights_still_needed > 0)
7809   {
7810     int element = Feld[x][y];
7811     int graphic = el2img(element);
7812
7813     if (IS_ANIMATED(graphic))
7814       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7815
7816     return;
7817   }
7818
7819   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7820     return;
7821
7822   Feld[x][y] = EL_STEEL_EXIT_OPENING;
7823
7824   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7825 }
7826
7827 void CheckExitSteelEM(int x, int y)
7828 {
7829   if (local_player->gems_still_needed > 0 ||
7830       local_player->sokobanfields_still_needed > 0 ||
7831       local_player->lights_still_needed > 0)
7832   {
7833     int element = Feld[x][y];
7834     int graphic = el2img(element);
7835
7836     if (IS_ANIMATED(graphic))
7837       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7838
7839     return;
7840   }
7841
7842   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7843     return;
7844
7845   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7846
7847   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7848 }
7849
7850 void CheckExitSP(int x, int y)
7851 {
7852   if (local_player->gems_still_needed > 0)
7853   {
7854     int element = Feld[x][y];
7855     int graphic = el2img(element);
7856
7857     if (IS_ANIMATED(graphic))
7858       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7859
7860     return;
7861   }
7862
7863   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7864     return;
7865
7866   Feld[x][y] = EL_SP_EXIT_OPENING;
7867
7868   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7869 }
7870
7871 static void CloseAllOpenTimegates()
7872 {
7873   int x, y;
7874
7875   SCAN_PLAYFIELD(x, y)
7876   {
7877     int element = Feld[x][y];
7878
7879     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7880     {
7881       Feld[x][y] = EL_TIMEGATE_CLOSING;
7882
7883       PlayLevelSoundAction(x, y, ACTION_CLOSING);
7884     }
7885   }
7886 }
7887
7888 void DrawTwinkleOnField(int x, int y)
7889 {
7890   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7891     return;
7892
7893   if (Feld[x][y] == EL_BD_DIAMOND)
7894     return;
7895
7896   if (MovDelay[x][y] == 0)      /* next animation frame */
7897     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7898
7899   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7900   {
7901     MovDelay[x][y]--;
7902
7903     if (setup.direct_draw && MovDelay[x][y])
7904       SetDrawtoField(DRAW_BUFFERED);
7905
7906     DrawLevelElementAnimation(x, y, Feld[x][y]);
7907
7908     if (MovDelay[x][y] != 0)
7909     {
7910       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7911                                            10 - MovDelay[x][y]);
7912
7913       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7914
7915       if (setup.direct_draw)
7916       {
7917         int dest_x, dest_y;
7918
7919         dest_x = FX + SCREENX(x) * TILEX;
7920         dest_y = FY + SCREENY(y) * TILEY;
7921
7922         BlitBitmap(drawto_field, window,
7923                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7924         SetDrawtoField(DRAW_DIRECT);
7925       }
7926     }
7927   }
7928 }
7929
7930 void MauerWaechst(int x, int y)
7931 {
7932   int delay = 6;
7933
7934   if (!MovDelay[x][y])          /* next animation frame */
7935     MovDelay[x][y] = 3 * delay;
7936
7937   if (MovDelay[x][y])           /* wait some time before next frame */
7938   {
7939     MovDelay[x][y]--;
7940
7941     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7942     {
7943       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7944       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7945
7946       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7947     }
7948
7949     if (!MovDelay[x][y])
7950     {
7951       if (MovDir[x][y] == MV_LEFT)
7952       {
7953         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7954           DrawLevelField(x - 1, y);
7955       }
7956       else if (MovDir[x][y] == MV_RIGHT)
7957       {
7958         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7959           DrawLevelField(x + 1, y);
7960       }
7961       else if (MovDir[x][y] == MV_UP)
7962       {
7963         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7964           DrawLevelField(x, y - 1);
7965       }
7966       else
7967       {
7968         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7969           DrawLevelField(x, y + 1);
7970       }
7971
7972       Feld[x][y] = Store[x][y];
7973       Store[x][y] = 0;
7974       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7975       DrawLevelField(x, y);
7976     }
7977   }
7978 }
7979
7980 void MauerAbleger(int ax, int ay)
7981 {
7982   int element = Feld[ax][ay];
7983   int graphic = el2img(element);
7984   boolean oben_frei = FALSE, unten_frei = FALSE;
7985   boolean links_frei = FALSE, rechts_frei = FALSE;
7986   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7987   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7988   boolean new_wall = FALSE;
7989
7990   if (IS_ANIMATED(graphic))
7991     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7992
7993   if (!MovDelay[ax][ay])        /* start building new wall */
7994     MovDelay[ax][ay] = 6;
7995
7996   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7997   {
7998     MovDelay[ax][ay]--;
7999     if (MovDelay[ax][ay])
8000       return;
8001   }
8002
8003   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8004     oben_frei = TRUE;
8005   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8006     unten_frei = TRUE;
8007   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8008     links_frei = TRUE;
8009   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8010     rechts_frei = TRUE;
8011
8012   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8013       element == EL_EXPANDABLE_WALL_ANY)
8014   {
8015     if (oben_frei)
8016     {
8017       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8018       Store[ax][ay-1] = element;
8019       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8020       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8021         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8022                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8023       new_wall = TRUE;
8024     }
8025     if (unten_frei)
8026     {
8027       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8028       Store[ax][ay+1] = element;
8029       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8030       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8031         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8032                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8033       new_wall = TRUE;
8034     }
8035   }
8036
8037   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8038       element == EL_EXPANDABLE_WALL_ANY ||
8039       element == EL_EXPANDABLE_WALL ||
8040       element == EL_BD_EXPANDABLE_WALL)
8041   {
8042     if (links_frei)
8043     {
8044       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8045       Store[ax-1][ay] = element;
8046       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8047       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8048         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8049                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8050       new_wall = TRUE;
8051     }
8052
8053     if (rechts_frei)
8054     {
8055       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8056       Store[ax+1][ay] = element;
8057       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8058       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8059         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8060                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8061       new_wall = TRUE;
8062     }
8063   }
8064
8065   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8066     DrawLevelField(ax, ay);
8067
8068   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8069     oben_massiv = TRUE;
8070   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8071     unten_massiv = TRUE;
8072   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8073     links_massiv = TRUE;
8074   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8075     rechts_massiv = TRUE;
8076
8077   if (((oben_massiv && unten_massiv) ||
8078        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8079        element == EL_EXPANDABLE_WALL) &&
8080       ((links_massiv && rechts_massiv) ||
8081        element == EL_EXPANDABLE_WALL_VERTICAL))
8082     Feld[ax][ay] = EL_WALL;
8083
8084   if (new_wall)
8085     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8086 }
8087
8088 void MauerAblegerStahl(int ax, int ay)
8089 {
8090   int element = Feld[ax][ay];
8091   int graphic = el2img(element);
8092   boolean oben_frei = FALSE, unten_frei = FALSE;
8093   boolean links_frei = FALSE, rechts_frei = FALSE;
8094   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8095   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8096   boolean new_wall = FALSE;
8097
8098   if (IS_ANIMATED(graphic))
8099     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8100
8101   if (!MovDelay[ax][ay])        /* start building new wall */
8102     MovDelay[ax][ay] = 6;
8103
8104   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8105   {
8106     MovDelay[ax][ay]--;
8107     if (MovDelay[ax][ay])
8108       return;
8109   }
8110
8111   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8112     oben_frei = TRUE;
8113   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8114     unten_frei = TRUE;
8115   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8116     links_frei = TRUE;
8117   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8118     rechts_frei = TRUE;
8119
8120   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8121       element == EL_EXPANDABLE_STEELWALL_ANY)
8122   {
8123     if (oben_frei)
8124     {
8125       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8126       Store[ax][ay-1] = element;
8127       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8128       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8129         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8130                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8131       new_wall = TRUE;
8132     }
8133     if (unten_frei)
8134     {
8135       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8136       Store[ax][ay+1] = element;
8137       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8138       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8139         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8140                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8141       new_wall = TRUE;
8142     }
8143   }
8144
8145   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8146       element == EL_EXPANDABLE_STEELWALL_ANY)
8147   {
8148     if (links_frei)
8149     {
8150       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8151       Store[ax-1][ay] = element;
8152       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8153       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8154         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8155                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8156       new_wall = TRUE;
8157     }
8158
8159     if (rechts_frei)
8160     {
8161       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8162       Store[ax+1][ay] = element;
8163       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8164       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8165         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8166                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8167       new_wall = TRUE;
8168     }
8169   }
8170
8171   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8172     oben_massiv = TRUE;
8173   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8174     unten_massiv = TRUE;
8175   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8176     links_massiv = TRUE;
8177   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8178     rechts_massiv = TRUE;
8179
8180   if (((oben_massiv && unten_massiv) ||
8181        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8182       ((links_massiv && rechts_massiv) ||
8183        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8184     Feld[ax][ay] = EL_WALL;
8185
8186   if (new_wall)
8187     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8188 }
8189
8190 void CheckForDragon(int x, int y)
8191 {
8192   int i, j;
8193   boolean dragon_found = FALSE;
8194   static int xy[4][2] =
8195   {
8196     { 0, -1 },
8197     { -1, 0 },
8198     { +1, 0 },
8199     { 0, +1 }
8200   };
8201
8202   for (i = 0; i < NUM_DIRECTIONS; i++)
8203   {
8204     for (j = 0; j < 4; j++)
8205     {
8206       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8207
8208       if (IN_LEV_FIELD(xx, yy) &&
8209           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8210       {
8211         if (Feld[xx][yy] == EL_DRAGON)
8212           dragon_found = TRUE;
8213       }
8214       else
8215         break;
8216     }
8217   }
8218
8219   if (!dragon_found)
8220   {
8221     for (i = 0; i < NUM_DIRECTIONS; i++)
8222     {
8223       for (j = 0; j < 3; j++)
8224       {
8225         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8226   
8227         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8228         {
8229           Feld[xx][yy] = EL_EMPTY;
8230           DrawLevelField(xx, yy);
8231         }
8232         else
8233           break;
8234       }
8235     }
8236   }
8237 }
8238
8239 static void InitBuggyBase(int x, int y)
8240 {
8241   int element = Feld[x][y];
8242   int activating_delay = FRAMES_PER_SECOND / 4;
8243
8244   ChangeDelay[x][y] =
8245     (element == EL_SP_BUGGY_BASE ?
8246      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8247      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8248      activating_delay :
8249      element == EL_SP_BUGGY_BASE_ACTIVE ?
8250      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8251 }
8252
8253 static void WarnBuggyBase(int x, int y)
8254 {
8255   int i;
8256   static int xy[4][2] =
8257   {
8258     { 0, -1 },
8259     { -1, 0 },
8260     { +1, 0 },
8261     { 0, +1 }
8262   };
8263
8264   for (i = 0; i < NUM_DIRECTIONS; i++)
8265   {
8266     int xx = x + xy[i][0];
8267     int yy = y + xy[i][1];
8268
8269     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8270     {
8271       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8272
8273       break;
8274     }
8275   }
8276 }
8277
8278 static void InitTrap(int x, int y)
8279 {
8280   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8281 }
8282
8283 static void ActivateTrap(int x, int y)
8284 {
8285   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8286 }
8287
8288 static void ChangeActiveTrap(int x, int y)
8289 {
8290   int graphic = IMG_TRAP_ACTIVE;
8291
8292   /* if new animation frame was drawn, correct crumbled sand border */
8293   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8294     DrawLevelFieldCrumbledSand(x, y);
8295 }
8296
8297 static int getSpecialActionElement(int element, int number, int base_element)
8298 {
8299   return (element != EL_EMPTY ? element :
8300           number != -1 ? base_element + number - 1 :
8301           EL_EMPTY);
8302 }
8303
8304 static int getModifiedActionNumber(int value_old, int operator, int operand,
8305                                    int value_min, int value_max)
8306 {
8307   int value_new = (operator == CA_MODE_SET      ? operand :
8308                    operator == CA_MODE_ADD      ? value_old + operand :
8309                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8310                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8311                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8312                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8313                    value_old);
8314
8315   return (value_new < value_min ? value_min :
8316           value_new > value_max ? value_max :
8317           value_new);
8318 }
8319
8320 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8321 {
8322   struct ElementInfo *ei = &element_info[element];
8323   struct ElementChangeInfo *change = &ei->change_page[page];
8324   int target_element = change->target_element;
8325   int action_type = change->action_type;
8326   int action_mode = change->action_mode;
8327   int action_arg = change->action_arg;
8328   int i;
8329
8330   if (!change->has_action)
8331     return;
8332
8333   /* ---------- determine action paramater values -------------------------- */
8334
8335   int level_time_value =
8336     (level.time > 0 ? TimeLeft :
8337      TimePlayed);
8338
8339   int action_arg_element =
8340     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8341      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8342      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8343      EL_EMPTY);
8344
8345   int action_arg_direction =
8346     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8347      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8348      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8349      change->actual_trigger_side :
8350      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8351      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8352      MV_NONE);
8353
8354   int action_arg_number_min =
8355     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8356      CA_ARG_MIN);
8357
8358   int action_arg_number_max =
8359     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8360      action_type == CA_SET_LEVEL_GEMS ? 999 :
8361      action_type == CA_SET_LEVEL_TIME ? 9999 :
8362      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8363      action_type == CA_SET_CE_VALUE ? 9999 :
8364      action_type == CA_SET_CE_SCORE ? 9999 :
8365      CA_ARG_MAX);
8366
8367   int action_arg_number_reset =
8368     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8369      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8370      action_type == CA_SET_LEVEL_TIME ? level.time :
8371      action_type == CA_SET_LEVEL_SCORE ? 0 :
8372 #if USE_NEW_CUSTOM_VALUE
8373      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8374 #else
8375      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8376 #endif
8377      action_type == CA_SET_CE_SCORE ? 0 :
8378      0);
8379
8380   int action_arg_number =
8381     (action_arg <= CA_ARG_MAX ? action_arg :
8382      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8383      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8384      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8385      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8386      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8387      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8388 #if USE_NEW_CUSTOM_VALUE
8389      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8390 #else
8391      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8392 #endif
8393      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8394      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8395      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8396      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8397      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8398      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8399      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8400      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8401      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8402      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8403      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8404      -1);
8405
8406   int action_arg_number_old =
8407     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8408      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8409      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8410      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8411      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8412      0);
8413
8414   int action_arg_number_new =
8415     getModifiedActionNumber(action_arg_number_old,
8416                             action_mode, action_arg_number,
8417                             action_arg_number_min, action_arg_number_max);
8418
8419   int trigger_player_bits =
8420     (change->actual_trigger_player >= EL_PLAYER_1 &&
8421      change->actual_trigger_player <= EL_PLAYER_4 ?
8422      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8423      PLAYER_BITS_ANY);
8424
8425   int action_arg_player_bits =
8426     (action_arg >= CA_ARG_PLAYER_1 &&
8427      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8428      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8429      PLAYER_BITS_ANY);
8430
8431   /* ---------- execute action  -------------------------------------------- */
8432
8433   switch (action_type)
8434   {
8435     case CA_NO_ACTION:
8436     {
8437       return;
8438     }
8439
8440     /* ---------- level actions  ------------------------------------------- */
8441
8442     case CA_RESTART_LEVEL:
8443     {
8444       game.restart_level = TRUE;
8445
8446       break;
8447     }
8448
8449     case CA_SHOW_ENVELOPE:
8450     {
8451       int element = getSpecialActionElement(action_arg_element,
8452                                             action_arg_number, EL_ENVELOPE_1);
8453
8454       if (IS_ENVELOPE(element))
8455         local_player->show_envelope = element;
8456
8457       break;
8458     }
8459
8460     case CA_SET_LEVEL_TIME:
8461     {
8462       if (level.time > 0)       /* only modify limited time value */
8463       {
8464         TimeLeft = action_arg_number_new;
8465
8466         DrawGameValue_Time(TimeLeft);
8467
8468         if (!TimeLeft && setup.time_limit)
8469           for (i = 0; i < MAX_PLAYERS; i++)
8470             KillPlayer(&stored_player[i]);
8471       }
8472
8473       break;
8474     }
8475
8476     case CA_SET_LEVEL_SCORE:
8477     {
8478       local_player->score = action_arg_number_new;
8479
8480       DrawGameValue_Score(local_player->score);
8481
8482       break;
8483     }
8484
8485     case CA_SET_LEVEL_GEMS:
8486     {
8487       local_player->gems_still_needed = action_arg_number_new;
8488
8489       DrawGameValue_Emeralds(local_player->gems_still_needed);
8490
8491       break;
8492     }
8493
8494 #if !USE_PLAYER_GRAVITY
8495     case CA_SET_LEVEL_GRAVITY:
8496     {
8497       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
8498                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
8499                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8500                       game.gravity);
8501       break;
8502     }
8503 #endif
8504
8505     case CA_SET_LEVEL_WIND:
8506     {
8507       game.wind_direction = action_arg_direction;
8508
8509       break;
8510     }
8511
8512     /* ---------- player actions  ------------------------------------------ */
8513
8514     case CA_MOVE_PLAYER:
8515     {
8516       /* automatically move to the next field in specified direction */
8517       for (i = 0; i < MAX_PLAYERS; i++)
8518         if (trigger_player_bits & (1 << i))
8519           stored_player[i].programmed_action = action_arg_direction;
8520
8521       break;
8522     }
8523
8524     case CA_EXIT_PLAYER:
8525     {
8526       for (i = 0; i < MAX_PLAYERS; i++)
8527         if (action_arg_player_bits & (1 << i))
8528           PlayerWins(&stored_player[i]);
8529
8530       break;
8531     }
8532
8533     case CA_KILL_PLAYER:
8534     {
8535       for (i = 0; i < MAX_PLAYERS; i++)
8536         if (action_arg_player_bits & (1 << i))
8537           KillPlayer(&stored_player[i]);
8538
8539       break;
8540     }
8541
8542     case CA_SET_PLAYER_KEYS:
8543     {
8544       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8545       int element = getSpecialActionElement(action_arg_element,
8546                                             action_arg_number, EL_KEY_1);
8547
8548       if (IS_KEY(element))
8549       {
8550         for (i = 0; i < MAX_PLAYERS; i++)
8551         {
8552           if (trigger_player_bits & (1 << i))
8553           {
8554             stored_player[i].key[KEY_NR(element)] = key_state;
8555
8556             DrawGameDoorValues();
8557           }
8558         }
8559       }
8560
8561       break;
8562     }
8563
8564     case CA_SET_PLAYER_SPEED:
8565     {
8566       for (i = 0; i < MAX_PLAYERS; i++)
8567       {
8568         if (trigger_player_bits & (1 << i))
8569         {
8570           int move_stepsize = TILEX / stored_player[i].move_delay_value;
8571
8572           if (action_arg == CA_ARG_SPEED_FASTER &&
8573               stored_player[i].cannot_move)
8574           {
8575             action_arg_number = STEPSIZE_VERY_SLOW;
8576           }
8577           else if (action_arg == CA_ARG_SPEED_SLOWER ||
8578                    action_arg == CA_ARG_SPEED_FASTER)
8579           {
8580             action_arg_number = 2;
8581             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8582                            CA_MODE_MULTIPLY);
8583           }
8584           else if (action_arg == CA_ARG_NUMBER_RESET)
8585           {
8586             action_arg_number = level.initial_player_stepsize[i];
8587           }
8588
8589           move_stepsize =
8590             getModifiedActionNumber(move_stepsize,
8591                                     action_mode,
8592                                     action_arg_number,
8593                                     action_arg_number_min,
8594                                     action_arg_number_max);
8595
8596           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8597         }
8598       }
8599
8600       break;
8601     }
8602
8603     case CA_SET_PLAYER_SHIELD:
8604     {
8605       for (i = 0; i < MAX_PLAYERS; i++)
8606       {
8607         if (trigger_player_bits & (1 << i))
8608         {
8609           if (action_arg == CA_ARG_SHIELD_OFF)
8610           {
8611             stored_player[i].shield_normal_time_left = 0;
8612             stored_player[i].shield_deadly_time_left = 0;
8613           }
8614           else if (action_arg == CA_ARG_SHIELD_NORMAL)
8615           {
8616             stored_player[i].shield_normal_time_left = 999999;
8617           }
8618           else if (action_arg == CA_ARG_SHIELD_DEADLY)
8619           {
8620             stored_player[i].shield_normal_time_left = 999999;
8621             stored_player[i].shield_deadly_time_left = 999999;
8622           }
8623         }
8624       }
8625
8626       break;
8627     }
8628
8629 #if USE_PLAYER_GRAVITY
8630     case CA_SET_PLAYER_GRAVITY:
8631     {
8632       for (i = 0; i < MAX_PLAYERS; i++)
8633       {
8634         if (trigger_player_bits & (1 << i))
8635         {
8636           stored_player[i].gravity =
8637             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
8638              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
8639              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8640              stored_player[i].gravity);
8641         }
8642       }
8643
8644       break;
8645     }
8646 #endif
8647
8648     case CA_SET_PLAYER_ARTWORK:
8649     {
8650       for (i = 0; i < MAX_PLAYERS; i++)
8651       {
8652         if (trigger_player_bits & (1 << i))
8653         {
8654           int artwork_element = action_arg_element;
8655
8656           if (action_arg == CA_ARG_ELEMENT_RESET)
8657             artwork_element =
8658               (level.use_artwork_element[i] ? level.artwork_element[i] :
8659                stored_player[i].element_nr);
8660
8661 #if USE_GFX_RESET_PLAYER_ARTWORK
8662           if (stored_player[i].artwork_element != artwork_element)
8663             stored_player[i].Frame = 0;
8664 #endif
8665
8666           stored_player[i].artwork_element = artwork_element;
8667
8668           SetPlayerWaiting(&stored_player[i], FALSE);
8669
8670           /* set number of special actions for bored and sleeping animation */
8671           stored_player[i].num_special_action_bored =
8672             get_num_special_action(artwork_element,
8673                                    ACTION_BORING_1, ACTION_BORING_LAST);
8674           stored_player[i].num_special_action_sleeping =
8675             get_num_special_action(artwork_element,
8676                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8677         }
8678       }
8679
8680       break;
8681     }
8682
8683     /* ---------- CE actions  ---------------------------------------------- */
8684
8685     case CA_SET_CE_VALUE:
8686     {
8687 #if USE_NEW_CUSTOM_VALUE
8688       int last_ce_value = CustomValue[x][y];
8689
8690       CustomValue[x][y] = action_arg_number_new;
8691
8692       if (CustomValue[x][y] != last_ce_value)
8693       {
8694         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8695         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8696
8697         if (CustomValue[x][y] == 0)
8698         {
8699           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8700           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8701         }
8702       }
8703 #endif
8704
8705       break;
8706     }
8707
8708     case CA_SET_CE_SCORE:
8709     {
8710 #if USE_NEW_CUSTOM_VALUE
8711       int last_ce_score = ei->collect_score;
8712
8713       ei->collect_score = action_arg_number_new;
8714
8715       if (ei->collect_score != last_ce_score)
8716       {
8717         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8718         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8719
8720         if (ei->collect_score == 0)
8721         {
8722           int xx, yy;
8723
8724           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8725           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8726
8727           /*
8728             This is a very special case that seems to be a mixture between
8729             CheckElementChange() and CheckTriggeredElementChange(): while
8730             the first one only affects single elements that are triggered
8731             directly, the second one affects multiple elements in the playfield
8732             that are triggered indirectly by another element. This is a third
8733             case: Changing the CE score always affects multiple identical CEs,
8734             so every affected CE must be checked, not only the single CE for
8735             which the CE score was changed in the first place (as every instance
8736             of that CE shares the same CE score, and therefore also can change)!
8737           */
8738           SCAN_PLAYFIELD(xx, yy)
8739           {
8740             if (Feld[xx][yy] == element)
8741               CheckElementChange(xx, yy, element, EL_UNDEFINED,
8742                                  CE_SCORE_GETS_ZERO);
8743           }
8744         }
8745       }
8746 #endif
8747
8748       break;
8749     }
8750
8751     /* ---------- engine actions  ------------------------------------------ */
8752
8753     case CA_SET_ENGINE_SCAN_MODE:
8754     {
8755       InitPlayfieldScanMode(action_arg);
8756
8757       break;
8758     }
8759
8760     default:
8761       break;
8762   }
8763 }
8764
8765 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8766 {
8767   int old_element = Feld[x][y];
8768   int new_element = get_element_from_group_element(element);
8769   int previous_move_direction = MovDir[x][y];
8770 #if USE_NEW_CUSTOM_VALUE
8771   int last_ce_value = CustomValue[x][y];
8772 #endif
8773   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8774   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8775   boolean add_player_onto_element = (new_element_is_player &&
8776 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8777                                      /* this breaks SnakeBite when a snake is
8778                                         halfway through a door that closes */
8779                                      /* NOW FIXED AT LEVEL INIT IN files.c */
8780                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
8781 #endif
8782                                      IS_WALKABLE(old_element));
8783
8784 #if 0
8785   /* check if element under the player changes from accessible to unaccessible
8786      (needed for special case of dropping element which then changes) */
8787   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8788       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8789   {
8790     Bang(x, y);
8791
8792     return;
8793   }
8794 #endif
8795
8796   if (!add_player_onto_element)
8797   {
8798     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8799       RemoveMovingField(x, y);
8800     else
8801       RemoveField(x, y);
8802
8803     Feld[x][y] = new_element;
8804
8805 #if !USE_GFX_RESET_GFX_ANIMATION
8806     ResetGfxAnimation(x, y);
8807     ResetRandomAnimationValue(x, y);
8808 #endif
8809
8810     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8811       MovDir[x][y] = previous_move_direction;
8812
8813 #if USE_NEW_CUSTOM_VALUE
8814     if (element_info[new_element].use_last_ce_value)
8815       CustomValue[x][y] = last_ce_value;
8816 #endif
8817
8818     InitField_WithBug1(x, y, FALSE);
8819
8820     new_element = Feld[x][y];   /* element may have changed */
8821
8822 #if USE_GFX_RESET_GFX_ANIMATION
8823     ResetGfxAnimation(x, y);
8824     ResetRandomAnimationValue(x, y);
8825 #endif
8826
8827     DrawLevelField(x, y);
8828
8829     if (GFX_CRUMBLED(new_element))
8830       DrawLevelFieldCrumbledSandNeighbours(x, y);
8831   }
8832
8833 #if 1
8834   /* check if element under the player changes from accessible to unaccessible
8835      (needed for special case of dropping element which then changes) */
8836   /* (must be checked after creating new element for walkable group elements) */
8837 #if USE_FIX_KILLED_BY_NON_WALKABLE
8838   if (IS_PLAYER(x, y) && !player_explosion_protected &&
8839       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8840   {
8841     Bang(x, y);
8842
8843     return;
8844   }
8845 #else
8846   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8847       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8848   {
8849     Bang(x, y);
8850
8851     return;
8852   }
8853 #endif
8854 #endif
8855
8856   /* "ChangeCount" not set yet to allow "entered by player" change one time */
8857   if (new_element_is_player)
8858     RelocatePlayer(x, y, new_element);
8859
8860   if (is_change)
8861     ChangeCount[x][y]++;        /* count number of changes in the same frame */
8862
8863   TestIfBadThingTouchesPlayer(x, y);
8864   TestIfPlayerTouchesCustomElement(x, y);
8865   TestIfElementTouchesCustomElement(x, y);
8866 }
8867
8868 static void CreateField(int x, int y, int element)
8869 {
8870   CreateFieldExt(x, y, element, FALSE);
8871 }
8872
8873 static void CreateElementFromChange(int x, int y, int element)
8874 {
8875   element = GET_VALID_RUNTIME_ELEMENT(element);
8876
8877 #if USE_STOP_CHANGED_ELEMENTS
8878   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8879   {
8880     int old_element = Feld[x][y];
8881
8882     /* prevent changed element from moving in same engine frame
8883        unless both old and new element can either fall or move */
8884     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8885         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8886       Stop[x][y] = TRUE;
8887   }
8888 #endif
8889
8890   CreateFieldExt(x, y, element, TRUE);
8891 }
8892
8893 static boolean ChangeElement(int x, int y, int element, int page)
8894 {
8895   struct ElementInfo *ei = &element_info[element];
8896   struct ElementChangeInfo *change = &ei->change_page[page];
8897   int ce_value = CustomValue[x][y];
8898   int ce_score = ei->collect_score;
8899   int target_element;
8900   int old_element = Feld[x][y];
8901
8902   /* always use default change event to prevent running into a loop */
8903   if (ChangeEvent[x][y] == -1)
8904     ChangeEvent[x][y] = CE_DELAY;
8905
8906   if (ChangeEvent[x][y] == CE_DELAY)
8907   {
8908     /* reset actual trigger element, trigger player and action element */
8909     change->actual_trigger_element = EL_EMPTY;
8910     change->actual_trigger_player = EL_PLAYER_1;
8911     change->actual_trigger_side = CH_SIDE_NONE;
8912     change->actual_trigger_ce_value = 0;
8913     change->actual_trigger_ce_score = 0;
8914   }
8915
8916   /* do not change elements more than a specified maximum number of changes */
8917   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8918     return FALSE;
8919
8920   ChangeCount[x][y]++;          /* count number of changes in the same frame */
8921
8922   if (change->explode)
8923   {
8924     Bang(x, y);
8925
8926     return TRUE;
8927   }
8928
8929   if (change->use_target_content)
8930   {
8931     boolean complete_replace = TRUE;
8932     boolean can_replace[3][3];
8933     int xx, yy;
8934
8935     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8936     {
8937       boolean is_empty;
8938       boolean is_walkable;
8939       boolean is_diggable;
8940       boolean is_collectible;
8941       boolean is_removable;
8942       boolean is_destructible;
8943       int ex = x + xx - 1;
8944       int ey = y + yy - 1;
8945       int content_element = change->target_content.e[xx][yy];
8946       int e;
8947
8948       can_replace[xx][yy] = TRUE;
8949
8950       if (ex == x && ey == y)   /* do not check changing element itself */
8951         continue;
8952
8953       if (content_element == EL_EMPTY_SPACE)
8954       {
8955         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8956
8957         continue;
8958       }
8959
8960       if (!IN_LEV_FIELD(ex, ey))
8961       {
8962         can_replace[xx][yy] = FALSE;
8963         complete_replace = FALSE;
8964
8965         continue;
8966       }
8967
8968       e = Feld[ex][ey];
8969
8970       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8971         e = MovingOrBlocked2Element(ex, ey);
8972
8973       is_empty = (IS_FREE(ex, ey) ||
8974                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8975
8976       is_walkable     = (is_empty || IS_WALKABLE(e));
8977       is_diggable     = (is_empty || IS_DIGGABLE(e));
8978       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8979       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8980       is_removable    = (is_diggable || is_collectible);
8981
8982       can_replace[xx][yy] =
8983         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
8984           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
8985           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
8986           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
8987           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
8988           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8989          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8990
8991       if (!can_replace[xx][yy])
8992         complete_replace = FALSE;
8993     }
8994
8995     if (!change->only_if_complete || complete_replace)
8996     {
8997       boolean something_has_changed = FALSE;
8998
8999       if (change->only_if_complete && change->use_random_replace &&
9000           RND(100) < change->random_percentage)
9001         return FALSE;
9002
9003       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9004       {
9005         int ex = x + xx - 1;
9006         int ey = y + yy - 1;
9007         int content_element;
9008
9009         if (can_replace[xx][yy] && (!change->use_random_replace ||
9010                                     RND(100) < change->random_percentage))
9011         {
9012           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9013             RemoveMovingField(ex, ey);
9014
9015           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9016
9017           content_element = change->target_content.e[xx][yy];
9018           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9019                                               ce_value, ce_score);
9020
9021           CreateElementFromChange(ex, ey, target_element);
9022
9023           something_has_changed = TRUE;
9024
9025           /* for symmetry reasons, freeze newly created border elements */
9026           if (ex != x || ey != y)
9027             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9028         }
9029       }
9030
9031       if (something_has_changed)
9032       {
9033         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9034         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9035       }
9036     }
9037   }
9038   else
9039   {
9040     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9041                                         ce_value, ce_score);
9042
9043     if (element == EL_DIAGONAL_GROWING ||
9044         element == EL_DIAGONAL_SHRINKING)
9045     {
9046       target_element = Store[x][y];
9047
9048       Store[x][y] = EL_EMPTY;
9049     }
9050
9051     CreateElementFromChange(x, y, target_element);
9052
9053     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9054     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9055   }
9056
9057   /* this uses direct change before indirect change */
9058   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9059
9060   return TRUE;
9061 }
9062
9063 #if USE_NEW_DELAYED_ACTION
9064
9065 static void HandleElementChange(int x, int y, int page)
9066 {
9067   int element = MovingOrBlocked2Element(x, y);
9068   struct ElementInfo *ei = &element_info[element];
9069   struct ElementChangeInfo *change = &ei->change_page[page];
9070
9071 #ifdef DEBUG
9072   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9073       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9074   {
9075     printf("\n\n");
9076     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9077            x, y, element, element_info[element].token_name);
9078     printf("HandleElementChange(): This should never happen!\n");
9079     printf("\n\n");
9080   }
9081 #endif
9082
9083   /* this can happen with classic bombs on walkable, changing elements */
9084   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9085   {
9086 #if 0
9087     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9088       ChangeDelay[x][y] = 0;
9089 #endif
9090
9091     return;
9092   }
9093
9094   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9095   {
9096     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9097
9098     if (change->can_change)
9099     {
9100       ResetGfxAnimation(x, y);
9101       ResetRandomAnimationValue(x, y);
9102
9103       if (change->pre_change_function)
9104         change->pre_change_function(x, y);
9105     }
9106   }
9107
9108   ChangeDelay[x][y]--;
9109
9110   if (ChangeDelay[x][y] != 0)           /* continue element change */
9111   {
9112     if (change->can_change)
9113     {
9114       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9115
9116       if (IS_ANIMATED(graphic))
9117         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9118
9119       if (change->change_function)
9120         change->change_function(x, y);
9121     }
9122   }
9123   else                                  /* finish element change */
9124   {
9125     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9126     {
9127       page = ChangePage[x][y];
9128       ChangePage[x][y] = -1;
9129
9130       change = &ei->change_page[page];
9131     }
9132
9133     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9134     {
9135       ChangeDelay[x][y] = 1;            /* try change after next move step */
9136       ChangePage[x][y] = page;          /* remember page to use for change */
9137
9138       return;
9139     }
9140
9141     if (change->can_change)
9142     {
9143       if (ChangeElement(x, y, element, page))
9144       {
9145         if (change->post_change_function)
9146           change->post_change_function(x, y);
9147       }
9148     }
9149
9150     if (change->has_action)
9151       ExecuteCustomElementAction(x, y, element, page);
9152   }
9153 }
9154
9155 #else
9156
9157 static void HandleElementChange(int x, int y, int page)
9158 {
9159   int element = MovingOrBlocked2Element(x, y);
9160   struct ElementInfo *ei = &element_info[element];
9161   struct ElementChangeInfo *change = &ei->change_page[page];
9162
9163 #ifdef DEBUG
9164   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9165   {
9166     printf("\n\n");
9167     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9168            x, y, element, element_info[element].token_name);
9169     printf("HandleElementChange(): This should never happen!\n");
9170     printf("\n\n");
9171   }
9172 #endif
9173
9174   /* this can happen with classic bombs on walkable, changing elements */
9175   if (!CAN_CHANGE(element))
9176   {
9177 #if 0
9178     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9179       ChangeDelay[x][y] = 0;
9180 #endif
9181
9182     return;
9183   }
9184
9185   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9186   {
9187     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9188
9189     ResetGfxAnimation(x, y);
9190     ResetRandomAnimationValue(x, y);
9191
9192     if (change->pre_change_function)
9193       change->pre_change_function(x, y);
9194   }
9195
9196   ChangeDelay[x][y]--;
9197
9198   if (ChangeDelay[x][y] != 0)           /* continue element change */
9199   {
9200     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9201
9202     if (IS_ANIMATED(graphic))
9203       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9204
9205     if (change->change_function)
9206       change->change_function(x, y);
9207   }
9208   else                                  /* finish element change */
9209   {
9210     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9211     {
9212       page = ChangePage[x][y];
9213       ChangePage[x][y] = -1;
9214
9215       change = &ei->change_page[page];
9216     }
9217
9218     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9219     {
9220       ChangeDelay[x][y] = 1;            /* try change after next move step */
9221       ChangePage[x][y] = page;          /* remember page to use for change */
9222
9223       return;
9224     }
9225
9226     if (ChangeElement(x, y, element, page))
9227     {
9228       if (change->post_change_function)
9229         change->post_change_function(x, y);
9230     }
9231   }
9232 }
9233
9234 #endif
9235
9236 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9237                                               int trigger_element,
9238                                               int trigger_event,
9239                                               int trigger_player,
9240                                               int trigger_side,
9241                                               int trigger_page)
9242 {
9243   boolean change_done_any = FALSE;
9244   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9245   int i;
9246
9247   if (!(trigger_events[trigger_element][trigger_event]))
9248     return FALSE;
9249
9250 #if 0
9251   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9252          trigger_event, recursion_loop_depth, recursion_loop_detected,
9253          recursion_loop_element, EL_NAME(recursion_loop_element));
9254 #endif
9255
9256   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9257
9258   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9259   {
9260     int element = EL_CUSTOM_START + i;
9261     boolean change_done = FALSE;
9262     int p;
9263
9264     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9265         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9266       continue;
9267
9268     for (p = 0; p < element_info[element].num_change_pages; p++)
9269     {
9270       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9271
9272       if (change->can_change_or_has_action &&
9273           change->has_event[trigger_event] &&
9274           change->trigger_side & trigger_side &&
9275           change->trigger_player & trigger_player &&
9276           change->trigger_page & trigger_page_bits &&
9277           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9278       {
9279         change->actual_trigger_element = trigger_element;
9280         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9281         change->actual_trigger_side = trigger_side;
9282         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9283         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9284
9285         if ((change->can_change && !change_done) || change->has_action)
9286         {
9287           int x, y;
9288
9289           SCAN_PLAYFIELD(x, y)
9290           {
9291             if (Feld[x][y] == element)
9292             {
9293               if (change->can_change && !change_done)
9294               {
9295                 ChangeDelay[x][y] = 1;
9296                 ChangeEvent[x][y] = trigger_event;
9297
9298                 HandleElementChange(x, y, p);
9299               }
9300 #if USE_NEW_DELAYED_ACTION
9301               else if (change->has_action)
9302               {
9303                 ExecuteCustomElementAction(x, y, element, p);
9304                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9305               }
9306 #else
9307               if (change->has_action)
9308               {
9309                 ExecuteCustomElementAction(x, y, element, p);
9310                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9311               }
9312 #endif
9313             }
9314           }
9315
9316           if (change->can_change)
9317           {
9318             change_done = TRUE;
9319             change_done_any = TRUE;
9320           }
9321         }
9322       }
9323     }
9324   }
9325
9326   RECURSION_LOOP_DETECTION_END();
9327
9328   return change_done_any;
9329 }
9330
9331 static boolean CheckElementChangeExt(int x, int y,
9332                                      int element,
9333                                      int trigger_element,
9334                                      int trigger_event,
9335                                      int trigger_player,
9336                                      int trigger_side)
9337 {
9338   boolean change_done = FALSE;
9339   int p;
9340
9341   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9342       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9343     return FALSE;
9344
9345   if (Feld[x][y] == EL_BLOCKED)
9346   {
9347     Blocked2Moving(x, y, &x, &y);
9348     element = Feld[x][y];
9349   }
9350
9351 #if 0
9352   /* check if element has already changed */
9353   if (Feld[x][y] != element)
9354     return FALSE;
9355 #else
9356   /* check if element has already changed or is about to change after moving */
9357   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9358        Feld[x][y] != element) ||
9359
9360       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9361        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9362         ChangePage[x][y] != -1)))
9363     return FALSE;
9364 #endif
9365
9366 #if 0
9367   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9368          trigger_event, recursion_loop_depth, recursion_loop_detected,
9369          recursion_loop_element, EL_NAME(recursion_loop_element));
9370 #endif
9371
9372   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9373
9374   for (p = 0; p < element_info[element].num_change_pages; p++)
9375   {
9376     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9377
9378     /* check trigger element for all events where the element that is checked
9379        for changing interacts with a directly adjacent element -- this is
9380        different to element changes that affect other elements to change on the
9381        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9382     boolean check_trigger_element =
9383       (trigger_event == CE_TOUCHING_X ||
9384        trigger_event == CE_HITTING_X ||
9385        trigger_event == CE_HIT_BY_X ||
9386 #if 1
9387        /* this one was forgotten until 3.2.3 */
9388        trigger_event == CE_DIGGING_X);
9389 #endif
9390
9391     if (change->can_change_or_has_action &&
9392         change->has_event[trigger_event] &&
9393         change->trigger_side & trigger_side &&
9394         change->trigger_player & trigger_player &&
9395         (!check_trigger_element ||
9396          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9397     {
9398       change->actual_trigger_element = trigger_element;
9399       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9400       change->actual_trigger_side = trigger_side;
9401       change->actual_trigger_ce_value = CustomValue[x][y];
9402       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9403
9404       /* special case: trigger element not at (x,y) position for some events */
9405       if (check_trigger_element)
9406       {
9407         static struct
9408         {
9409           int dx, dy;
9410         } move_xy[] =
9411           {
9412             {  0,  0 },
9413             { -1,  0 },
9414             { +1,  0 },
9415             {  0,  0 },
9416             {  0, -1 },
9417             {  0,  0 }, { 0, 0 }, { 0, 0 },
9418             {  0, +1 }
9419           };
9420
9421         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9422         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9423
9424         change->actual_trigger_ce_value = CustomValue[xx][yy];
9425         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9426       }
9427
9428       if (change->can_change && !change_done)
9429       {
9430         ChangeDelay[x][y] = 1;
9431         ChangeEvent[x][y] = trigger_event;
9432
9433         HandleElementChange(x, y, p);
9434
9435         change_done = TRUE;
9436       }
9437 #if USE_NEW_DELAYED_ACTION
9438       else if (change->has_action)
9439       {
9440         ExecuteCustomElementAction(x, y, element, p);
9441         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9442       }
9443 #else
9444       if (change->has_action)
9445       {
9446         ExecuteCustomElementAction(x, y, element, p);
9447         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9448       }
9449 #endif
9450     }
9451   }
9452
9453   RECURSION_LOOP_DETECTION_END();
9454
9455   return change_done;
9456 }
9457
9458 static void PlayPlayerSound(struct PlayerInfo *player)
9459 {
9460   int jx = player->jx, jy = player->jy;
9461   int sound_element = player->artwork_element;
9462   int last_action = player->last_action_waiting;
9463   int action = player->action_waiting;
9464
9465   if (player->is_waiting)
9466   {
9467     if (action != last_action)
9468       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9469     else
9470       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9471   }
9472   else
9473   {
9474     if (action != last_action)
9475       StopSound(element_info[sound_element].sound[last_action]);
9476
9477     if (last_action == ACTION_SLEEPING)
9478       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9479   }
9480 }
9481
9482 static void PlayAllPlayersSound()
9483 {
9484   int i;
9485
9486   for (i = 0; i < MAX_PLAYERS; i++)
9487     if (stored_player[i].active)
9488       PlayPlayerSound(&stored_player[i]);
9489 }
9490
9491 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9492 {
9493   boolean last_waiting = player->is_waiting;
9494   int move_dir = player->MovDir;
9495
9496   player->dir_waiting = move_dir;
9497   player->last_action_waiting = player->action_waiting;
9498
9499   if (is_waiting)
9500   {
9501     if (!last_waiting)          /* not waiting -> waiting */
9502     {
9503       player->is_waiting = TRUE;
9504
9505       player->frame_counter_bored =
9506         FrameCounter +
9507         game.player_boring_delay_fixed +
9508         GetSimpleRandom(game.player_boring_delay_random);
9509       player->frame_counter_sleeping =
9510         FrameCounter +
9511         game.player_sleeping_delay_fixed +
9512         GetSimpleRandom(game.player_sleeping_delay_random);
9513
9514       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9515     }
9516
9517     if (game.player_sleeping_delay_fixed +
9518         game.player_sleeping_delay_random > 0 &&
9519         player->anim_delay_counter == 0 &&
9520         player->post_delay_counter == 0 &&
9521         FrameCounter >= player->frame_counter_sleeping)
9522       player->is_sleeping = TRUE;
9523     else if (game.player_boring_delay_fixed +
9524              game.player_boring_delay_random > 0 &&
9525              FrameCounter >= player->frame_counter_bored)
9526       player->is_bored = TRUE;
9527
9528     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9529                               player->is_bored ? ACTION_BORING :
9530                               ACTION_WAITING);
9531
9532     if (player->is_sleeping && player->use_murphy)
9533     {
9534       /* special case for sleeping Murphy when leaning against non-free tile */
9535
9536       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9537           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9538            !IS_MOVING(player->jx - 1, player->jy)))
9539         move_dir = MV_LEFT;
9540       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9541                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9542                 !IS_MOVING(player->jx + 1, player->jy)))
9543         move_dir = MV_RIGHT;
9544       else
9545         player->is_sleeping = FALSE;
9546
9547       player->dir_waiting = move_dir;
9548     }
9549
9550     if (player->is_sleeping)
9551     {
9552       if (player->num_special_action_sleeping > 0)
9553       {
9554         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9555         {
9556           int last_special_action = player->special_action_sleeping;
9557           int num_special_action = player->num_special_action_sleeping;
9558           int special_action =
9559             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9560              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9561              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9562              last_special_action + 1 : ACTION_SLEEPING);
9563           int special_graphic =
9564             el_act_dir2img(player->artwork_element, special_action, move_dir);
9565
9566           player->anim_delay_counter =
9567             graphic_info[special_graphic].anim_delay_fixed +
9568             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9569           player->post_delay_counter =
9570             graphic_info[special_graphic].post_delay_fixed +
9571             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9572
9573           player->special_action_sleeping = special_action;
9574         }
9575
9576         if (player->anim_delay_counter > 0)
9577         {
9578           player->action_waiting = player->special_action_sleeping;
9579           player->anim_delay_counter--;
9580         }
9581         else if (player->post_delay_counter > 0)
9582         {
9583           player->post_delay_counter--;
9584         }
9585       }
9586     }
9587     else if (player->is_bored)
9588     {
9589       if (player->num_special_action_bored > 0)
9590       {
9591         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9592         {
9593           int special_action =
9594             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9595           int special_graphic =
9596             el_act_dir2img(player->artwork_element, special_action, move_dir);
9597
9598           player->anim_delay_counter =
9599             graphic_info[special_graphic].anim_delay_fixed +
9600             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9601           player->post_delay_counter =
9602             graphic_info[special_graphic].post_delay_fixed +
9603             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9604
9605           player->special_action_bored = special_action;
9606         }
9607
9608         if (player->anim_delay_counter > 0)
9609         {
9610           player->action_waiting = player->special_action_bored;
9611           player->anim_delay_counter--;
9612         }
9613         else if (player->post_delay_counter > 0)
9614         {
9615           player->post_delay_counter--;
9616         }
9617       }
9618     }
9619   }
9620   else if (last_waiting)        /* waiting -> not waiting */
9621   {
9622     player->is_waiting = FALSE;
9623     player->is_bored = FALSE;
9624     player->is_sleeping = FALSE;
9625
9626     player->frame_counter_bored = -1;
9627     player->frame_counter_sleeping = -1;
9628
9629     player->anim_delay_counter = 0;
9630     player->post_delay_counter = 0;
9631
9632     player->dir_waiting = player->MovDir;
9633     player->action_waiting = ACTION_DEFAULT;
9634
9635     player->special_action_bored = ACTION_DEFAULT;
9636     player->special_action_sleeping = ACTION_DEFAULT;
9637   }
9638 }
9639
9640 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9641 {
9642   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9643   int left      = player_action & JOY_LEFT;
9644   int right     = player_action & JOY_RIGHT;
9645   int up        = player_action & JOY_UP;
9646   int down      = player_action & JOY_DOWN;
9647   int button1   = player_action & JOY_BUTTON_1;
9648   int button2   = player_action & JOY_BUTTON_2;
9649   int dx        = (left ? -1 : right ? 1 : 0);
9650   int dy        = (up   ? -1 : down  ? 1 : 0);
9651
9652   if (!player->active || tape.pausing)
9653     return 0;
9654
9655   if (player_action)
9656   {
9657     if (button1)
9658       snapped = SnapField(player, dx, dy);
9659     else
9660     {
9661       if (button2)
9662         dropped = DropElement(player);
9663
9664       moved = MovePlayer(player, dx, dy);
9665     }
9666
9667     if (tape.single_step && tape.recording && !tape.pausing)
9668     {
9669       if (button1 || (dropped && !moved))
9670       {
9671         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9672         SnapField(player, 0, 0);                /* stop snapping */
9673       }
9674     }
9675
9676     SetPlayerWaiting(player, FALSE);
9677
9678     return player_action;
9679   }
9680   else
9681   {
9682     /* no actions for this player (no input at player's configured device) */
9683
9684     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9685     SnapField(player, 0, 0);
9686     CheckGravityMovementWhenNotMoving(player);
9687
9688     if (player->MovPos == 0)
9689       SetPlayerWaiting(player, TRUE);
9690
9691     if (player->MovPos == 0)    /* needed for tape.playing */
9692       player->is_moving = FALSE;
9693
9694     player->is_dropping = FALSE;
9695     player->is_dropping_pressed = FALSE;
9696     player->drop_pressed_delay = 0;
9697
9698     return 0;
9699   }
9700 }
9701
9702 static void CheckLevelTime()
9703 {
9704   int i;
9705
9706   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9707   {
9708     if (level.native_em_level->lev->home == 0)  /* all players at home */
9709     {
9710       PlayerWins(local_player);
9711
9712       AllPlayersGone = TRUE;
9713
9714       level.native_em_level->lev->home = -1;
9715     }
9716
9717     if (level.native_em_level->ply[0]->alive == 0 &&
9718         level.native_em_level->ply[1]->alive == 0 &&
9719         level.native_em_level->ply[2]->alive == 0 &&
9720         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9721       AllPlayersGone = TRUE;
9722   }
9723
9724   if (TimeFrames >= FRAMES_PER_SECOND)
9725   {
9726     TimeFrames = 0;
9727     TapeTime++;
9728
9729     for (i = 0; i < MAX_PLAYERS; i++)
9730     {
9731       struct PlayerInfo *player = &stored_player[i];
9732
9733       if (SHIELD_ON(player))
9734       {
9735         player->shield_normal_time_left--;
9736
9737         if (player->shield_deadly_time_left > 0)
9738           player->shield_deadly_time_left--;
9739       }
9740     }
9741
9742     if (!local_player->LevelSolved && !level.use_step_counter)
9743     {
9744       TimePlayed++;
9745
9746       if (TimeLeft > 0)
9747       {
9748         TimeLeft--;
9749
9750         if (TimeLeft <= 10 && setup.time_limit)
9751           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9752
9753         DrawGameValue_Time(TimeLeft);
9754
9755         if (!TimeLeft && setup.time_limit)
9756         {
9757           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9758             level.native_em_level->lev->killed_out_of_time = TRUE;
9759           else
9760             for (i = 0; i < MAX_PLAYERS; i++)
9761               KillPlayer(&stored_player[i]);
9762         }
9763       }
9764       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9765         DrawGameValue_Time(TimePlayed);
9766
9767       level.native_em_level->lev->time =
9768         (level.time == 0 ? TimePlayed : TimeLeft);
9769     }
9770
9771     if (tape.recording || tape.playing)
9772       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9773   }
9774 }
9775
9776 void AdvanceFrameAndPlayerCounters(int player_nr)
9777 {
9778   int i;
9779
9780   /* advance frame counters (global frame counter and time frame counter) */
9781   FrameCounter++;
9782   TimeFrames++;
9783
9784   /* advance player counters (counters for move delay, move animation etc.) */
9785   for (i = 0; i < MAX_PLAYERS; i++)
9786   {
9787     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9788     int move_delay_value = stored_player[i].move_delay_value;
9789     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9790
9791     if (!advance_player_counters)       /* not all players may be affected */
9792       continue;
9793
9794 #if USE_NEW_PLAYER_ANIM
9795     if (move_frames == 0)       /* less than one move per game frame */
9796     {
9797       int stepsize = TILEX / move_delay_value;
9798       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9799       int count = (stored_player[i].is_moving ?
9800                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9801
9802       if (count % delay == 0)
9803         move_frames = 1;
9804     }
9805 #endif
9806
9807     stored_player[i].Frame += move_frames;
9808
9809     if (stored_player[i].MovPos != 0)
9810       stored_player[i].StepFrame += move_frames;
9811
9812     if (stored_player[i].move_delay > 0)
9813       stored_player[i].move_delay--;
9814
9815     /* due to bugs in previous versions, counter must count up, not down */
9816     if (stored_player[i].push_delay != -1)
9817       stored_player[i].push_delay++;
9818
9819     if (stored_player[i].drop_delay > 0)
9820       stored_player[i].drop_delay--;
9821
9822     if (stored_player[i].is_dropping_pressed)
9823       stored_player[i].drop_pressed_delay++;
9824   }
9825 }
9826
9827 void StartGameActions(boolean init_network_game, boolean record_tape,
9828                       long random_seed)
9829 {
9830   unsigned long new_random_seed = InitRND(random_seed);
9831
9832   if (record_tape)
9833     TapeStartRecording(new_random_seed);
9834
9835 #if defined(NETWORK_AVALIABLE)
9836   if (init_network_game)
9837   {
9838     SendToServer_StartPlaying();
9839
9840     return;
9841   }
9842 #endif
9843
9844   InitGame();
9845 }
9846
9847 void GameActions()
9848 {
9849   static unsigned long game_frame_delay = 0;
9850   unsigned long game_frame_delay_value;
9851   byte *recorded_player_action;
9852   byte summarized_player_action = 0;
9853   byte tape_action[MAX_PLAYERS];
9854   int i;
9855
9856   /* detect endless loops, caused by custom element programming */
9857   if (recursion_loop_detected && recursion_loop_depth == 0)
9858   {
9859     char *message = getStringCat3("Internal Error ! Element ",
9860                                   EL_NAME(recursion_loop_element),
9861                                   " caused endless loop ! Quit the game ?");
9862
9863     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9864           EL_NAME(recursion_loop_element));
9865
9866     RequestQuitGameExt(FALSE, level_editor_test_game, message);
9867
9868     recursion_loop_detected = FALSE;    /* if game should be continued */
9869
9870     free(message);
9871
9872     return;
9873   }
9874
9875   if (game.restart_level)
9876     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9877
9878   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9879   {
9880     if (level.native_em_level->lev->home == 0)  /* all players at home */
9881     {
9882       PlayerWins(local_player);
9883
9884       AllPlayersGone = TRUE;
9885
9886       level.native_em_level->lev->home = -1;
9887     }
9888
9889     if (level.native_em_level->ply[0]->alive == 0 &&
9890         level.native_em_level->ply[1]->alive == 0 &&
9891         level.native_em_level->ply[2]->alive == 0 &&
9892         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9893       AllPlayersGone = TRUE;
9894   }
9895
9896   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
9897     GameWon();
9898
9899   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9900     TapeStop();
9901
9902   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9903     return;
9904
9905   game_frame_delay_value =
9906     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9907
9908   if (tape.playing && tape.warp_forward && !tape.pausing)
9909     game_frame_delay_value = 0;
9910
9911   /* ---------- main game synchronization point ---------- */
9912
9913   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9914
9915   if (network_playing && !network_player_action_received)
9916   {
9917     /* try to get network player actions in time */
9918
9919 #if defined(NETWORK_AVALIABLE)
9920     /* last chance to get network player actions without main loop delay */
9921     HandleNetworking();
9922 #endif
9923
9924     /* game was quit by network peer */
9925     if (game_status != GAME_MODE_PLAYING)
9926       return;
9927
9928     if (!network_player_action_received)
9929       return;           /* failed to get network player actions in time */
9930
9931     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9932   }
9933
9934   if (tape.pausing)
9935     return;
9936
9937   /* at this point we know that we really continue executing the game */
9938
9939   network_player_action_received = FALSE;
9940
9941   /* when playing tape, read previously recorded player input from tape data */
9942   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9943
9944 #if 1
9945   /* TapePlayAction() may return NULL when toggling to "pause before death" */
9946   if (tape.pausing)
9947     return;
9948 #endif
9949
9950   if (tape.set_centered_player)
9951   {
9952     game.centered_player_nr_next = tape.centered_player_nr_next;
9953     game.set_centered_player = TRUE;
9954   }
9955
9956   for (i = 0; i < MAX_PLAYERS; i++)
9957   {
9958     summarized_player_action |= stored_player[i].action;
9959
9960     if (!network_playing)
9961       stored_player[i].effective_action = stored_player[i].action;
9962   }
9963
9964 #if defined(NETWORK_AVALIABLE)
9965   if (network_playing)
9966     SendToServer_MovePlayer(summarized_player_action);
9967 #endif
9968
9969   if (!options.network && !setup.team_mode)
9970     local_player->effective_action = summarized_player_action;
9971
9972   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9973   {
9974     for (i = 0; i < MAX_PLAYERS; i++)
9975       stored_player[i].effective_action =
9976         (i == game.centered_player_nr ? summarized_player_action : 0);
9977   }
9978
9979   if (recorded_player_action != NULL)
9980     for (i = 0; i < MAX_PLAYERS; i++)
9981       stored_player[i].effective_action = recorded_player_action[i];
9982
9983   for (i = 0; i < MAX_PLAYERS; i++)
9984   {
9985     tape_action[i] = stored_player[i].effective_action;
9986
9987     /* (this can only happen in the R'n'D game engine) */
9988     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9989       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9990   }
9991
9992   /* only record actions from input devices, but not programmed actions */
9993   if (tape.recording)
9994     TapeRecordAction(tape_action);
9995
9996   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9997   {
9998     GameActions_EM_Main();
9999   }
10000   else
10001   {
10002     GameActions_RND();
10003   }
10004 }
10005
10006 void GameActions_EM_Main()
10007 {
10008   byte effective_action[MAX_PLAYERS];
10009   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10010   int i;
10011
10012   for (i = 0; i < MAX_PLAYERS; i++)
10013     effective_action[i] = stored_player[i].effective_action;
10014
10015   GameActions_EM(effective_action, warp_mode);
10016
10017   CheckLevelTime();
10018
10019   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10020 }
10021
10022 void GameActions_RND()
10023 {
10024   int magic_wall_x = 0, magic_wall_y = 0;
10025   int i, x, y, element, graphic;
10026
10027   InitPlayfieldScanModeVars();
10028
10029 #if USE_ONE_MORE_CHANGE_PER_FRAME
10030   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10031   {
10032     SCAN_PLAYFIELD(x, y)
10033     {
10034       ChangeCount[x][y] = 0;
10035       ChangeEvent[x][y] = -1;
10036     }
10037   }
10038 #endif
10039
10040   if (game.set_centered_player)
10041   {
10042     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10043
10044     /* switching to "all players" only possible if all players fit to screen */
10045     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10046     {
10047       game.centered_player_nr_next = game.centered_player_nr;
10048       game.set_centered_player = FALSE;
10049     }
10050
10051     /* do not switch focus to non-existing (or non-active) player */
10052     if (game.centered_player_nr_next >= 0 &&
10053         !stored_player[game.centered_player_nr_next].active)
10054     {
10055       game.centered_player_nr_next = game.centered_player_nr;
10056       game.set_centered_player = FALSE;
10057     }
10058   }
10059
10060   if (game.set_centered_player &&
10061       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10062   {
10063     int sx, sy;
10064
10065     if (game.centered_player_nr_next == -1)
10066     {
10067       setScreenCenteredToAllPlayers(&sx, &sy);
10068     }
10069     else
10070     {
10071       sx = stored_player[game.centered_player_nr_next].jx;
10072       sy = stored_player[game.centered_player_nr_next].jy;
10073     }
10074
10075     game.centered_player_nr = game.centered_player_nr_next;
10076     game.set_centered_player = FALSE;
10077
10078     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10079     DrawGameDoorValues();
10080   }
10081
10082   for (i = 0; i < MAX_PLAYERS; i++)
10083   {
10084     int actual_player_action = stored_player[i].effective_action;
10085
10086 #if 1
10087     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10088        - rnd_equinox_tetrachloride 048
10089        - rnd_equinox_tetrachloride_ii 096
10090        - rnd_emanuel_schmieg 002
10091        - doctor_sloan_ww 001, 020
10092     */
10093     if (stored_player[i].MovPos == 0)
10094       CheckGravityMovement(&stored_player[i]);
10095 #endif
10096
10097     /* overwrite programmed action with tape action */
10098     if (stored_player[i].programmed_action)
10099       actual_player_action = stored_player[i].programmed_action;
10100
10101     PlayerActions(&stored_player[i], actual_player_action);
10102
10103     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10104   }
10105
10106   ScrollScreen(NULL, SCROLL_GO_ON);
10107
10108   /* for backwards compatibility, the following code emulates a fixed bug that
10109      occured when pushing elements (causing elements that just made their last
10110      pushing step to already (if possible) make their first falling step in the
10111      same game frame, which is bad); this code is also needed to use the famous
10112      "spring push bug" which is used in older levels and might be wanted to be
10113      used also in newer levels, but in this case the buggy pushing code is only
10114      affecting the "spring" element and no other elements */
10115
10116   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10117   {
10118     for (i = 0; i < MAX_PLAYERS; i++)
10119     {
10120       struct PlayerInfo *player = &stored_player[i];
10121       int x = player->jx;
10122       int y = player->jy;
10123
10124       if (player->active && player->is_pushing && player->is_moving &&
10125           IS_MOVING(x, y) &&
10126           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10127            Feld[x][y] == EL_SPRING))
10128       {
10129         ContinueMoving(x, y);
10130
10131         /* continue moving after pushing (this is actually a bug) */
10132         if (!IS_MOVING(x, y))
10133         {
10134           Stop[x][y] = FALSE;
10135         }
10136       }
10137     }
10138   }
10139
10140   SCAN_PLAYFIELD(x, y)
10141   {
10142     ChangeCount[x][y] = 0;
10143     ChangeEvent[x][y] = -1;
10144
10145     /* this must be handled before main playfield loop */
10146     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10147     {
10148       MovDelay[x][y]--;
10149       if (MovDelay[x][y] <= 0)
10150         RemoveField(x, y);
10151     }
10152
10153 #if USE_NEW_SNAP_DELAY
10154     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10155     {
10156       MovDelay[x][y]--;
10157       if (MovDelay[x][y] <= 0)
10158       {
10159         RemoveField(x, y);
10160         DrawLevelField(x, y);
10161
10162         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10163       }
10164     }
10165 #endif
10166
10167 #if DEBUG
10168     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10169     {
10170       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10171       printf("GameActions(): This should never happen!\n");
10172
10173       ChangePage[x][y] = -1;
10174     }
10175 #endif
10176
10177     Stop[x][y] = FALSE;
10178     if (WasJustMoving[x][y] > 0)
10179       WasJustMoving[x][y]--;
10180     if (WasJustFalling[x][y] > 0)
10181       WasJustFalling[x][y]--;
10182     if (CheckCollision[x][y] > 0)
10183       CheckCollision[x][y]--;
10184     if (CheckImpact[x][y] > 0)
10185       CheckImpact[x][y]--;
10186
10187     GfxFrame[x][y]++;
10188
10189     /* reset finished pushing action (not done in ContinueMoving() to allow
10190        continuous pushing animation for elements with zero push delay) */
10191     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10192     {
10193       ResetGfxAnimation(x, y);
10194       DrawLevelField(x, y);
10195     }
10196
10197 #if DEBUG
10198     if (IS_BLOCKED(x, y))
10199     {
10200       int oldx, oldy;
10201
10202       Blocked2Moving(x, y, &oldx, &oldy);
10203       if (!IS_MOVING(oldx, oldy))
10204       {
10205         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10206         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10207         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10208         printf("GameActions(): This should never happen!\n");
10209       }
10210     }
10211 #endif
10212   }
10213
10214   SCAN_PLAYFIELD(x, y)
10215   {
10216     element = Feld[x][y];
10217     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10218
10219     ResetGfxFrame(x, y, TRUE);
10220
10221     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10222         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10223       ResetRandomAnimationValue(x, y);
10224
10225     SetRandomAnimationValue(x, y);
10226
10227     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10228
10229     if (IS_INACTIVE(element))
10230     {
10231       if (IS_ANIMATED(graphic))
10232         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10233
10234       continue;
10235     }
10236
10237     /* this may take place after moving, so 'element' may have changed */
10238     if (IS_CHANGING(x, y) &&
10239         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10240     {
10241       int page = element_info[element].event_page_nr[CE_DELAY];
10242
10243 #if 1
10244       HandleElementChange(x, y, page);
10245 #else
10246       if (CAN_CHANGE(element))
10247         HandleElementChange(x, y, page);
10248
10249       if (HAS_ACTION(element))
10250         ExecuteCustomElementAction(x, y, element, page);
10251 #endif
10252
10253       element = Feld[x][y];
10254       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10255     }
10256
10257     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10258     {
10259       StartMoving(x, y);
10260
10261       element = Feld[x][y];
10262       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10263
10264       if (IS_ANIMATED(graphic) &&
10265           !IS_MOVING(x, y) &&
10266           !Stop[x][y])
10267         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10268
10269       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10270         DrawTwinkleOnField(x, y);
10271     }
10272     else if ((element == EL_ACID ||
10273               element == EL_EXIT_OPEN ||
10274               element == EL_EM_EXIT_OPEN ||
10275               element == EL_SP_EXIT_OPEN ||
10276               element == EL_STEEL_EXIT_OPEN ||
10277               element == EL_EM_STEEL_EXIT_OPEN ||
10278               element == EL_SP_TERMINAL ||
10279               element == EL_SP_TERMINAL_ACTIVE ||
10280               element == EL_EXTRA_TIME ||
10281               element == EL_SHIELD_NORMAL ||
10282               element == EL_SHIELD_DEADLY) &&
10283              IS_ANIMATED(graphic))
10284       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10285     else if (IS_MOVING(x, y))
10286       ContinueMoving(x, y);
10287     else if (IS_ACTIVE_BOMB(element))
10288       CheckDynamite(x, y);
10289     else if (element == EL_AMOEBA_GROWING)
10290       AmoebeWaechst(x, y);
10291     else if (element == EL_AMOEBA_SHRINKING)
10292       AmoebaDisappearing(x, y);
10293
10294 #if !USE_NEW_AMOEBA_CODE
10295     else if (IS_AMOEBALIVE(element))
10296       AmoebeAbleger(x, y);
10297 #endif
10298
10299     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10300       Life(x, y);
10301     else if (element == EL_EXIT_CLOSED)
10302       CheckExit(x, y);
10303     else if (element == EL_EM_EXIT_CLOSED)
10304       CheckExitEM(x, y);
10305     else if (element == EL_STEEL_EXIT_CLOSED)
10306       CheckExitSteel(x, y);
10307     else if (element == EL_EM_STEEL_EXIT_CLOSED)
10308       CheckExitSteelEM(x, y);
10309     else if (element == EL_SP_EXIT_CLOSED)
10310       CheckExitSP(x, y);
10311     else if (element == EL_EXPANDABLE_WALL_GROWING ||
10312              element == EL_EXPANDABLE_STEELWALL_GROWING)
10313       MauerWaechst(x, y);
10314     else if (element == EL_EXPANDABLE_WALL ||
10315              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10316              element == EL_EXPANDABLE_WALL_VERTICAL ||
10317              element == EL_EXPANDABLE_WALL_ANY ||
10318              element == EL_BD_EXPANDABLE_WALL)
10319       MauerAbleger(x, y);
10320     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10321              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10322              element == EL_EXPANDABLE_STEELWALL_ANY)
10323       MauerAblegerStahl(x, y);
10324     else if (element == EL_FLAMES)
10325       CheckForDragon(x, y);
10326     else if (element == EL_EXPLOSION)
10327       ; /* drawing of correct explosion animation is handled separately */
10328     else if (element == EL_ELEMENT_SNAPPING ||
10329              element == EL_DIAGONAL_SHRINKING ||
10330              element == EL_DIAGONAL_GROWING)
10331     {
10332       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10333
10334       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10335     }
10336     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10337       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10338
10339     if (IS_BELT_ACTIVE(element))
10340       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10341
10342     if (game.magic_wall_active)
10343     {
10344       int jx = local_player->jx, jy = local_player->jy;
10345
10346       /* play the element sound at the position nearest to the player */
10347       if ((element == EL_MAGIC_WALL_FULL ||
10348            element == EL_MAGIC_WALL_ACTIVE ||
10349            element == EL_MAGIC_WALL_EMPTYING ||
10350            element == EL_BD_MAGIC_WALL_FULL ||
10351            element == EL_BD_MAGIC_WALL_ACTIVE ||
10352            element == EL_BD_MAGIC_WALL_EMPTYING ||
10353            element == EL_DC_MAGIC_WALL_FULL ||
10354            element == EL_DC_MAGIC_WALL_ACTIVE ||
10355            element == EL_DC_MAGIC_WALL_EMPTYING) &&
10356           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10357       {
10358         magic_wall_x = x;
10359         magic_wall_y = y;
10360       }
10361     }
10362   }
10363
10364 #if USE_NEW_AMOEBA_CODE
10365   /* new experimental amoeba growth stuff */
10366   if (!(FrameCounter % 8))
10367   {
10368     static unsigned long random = 1684108901;
10369
10370     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10371     {
10372       x = RND(lev_fieldx);
10373       y = RND(lev_fieldy);
10374       element = Feld[x][y];
10375
10376       if (!IS_PLAYER(x,y) &&
10377           (element == EL_EMPTY ||
10378            CAN_GROW_INTO(element) ||
10379            element == EL_QUICKSAND_EMPTY ||
10380            element == EL_QUICKSAND_FAST_EMPTY ||
10381            element == EL_ACID_SPLASH_LEFT ||
10382            element == EL_ACID_SPLASH_RIGHT))
10383       {
10384         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10385             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10386             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10387             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10388           Feld[x][y] = EL_AMOEBA_DROP;
10389       }
10390
10391       random = random * 129 + 1;
10392     }
10393   }
10394 #endif
10395
10396 #if 0
10397   if (game.explosions_delayed)
10398 #endif
10399   {
10400     game.explosions_delayed = FALSE;
10401
10402     SCAN_PLAYFIELD(x, y)
10403     {
10404       element = Feld[x][y];
10405
10406       if (ExplodeField[x][y])
10407         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10408       else if (element == EL_EXPLOSION)
10409         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10410
10411       ExplodeField[x][y] = EX_TYPE_NONE;
10412     }
10413
10414     game.explosions_delayed = TRUE;
10415   }
10416
10417   if (game.magic_wall_active)
10418   {
10419     if (!(game.magic_wall_time_left % 4))
10420     {
10421       int element = Feld[magic_wall_x][magic_wall_y];
10422
10423       if (element == EL_BD_MAGIC_WALL_FULL ||
10424           element == EL_BD_MAGIC_WALL_ACTIVE ||
10425           element == EL_BD_MAGIC_WALL_EMPTYING)
10426         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10427       else if (element == EL_DC_MAGIC_WALL_FULL ||
10428                element == EL_DC_MAGIC_WALL_ACTIVE ||
10429                element == EL_DC_MAGIC_WALL_EMPTYING)
10430         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10431       else
10432         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10433     }
10434
10435     if (game.magic_wall_time_left > 0)
10436     {
10437       game.magic_wall_time_left--;
10438       if (!game.magic_wall_time_left)
10439       {
10440         SCAN_PLAYFIELD(x, y)
10441         {
10442           element = Feld[x][y];
10443
10444           if (element == EL_MAGIC_WALL_ACTIVE ||
10445               element == EL_MAGIC_WALL_FULL)
10446           {
10447             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10448             DrawLevelField(x, y);
10449           }
10450           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10451                    element == EL_BD_MAGIC_WALL_FULL)
10452           {
10453             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10454             DrawLevelField(x, y);
10455           }
10456           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10457                    element == EL_DC_MAGIC_WALL_FULL)
10458           {
10459             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10460             DrawLevelField(x, y);
10461           }
10462         }
10463
10464         game.magic_wall_active = FALSE;
10465       }
10466     }
10467   }
10468
10469   if (game.light_time_left > 0)
10470   {
10471     game.light_time_left--;
10472
10473     if (game.light_time_left == 0)
10474       RedrawAllLightSwitchesAndInvisibleElements();
10475   }
10476
10477   if (game.timegate_time_left > 0)
10478   {
10479     game.timegate_time_left--;
10480
10481     if (game.timegate_time_left == 0)
10482       CloseAllOpenTimegates();
10483   }
10484
10485   if (game.lenses_time_left > 0)
10486   {
10487     game.lenses_time_left--;
10488
10489     if (game.lenses_time_left == 0)
10490       RedrawAllInvisibleElementsForLenses();
10491   }
10492
10493   if (game.magnify_time_left > 0)
10494   {
10495     game.magnify_time_left--;
10496
10497     if (game.magnify_time_left == 0)
10498       RedrawAllInvisibleElementsForMagnifier();
10499   }
10500
10501   for (i = 0; i < MAX_PLAYERS; i++)
10502   {
10503     struct PlayerInfo *player = &stored_player[i];
10504
10505     if (SHIELD_ON(player))
10506     {
10507       if (player->shield_deadly_time_left)
10508         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10509       else if (player->shield_normal_time_left)
10510         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10511     }
10512   }
10513
10514   CheckLevelTime();
10515
10516   DrawAllPlayers();
10517   PlayAllPlayersSound();
10518
10519   if (options.debug)                    /* calculate frames per second */
10520   {
10521     static unsigned long fps_counter = 0;
10522     static int fps_frames = 0;
10523     unsigned long fps_delay_ms = Counter() - fps_counter;
10524
10525     fps_frames++;
10526
10527     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10528     {
10529       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10530
10531       fps_frames = 0;
10532       fps_counter = Counter();
10533     }
10534
10535     redraw_mask |= REDRAW_FPS;
10536   }
10537
10538   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10539
10540   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10541   {
10542     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10543
10544     local_player->show_envelope = 0;
10545   }
10546
10547   /* use random number generator in every frame to make it less predictable */
10548   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10549     RND(1);
10550 }
10551
10552 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10553 {
10554   int min_x = x, min_y = y, max_x = x, max_y = y;
10555   int i;
10556
10557   for (i = 0; i < MAX_PLAYERS; i++)
10558   {
10559     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10560
10561     if (!stored_player[i].active || &stored_player[i] == player)
10562       continue;
10563
10564     min_x = MIN(min_x, jx);
10565     min_y = MIN(min_y, jy);
10566     max_x = MAX(max_x, jx);
10567     max_y = MAX(max_y, jy);
10568   }
10569
10570   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10571 }
10572
10573 static boolean AllPlayersInVisibleScreen()
10574 {
10575   int i;
10576
10577   for (i = 0; i < MAX_PLAYERS; i++)
10578   {
10579     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10580
10581     if (!stored_player[i].active)
10582       continue;
10583
10584     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10585       return FALSE;
10586   }
10587
10588   return TRUE;
10589 }
10590
10591 void ScrollLevel(int dx, int dy)
10592 {
10593 #if 1
10594   static Bitmap *bitmap_db_field2 = NULL;
10595   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10596   int x, y;
10597 #else
10598   int i, x, y;
10599 #endif
10600
10601   /* only horizontal XOR vertical scroll direction allowed */
10602   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
10603     return;
10604
10605 #if 1
10606   if (bitmap_db_field2 == NULL)
10607     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
10608
10609   BlitBitmap(drawto_field, bitmap_db_field2,
10610              FX + TILEX * (dx == -1) - softscroll_offset,
10611              FY + TILEY * (dy == -1) - softscroll_offset,
10612              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10613              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10614              FX + TILEX * (dx == 1) - softscroll_offset,
10615              FY + TILEY * (dy == 1) - softscroll_offset);
10616   BlitBitmap(bitmap_db_field2, drawto_field,
10617              FX + TILEX * (dx == 1) - softscroll_offset,
10618              FY + TILEY * (dy == 1) - softscroll_offset,
10619              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10620              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10621              FX + TILEX * (dx == 1) - softscroll_offset,
10622              FY + TILEY * (dy == 1) - softscroll_offset);
10623
10624 #else
10625
10626 #if 1
10627   int xsize = (BX2 - BX1 + 1);
10628   int ysize = (BY2 - BY1 + 1);
10629   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
10630   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
10631   int step  = (start < end ? +1 : -1);
10632
10633   for (i = start; i != end; i += step)
10634   {
10635     BlitBitmap(drawto_field, drawto_field,
10636                FX + TILEX * (dx != 0 ? i + step : 0),
10637                FY + TILEY * (dy != 0 ? i + step : 0),
10638                TILEX * (dx != 0 ? 1 : xsize),
10639                TILEY * (dy != 0 ? 1 : ysize),
10640                FX + TILEX * (dx != 0 ? i : 0),
10641                FY + TILEY * (dy != 0 ? i : 0));
10642   }
10643
10644 #else
10645
10646   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10647
10648   BlitBitmap(drawto_field, drawto_field,
10649              FX + TILEX * (dx == -1) - softscroll_offset,
10650              FY + TILEY * (dy == -1) - softscroll_offset,
10651              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10652              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10653              FX + TILEX * (dx == 1) - softscroll_offset,
10654              FY + TILEY * (dy == 1) - softscroll_offset);
10655 #endif
10656 #endif
10657
10658   if (dx != 0)
10659   {
10660     x = (dx == 1 ? BX1 : BX2);
10661     for (y = BY1; y <= BY2; y++)
10662       DrawScreenField(x, y);
10663   }
10664
10665   if (dy != 0)
10666   {
10667     y = (dy == 1 ? BY1 : BY2);
10668     for (x = BX1; x <= BX2; x++)
10669       DrawScreenField(x, y);
10670   }
10671
10672   redraw_mask |= REDRAW_FIELD;
10673 }
10674
10675 static boolean canFallDown(struct PlayerInfo *player)
10676 {
10677   int jx = player->jx, jy = player->jy;
10678
10679   return (IN_LEV_FIELD(jx, jy + 1) &&
10680           (IS_FREE(jx, jy + 1) ||
10681            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10682           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10683           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10684 }
10685
10686 static boolean canPassField(int x, int y, int move_dir)
10687 {
10688   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10689   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10690   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10691   int nextx = x + dx;
10692   int nexty = y + dy;
10693   int element = Feld[x][y];
10694
10695   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10696           !CAN_MOVE(element) &&
10697           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10698           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10699           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10700 }
10701
10702 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10703 {
10704   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10705   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10706   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10707   int newx = x + dx;
10708   int newy = y + dy;
10709
10710   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10711           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10712           (IS_DIGGABLE(Feld[newx][newy]) ||
10713            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10714            canPassField(newx, newy, move_dir)));
10715 }
10716
10717 static void CheckGravityMovement(struct PlayerInfo *player)
10718 {
10719 #if USE_PLAYER_GRAVITY
10720   if (player->gravity && !player->programmed_action)
10721 #else
10722   if (game.gravity && !player->programmed_action)
10723 #endif
10724   {
10725     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10726     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10727     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10728     int jx = player->jx, jy = player->jy;
10729     boolean player_is_moving_to_valid_field =
10730       (!player_is_snapping &&
10731        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10732         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10733     boolean player_can_fall_down = canFallDown(player);
10734
10735     if (player_can_fall_down &&
10736         !player_is_moving_to_valid_field)
10737       player->programmed_action = MV_DOWN;
10738   }
10739 }
10740
10741 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10742 {
10743   return CheckGravityMovement(player);
10744
10745 #if USE_PLAYER_GRAVITY
10746   if (player->gravity && !player->programmed_action)
10747 #else
10748   if (game.gravity && !player->programmed_action)
10749 #endif
10750   {
10751     int jx = player->jx, jy = player->jy;
10752     boolean field_under_player_is_free =
10753       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10754     boolean player_is_standing_on_valid_field =
10755       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10756        (IS_WALKABLE(Feld[jx][jy]) &&
10757         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10758
10759     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10760       player->programmed_action = MV_DOWN;
10761   }
10762 }
10763
10764 /*
10765   MovePlayerOneStep()
10766   -----------------------------------------------------------------------------
10767   dx, dy:               direction (non-diagonal) to try to move the player to
10768   real_dx, real_dy:     direction as read from input device (can be diagonal)
10769 */
10770
10771 boolean MovePlayerOneStep(struct PlayerInfo *player,
10772                           int dx, int dy, int real_dx, int real_dy)
10773 {
10774   int jx = player->jx, jy = player->jy;
10775   int new_jx = jx + dx, new_jy = jy + dy;
10776 #if !USE_FIXED_DONT_RUN_INTO
10777   int element;
10778 #endif
10779   int can_move;
10780   boolean player_can_move = !player->cannot_move;
10781
10782   if (!player->active || (!dx && !dy))
10783     return MP_NO_ACTION;
10784
10785   player->MovDir = (dx < 0 ? MV_LEFT :
10786                     dx > 0 ? MV_RIGHT :
10787                     dy < 0 ? MV_UP :
10788                     dy > 0 ? MV_DOWN :  MV_NONE);
10789
10790   if (!IN_LEV_FIELD(new_jx, new_jy))
10791     return MP_NO_ACTION;
10792
10793   if (!player_can_move)
10794   {
10795     if (player->MovPos == 0)
10796     {
10797       player->is_moving = FALSE;
10798       player->is_digging = FALSE;
10799       player->is_collecting = FALSE;
10800       player->is_snapping = FALSE;
10801       player->is_pushing = FALSE;
10802     }
10803   }
10804
10805 #if 1
10806   if (!options.network && game.centered_player_nr == -1 &&
10807       !AllPlayersInSight(player, new_jx, new_jy))
10808     return MP_NO_ACTION;
10809 #else
10810   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10811     return MP_NO_ACTION;
10812 #endif
10813
10814 #if !USE_FIXED_DONT_RUN_INTO
10815   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10816
10817   /* (moved to DigField()) */
10818   if (player_can_move && DONT_RUN_INTO(element))
10819   {
10820     if (element == EL_ACID && dx == 0 && dy == 1)
10821     {
10822       SplashAcid(new_jx, new_jy);
10823       Feld[jx][jy] = EL_PLAYER_1;
10824       InitMovingField(jx, jy, MV_DOWN);
10825       Store[jx][jy] = EL_ACID;
10826       ContinueMoving(jx, jy);
10827       BuryPlayer(player);
10828     }
10829     else
10830       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10831
10832     return MP_MOVING;
10833   }
10834 #endif
10835
10836   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10837   if (can_move != MP_MOVING)
10838     return can_move;
10839
10840   /* check if DigField() has caused relocation of the player */
10841   if (player->jx != jx || player->jy != jy)
10842     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10843
10844   StorePlayer[jx][jy] = 0;
10845   player->last_jx = jx;
10846   player->last_jy = jy;
10847   player->jx = new_jx;
10848   player->jy = new_jy;
10849   StorePlayer[new_jx][new_jy] = player->element_nr;
10850
10851   if (player->move_delay_value_next != -1)
10852   {
10853     player->move_delay_value = player->move_delay_value_next;
10854     player->move_delay_value_next = -1;
10855   }
10856
10857   player->MovPos =
10858     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10859
10860   player->step_counter++;
10861
10862   PlayerVisit[jx][jy] = FrameCounter;
10863
10864 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10865   player->is_moving = TRUE;
10866 #endif
10867
10868 #if 1
10869   /* should better be called in MovePlayer(), but this breaks some tapes */
10870   ScrollPlayer(player, SCROLL_INIT);
10871 #endif
10872
10873   return MP_MOVING;
10874 }
10875
10876 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10877 {
10878   int jx = player->jx, jy = player->jy;
10879   int old_jx = jx, old_jy = jy;
10880   int moved = MP_NO_ACTION;
10881
10882   if (!player->active)
10883     return FALSE;
10884
10885   if (!dx && !dy)
10886   {
10887     if (player->MovPos == 0)
10888     {
10889       player->is_moving = FALSE;
10890       player->is_digging = FALSE;
10891       player->is_collecting = FALSE;
10892       player->is_snapping = FALSE;
10893       player->is_pushing = FALSE;
10894     }
10895
10896     return FALSE;
10897   }
10898
10899   if (player->move_delay > 0)
10900     return FALSE;
10901
10902   player->move_delay = -1;              /* set to "uninitialized" value */
10903
10904   /* store if player is automatically moved to next field */
10905   player->is_auto_moving = (player->programmed_action != MV_NONE);
10906
10907   /* remove the last programmed player action */
10908   player->programmed_action = 0;
10909
10910   if (player->MovPos)
10911   {
10912     /* should only happen if pre-1.2 tape recordings are played */
10913     /* this is only for backward compatibility */
10914
10915     int original_move_delay_value = player->move_delay_value;
10916
10917 #if DEBUG
10918     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10919            tape.counter);
10920 #endif
10921
10922     /* scroll remaining steps with finest movement resolution */
10923     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10924
10925     while (player->MovPos)
10926     {
10927       ScrollPlayer(player, SCROLL_GO_ON);
10928       ScrollScreen(NULL, SCROLL_GO_ON);
10929
10930       AdvanceFrameAndPlayerCounters(player->index_nr);
10931
10932       DrawAllPlayers();
10933       BackToFront();
10934     }
10935
10936     player->move_delay_value = original_move_delay_value;
10937   }
10938
10939   player->is_active = FALSE;
10940
10941   if (player->last_move_dir & MV_HORIZONTAL)
10942   {
10943     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10944       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10945   }
10946   else
10947   {
10948     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10949       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10950   }
10951
10952 #if USE_FIXED_BORDER_RUNNING_GFX
10953   if (!moved && !player->is_active)
10954   {
10955     player->is_moving = FALSE;
10956     player->is_digging = FALSE;
10957     player->is_collecting = FALSE;
10958     player->is_snapping = FALSE;
10959     player->is_pushing = FALSE;
10960   }
10961 #endif
10962
10963   jx = player->jx;
10964   jy = player->jy;
10965
10966 #if 1
10967   if (moved & MP_MOVING && !ScreenMovPos &&
10968       (player->index_nr == game.centered_player_nr ||
10969        game.centered_player_nr == -1))
10970 #else
10971   if (moved & MP_MOVING && !ScreenMovPos &&
10972       (player == local_player || !options.network))
10973 #endif
10974   {
10975     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10976     int offset = (setup.scroll_delay ? 3 : 0);
10977
10978     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10979     {
10980       /* actual player has left the screen -- scroll in that direction */
10981       if (jx != old_jx)         /* player has moved horizontally */
10982         scroll_x += (jx - old_jx);
10983       else                      /* player has moved vertically */
10984         scroll_y += (jy - old_jy);
10985     }
10986     else
10987     {
10988       if (jx != old_jx)         /* player has moved horizontally */
10989       {
10990         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10991             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10992           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10993
10994         /* don't scroll over playfield boundaries */
10995         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10996           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10997
10998         /* don't scroll more than one field at a time */
10999         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11000
11001         /* don't scroll against the player's moving direction */
11002         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
11003             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11004           scroll_x = old_scroll_x;
11005       }
11006       else                      /* player has moved vertically */
11007       {
11008         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
11009             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11010           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11011
11012         /* don't scroll over playfield boundaries */
11013         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11014           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11015
11016         /* don't scroll more than one field at a time */
11017         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11018
11019         /* don't scroll against the player's moving direction */
11020         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
11021             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11022           scroll_y = old_scroll_y;
11023       }
11024     }
11025
11026     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11027     {
11028 #if 1
11029       if (!options.network && game.centered_player_nr == -1 &&
11030           !AllPlayersInVisibleScreen())
11031       {
11032         scroll_x = old_scroll_x;
11033         scroll_y = old_scroll_y;
11034       }
11035       else
11036 #else
11037       if (!options.network && !AllPlayersInVisibleScreen())
11038       {
11039         scroll_x = old_scroll_x;
11040         scroll_y = old_scroll_y;
11041       }
11042       else
11043 #endif
11044       {
11045         ScrollScreen(player, SCROLL_INIT);
11046         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11047       }
11048     }
11049   }
11050
11051   player->StepFrame = 0;
11052
11053   if (moved & MP_MOVING)
11054   {
11055     if (old_jx != jx && old_jy == jy)
11056       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11057     else if (old_jx == jx && old_jy != jy)
11058       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11059
11060     DrawLevelField(jx, jy);     /* for "crumbled sand" */
11061
11062     player->last_move_dir = player->MovDir;
11063     player->is_moving = TRUE;
11064     player->is_snapping = FALSE;
11065     player->is_switching = FALSE;
11066     player->is_dropping = FALSE;
11067     player->is_dropping_pressed = FALSE;
11068     player->drop_pressed_delay = 0;
11069
11070 #if 0
11071     /* should better be called here than above, but this breaks some tapes */
11072     ScrollPlayer(player, SCROLL_INIT);
11073 #endif
11074   }
11075   else
11076   {
11077     CheckGravityMovementWhenNotMoving(player);
11078
11079     player->is_moving = FALSE;
11080
11081     /* at this point, the player is allowed to move, but cannot move right now
11082        (e.g. because of something blocking the way) -- ensure that the player
11083        is also allowed to move in the next frame (in old versions before 3.1.1,
11084        the player was forced to wait again for eight frames before next try) */
11085
11086     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11087       player->move_delay = 0;   /* allow direct movement in the next frame */
11088   }
11089
11090   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11091     player->move_delay = player->move_delay_value;
11092
11093   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11094   {
11095     TestIfPlayerTouchesBadThing(jx, jy);
11096     TestIfPlayerTouchesCustomElement(jx, jy);
11097   }
11098
11099   if (!player->active)
11100     RemovePlayer(player);
11101
11102   return moved;
11103 }
11104
11105 void ScrollPlayer(struct PlayerInfo *player, int mode)
11106 {
11107   int jx = player->jx, jy = player->jy;
11108   int last_jx = player->last_jx, last_jy = player->last_jy;
11109   int move_stepsize = TILEX / player->move_delay_value;
11110
11111 #if USE_NEW_PLAYER_SPEED
11112   if (!player->active)
11113     return;
11114
11115   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11116     return;
11117 #else
11118   if (!player->active || player->MovPos == 0)
11119     return;
11120 #endif
11121
11122   if (mode == SCROLL_INIT)
11123   {
11124     player->actual_frame_counter = FrameCounter;
11125     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11126
11127     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11128         Feld[last_jx][last_jy] == EL_EMPTY)
11129     {
11130       int last_field_block_delay = 0;   /* start with no blocking at all */
11131       int block_delay_adjustment = player->block_delay_adjustment;
11132
11133       /* if player blocks last field, add delay for exactly one move */
11134       if (player->block_last_field)
11135       {
11136         last_field_block_delay += player->move_delay_value;
11137
11138         /* when blocking enabled, prevent moving up despite gravity */
11139 #if USE_PLAYER_GRAVITY
11140         if (player->gravity && player->MovDir == MV_UP)
11141           block_delay_adjustment = -1;
11142 #else
11143         if (game.gravity && player->MovDir == MV_UP)
11144           block_delay_adjustment = -1;
11145 #endif
11146       }
11147
11148       /* add block delay adjustment (also possible when not blocking) */
11149       last_field_block_delay += block_delay_adjustment;
11150
11151       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11152       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11153     }
11154
11155 #if USE_NEW_PLAYER_SPEED
11156     if (player->MovPos != 0)    /* player has not yet reached destination */
11157       return;
11158 #else
11159     return;
11160 #endif
11161   }
11162   else if (!FrameReached(&player->actual_frame_counter, 1))
11163     return;
11164
11165 #if USE_NEW_PLAYER_SPEED
11166   if (player->MovPos != 0)
11167   {
11168     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11169     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11170
11171     /* before DrawPlayer() to draw correct player graphic for this case */
11172     if (player->MovPos == 0)
11173       CheckGravityMovement(player);
11174   }
11175 #else
11176   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11177   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11178
11179   /* before DrawPlayer() to draw correct player graphic for this case */
11180   if (player->MovPos == 0)
11181     CheckGravityMovement(player);
11182 #endif
11183
11184   if (player->MovPos == 0)      /* player reached destination field */
11185   {
11186     if (player->move_delay_reset_counter > 0)
11187     {
11188       player->move_delay_reset_counter--;
11189
11190       if (player->move_delay_reset_counter == 0)
11191       {
11192         /* continue with normal speed after quickly moving through gate */
11193         HALVE_PLAYER_SPEED(player);
11194
11195         /* be able to make the next move without delay */
11196         player->move_delay = 0;
11197       }
11198     }
11199
11200     player->last_jx = jx;
11201     player->last_jy = jy;
11202
11203     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11204         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11205         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11206         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11207         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11208         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11209     {
11210       DrawPlayer(player);       /* needed here only to cleanup last field */
11211       RemovePlayer(player);
11212
11213       if (local_player->friends_still_needed == 0 ||
11214           IS_SP_ELEMENT(Feld[jx][jy]))
11215         PlayerWins(player);
11216     }
11217
11218     /* this breaks one level: "machine", level 000 */
11219     {
11220       int move_direction = player->MovDir;
11221       int enter_side = MV_DIR_OPPOSITE(move_direction);
11222       int leave_side = move_direction;
11223       int old_jx = last_jx;
11224       int old_jy = last_jy;
11225       int old_element = Feld[old_jx][old_jy];
11226       int new_element = Feld[jx][jy];
11227
11228       if (IS_CUSTOM_ELEMENT(old_element))
11229         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11230                                    CE_LEFT_BY_PLAYER,
11231                                    player->index_bit, leave_side);
11232
11233       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11234                                           CE_PLAYER_LEAVES_X,
11235                                           player->index_bit, leave_side);
11236
11237       if (IS_CUSTOM_ELEMENT(new_element))
11238         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11239                                    player->index_bit, enter_side);
11240
11241       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11242                                           CE_PLAYER_ENTERS_X,
11243                                           player->index_bit, enter_side);
11244
11245       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11246                                         CE_MOVE_OF_X, move_direction);
11247     }
11248
11249     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11250     {
11251       TestIfPlayerTouchesBadThing(jx, jy);
11252       TestIfPlayerTouchesCustomElement(jx, jy);
11253
11254       /* needed because pushed element has not yet reached its destination,
11255          so it would trigger a change event at its previous field location */
11256       if (!player->is_pushing)
11257         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
11258
11259       if (!player->active)
11260         RemovePlayer(player);
11261     }
11262
11263     if (!local_player->LevelSolved && level.use_step_counter)
11264     {
11265       int i;
11266
11267       TimePlayed++;
11268
11269       if (TimeLeft > 0)
11270       {
11271         TimeLeft--;
11272
11273         if (TimeLeft <= 10 && setup.time_limit)
11274           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11275
11276         DrawGameValue_Time(TimeLeft);
11277
11278         if (!TimeLeft && setup.time_limit)
11279           for (i = 0; i < MAX_PLAYERS; i++)
11280             KillPlayer(&stored_player[i]);
11281       }
11282       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11283         DrawGameValue_Time(TimePlayed);
11284     }
11285
11286     if (tape.single_step && tape.recording && !tape.pausing &&
11287         !player->programmed_action)
11288       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11289   }
11290 }
11291
11292 void ScrollScreen(struct PlayerInfo *player, int mode)
11293 {
11294   static unsigned long screen_frame_counter = 0;
11295
11296   if (mode == SCROLL_INIT)
11297   {
11298     /* set scrolling step size according to actual player's moving speed */
11299     ScrollStepSize = TILEX / player->move_delay_value;
11300
11301     screen_frame_counter = FrameCounter;
11302     ScreenMovDir = player->MovDir;
11303     ScreenMovPos = player->MovPos;
11304     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11305     return;
11306   }
11307   else if (!FrameReached(&screen_frame_counter, 1))
11308     return;
11309
11310   if (ScreenMovPos)
11311   {
11312     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11313     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11314     redraw_mask |= REDRAW_FIELD;
11315   }
11316   else
11317     ScreenMovDir = MV_NONE;
11318 }
11319
11320 void TestIfPlayerTouchesCustomElement(int x, int y)
11321 {
11322   static int xy[4][2] =
11323   {
11324     { 0, -1 },
11325     { -1, 0 },
11326     { +1, 0 },
11327     { 0, +1 }
11328   };
11329   static int trigger_sides[4][2] =
11330   {
11331     /* center side       border side */
11332     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11333     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11334     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11335     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11336   };
11337   static int touch_dir[4] =
11338   {
11339     MV_LEFT | MV_RIGHT,
11340     MV_UP   | MV_DOWN,
11341     MV_UP   | MV_DOWN,
11342     MV_LEFT | MV_RIGHT
11343   };
11344   int center_element = Feld[x][y];      /* should always be non-moving! */
11345   int i;
11346
11347   for (i = 0; i < NUM_DIRECTIONS; i++)
11348   {
11349     int xx = x + xy[i][0];
11350     int yy = y + xy[i][1];
11351     int center_side = trigger_sides[i][0];
11352     int border_side = trigger_sides[i][1];
11353     int border_element;
11354
11355     if (!IN_LEV_FIELD(xx, yy))
11356       continue;
11357
11358     if (IS_PLAYER(x, y))
11359     {
11360       struct PlayerInfo *player = PLAYERINFO(x, y);
11361
11362       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11363         border_element = Feld[xx][yy];          /* may be moving! */
11364       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11365         border_element = Feld[xx][yy];
11366       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11367         border_element = MovingOrBlocked2Element(xx, yy);
11368       else
11369         continue;               /* center and border element do not touch */
11370
11371       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11372                                  player->index_bit, border_side);
11373       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11374                                           CE_PLAYER_TOUCHES_X,
11375                                           player->index_bit, border_side);
11376     }
11377     else if (IS_PLAYER(xx, yy))
11378     {
11379       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11380
11381       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11382       {
11383         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11384           continue;             /* center and border element do not touch */
11385       }
11386
11387       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11388                                  player->index_bit, center_side);
11389       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11390                                           CE_PLAYER_TOUCHES_X,
11391                                           player->index_bit, center_side);
11392       break;
11393     }
11394   }
11395 }
11396
11397 #if USE_ELEMENT_TOUCHING_BUGFIX
11398
11399 void TestIfElementTouchesCustomElement(int x, int y)
11400 {
11401   static int xy[4][2] =
11402   {
11403     { 0, -1 },
11404     { -1, 0 },
11405     { +1, 0 },
11406     { 0, +1 }
11407   };
11408   static int trigger_sides[4][2] =
11409   {
11410     /* center side      border side */
11411     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11412     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11413     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11414     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11415   };
11416   static int touch_dir[4] =
11417   {
11418     MV_LEFT | MV_RIGHT,
11419     MV_UP   | MV_DOWN,
11420     MV_UP   | MV_DOWN,
11421     MV_LEFT | MV_RIGHT
11422   };
11423   boolean change_center_element = FALSE;
11424   int center_element = Feld[x][y];      /* should always be non-moving! */
11425   int border_element_old[NUM_DIRECTIONS];
11426   int i;
11427
11428   for (i = 0; i < NUM_DIRECTIONS; i++)
11429   {
11430     int xx = x + xy[i][0];
11431     int yy = y + xy[i][1];
11432     int border_element;
11433
11434     border_element_old[i] = -1;
11435
11436     if (!IN_LEV_FIELD(xx, yy))
11437       continue;
11438
11439     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11440       border_element = Feld[xx][yy];    /* may be moving! */
11441     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11442       border_element = Feld[xx][yy];
11443     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11444       border_element = MovingOrBlocked2Element(xx, yy);
11445     else
11446       continue;                 /* center and border element do not touch */
11447
11448     border_element_old[i] = border_element;
11449   }
11450
11451   for (i = 0; i < NUM_DIRECTIONS; i++)
11452   {
11453     int xx = x + xy[i][0];
11454     int yy = y + xy[i][1];
11455     int center_side = trigger_sides[i][0];
11456     int border_element = border_element_old[i];
11457
11458     if (border_element == -1)
11459       continue;
11460
11461     /* check for change of border element */
11462     CheckElementChangeBySide(xx, yy, border_element, center_element,
11463                              CE_TOUCHING_X, center_side);
11464   }
11465
11466   for (i = 0; i < NUM_DIRECTIONS; i++)
11467   {
11468     int border_side = trigger_sides[i][1];
11469     int border_element = border_element_old[i];
11470
11471     if (border_element == -1)
11472       continue;
11473
11474     /* check for change of center element (but change it only once) */
11475     if (!change_center_element)
11476       change_center_element =
11477         CheckElementChangeBySide(x, y, center_element, border_element,
11478                                  CE_TOUCHING_X, border_side);
11479   }
11480 }
11481
11482 #else
11483
11484 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11485 {
11486   static int xy[4][2] =
11487   {
11488     { 0, -1 },
11489     { -1, 0 },
11490     { +1, 0 },
11491     { 0, +1 }
11492   };
11493   static int trigger_sides[4][2] =
11494   {
11495     /* center side      border side */
11496     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11497     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11498     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11499     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11500   };
11501   static int touch_dir[4] =
11502   {
11503     MV_LEFT | MV_RIGHT,
11504     MV_UP   | MV_DOWN,
11505     MV_UP   | MV_DOWN,
11506     MV_LEFT | MV_RIGHT
11507   };
11508   boolean change_center_element = FALSE;
11509   int center_element = Feld[x][y];      /* should always be non-moving! */
11510   int i;
11511
11512   for (i = 0; i < NUM_DIRECTIONS; i++)
11513   {
11514     int xx = x + xy[i][0];
11515     int yy = y + xy[i][1];
11516     int center_side = trigger_sides[i][0];
11517     int border_side = trigger_sides[i][1];
11518     int border_element;
11519
11520     if (!IN_LEV_FIELD(xx, yy))
11521       continue;
11522
11523     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11524       border_element = Feld[xx][yy];    /* may be moving! */
11525     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11526       border_element = Feld[xx][yy];
11527     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11528       border_element = MovingOrBlocked2Element(xx, yy);
11529     else
11530       continue;                 /* center and border element do not touch */
11531
11532     /* check for change of center element (but change it only once) */
11533     if (!change_center_element)
11534       change_center_element =
11535         CheckElementChangeBySide(x, y, center_element, border_element,
11536                                  CE_TOUCHING_X, border_side);
11537
11538     /* check for change of border element */
11539     CheckElementChangeBySide(xx, yy, border_element, center_element,
11540                              CE_TOUCHING_X, center_side);
11541   }
11542 }
11543
11544 #endif
11545
11546 void TestIfElementHitsCustomElement(int x, int y, int direction)
11547 {
11548   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11549   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11550   int hitx = x + dx, hity = y + dy;
11551   int hitting_element = Feld[x][y];
11552   int touched_element;
11553
11554   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11555     return;
11556
11557   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11558                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11559
11560   if (IN_LEV_FIELD(hitx, hity))
11561   {
11562     int opposite_direction = MV_DIR_OPPOSITE(direction);
11563     int hitting_side = direction;
11564     int touched_side = opposite_direction;
11565     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11566                           MovDir[hitx][hity] != direction ||
11567                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11568
11569     object_hit = TRUE;
11570
11571     if (object_hit)
11572     {
11573       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11574                                CE_HITTING_X, touched_side);
11575
11576       CheckElementChangeBySide(hitx, hity, touched_element,
11577                                hitting_element, CE_HIT_BY_X, hitting_side);
11578
11579       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11580                                CE_HIT_BY_SOMETHING, opposite_direction);
11581     }
11582   }
11583
11584   /* "hitting something" is also true when hitting the playfield border */
11585   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11586                            CE_HITTING_SOMETHING, direction);
11587 }
11588
11589 #if 0
11590 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11591 {
11592   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11593   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11594   int hitx = x + dx, hity = y + dy;
11595   int hitting_element = Feld[x][y];
11596   int touched_element;
11597 #if 0
11598   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11599                         !IS_FREE(hitx, hity) &&
11600                         (!IS_MOVING(hitx, hity) ||
11601                          MovDir[hitx][hity] != direction ||
11602                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11603 #endif
11604
11605   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11606     return;
11607
11608 #if 0
11609   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11610     return;
11611 #endif
11612
11613   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11614                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11615
11616   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11617                            EP_CAN_SMASH_EVERYTHING, direction);
11618
11619   if (IN_LEV_FIELD(hitx, hity))
11620   {
11621     int opposite_direction = MV_DIR_OPPOSITE(direction);
11622     int hitting_side = direction;
11623     int touched_side = opposite_direction;
11624 #if 0
11625     int touched_element = MovingOrBlocked2Element(hitx, hity);
11626 #endif
11627 #if 1
11628     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11629                           MovDir[hitx][hity] != direction ||
11630                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11631
11632     object_hit = TRUE;
11633 #endif
11634
11635     if (object_hit)
11636     {
11637       int i;
11638
11639       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11640                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11641
11642       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11643                                CE_OTHER_IS_SMASHING, touched_side);
11644
11645       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11646                                CE_OTHER_GETS_SMASHED, hitting_side);
11647     }
11648   }
11649 }
11650 #endif
11651
11652 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11653 {
11654   int i, kill_x = -1, kill_y = -1;
11655
11656   int bad_element = -1;
11657   static int test_xy[4][2] =
11658   {
11659     { 0, -1 },
11660     { -1, 0 },
11661     { +1, 0 },
11662     { 0, +1 }
11663   };
11664   static int test_dir[4] =
11665   {
11666     MV_UP,
11667     MV_LEFT,
11668     MV_RIGHT,
11669     MV_DOWN
11670   };
11671
11672   for (i = 0; i < NUM_DIRECTIONS; i++)
11673   {
11674     int test_x, test_y, test_move_dir, test_element;
11675
11676     test_x = good_x + test_xy[i][0];
11677     test_y = good_y + test_xy[i][1];
11678
11679     if (!IN_LEV_FIELD(test_x, test_y))
11680       continue;
11681
11682     test_move_dir =
11683       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11684
11685     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11686
11687     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11688        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11689     */
11690     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11691         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11692     {
11693       kill_x = test_x;
11694       kill_y = test_y;
11695       bad_element = test_element;
11696
11697       break;
11698     }
11699   }
11700
11701   if (kill_x != -1 || kill_y != -1)
11702   {
11703     if (IS_PLAYER(good_x, good_y))
11704     {
11705       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11706
11707       if (player->shield_deadly_time_left > 0 &&
11708           !IS_INDESTRUCTIBLE(bad_element))
11709         Bang(kill_x, kill_y);
11710       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11711         KillPlayer(player);
11712     }
11713     else
11714       Bang(good_x, good_y);
11715   }
11716 }
11717
11718 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11719 {
11720   int i, kill_x = -1, kill_y = -1;
11721   int bad_element = Feld[bad_x][bad_y];
11722   static int test_xy[4][2] =
11723   {
11724     { 0, -1 },
11725     { -1, 0 },
11726     { +1, 0 },
11727     { 0, +1 }
11728   };
11729   static int touch_dir[4] =
11730   {
11731     MV_LEFT | MV_RIGHT,
11732     MV_UP   | MV_DOWN,
11733     MV_UP   | MV_DOWN,
11734     MV_LEFT | MV_RIGHT
11735   };
11736   static int test_dir[4] =
11737   {
11738     MV_UP,
11739     MV_LEFT,
11740     MV_RIGHT,
11741     MV_DOWN
11742   };
11743
11744   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11745     return;
11746
11747   for (i = 0; i < NUM_DIRECTIONS; i++)
11748   {
11749     int test_x, test_y, test_move_dir, test_element;
11750
11751     test_x = bad_x + test_xy[i][0];
11752     test_y = bad_y + test_xy[i][1];
11753     if (!IN_LEV_FIELD(test_x, test_y))
11754       continue;
11755
11756     test_move_dir =
11757       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11758
11759     test_element = Feld[test_x][test_y];
11760
11761     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11762        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11763     */
11764     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11765         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11766     {
11767       /* good thing is player or penguin that does not move away */
11768       if (IS_PLAYER(test_x, test_y))
11769       {
11770         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11771
11772         if (bad_element == EL_ROBOT && player->is_moving)
11773           continue;     /* robot does not kill player if he is moving */
11774
11775         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11776         {
11777           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11778             continue;           /* center and border element do not touch */
11779         }
11780
11781         kill_x = test_x;
11782         kill_y = test_y;
11783         break;
11784       }
11785       else if (test_element == EL_PENGUIN)
11786       {
11787         kill_x = test_x;
11788         kill_y = test_y;
11789         break;
11790       }
11791     }
11792   }
11793
11794   if (kill_x != -1 || kill_y != -1)
11795   {
11796     if (IS_PLAYER(kill_x, kill_y))
11797     {
11798       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11799
11800       if (player->shield_deadly_time_left > 0 &&
11801           !IS_INDESTRUCTIBLE(bad_element))
11802         Bang(bad_x, bad_y);
11803       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11804         KillPlayer(player);
11805     }
11806     else
11807       Bang(kill_x, kill_y);
11808   }
11809 }
11810
11811 void TestIfPlayerTouchesBadThing(int x, int y)
11812 {
11813   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11814 }
11815
11816 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11817 {
11818   TestIfGoodThingHitsBadThing(x, y, move_dir);
11819 }
11820
11821 void TestIfBadThingTouchesPlayer(int x, int y)
11822 {
11823   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11824 }
11825
11826 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11827 {
11828   TestIfBadThingHitsGoodThing(x, y, move_dir);
11829 }
11830
11831 void TestIfFriendTouchesBadThing(int x, int y)
11832 {
11833   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11834 }
11835
11836 void TestIfBadThingTouchesFriend(int x, int y)
11837 {
11838   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11839 }
11840
11841 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11842 {
11843   int i, kill_x = bad_x, kill_y = bad_y;
11844   static int xy[4][2] =
11845   {
11846     { 0, -1 },
11847     { -1, 0 },
11848     { +1, 0 },
11849     { 0, +1 }
11850   };
11851
11852   for (i = 0; i < NUM_DIRECTIONS; i++)
11853   {
11854     int x, y, element;
11855
11856     x = bad_x + xy[i][0];
11857     y = bad_y + xy[i][1];
11858     if (!IN_LEV_FIELD(x, y))
11859       continue;
11860
11861     element = Feld[x][y];
11862     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11863         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11864     {
11865       kill_x = x;
11866       kill_y = y;
11867       break;
11868     }
11869   }
11870
11871   if (kill_x != bad_x || kill_y != bad_y)
11872     Bang(bad_x, bad_y);
11873 }
11874
11875 void KillPlayer(struct PlayerInfo *player)
11876 {
11877   int jx = player->jx, jy = player->jy;
11878
11879   if (!player->active)
11880     return;
11881
11882   /* the following code was introduced to prevent an infinite loop when calling
11883      -> Bang()
11884      -> CheckTriggeredElementChangeExt()
11885      -> ExecuteCustomElementAction()
11886      -> KillPlayer()
11887      -> (infinitely repeating the above sequence of function calls)
11888      which occurs when killing the player while having a CE with the setting
11889      "kill player X when explosion of <player X>"; the solution using a new
11890      field "player->killed" was chosen for backwards compatibility, although
11891      clever use of the fields "player->active" etc. would probably also work */
11892 #if 1
11893   if (player->killed)
11894     return;
11895 #endif
11896
11897   player->killed = TRUE;
11898
11899   /* remove accessible field at the player's position */
11900   Feld[jx][jy] = EL_EMPTY;
11901
11902   /* deactivate shield (else Bang()/Explode() would not work right) */
11903   player->shield_normal_time_left = 0;
11904   player->shield_deadly_time_left = 0;
11905
11906   Bang(jx, jy);
11907   BuryPlayer(player);
11908 }
11909
11910 static void KillPlayerUnlessEnemyProtected(int x, int y)
11911 {
11912   if (!PLAYER_ENEMY_PROTECTED(x, y))
11913     KillPlayer(PLAYERINFO(x, y));
11914 }
11915
11916 static void KillPlayerUnlessExplosionProtected(int x, int y)
11917 {
11918   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11919     KillPlayer(PLAYERINFO(x, y));
11920 }
11921
11922 void BuryPlayer(struct PlayerInfo *player)
11923 {
11924   int jx = player->jx, jy = player->jy;
11925
11926   if (!player->active)
11927     return;
11928
11929   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11930   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11931
11932   player->GameOver = TRUE;
11933   RemovePlayer(player);
11934 }
11935
11936 void RemovePlayer(struct PlayerInfo *player)
11937 {
11938   int jx = player->jx, jy = player->jy;
11939   int i, found = FALSE;
11940
11941   player->present = FALSE;
11942   player->active = FALSE;
11943
11944   if (!ExplodeField[jx][jy])
11945     StorePlayer[jx][jy] = 0;
11946
11947   if (player->is_moving)
11948     DrawLevelField(player->last_jx, player->last_jy);
11949
11950   for (i = 0; i < MAX_PLAYERS; i++)
11951     if (stored_player[i].active)
11952       found = TRUE;
11953
11954   if (!found)
11955     AllPlayersGone = TRUE;
11956
11957   ExitX = ZX = jx;
11958   ExitY = ZY = jy;
11959 }
11960
11961 #if USE_NEW_SNAP_DELAY
11962 static void setFieldForSnapping(int x, int y, int element, int direction)
11963 {
11964   struct ElementInfo *ei = &element_info[element];
11965   int direction_bit = MV_DIR_TO_BIT(direction);
11966   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11967   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11968                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11969
11970   Feld[x][y] = EL_ELEMENT_SNAPPING;
11971   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11972
11973   ResetGfxAnimation(x, y);
11974
11975   GfxElement[x][y] = element;
11976   GfxAction[x][y] = action;
11977   GfxDir[x][y] = direction;
11978   GfxFrame[x][y] = -1;
11979 }
11980 #endif
11981
11982 /*
11983   =============================================================================
11984   checkDiagonalPushing()
11985   -----------------------------------------------------------------------------
11986   check if diagonal input device direction results in pushing of object
11987   (by checking if the alternative direction is walkable, diggable, ...)
11988   =============================================================================
11989 */
11990
11991 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11992                                     int x, int y, int real_dx, int real_dy)
11993 {
11994   int jx, jy, dx, dy, xx, yy;
11995
11996   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11997     return TRUE;
11998
11999   /* diagonal direction: check alternative direction */
12000   jx = player->jx;
12001   jy = player->jy;
12002   dx = x - jx;
12003   dy = y - jy;
12004   xx = jx + (dx == 0 ? real_dx : 0);
12005   yy = jy + (dy == 0 ? real_dy : 0);
12006
12007   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12008 }
12009
12010 /*
12011   =============================================================================
12012   DigField()
12013   -----------------------------------------------------------------------------
12014   x, y:                 field next to player (non-diagonal) to try to dig to
12015   real_dx, real_dy:     direction as read from input device (can be diagonal)
12016   =============================================================================
12017 */
12018
12019 int DigField(struct PlayerInfo *player,
12020              int oldx, int oldy, int x, int y,
12021              int real_dx, int real_dy, int mode)
12022 {
12023   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12024   boolean player_was_pushing = player->is_pushing;
12025   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12026   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12027   int jx = oldx, jy = oldy;
12028   int dx = x - jx, dy = y - jy;
12029   int nextx = x + dx, nexty = y + dy;
12030   int move_direction = (dx == -1 ? MV_LEFT  :
12031                         dx == +1 ? MV_RIGHT :
12032                         dy == -1 ? MV_UP    :
12033                         dy == +1 ? MV_DOWN  : MV_NONE);
12034   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12035   int dig_side = MV_DIR_OPPOSITE(move_direction);
12036   int old_element = Feld[jx][jy];
12037 #if USE_FIXED_DONT_RUN_INTO
12038   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12039 #else
12040   int element;
12041 #endif
12042   int collect_count;
12043
12044   if (is_player)                /* function can also be called by EL_PENGUIN */
12045   {
12046     if (player->MovPos == 0)
12047     {
12048       player->is_digging = FALSE;
12049       player->is_collecting = FALSE;
12050     }
12051
12052     if (player->MovPos == 0)    /* last pushing move finished */
12053       player->is_pushing = FALSE;
12054
12055     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
12056     {
12057       player->is_switching = FALSE;
12058       player->push_delay = -1;
12059
12060       return MP_NO_ACTION;
12061     }
12062   }
12063
12064 #if !USE_FIXED_DONT_RUN_INTO
12065   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12066     return MP_NO_ACTION;
12067 #endif
12068
12069   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12070     old_element = Back[jx][jy];
12071
12072   /* in case of element dropped at player position, check background */
12073   else if (Back[jx][jy] != EL_EMPTY &&
12074            game.engine_version >= VERSION_IDENT(2,2,0,0))
12075     old_element = Back[jx][jy];
12076
12077   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12078     return MP_NO_ACTION;        /* field has no opening in this direction */
12079
12080   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12081     return MP_NO_ACTION;        /* field has no opening in this direction */
12082
12083 #if USE_FIXED_DONT_RUN_INTO
12084   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12085   {
12086     SplashAcid(x, y);
12087
12088     Feld[jx][jy] = player->artwork_element;
12089     InitMovingField(jx, jy, MV_DOWN);
12090     Store[jx][jy] = EL_ACID;
12091     ContinueMoving(jx, jy);
12092     BuryPlayer(player);
12093
12094     return MP_DONT_RUN_INTO;
12095   }
12096 #endif
12097
12098 #if USE_FIXED_DONT_RUN_INTO
12099   if (player_can_move && DONT_RUN_INTO(element))
12100   {
12101     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12102
12103     return MP_DONT_RUN_INTO;
12104   }
12105 #endif
12106
12107 #if USE_FIXED_DONT_RUN_INTO
12108   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12109     return MP_NO_ACTION;
12110 #endif
12111
12112 #if !USE_FIXED_DONT_RUN_INTO
12113   element = Feld[x][y];
12114 #endif
12115
12116   collect_count = element_info[element].collect_count_initial;
12117
12118   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12119     return MP_NO_ACTION;
12120
12121   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12122     player_can_move = player_can_move_or_snap;
12123
12124   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12125       game.engine_version >= VERSION_IDENT(2,2,0,0))
12126   {
12127     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12128                                player->index_bit, dig_side);
12129     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12130                                         player->index_bit, dig_side);
12131
12132     if (element == EL_DC_LANDMINE)
12133       Bang(x, y);
12134
12135     if (Feld[x][y] != element)          /* field changed by snapping */
12136       return MP_ACTION;
12137
12138     return MP_NO_ACTION;
12139   }
12140
12141 #if USE_PLAYER_GRAVITY
12142   if (player->gravity && is_player && !player->is_auto_moving &&
12143       canFallDown(player) && move_direction != MV_DOWN &&
12144       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12145     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12146 #else
12147   if (game.gravity && is_player && !player->is_auto_moving &&
12148       canFallDown(player) && move_direction != MV_DOWN &&
12149       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12150     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12151 #endif
12152
12153   if (player_can_move &&
12154       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12155   {
12156     int sound_element = SND_ELEMENT(element);
12157     int sound_action = ACTION_WALKING;
12158
12159     if (IS_RND_GATE(element))
12160     {
12161       if (!player->key[RND_GATE_NR(element)])
12162         return MP_NO_ACTION;
12163     }
12164     else if (IS_RND_GATE_GRAY(element))
12165     {
12166       if (!player->key[RND_GATE_GRAY_NR(element)])
12167         return MP_NO_ACTION;
12168     }
12169     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12170     {
12171       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12172         return MP_NO_ACTION;
12173     }
12174     else if (element == EL_EXIT_OPEN ||
12175              element == EL_EM_EXIT_OPEN ||
12176              element == EL_STEEL_EXIT_OPEN ||
12177              element == EL_EM_STEEL_EXIT_OPEN ||
12178              element == EL_SP_EXIT_OPEN ||
12179              element == EL_SP_EXIT_OPENING)
12180     {
12181       sound_action = ACTION_PASSING;    /* player is passing exit */
12182     }
12183     else if (element == EL_EMPTY)
12184     {
12185       sound_action = ACTION_MOVING;             /* nothing to walk on */
12186     }
12187
12188     /* play sound from background or player, whatever is available */
12189     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12190       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12191     else
12192       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12193   }
12194   else if (player_can_move &&
12195            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12196   {
12197     if (!ACCESS_FROM(element, opposite_direction))
12198       return MP_NO_ACTION;      /* field not accessible from this direction */
12199
12200     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12201       return MP_NO_ACTION;
12202
12203     if (IS_EM_GATE(element))
12204     {
12205       if (!player->key[EM_GATE_NR(element)])
12206         return MP_NO_ACTION;
12207     }
12208     else if (IS_EM_GATE_GRAY(element))
12209     {
12210       if (!player->key[EM_GATE_GRAY_NR(element)])
12211         return MP_NO_ACTION;
12212     }
12213     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12214     {
12215       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12216         return MP_NO_ACTION;
12217     }
12218     else if (IS_EMC_GATE(element))
12219     {
12220       if (!player->key[EMC_GATE_NR(element)])
12221         return MP_NO_ACTION;
12222     }
12223     else if (IS_EMC_GATE_GRAY(element))
12224     {
12225       if (!player->key[EMC_GATE_GRAY_NR(element)])
12226         return MP_NO_ACTION;
12227     }
12228     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12229     {
12230       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12231         return MP_NO_ACTION;
12232     }
12233     else if (element == EL_DC_GATE_WHITE ||
12234              element == EL_DC_GATE_WHITE_GRAY ||
12235              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12236     {
12237       if (player->num_white_keys == 0)
12238         return MP_NO_ACTION;
12239
12240       player->num_white_keys--;
12241     }
12242     else if (IS_SP_PORT(element))
12243     {
12244       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12245           element == EL_SP_GRAVITY_PORT_RIGHT ||
12246           element == EL_SP_GRAVITY_PORT_UP ||
12247           element == EL_SP_GRAVITY_PORT_DOWN)
12248 #if USE_PLAYER_GRAVITY
12249         player->gravity = !player->gravity;
12250 #else
12251         game.gravity = !game.gravity;
12252 #endif
12253       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12254                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12255                element == EL_SP_GRAVITY_ON_PORT_UP ||
12256                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12257 #if USE_PLAYER_GRAVITY
12258         player->gravity = TRUE;
12259 #else
12260         game.gravity = TRUE;
12261 #endif
12262       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12263                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12264                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12265                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12266 #if USE_PLAYER_GRAVITY
12267         player->gravity = FALSE;
12268 #else
12269         game.gravity = FALSE;
12270 #endif
12271     }
12272
12273     /* automatically move to the next field with double speed */
12274     player->programmed_action = move_direction;
12275
12276     if (player->move_delay_reset_counter == 0)
12277     {
12278       player->move_delay_reset_counter = 2;     /* two double speed steps */
12279
12280       DOUBLE_PLAYER_SPEED(player);
12281     }
12282
12283     PlayLevelSoundAction(x, y, ACTION_PASSING);
12284   }
12285   else if (player_can_move_or_snap && IS_DIGGABLE(element))
12286   {
12287     RemoveField(x, y);
12288
12289     if (mode != DF_SNAP)
12290     {
12291       GfxElement[x][y] = GFX_ELEMENT(element);
12292       player->is_digging = TRUE;
12293     }
12294
12295     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12296
12297     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12298                                         player->index_bit, dig_side);
12299
12300     if (mode == DF_SNAP)
12301     {
12302 #if USE_NEW_SNAP_DELAY
12303       if (level.block_snap_field)
12304         setFieldForSnapping(x, y, element, move_direction);
12305       else
12306         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12307 #else
12308       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12309 #endif
12310
12311       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12312                                           player->index_bit, dig_side);
12313     }
12314   }
12315   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12316   {
12317     RemoveField(x, y);
12318
12319     if (is_player && mode != DF_SNAP)
12320     {
12321       GfxElement[x][y] = element;
12322       player->is_collecting = TRUE;
12323     }
12324
12325     if (element == EL_SPEED_PILL)
12326     {
12327       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12328     }
12329     else if (element == EL_EXTRA_TIME && level.time > 0)
12330     {
12331       TimeLeft += level.extra_time;
12332       DrawGameValue_Time(TimeLeft);
12333     }
12334     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12335     {
12336       player->shield_normal_time_left += level.shield_normal_time;
12337       if (element == EL_SHIELD_DEADLY)
12338         player->shield_deadly_time_left += level.shield_deadly_time;
12339     }
12340     else if (element == EL_DYNAMITE ||
12341              element == EL_EM_DYNAMITE ||
12342              element == EL_SP_DISK_RED)
12343     {
12344       if (player->inventory_size < MAX_INVENTORY_SIZE)
12345         player->inventory_element[player->inventory_size++] = element;
12346
12347       DrawGameDoorValues();
12348     }
12349     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12350     {
12351       player->dynabomb_count++;
12352       player->dynabombs_left++;
12353     }
12354     else if (element == EL_DYNABOMB_INCREASE_SIZE)
12355     {
12356       player->dynabomb_size++;
12357     }
12358     else if (element == EL_DYNABOMB_INCREASE_POWER)
12359     {
12360       player->dynabomb_xl = TRUE;
12361     }
12362     else if (IS_KEY(element))
12363     {
12364       player->key[KEY_NR(element)] = TRUE;
12365
12366       DrawGameDoorValues();
12367     }
12368     else if (element == EL_DC_KEY_WHITE)
12369     {
12370       player->num_white_keys++;
12371
12372       /* display white keys? */
12373       /* DrawGameDoorValues(); */
12374     }
12375     else if (IS_ENVELOPE(element))
12376     {
12377       player->show_envelope = element;
12378     }
12379     else if (element == EL_EMC_LENSES)
12380     {
12381       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12382
12383       RedrawAllInvisibleElementsForLenses();
12384     }
12385     else if (element == EL_EMC_MAGNIFIER)
12386     {
12387       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12388
12389       RedrawAllInvisibleElementsForMagnifier();
12390     }
12391     else if (IS_DROPPABLE(element) ||
12392              IS_THROWABLE(element))     /* can be collected and dropped */
12393     {
12394       int i;
12395
12396       if (collect_count == 0)
12397         player->inventory_infinite_element = element;
12398       else
12399         for (i = 0; i < collect_count; i++)
12400           if (player->inventory_size < MAX_INVENTORY_SIZE)
12401             player->inventory_element[player->inventory_size++] = element;
12402
12403       DrawGameDoorValues();
12404     }
12405     else if (collect_count > 0)
12406     {
12407       local_player->gems_still_needed -= collect_count;
12408       if (local_player->gems_still_needed < 0)
12409         local_player->gems_still_needed = 0;
12410
12411       DrawGameValue_Emeralds(local_player->gems_still_needed);
12412     }
12413
12414     RaiseScoreElement(element);
12415     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12416
12417     if (is_player)
12418       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12419                                           player->index_bit, dig_side);
12420
12421     if (mode == DF_SNAP)
12422     {
12423 #if USE_NEW_SNAP_DELAY
12424       if (level.block_snap_field)
12425         setFieldForSnapping(x, y, element, move_direction);
12426       else
12427         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12428 #else
12429       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12430 #endif
12431
12432       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12433                                           player->index_bit, dig_side);
12434     }
12435   }
12436   else if (player_can_move_or_snap && IS_PUSHABLE(element))
12437   {
12438     if (mode == DF_SNAP && element != EL_BD_ROCK)
12439       return MP_NO_ACTION;
12440
12441     if (CAN_FALL(element) && dy)
12442       return MP_NO_ACTION;
12443
12444     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12445         !(element == EL_SPRING && level.use_spring_bug))
12446       return MP_NO_ACTION;
12447
12448     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12449         ((move_direction & MV_VERTICAL &&
12450           ((element_info[element].move_pattern & MV_LEFT &&
12451             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12452            (element_info[element].move_pattern & MV_RIGHT &&
12453             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12454          (move_direction & MV_HORIZONTAL &&
12455           ((element_info[element].move_pattern & MV_UP &&
12456             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12457            (element_info[element].move_pattern & MV_DOWN &&
12458             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12459       return MP_NO_ACTION;
12460
12461     /* do not push elements already moving away faster than player */
12462     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12463         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12464       return MP_NO_ACTION;
12465
12466     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12467     {
12468       if (player->push_delay_value == -1 || !player_was_pushing)
12469         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12470     }
12471     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12472     {
12473       if (player->push_delay_value == -1)
12474         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12475     }
12476     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12477     {
12478       if (!player->is_pushing)
12479         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12480     }
12481
12482     player->is_pushing = TRUE;
12483     player->is_active = TRUE;
12484
12485     if (!(IN_LEV_FIELD(nextx, nexty) &&
12486           (IS_FREE(nextx, nexty) ||
12487            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12488             IS_SB_ELEMENT(element)))))
12489       return MP_NO_ACTION;
12490
12491     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12492       return MP_NO_ACTION;
12493
12494     if (player->push_delay == -1)       /* new pushing; restart delay */
12495       player->push_delay = 0;
12496
12497     if (player->push_delay < player->push_delay_value &&
12498         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12499         element != EL_SPRING && element != EL_BALLOON)
12500     {
12501       /* make sure that there is no move delay before next try to push */
12502       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12503         player->move_delay = 0;
12504
12505       return MP_NO_ACTION;
12506     }
12507
12508     if (IS_SB_ELEMENT(element))
12509     {
12510       if (element == EL_SOKOBAN_FIELD_FULL)
12511       {
12512         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12513         local_player->sokobanfields_still_needed++;
12514       }
12515
12516       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12517       {
12518         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12519         local_player->sokobanfields_still_needed--;
12520       }
12521
12522       Feld[x][y] = EL_SOKOBAN_OBJECT;
12523
12524       if (Back[x][y] == Back[nextx][nexty])
12525         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12526       else if (Back[x][y] != 0)
12527         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12528                                     ACTION_EMPTYING);
12529       else
12530         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12531                                     ACTION_FILLING);
12532
12533       if (local_player->sokobanfields_still_needed == 0 &&
12534           game.emulation == EMU_SOKOBAN)
12535       {
12536         PlayerWins(player);
12537
12538         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12539       }
12540     }
12541     else
12542       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12543
12544     InitMovingField(x, y, move_direction);
12545     GfxAction[x][y] = ACTION_PUSHING;
12546
12547     if (mode == DF_SNAP)
12548       ContinueMoving(x, y);
12549     else
12550       MovPos[x][y] = (dx != 0 ? dx : dy);
12551
12552     Pushed[x][y] = TRUE;
12553     Pushed[nextx][nexty] = TRUE;
12554
12555     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12556       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12557     else
12558       player->push_delay_value = -1;    /* get new value later */
12559
12560     /* check for element change _after_ element has been pushed */
12561     if (game.use_change_when_pushing_bug)
12562     {
12563       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12564                                  player->index_bit, dig_side);
12565       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12566                                           player->index_bit, dig_side);
12567     }
12568   }
12569   else if (IS_SWITCHABLE(element))
12570   {
12571     if (PLAYER_SWITCHING(player, x, y))
12572     {
12573       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12574                                           player->index_bit, dig_side);
12575
12576       return MP_ACTION;
12577     }
12578
12579     player->is_switching = TRUE;
12580     player->switch_x = x;
12581     player->switch_y = y;
12582
12583     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12584
12585     if (element == EL_ROBOT_WHEEL)
12586     {
12587       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12588       ZX = x;
12589       ZY = y;
12590
12591       DrawLevelField(x, y);
12592     }
12593     else if (element == EL_SP_TERMINAL)
12594     {
12595       int xx, yy;
12596
12597       SCAN_PLAYFIELD(xx, yy)
12598       {
12599         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12600           Bang(xx, yy);
12601         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12602           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12603       }
12604     }
12605     else if (IS_BELT_SWITCH(element))
12606     {
12607       ToggleBeltSwitch(x, y);
12608     }
12609     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12610              element == EL_SWITCHGATE_SWITCH_DOWN ||
12611              element == EL_DC_SWITCHGATE_SWITCH_UP ||
12612              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12613     {
12614       ToggleSwitchgateSwitch(x, y);
12615     }
12616     else if (element == EL_LIGHT_SWITCH ||
12617              element == EL_LIGHT_SWITCH_ACTIVE)
12618     {
12619       ToggleLightSwitch(x, y);
12620     }
12621     else if (element == EL_TIMEGATE_SWITCH ||
12622              element == EL_DC_TIMEGATE_SWITCH)
12623     {
12624       ActivateTimegateSwitch(x, y);
12625     }
12626     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12627              element == EL_BALLOON_SWITCH_RIGHT ||
12628              element == EL_BALLOON_SWITCH_UP    ||
12629              element == EL_BALLOON_SWITCH_DOWN  ||
12630              element == EL_BALLOON_SWITCH_NONE  ||
12631              element == EL_BALLOON_SWITCH_ANY)
12632     {
12633       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12634                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12635                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12636                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12637                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12638                              move_direction);
12639     }
12640     else if (element == EL_LAMP)
12641     {
12642       Feld[x][y] = EL_LAMP_ACTIVE;
12643       local_player->lights_still_needed--;
12644
12645       ResetGfxAnimation(x, y);
12646       DrawLevelField(x, y);
12647     }
12648     else if (element == EL_TIME_ORB_FULL)
12649     {
12650       Feld[x][y] = EL_TIME_ORB_EMPTY;
12651
12652       if (level.time > 0 || level.use_time_orb_bug)
12653       {
12654         TimeLeft += level.time_orb_time;
12655         DrawGameValue_Time(TimeLeft);
12656       }
12657
12658       ResetGfxAnimation(x, y);
12659       DrawLevelField(x, y);
12660     }
12661     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12662              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12663     {
12664       int xx, yy;
12665
12666       game.ball_state = !game.ball_state;
12667
12668       SCAN_PLAYFIELD(xx, yy)
12669       {
12670         int e = Feld[xx][yy];
12671
12672         if (game.ball_state)
12673         {
12674           if (e == EL_EMC_MAGIC_BALL)
12675             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12676           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12677             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12678         }
12679         else
12680         {
12681           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12682             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12683           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12684             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12685         }
12686       }
12687     }
12688
12689     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12690                                         player->index_bit, dig_side);
12691
12692     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12693                                         player->index_bit, dig_side);
12694
12695     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12696                                         player->index_bit, dig_side);
12697
12698     return MP_ACTION;
12699   }
12700   else
12701   {
12702     if (!PLAYER_SWITCHING(player, x, y))
12703     {
12704       player->is_switching = TRUE;
12705       player->switch_x = x;
12706       player->switch_y = y;
12707
12708       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12709                                  player->index_bit, dig_side);
12710       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12711                                           player->index_bit, dig_side);
12712
12713       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12714                                  player->index_bit, dig_side);
12715       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12716                                           player->index_bit, dig_side);
12717     }
12718
12719     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12720                                player->index_bit, dig_side);
12721     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12722                                         player->index_bit, dig_side);
12723
12724     return MP_NO_ACTION;
12725   }
12726
12727   player->push_delay = -1;
12728
12729   if (is_player)                /* function can also be called by EL_PENGUIN */
12730   {
12731     if (Feld[x][y] != element)          /* really digged/collected something */
12732     {
12733       player->is_collecting = !player->is_digging;
12734       player->is_active = TRUE;
12735     }
12736   }
12737
12738   return MP_MOVING;
12739 }
12740
12741 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12742 {
12743   int jx = player->jx, jy = player->jy;
12744   int x = jx + dx, y = jy + dy;
12745   int snap_direction = (dx == -1 ? MV_LEFT  :
12746                         dx == +1 ? MV_RIGHT :
12747                         dy == -1 ? MV_UP    :
12748                         dy == +1 ? MV_DOWN  : MV_NONE);
12749   boolean can_continue_snapping = (level.continuous_snapping &&
12750                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12751
12752   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12753     return FALSE;
12754
12755   if (!player->active || !IN_LEV_FIELD(x, y))
12756     return FALSE;
12757
12758   if (dx && dy)
12759     return FALSE;
12760
12761   if (!dx && !dy)
12762   {
12763     if (player->MovPos == 0)
12764       player->is_pushing = FALSE;
12765
12766     player->is_snapping = FALSE;
12767
12768     if (player->MovPos == 0)
12769     {
12770       player->is_moving = FALSE;
12771       player->is_digging = FALSE;
12772       player->is_collecting = FALSE;
12773     }
12774
12775     return FALSE;
12776   }
12777
12778 #if USE_NEW_CONTINUOUS_SNAPPING
12779   /* prevent snapping with already pressed snap key when not allowed */
12780   if (player->is_snapping && !can_continue_snapping)
12781     return FALSE;
12782 #else
12783   if (player->is_snapping)
12784     return FALSE;
12785 #endif
12786
12787   player->MovDir = snap_direction;
12788
12789   if (player->MovPos == 0)
12790   {
12791     player->is_moving = FALSE;
12792     player->is_digging = FALSE;
12793     player->is_collecting = FALSE;
12794   }
12795
12796   player->is_dropping = FALSE;
12797   player->is_dropping_pressed = FALSE;
12798   player->drop_pressed_delay = 0;
12799
12800   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12801     return FALSE;
12802
12803   player->is_snapping = TRUE;
12804   player->is_active = TRUE;
12805
12806   if (player->MovPos == 0)
12807   {
12808     player->is_moving = FALSE;
12809     player->is_digging = FALSE;
12810     player->is_collecting = FALSE;
12811   }
12812
12813   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12814     DrawLevelField(player->last_jx, player->last_jy);
12815
12816   DrawLevelField(x, y);
12817
12818   return TRUE;
12819 }
12820
12821 boolean DropElement(struct PlayerInfo *player)
12822 {
12823   int old_element, new_element;
12824   int dropx = player->jx, dropy = player->jy;
12825   int drop_direction = player->MovDir;
12826   int drop_side = drop_direction;
12827   int drop_element = (player->inventory_size > 0 ?
12828                       player->inventory_element[player->inventory_size - 1] :
12829                       player->inventory_infinite_element != EL_UNDEFINED ?
12830                       player->inventory_infinite_element :
12831                       player->dynabombs_left > 0 ?
12832                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12833                       EL_UNDEFINED);
12834
12835   player->is_dropping_pressed = TRUE;
12836
12837   /* do not drop an element on top of another element; when holding drop key
12838      pressed without moving, dropped element must move away before the next
12839      element can be dropped (this is especially important if the next element
12840      is dynamite, which can be placed on background for historical reasons) */
12841   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12842     return MP_ACTION;
12843
12844   if (IS_THROWABLE(drop_element))
12845   {
12846     dropx += GET_DX_FROM_DIR(drop_direction);
12847     dropy += GET_DY_FROM_DIR(drop_direction);
12848
12849     if (!IN_LEV_FIELD(dropx, dropy))
12850       return FALSE;
12851   }
12852
12853   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12854   new_element = drop_element;           /* default: no change when dropping */
12855
12856   /* check if player is active, not moving and ready to drop */
12857   if (!player->active || player->MovPos || player->drop_delay > 0)
12858     return FALSE;
12859
12860   /* check if player has anything that can be dropped */
12861   if (new_element == EL_UNDEFINED)
12862     return FALSE;
12863
12864   /* check if drop key was pressed long enough for EM style dynamite */
12865   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12866     return FALSE;
12867
12868   /* check if anything can be dropped at the current position */
12869   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12870     return FALSE;
12871
12872   /* collected custom elements can only be dropped on empty fields */
12873   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12874     return FALSE;
12875
12876   if (old_element != EL_EMPTY)
12877     Back[dropx][dropy] = old_element;   /* store old element on this field */
12878
12879   ResetGfxAnimation(dropx, dropy);
12880   ResetRandomAnimationValue(dropx, dropy);
12881
12882   if (player->inventory_size > 0 ||
12883       player->inventory_infinite_element != EL_UNDEFINED)
12884   {
12885     if (player->inventory_size > 0)
12886     {
12887       player->inventory_size--;
12888
12889       DrawGameDoorValues();
12890
12891       if (new_element == EL_DYNAMITE)
12892         new_element = EL_DYNAMITE_ACTIVE;
12893       else if (new_element == EL_EM_DYNAMITE)
12894         new_element = EL_EM_DYNAMITE_ACTIVE;
12895       else if (new_element == EL_SP_DISK_RED)
12896         new_element = EL_SP_DISK_RED_ACTIVE;
12897     }
12898
12899     Feld[dropx][dropy] = new_element;
12900
12901     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12902       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12903                           el2img(Feld[dropx][dropy]), 0);
12904
12905     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12906
12907     /* needed if previous element just changed to "empty" in the last frame */
12908     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12909
12910     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12911                                player->index_bit, drop_side);
12912     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12913                                         CE_PLAYER_DROPS_X,
12914                                         player->index_bit, drop_side);
12915
12916     TestIfElementTouchesCustomElement(dropx, dropy);
12917   }
12918   else          /* player is dropping a dyna bomb */
12919   {
12920     player->dynabombs_left--;
12921
12922     Feld[dropx][dropy] = new_element;
12923
12924     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12925       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12926                           el2img(Feld[dropx][dropy]), 0);
12927
12928     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12929   }
12930
12931   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12932     InitField_WithBug1(dropx, dropy, FALSE);
12933
12934   new_element = Feld[dropx][dropy];     /* element might have changed */
12935
12936   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12937       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12938   {
12939     int move_direction, nextx, nexty;
12940
12941     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12942       MovDir[dropx][dropy] = drop_direction;
12943
12944     move_direction = MovDir[dropx][dropy];
12945     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12946     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12947
12948     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12949
12950 #if USE_FIX_IMPACT_COLLISION
12951     /* do not cause impact style collision by dropping elements that can fall */
12952     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12953 #else
12954     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12955 #endif
12956   }
12957
12958   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12959   player->is_dropping = TRUE;
12960
12961   player->drop_pressed_delay = 0;
12962   player->is_dropping_pressed = FALSE;
12963
12964   player->drop_x = dropx;
12965   player->drop_y = dropy;
12966
12967   return TRUE;
12968 }
12969
12970 /* ------------------------------------------------------------------------- */
12971 /* game sound playing functions                                              */
12972 /* ------------------------------------------------------------------------- */
12973
12974 static int *loop_sound_frame = NULL;
12975 static int *loop_sound_volume = NULL;
12976
12977 void InitPlayLevelSound()
12978 {
12979   int num_sounds = getSoundListSize();
12980
12981   checked_free(loop_sound_frame);
12982   checked_free(loop_sound_volume);
12983
12984   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12985   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12986 }
12987
12988 static void PlayLevelSound(int x, int y, int nr)
12989 {
12990   int sx = SCREENX(x), sy = SCREENY(y);
12991   int volume, stereo_position;
12992   int max_distance = 8;
12993   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12994
12995   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12996       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12997     return;
12998
12999   if (!IN_LEV_FIELD(x, y) ||
13000       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13001       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13002     return;
13003
13004   volume = SOUND_MAX_VOLUME;
13005
13006   if (!IN_SCR_FIELD(sx, sy))
13007   {
13008     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13009     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13010
13011     volume -= volume * (dx > dy ? dx : dy) / max_distance;
13012   }
13013
13014   stereo_position = (SOUND_MAX_LEFT +
13015                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13016                      (SCR_FIELDX + 2 * max_distance));
13017
13018   if (IS_LOOP_SOUND(nr))
13019   {
13020     /* This assures that quieter loop sounds do not overwrite louder ones,
13021        while restarting sound volume comparison with each new game frame. */
13022
13023     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13024       return;
13025
13026     loop_sound_volume[nr] = volume;
13027     loop_sound_frame[nr] = FrameCounter;
13028   }
13029
13030   PlaySoundExt(nr, volume, stereo_position, type);
13031 }
13032
13033 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13034 {
13035   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13036                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13037                  y < LEVELY(BY1) ? LEVELY(BY1) :
13038                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13039                  sound_action);
13040 }
13041
13042 static void PlayLevelSoundAction(int x, int y, int action)
13043 {
13044   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13045 }
13046
13047 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13048 {
13049   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13050
13051   if (sound_effect != SND_UNDEFINED)
13052     PlayLevelSound(x, y, sound_effect);
13053 }
13054
13055 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13056                                               int action)
13057 {
13058   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13059
13060   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13061     PlayLevelSound(x, y, sound_effect);
13062 }
13063
13064 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13065 {
13066   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13067
13068   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13069     PlayLevelSound(x, y, sound_effect);
13070 }
13071
13072 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13073 {
13074   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13075
13076   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13077     StopSound(sound_effect);
13078 }
13079
13080 static void PlayLevelMusic()
13081 {
13082   if (levelset.music[level_nr] != MUS_UNDEFINED)
13083     PlayMusic(levelset.music[level_nr]);        /* from config file */
13084   else
13085     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13086 }
13087
13088 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13089 {
13090   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13091   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13092   int x = xx - 1 - offset;
13093   int y = yy - 1 - offset;
13094
13095   switch (sample)
13096   {
13097     case SAMPLE_blank:
13098       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13099       break;
13100
13101     case SAMPLE_roll:
13102       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13103       break;
13104
13105     case SAMPLE_stone:
13106       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13107       break;
13108
13109     case SAMPLE_nut:
13110       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13111       break;
13112
13113     case SAMPLE_crack:
13114       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13115       break;
13116
13117     case SAMPLE_bug:
13118       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13119       break;
13120
13121     case SAMPLE_tank:
13122       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13123       break;
13124
13125     case SAMPLE_android_clone:
13126       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13127       break;
13128
13129     case SAMPLE_android_move:
13130       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13131       break;
13132
13133     case SAMPLE_spring:
13134       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13135       break;
13136
13137     case SAMPLE_slurp:
13138       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13139       break;
13140
13141     case SAMPLE_eater:
13142       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13143       break;
13144
13145     case SAMPLE_eater_eat:
13146       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13147       break;
13148
13149     case SAMPLE_alien:
13150       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13151       break;
13152
13153     case SAMPLE_collect:
13154       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13155       break;
13156
13157     case SAMPLE_diamond:
13158       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13159       break;
13160
13161     case SAMPLE_squash:
13162       /* !!! CHECK THIS !!! */
13163 #if 1
13164       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13165 #else
13166       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13167 #endif
13168       break;
13169
13170     case SAMPLE_wonderfall:
13171       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13172       break;
13173
13174     case SAMPLE_drip:
13175       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13176       break;
13177
13178     case SAMPLE_push:
13179       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13180       break;
13181
13182     case SAMPLE_dirt:
13183       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13184       break;
13185
13186     case SAMPLE_acid:
13187       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13188       break;
13189
13190     case SAMPLE_ball:
13191       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13192       break;
13193
13194     case SAMPLE_grow:
13195       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13196       break;
13197
13198     case SAMPLE_wonder:
13199       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13200       break;
13201
13202     case SAMPLE_door:
13203       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13204       break;
13205
13206     case SAMPLE_exit_open:
13207       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13208       break;
13209
13210     case SAMPLE_exit_leave:
13211       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13212       break;
13213
13214     case SAMPLE_dynamite:
13215       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13216       break;
13217
13218     case SAMPLE_tick:
13219       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13220       break;
13221
13222     case SAMPLE_press:
13223       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13224       break;
13225
13226     case SAMPLE_wheel:
13227       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13228       break;
13229
13230     case SAMPLE_boom:
13231       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13232       break;
13233
13234     case SAMPLE_die:
13235       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13236       break;
13237
13238     case SAMPLE_time:
13239       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13240       break;
13241
13242     default:
13243       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13244       break;
13245   }
13246 }
13247
13248 #if 0
13249 void ChangeTime(int value)
13250 {
13251   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13252
13253   *time += value;
13254
13255   /* EMC game engine uses value from time counter of RND game engine */
13256   level.native_em_level->lev->time = *time;
13257
13258   DrawGameValue_Time(*time);
13259 }
13260
13261 void RaiseScore(int value)
13262 {
13263   /* EMC game engine and RND game engine have separate score counters */
13264   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13265                 &level.native_em_level->lev->score : &local_player->score);
13266
13267   *score += value;
13268
13269   DrawGameValue_Score(*score);
13270 }
13271 #endif
13272
13273 void RaiseScore(int value)
13274 {
13275   local_player->score += value;
13276
13277   DrawGameValue_Score(local_player->score);
13278 }
13279
13280 void RaiseScoreElement(int element)
13281 {
13282   switch (element)
13283   {
13284     case EL_EMERALD:
13285     case EL_BD_DIAMOND:
13286     case EL_EMERALD_YELLOW:
13287     case EL_EMERALD_RED:
13288     case EL_EMERALD_PURPLE:
13289     case EL_SP_INFOTRON:
13290       RaiseScore(level.score[SC_EMERALD]);
13291       break;
13292     case EL_DIAMOND:
13293       RaiseScore(level.score[SC_DIAMOND]);
13294       break;
13295     case EL_CRYSTAL:
13296       RaiseScore(level.score[SC_CRYSTAL]);
13297       break;
13298     case EL_PEARL:
13299       RaiseScore(level.score[SC_PEARL]);
13300       break;
13301     case EL_BUG:
13302     case EL_BD_BUTTERFLY:
13303     case EL_SP_ELECTRON:
13304       RaiseScore(level.score[SC_BUG]);
13305       break;
13306     case EL_SPACESHIP:
13307     case EL_BD_FIREFLY:
13308     case EL_SP_SNIKSNAK:
13309       RaiseScore(level.score[SC_SPACESHIP]);
13310       break;
13311     case EL_YAMYAM:
13312     case EL_DARK_YAMYAM:
13313       RaiseScore(level.score[SC_YAMYAM]);
13314       break;
13315     case EL_ROBOT:
13316       RaiseScore(level.score[SC_ROBOT]);
13317       break;
13318     case EL_PACMAN:
13319       RaiseScore(level.score[SC_PACMAN]);
13320       break;
13321     case EL_NUT:
13322       RaiseScore(level.score[SC_NUT]);
13323       break;
13324     case EL_DYNAMITE:
13325     case EL_EM_DYNAMITE:
13326     case EL_SP_DISK_RED:
13327     case EL_DYNABOMB_INCREASE_NUMBER:
13328     case EL_DYNABOMB_INCREASE_SIZE:
13329     case EL_DYNABOMB_INCREASE_POWER:
13330       RaiseScore(level.score[SC_DYNAMITE]);
13331       break;
13332     case EL_SHIELD_NORMAL:
13333     case EL_SHIELD_DEADLY:
13334       RaiseScore(level.score[SC_SHIELD]);
13335       break;
13336     case EL_EXTRA_TIME:
13337       RaiseScore(level.extra_time_score);
13338       break;
13339     case EL_KEY_1:
13340     case EL_KEY_2:
13341     case EL_KEY_3:
13342     case EL_KEY_4:
13343     case EL_EM_KEY_1:
13344     case EL_EM_KEY_2:
13345     case EL_EM_KEY_3:
13346     case EL_EM_KEY_4:
13347     case EL_EMC_KEY_5:
13348     case EL_EMC_KEY_6:
13349     case EL_EMC_KEY_7:
13350     case EL_EMC_KEY_8:
13351     case EL_DC_KEY_WHITE:
13352       RaiseScore(level.score[SC_KEY]);
13353       break;
13354     default:
13355       RaiseScore(element_info[element].collect_score);
13356       break;
13357   }
13358 }
13359
13360 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13361 {
13362   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13363   {
13364 #if defined(NETWORK_AVALIABLE)
13365     if (options.network)
13366       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13367     else
13368 #endif
13369     {
13370       if (quick_quit)
13371       {
13372         game_status = GAME_MODE_MAIN;
13373
13374         DrawMainMenu();
13375       }
13376       else
13377       {
13378         FadeOut(REDRAW_FIELD);
13379
13380         game_status = GAME_MODE_MAIN;
13381
13382         DrawAndFadeInMainMenu(REDRAW_FIELD);
13383       }
13384     }
13385   }
13386   else          /* continue playing the game */
13387   {
13388     if (tape.playing && tape.deactivate_display)
13389       TapeDeactivateDisplayOff(TRUE);
13390
13391     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13392
13393     if (tape.playing && tape.deactivate_display)
13394       TapeDeactivateDisplayOn();
13395   }
13396 }
13397
13398 void RequestQuitGame(boolean ask_if_really_quit)
13399 {
13400   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13401   boolean skip_request = AllPlayersGone || quick_quit;
13402
13403   RequestQuitGameExt(skip_request, quick_quit,
13404                      "Do you really want to quit the game ?");
13405 }
13406
13407
13408 /* ------------------------------------------------------------------------- */
13409 /* random generator functions                                                */
13410 /* ------------------------------------------------------------------------- */
13411
13412 unsigned int InitEngineRandom_RND(long seed)
13413 {
13414   game.num_random_calls = 0;
13415
13416 #if 0
13417   unsigned int rnd_seed = InitEngineRandom(seed);
13418
13419   printf("::: START RND: %d\n", rnd_seed);
13420
13421   return rnd_seed;
13422 #else
13423
13424   return InitEngineRandom(seed);
13425
13426 #endif
13427
13428 }
13429
13430 unsigned int RND(int max)
13431 {
13432   if (max > 0)
13433   {
13434     game.num_random_calls++;
13435
13436     return GetEngineRandom(max);
13437   }
13438
13439   return 0;
13440 }
13441
13442
13443 /* ------------------------------------------------------------------------- */
13444 /* game engine snapshot handling functions                                   */
13445 /* ------------------------------------------------------------------------- */
13446
13447 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
13448
13449 struct EngineSnapshotInfo
13450 {
13451   /* runtime values for custom element collect score */
13452   int collect_score[NUM_CUSTOM_ELEMENTS];
13453
13454   /* runtime values for group element choice position */
13455   int choice_pos[NUM_GROUP_ELEMENTS];
13456
13457   /* runtime values for belt position animations */
13458   int belt_graphic[4 * NUM_BELT_PARTS];
13459   int belt_anim_mode[4 * NUM_BELT_PARTS];
13460 };
13461
13462 struct EngineSnapshotNodeInfo
13463 {
13464   void *buffer_orig;
13465   void *buffer_copy;
13466   int size;
13467 };
13468
13469 static struct EngineSnapshotInfo engine_snapshot_rnd;
13470 static ListNode *engine_snapshot_list = NULL;
13471 static char *snapshot_level_identifier = NULL;
13472 static int snapshot_level_nr = -1;
13473
13474 void FreeEngineSnapshot()
13475 {
13476   while (engine_snapshot_list != NULL)
13477     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13478                        checked_free);
13479
13480   setString(&snapshot_level_identifier, NULL);
13481   snapshot_level_nr = -1;
13482 }
13483
13484 static void SaveEngineSnapshotValues_RND()
13485 {
13486   static int belt_base_active_element[4] =
13487   {
13488     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13489     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13490     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13491     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13492   };
13493   int i, j;
13494
13495   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13496   {
13497     int element = EL_CUSTOM_START + i;
13498
13499     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13500   }
13501
13502   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13503   {
13504     int element = EL_GROUP_START + i;
13505
13506     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13507   }
13508
13509   for (i = 0; i < 4; i++)
13510   {
13511     for (j = 0; j < NUM_BELT_PARTS; j++)
13512     {
13513       int element = belt_base_active_element[i] + j;
13514       int graphic = el2img(element);
13515       int anim_mode = graphic_info[graphic].anim_mode;
13516
13517       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13518       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13519     }
13520   }
13521 }
13522
13523 static void LoadEngineSnapshotValues_RND()
13524 {
13525   unsigned long num_random_calls = game.num_random_calls;
13526   int i, j;
13527
13528   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13529   {
13530     int element = EL_CUSTOM_START + i;
13531
13532     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13533   }
13534
13535   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13536   {
13537     int element = EL_GROUP_START + i;
13538
13539     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13540   }
13541
13542   for (i = 0; i < 4; i++)
13543   {
13544     for (j = 0; j < NUM_BELT_PARTS; j++)
13545     {
13546       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13547       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13548
13549       graphic_info[graphic].anim_mode = anim_mode;
13550     }
13551   }
13552
13553   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13554   {
13555     InitRND(tape.random_seed);
13556     for (i = 0; i < num_random_calls; i++)
13557       RND(1);
13558   }
13559
13560   if (game.num_random_calls != num_random_calls)
13561   {
13562     Error(ERR_RETURN, "number of random calls out of sync");
13563     Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
13564     Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
13565     Error(ERR_EXIT, "this should not happen -- please debug");
13566   }
13567 }
13568
13569 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13570 {
13571   struct EngineSnapshotNodeInfo *bi =
13572     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13573
13574   bi->buffer_orig = buffer;
13575   bi->buffer_copy = checked_malloc(size);
13576   bi->size = size;
13577
13578   memcpy(bi->buffer_copy, buffer, size);
13579
13580   addNodeToList(&engine_snapshot_list, NULL, bi);
13581 }
13582
13583 void SaveEngineSnapshot()
13584 {
13585   FreeEngineSnapshot();         /* free previous snapshot, if needed */
13586
13587   if (level_editor_test_game)   /* do not save snapshots from editor */
13588     return;
13589
13590   /* copy some special values to a structure better suited for the snapshot */
13591
13592   SaveEngineSnapshotValues_RND();
13593   SaveEngineSnapshotValues_EM();
13594
13595   /* save values stored in special snapshot structure */
13596
13597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13599
13600   /* save further RND engine values */
13601
13602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13604   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13605
13606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13609   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13610
13611   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13613   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13616
13617   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13620
13621   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13622
13623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13624
13625   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13626   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13627
13628   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13629   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13630   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13631   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13633   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13634   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13635   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13636   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13637   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13638   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13639   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13640   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13641   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13642   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13643   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13644   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13645   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13646
13647   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13648   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13649
13650   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13651   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13652   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13653
13654   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13655   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13656
13657   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13658   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13659   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13660   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13661   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13662
13663   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13664   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13665
13666   /* save level identification information */
13667
13668   setString(&snapshot_level_identifier, leveldir_current->identifier);
13669   snapshot_level_nr = level_nr;
13670
13671 #if 0
13672   ListNode *node = engine_snapshot_list;
13673   int num_bytes = 0;
13674
13675   while (node != NULL)
13676   {
13677     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13678
13679     node = node->next;
13680   }
13681
13682   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13683 #endif
13684 }
13685
13686 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13687 {
13688   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13689 }
13690
13691 void LoadEngineSnapshot()
13692 {
13693   ListNode *node = engine_snapshot_list;
13694
13695   if (engine_snapshot_list == NULL)
13696     return;
13697
13698   while (node != NULL)
13699   {
13700     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13701
13702     node = node->next;
13703   }
13704
13705   /* restore special values from snapshot structure */
13706
13707   LoadEngineSnapshotValues_RND();
13708   LoadEngineSnapshotValues_EM();
13709 }
13710
13711 boolean CheckEngineSnapshot()
13712 {
13713   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13714           snapshot_level_nr == level_nr);
13715 }
13716
13717
13718 /* ---------- new game button stuff ---------------------------------------- */
13719
13720 /* graphic position values for game buttons */
13721 #define GAME_BUTTON_XSIZE       30
13722 #define GAME_BUTTON_YSIZE       30
13723 #define GAME_BUTTON_XPOS        5
13724 #define GAME_BUTTON_YPOS        215
13725 #define SOUND_BUTTON_XPOS       5
13726 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13727
13728 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13729 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13730 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13731 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13732 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13733 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13734
13735 static struct
13736 {
13737   int x, y;
13738   int gadget_id;
13739   char *infotext;
13740 } gamebutton_info[NUM_GAME_BUTTONS] =
13741 {
13742   {
13743     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13744     GAME_CTRL_ID_STOP,
13745     "stop game"
13746   },
13747   {
13748     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13749     GAME_CTRL_ID_PAUSE,
13750     "pause game"
13751   },
13752   {
13753     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13754     GAME_CTRL_ID_PLAY,
13755     "play game"
13756   },
13757   {
13758     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13759     SOUND_CTRL_ID_MUSIC,
13760     "background music on/off"
13761   },
13762   {
13763     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13764     SOUND_CTRL_ID_LOOPS,
13765     "sound loops on/off"
13766   },
13767   {
13768     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13769     SOUND_CTRL_ID_SIMPLE,
13770     "normal sounds on/off"
13771   }
13772 };
13773
13774 void CreateGameButtons()
13775 {
13776   int i;
13777
13778   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13779   {
13780     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13781     struct GadgetInfo *gi;
13782     int button_type;
13783     boolean checked;
13784     unsigned long event_mask;
13785     int gd_xoffset, gd_yoffset;
13786     int gd_x1, gd_x2, gd_y1, gd_y2;
13787     int id = i;
13788
13789     gd_xoffset = gamebutton_info[i].x;
13790     gd_yoffset = gamebutton_info[i].y;
13791     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13792     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13793
13794     if (id == GAME_CTRL_ID_STOP ||
13795         id == GAME_CTRL_ID_PAUSE ||
13796         id == GAME_CTRL_ID_PLAY)
13797     {
13798       button_type = GD_TYPE_NORMAL_BUTTON;
13799       checked = FALSE;
13800       event_mask = GD_EVENT_RELEASED;
13801       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13802       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13803     }
13804     else
13805     {
13806       button_type = GD_TYPE_CHECK_BUTTON;
13807       checked =
13808         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13809          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13810          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13811       event_mask = GD_EVENT_PRESSED;
13812       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
13813       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13814     }
13815
13816     gi = CreateGadget(GDI_CUSTOM_ID, id,
13817                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
13818                       GDI_X, DX + gd_xoffset,
13819                       GDI_Y, DY + gd_yoffset,
13820                       GDI_WIDTH, GAME_BUTTON_XSIZE,
13821                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
13822                       GDI_TYPE, button_type,
13823                       GDI_STATE, GD_BUTTON_UNPRESSED,
13824                       GDI_CHECKED, checked,
13825                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13826                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13827                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13828                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13829                       GDI_EVENT_MASK, event_mask,
13830                       GDI_CALLBACK_ACTION, HandleGameButtons,
13831                       GDI_END);
13832
13833     if (gi == NULL)
13834       Error(ERR_EXIT, "cannot create gadget");
13835
13836     game_gadget[id] = gi;
13837   }
13838 }
13839
13840 void FreeGameButtons()
13841 {
13842   int i;
13843
13844   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13845     FreeGadget(game_gadget[i]);
13846 }
13847
13848 static void MapGameButtons()
13849 {
13850   int i;
13851
13852   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13853     MapGadget(game_gadget[i]);
13854 }
13855
13856 void UnmapGameButtons()
13857 {
13858   int i;
13859
13860   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13861     UnmapGadget(game_gadget[i]);
13862 }
13863
13864 static void HandleGameButtons(struct GadgetInfo *gi)
13865 {
13866   int id = gi->custom_id;
13867
13868   if (game_status != GAME_MODE_PLAYING)
13869     return;
13870
13871   switch (id)
13872   {
13873     case GAME_CTRL_ID_STOP:
13874       if (tape.playing)
13875         TapeStop();
13876       else
13877         RequestQuitGame(TRUE);
13878       break;
13879
13880     case GAME_CTRL_ID_PAUSE:
13881       if (options.network)
13882       {
13883 #if defined(NETWORK_AVALIABLE)
13884         if (tape.pausing)
13885           SendToServer_ContinuePlaying();
13886         else
13887           SendToServer_PausePlaying();
13888 #endif
13889       }
13890       else
13891         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13892       break;
13893
13894     case GAME_CTRL_ID_PLAY:
13895       if (tape.pausing)
13896       {
13897 #if defined(NETWORK_AVALIABLE)
13898         if (options.network)
13899           SendToServer_ContinuePlaying();
13900         else
13901 #endif
13902         {
13903           tape.pausing = FALSE;
13904           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13905         }
13906       }
13907       break;
13908
13909     case SOUND_CTRL_ID_MUSIC:
13910       if (setup.sound_music)
13911       { 
13912         setup.sound_music = FALSE;
13913         FadeMusic();
13914       }
13915       else if (audio.music_available)
13916       { 
13917         setup.sound = setup.sound_music = TRUE;
13918
13919         SetAudioMode(setup.sound);
13920
13921         PlayLevelMusic();
13922       }
13923       break;
13924
13925     case SOUND_CTRL_ID_LOOPS:
13926       if (setup.sound_loops)
13927         setup.sound_loops = FALSE;
13928       else if (audio.loops_available)
13929       {
13930         setup.sound = setup.sound_loops = TRUE;
13931         SetAudioMode(setup.sound);
13932       }
13933       break;
13934
13935     case SOUND_CTRL_ID_SIMPLE:
13936       if (setup.sound_simple)
13937         setup.sound_simple = FALSE;
13938       else if (audio.sound_available)
13939       {
13940         setup.sound = setup.sound_simple = TRUE;
13941         SetAudioMode(setup.sound);
13942       }
13943       break;
13944
13945     default:
13946       break;
13947   }
13948 }