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