rnd-20070125-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_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0)
90 #define PANEL_XPOS(p)           (DX + ALIGNED_MENU_XPOS(p))
91 #define PANEL_YPOS(p)           (DY + ALIGNED_MENU_YPOS(p))
92 #else
93 #define PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
94 #define PANEL_XPOS(p)           (ALIGNED_XPOS((p).x, (p).width, (p).align))
95 #define PANEL_YPOS(p)           ((p).y)
96 #endif
97
98 /* special positions in the game control window (relative to control window) */
99 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
100 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
101 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
102 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
103 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
104 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
105 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
106 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
107 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
108 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
109 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
110 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
111 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
112 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
113 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
114 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
115
116 /* special positions in the game control window (relative to main window) */
117 #define DX_LEVEL1               (DX + XX_LEVEL1)
118 #define DX_LEVEL2               (DX + XX_LEVEL2)
119 #define DX_LEVEL                (DX + XX_LEVEL)
120 #define DY_LEVEL                (DY + YY_LEVEL)
121 #define DX_EMERALDS             (DX + XX_EMERALDS)
122 #define DY_EMERALDS             (DY + YY_EMERALDS)
123 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
124 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
125 #define DX_KEYS                 (DX + XX_KEYS)
126 #define DY_KEYS                 (DY + YY_KEYS)
127 #define DX_SCORE                (DX + XX_SCORE)
128 #define DY_SCORE                (DY + YY_SCORE)
129 #define DX_TIME1                (DX + XX_TIME1)
130 #define DX_TIME2                (DX + XX_TIME2)
131 #define DX_TIME                 (DX + XX_TIME)
132 #define DY_TIME                 (DY + YY_TIME)
133
134 /* values for delayed check of falling and moving elements and for collision */
135 #define CHECK_DELAY_MOVING      3
136 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
137 #define CHECK_DELAY_COLLISION   2
138 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
139
140 /* values for initial player move delay (initial delay counter value) */
141 #define INITIAL_MOVE_DELAY_OFF  -1
142 #define INITIAL_MOVE_DELAY_ON   0
143
144 /* values for player movement speed (which is in fact a delay value) */
145 #define MOVE_DELAY_MIN_SPEED    32
146 #define MOVE_DELAY_NORMAL_SPEED 8
147 #define MOVE_DELAY_HIGH_SPEED   4
148 #define MOVE_DELAY_MAX_SPEED    1
149
150 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
151 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
152
153 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
154 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
155
156 /* values for other actions */
157 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
158 #define MOVE_STEPSIZE_MIN       (1)
159 #define MOVE_STEPSIZE_MAX       (TILEX)
160
161 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
162 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
163
164 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
165
166 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
167                                  RND(element_info[e].push_delay_random))
168 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
169                                  RND(element_info[e].drop_delay_random))
170 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
171                                  RND(element_info[e].move_delay_random))
172 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
173                                     (element_info[e].move_delay_random))
174 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
175                                  RND(element_info[e].ce_value_random_initial))
176 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
177 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
178                                  RND((c)->delay_random * (c)->delay_frames))
179 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
180                                  RND((c)->delay_random))
181
182
183 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
184          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
185
186 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
187         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
188          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
189          (be) + (e) - EL_SELF)
190
191 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
192         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
193          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
194          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
195          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
196          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
197          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
198          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
199          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
200          (e))
201
202 #define CAN_GROW_INTO(e)                                                \
203         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
204
205 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
206                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
207                                         (condition)))
208
209 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
210                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
211                                         (CAN_MOVE_INTO_ACID(e) &&       \
212                                          Feld[x][y] == EL_ACID) ||      \
213                                         (condition)))
214
215 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
216                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
217                                         (CAN_MOVE_INTO_ACID(e) &&       \
218                                          Feld[x][y] == EL_ACID) ||      \
219                                         (condition)))
220
221 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
222                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
223                                         (condition) ||                  \
224                                         (CAN_MOVE_INTO_ACID(e) &&       \
225                                          Feld[x][y] == EL_ACID) ||      \
226                                         (DONT_COLLIDE_WITH(e) &&        \
227                                          IS_PLAYER(x, y) &&             \
228                                          !PLAYER_ENEMY_PROTECTED(x, y))))
229
230 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
231         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
232
233 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
234         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
235
236 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
237         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
238
239 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
240         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
241                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
242
243 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
244         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
245
246 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
247         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
248
249 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
250         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
251
252 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
253         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
254
255 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
256         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
257
258 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
259         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
260                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
261                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
262                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
263                                                  IS_FOOD_PENGUIN(Feld[x][y])))
264 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
265         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
266
267 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
268         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
269
270 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
271         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
272
273 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
274         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
275                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
276
277 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
278
279 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
280                 (!IS_PLAYER(x, y) &&                                    \
281                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
282
283 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
284         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
285
286 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
287 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
288
289 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
290 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
291 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
292 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
293
294 /* game button identifiers */
295 #define GAME_CTRL_ID_STOP               0
296 #define GAME_CTRL_ID_PAUSE              1
297 #define GAME_CTRL_ID_PLAY               2
298 #define SOUND_CTRL_ID_MUSIC             3
299 #define SOUND_CTRL_ID_LOOPS             4
300 #define SOUND_CTRL_ID_SIMPLE            5
301
302 #define NUM_GAME_BUTTONS                6
303
304
305 /* forward declaration for internal use */
306
307 static void CreateField(int, int, int);
308
309 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
310 static void AdvanceFrameAndPlayerCounters(int);
311
312 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
313 static boolean MovePlayer(struct PlayerInfo *, int, int);
314 static void ScrollPlayer(struct PlayerInfo *, int);
315 static void ScrollScreen(struct PlayerInfo *, int);
316
317 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
318
319 static void InitBeltMovement(void);
320 static void CloseAllOpenTimegates(void);
321 static void CheckGravityMovement(struct PlayerInfo *);
322 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
323 static void KillPlayerUnlessEnemyProtected(int, int);
324 static void KillPlayerUnlessExplosionProtected(int, int);
325
326 static void TestIfPlayerTouchesCustomElement(int, int);
327 static void TestIfElementTouchesCustomElement(int, int);
328 static void TestIfElementHitsCustomElement(int, int, int);
329 #if 0
330 static void TestIfElementSmashesCustomElement(int, int, int);
331 #endif
332
333 static void HandleElementChange(int, int, int);
334 static void ExecuteCustomElementAction(int, int, int, int);
335 static boolean ChangeElement(int, int, int, int);
336
337 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
338 #define CheckTriggeredElementChange(x, y, e, ev)                        \
339         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
340 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
341         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
342 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
343         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
344 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
345         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
346
347 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
348 #define CheckElementChange(x, y, e, te, ev)                             \
349         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
350 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
351         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
352 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
353         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
354
355 static void PlayLevelSound(int, int, int);
356 static void PlayLevelSoundNearest(int, int, int);
357 static void PlayLevelSoundAction(int, int, int);
358 static void PlayLevelSoundElementAction(int, int, int, int);
359 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
360 static void PlayLevelSoundActionIfLoop(int, int, int);
361 static void StopLevelSoundActionIfLoop(int, int, int);
362 static void PlayLevelMusic();
363
364 static void MapGameButtons();
365 static void HandleGameButtons(struct GadgetInfo *);
366
367 int AmoebeNachbarNr(int, int);
368 void AmoebeUmwandeln(int, int);
369 void ContinueMoving(int, int);
370 void Bang(int, int);
371 void InitMovDir(int, int);
372 void InitAmoebaNr(int, int);
373 int NewHiScore(void);
374
375 void TestIfGoodThingHitsBadThing(int, int, int);
376 void TestIfBadThingHitsGoodThing(int, int, int);
377 void TestIfPlayerTouchesBadThing(int, int);
378 void TestIfPlayerRunsIntoBadThing(int, int, int);
379 void TestIfBadThingTouchesPlayer(int, int);
380 void TestIfBadThingRunsIntoPlayer(int, int, int);
381 void TestIfFriendTouchesBadThing(int, int);
382 void TestIfBadThingTouchesFriend(int, int);
383 void TestIfBadThingTouchesOtherBadThing(int, int);
384
385 void KillPlayer(struct PlayerInfo *);
386 void BuryPlayer(struct PlayerInfo *);
387 void RemovePlayer(struct PlayerInfo *);
388
389 boolean SnapField(struct PlayerInfo *, int, int);
390 boolean DropElement(struct PlayerInfo *);
391
392 static int getInvisibleActiveFromInvisibleElement(int);
393 static int getInvisibleFromInvisibleActiveElement(int);
394
395 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
396
397 /* for detection of endless loops, caused by custom element programming */
398 /* (using maximal playfield width x 10 is just a rough approximation) */
399 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
400
401 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
402 {                                                                       \
403   if (recursion_loop_detected)                                          \
404     return (rc);                                                        \
405                                                                         \
406   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
407   {                                                                     \
408     recursion_loop_detected = TRUE;                                     \
409     recursion_loop_element = (e);                                       \
410   }                                                                     \
411                                                                         \
412   recursion_loop_depth++;                                               \
413 }
414
415 #define RECURSION_LOOP_DETECTION_END()                                  \
416 {                                                                       \
417   recursion_loop_depth--;                                               \
418 }
419
420 static int recursion_loop_depth;
421 static boolean recursion_loop_detected;
422 static boolean recursion_loop_element;
423
424
425 /* ------------------------------------------------------------------------- */
426 /* definition of elements that automatically change to other elements after  */
427 /* a specified time, eventually calling a function when changing             */
428 /* ------------------------------------------------------------------------- */
429
430 /* forward declaration for changer functions */
431 static void InitBuggyBase(int, int);
432 static void WarnBuggyBase(int, int);
433
434 static void InitTrap(int, int);
435 static void ActivateTrap(int, int);
436 static void ChangeActiveTrap(int, int);
437
438 static void InitRobotWheel(int, int);
439 static void RunRobotWheel(int, int);
440 static void StopRobotWheel(int, int);
441
442 static void InitTimegateWheel(int, int);
443 static void RunTimegateWheel(int, int);
444
445 static void InitMagicBallDelay(int, int);
446 static void ActivateMagicBall(int, int);
447
448 struct ChangingElementInfo
449 {
450   int element;
451   int target_element;
452   int change_delay;
453   void (*pre_change_function)(int x, int y);
454   void (*change_function)(int x, int y);
455   void (*post_change_function)(int x, int y);
456 };
457
458 static struct ChangingElementInfo change_delay_list[] =
459 {
460   {
461     EL_NUT_BREAKING,
462     EL_EMERALD,
463     6,
464     NULL,
465     NULL,
466     NULL
467   },
468   {
469     EL_PEARL_BREAKING,
470     EL_EMPTY,
471     8,
472     NULL,
473     NULL,
474     NULL
475   },
476   {
477     EL_EXIT_OPENING,
478     EL_EXIT_OPEN,
479     29,
480     NULL,
481     NULL,
482     NULL
483   },
484   {
485     EL_EXIT_CLOSING,
486     EL_EXIT_CLOSED,
487     29,
488     NULL,
489     NULL,
490     NULL
491   },
492   {
493     EL_STEEL_EXIT_OPENING,
494     EL_STEEL_EXIT_OPEN,
495     29,
496     NULL,
497     NULL,
498     NULL
499   },
500   {
501     EL_STEEL_EXIT_CLOSING,
502     EL_STEEL_EXIT_CLOSED,
503     29,
504     NULL,
505     NULL,
506     NULL
507   },
508   {
509     EL_EM_EXIT_OPENING,
510     EL_EM_EXIT_OPEN,
511     29,
512     NULL,
513     NULL,
514     NULL
515   },
516   {
517     EL_EM_EXIT_CLOSING,
518 #if 1
519     EL_EMPTY,
520 #else
521     EL_EM_EXIT_CLOSED,
522 #endif
523     29,
524     NULL,
525     NULL,
526     NULL
527   },
528   {
529     EL_EM_STEEL_EXIT_OPENING,
530     EL_EM_STEEL_EXIT_OPEN,
531     29,
532     NULL,
533     NULL,
534     NULL
535   },
536   {
537     EL_EM_STEEL_EXIT_CLOSING,
538 #if 1
539     EL_STEELWALL,
540 #else
541     EL_EM_STEEL_EXIT_CLOSED,
542 #endif
543     29,
544     NULL,
545     NULL,
546     NULL
547   },
548   {
549     EL_SP_EXIT_OPENING,
550     EL_SP_EXIT_OPEN,
551     29,
552     NULL,
553     NULL,
554     NULL
555   },
556   {
557     EL_SP_EXIT_CLOSING,
558     EL_SP_EXIT_CLOSED,
559     29,
560     NULL,
561     NULL,
562     NULL
563   },
564   {
565     EL_SWITCHGATE_OPENING,
566     EL_SWITCHGATE_OPEN,
567     29,
568     NULL,
569     NULL,
570     NULL
571   },
572   {
573     EL_SWITCHGATE_CLOSING,
574     EL_SWITCHGATE_CLOSED,
575     29,
576     NULL,
577     NULL,
578     NULL
579   },
580   {
581     EL_TIMEGATE_OPENING,
582     EL_TIMEGATE_OPEN,
583     29,
584     NULL,
585     NULL,
586     NULL
587   },
588   {
589     EL_TIMEGATE_CLOSING,
590     EL_TIMEGATE_CLOSED,
591     29,
592     NULL,
593     NULL,
594     NULL
595   },
596
597   {
598     EL_ACID_SPLASH_LEFT,
599     EL_EMPTY,
600     8,
601     NULL,
602     NULL,
603     NULL
604   },
605   {
606     EL_ACID_SPLASH_RIGHT,
607     EL_EMPTY,
608     8,
609     NULL,
610     NULL,
611     NULL
612   },
613   {
614     EL_SP_BUGGY_BASE,
615     EL_SP_BUGGY_BASE_ACTIVATING,
616     0,
617     InitBuggyBase,
618     NULL,
619     NULL
620   },
621   {
622     EL_SP_BUGGY_BASE_ACTIVATING,
623     EL_SP_BUGGY_BASE_ACTIVE,
624     0,
625     InitBuggyBase,
626     NULL,
627     NULL
628   },
629   {
630     EL_SP_BUGGY_BASE_ACTIVE,
631     EL_SP_BUGGY_BASE,
632     0,
633     InitBuggyBase,
634     WarnBuggyBase,
635     NULL
636   },
637   {
638     EL_TRAP,
639     EL_TRAP_ACTIVE,
640     0,
641     InitTrap,
642     NULL,
643     ActivateTrap
644   },
645   {
646     EL_TRAP_ACTIVE,
647     EL_TRAP,
648     31,
649     NULL,
650     ChangeActiveTrap,
651     NULL
652   },
653   {
654     EL_ROBOT_WHEEL_ACTIVE,
655     EL_ROBOT_WHEEL,
656     0,
657     InitRobotWheel,
658     RunRobotWheel,
659     StopRobotWheel
660   },
661   {
662     EL_TIMEGATE_SWITCH_ACTIVE,
663     EL_TIMEGATE_SWITCH,
664     0,
665     InitTimegateWheel,
666     RunTimegateWheel,
667     NULL
668   },
669   {
670     EL_DC_TIMEGATE_SWITCH_ACTIVE,
671     EL_DC_TIMEGATE_SWITCH,
672     0,
673     InitTimegateWheel,
674     RunTimegateWheel,
675     NULL
676   },
677   {
678     EL_EMC_MAGIC_BALL_ACTIVE,
679     EL_EMC_MAGIC_BALL_ACTIVE,
680     0,
681     InitMagicBallDelay,
682     NULL,
683     ActivateMagicBall
684   },
685   {
686     EL_EMC_SPRING_BUMPER_ACTIVE,
687     EL_EMC_SPRING_BUMPER,
688     8,
689     NULL,
690     NULL,
691     NULL
692   },
693   {
694     EL_DIAGONAL_SHRINKING,
695     EL_UNDEFINED,
696     0,
697     NULL,
698     NULL,
699     NULL
700   },
701   {
702     EL_DIAGONAL_GROWING,
703     EL_UNDEFINED,
704     0,
705     NULL,
706     NULL,
707     NULL,
708   },
709
710   {
711     EL_UNDEFINED,
712     EL_UNDEFINED,
713     -1,
714     NULL,
715     NULL,
716     NULL
717   }
718 };
719
720 struct
721 {
722   int element;
723   int push_delay_fixed, push_delay_random;
724 }
725 push_delay_list[] =
726 {
727   { EL_SPRING,                  0, 0 },
728   { EL_BALLOON,                 0, 0 },
729
730   { EL_SOKOBAN_OBJECT,          2, 0 },
731   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
732   { EL_SATELLITE,               2, 0 },
733   { EL_SP_DISK_YELLOW,          2, 0 },
734
735   { EL_UNDEFINED,               0, 0 },
736 };
737
738 struct
739 {
740   int element;
741   int move_stepsize;
742 }
743 move_stepsize_list[] =
744 {
745   { EL_AMOEBA_DROP,             2 },
746   { EL_AMOEBA_DROPPING,         2 },
747   { EL_QUICKSAND_FILLING,       1 },
748   { EL_QUICKSAND_EMPTYING,      1 },
749   { EL_QUICKSAND_FAST_FILLING,  2 },
750   { EL_QUICKSAND_FAST_EMPTYING, 2 },
751   { EL_MAGIC_WALL_FILLING,      2 },
752   { EL_MAGIC_WALL_EMPTYING,     2 },
753   { EL_BD_MAGIC_WALL_FILLING,   2 },
754   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
755   { EL_DC_MAGIC_WALL_FILLING,   2 },
756   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
757
758   { EL_UNDEFINED,               0 },
759 };
760
761 struct
762 {
763   int element;
764   int count;
765 }
766 collect_count_list[] =
767 {
768   { EL_EMERALD,                 1 },
769   { EL_BD_DIAMOND,              1 },
770   { EL_EMERALD_YELLOW,          1 },
771   { EL_EMERALD_RED,             1 },
772   { EL_EMERALD_PURPLE,          1 },
773   { EL_DIAMOND,                 3 },
774   { EL_SP_INFOTRON,             1 },
775   { EL_PEARL,                   5 },
776   { EL_CRYSTAL,                 8 },
777
778   { EL_UNDEFINED,               0 },
779 };
780
781 struct
782 {
783   int element;
784   int direction;
785 }
786 access_direction_list[] =
787 {
788   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
789   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
790   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
791   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
792   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
793   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
794   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
795   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
796   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
797   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
798   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
799
800   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
801   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
802   { EL_SP_PORT_UP,                                                   MV_DOWN },
803   { EL_SP_PORT_DOWN,                                         MV_UP           },
804   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
805   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
806   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
807   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
808   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
809   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
810   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
811   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
812   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
813   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
814   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
815   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
816   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
817   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
818   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
819
820   { EL_UNDEFINED,                       MV_NONE                              }
821 };
822
823 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
824
825 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
826 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
827 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
828                                  IS_JUST_CHANGING(x, y))
829
830 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
831
832 /* static variables for playfield scan mode (scanning forward or backward) */
833 static int playfield_scan_start_x = 0;
834 static int playfield_scan_start_y = 0;
835 static int playfield_scan_delta_x = 1;
836 static int playfield_scan_delta_y = 1;
837
838 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
839                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
840                                      (y) += playfield_scan_delta_y)     \
841                                 for ((x) = playfield_scan_start_x;      \
842                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
843                                      (x) += playfield_scan_delta_x)     \
844
845 #ifdef DEBUG
846 void DEBUG_SetMaximumDynamite()
847 {
848   int i;
849
850   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
851     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
852       local_player->inventory_element[local_player->inventory_size++] =
853         EL_DYNAMITE;
854 }
855 #endif
856
857 static void InitPlayfieldScanModeVars()
858 {
859   if (game.use_reverse_scan_direction)
860   {
861     playfield_scan_start_x = lev_fieldx - 1;
862     playfield_scan_start_y = lev_fieldy - 1;
863
864     playfield_scan_delta_x = -1;
865     playfield_scan_delta_y = -1;
866   }
867   else
868   {
869     playfield_scan_start_x = 0;
870     playfield_scan_start_y = 0;
871
872     playfield_scan_delta_x = 1;
873     playfield_scan_delta_y = 1;
874   }
875 }
876
877 static void InitPlayfieldScanMode(int mode)
878 {
879   game.use_reverse_scan_direction =
880     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
881
882   InitPlayfieldScanModeVars();
883 }
884
885 static int get_move_delay_from_stepsize(int move_stepsize)
886 {
887   move_stepsize =
888     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
889
890   /* make sure that stepsize value is always a power of 2 */
891   move_stepsize = (1 << log_2(move_stepsize));
892
893   return TILEX / move_stepsize;
894 }
895
896 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
897                                boolean init_game)
898 {
899   int player_nr = player->index_nr;
900   int move_delay = get_move_delay_from_stepsize(move_stepsize);
901   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
902
903   /* do no immediately change move delay -- the player might just be moving */
904   player->move_delay_value_next = move_delay;
905
906   /* information if player can move must be set separately */
907   player->cannot_move = cannot_move;
908
909   if (init_game)
910   {
911     player->move_delay       = game.initial_move_delay[player_nr];
912     player->move_delay_value = game.initial_move_delay_value[player_nr];
913
914     player->move_delay_value_next = -1;
915
916     player->move_delay_reset_counter = 0;
917   }
918 }
919
920 void GetPlayerConfig()
921 {
922   if (!audio.sound_available)
923     setup.sound_simple = FALSE;
924
925   if (!audio.loops_available)
926     setup.sound_loops = FALSE;
927
928   if (!audio.music_available)
929     setup.sound_music = FALSE;
930
931   if (!video.fullscreen_available)
932     setup.fullscreen = FALSE;
933
934   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
935
936   SetAudioMode(setup.sound);
937   InitJoysticks();
938 }
939
940 static int getBeltNrFromBeltElement(int element)
941 {
942   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
943           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
944           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
945 }
946
947 static int getBeltNrFromBeltActiveElement(int element)
948 {
949   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
950           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
951           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
952 }
953
954 static int getBeltNrFromBeltSwitchElement(int element)
955 {
956   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
957           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
958           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
959 }
960
961 static int getBeltDirNrFromBeltSwitchElement(int element)
962 {
963   static int belt_base_element[4] =
964   {
965     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
966     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
967     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
968     EL_CONVEYOR_BELT_4_SWITCH_LEFT
969   };
970
971   int belt_nr = getBeltNrFromBeltSwitchElement(element);
972   int belt_dir_nr = element - belt_base_element[belt_nr];
973
974   return (belt_dir_nr % 3);
975 }
976
977 static int getBeltDirFromBeltSwitchElement(int element)
978 {
979   static int belt_move_dir[3] =
980   {
981     MV_LEFT,
982     MV_NONE,
983     MV_RIGHT
984   };
985
986   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
987
988   return belt_move_dir[belt_dir_nr];
989 }
990
991 static int get_element_from_group_element(int element)
992 {
993   if (IS_GROUP_ELEMENT(element))
994   {
995     struct ElementGroupInfo *group = element_info[element].group;
996     int last_anim_random_frame = gfx.anim_random_frame;
997     int element_pos;
998
999     if (group->choice_mode == ANIM_RANDOM)
1000       gfx.anim_random_frame = RND(group->num_elements_resolved);
1001
1002     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1003                                     group->choice_mode, 0,
1004                                     group->choice_pos);
1005
1006     if (group->choice_mode == ANIM_RANDOM)
1007       gfx.anim_random_frame = last_anim_random_frame;
1008
1009     group->choice_pos++;
1010
1011     element = group->element_resolved[element_pos];
1012   }
1013
1014   return element;
1015 }
1016
1017 static void InitPlayerField(int x, int y, int element, boolean init_game)
1018 {
1019   if (element == EL_SP_MURPHY)
1020   {
1021     if (init_game)
1022     {
1023       if (stored_player[0].present)
1024       {
1025         Feld[x][y] = EL_SP_MURPHY_CLONE;
1026
1027         return;
1028       }
1029       else
1030       {
1031         stored_player[0].use_murphy = TRUE;
1032
1033         if (!level.use_artwork_element[0])
1034           stored_player[0].artwork_element = EL_SP_MURPHY;
1035       }
1036
1037       Feld[x][y] = EL_PLAYER_1;
1038     }
1039   }
1040
1041   if (init_game)
1042   {
1043     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1044     int jx = player->jx, jy = player->jy;
1045
1046     player->present = TRUE;
1047
1048     player->block_last_field = (element == EL_SP_MURPHY ?
1049                                 level.sp_block_last_field :
1050                                 level.block_last_field);
1051
1052     /* ---------- initialize player's last field block delay --------------- */
1053
1054     /* always start with reliable default value (no adjustment needed) */
1055     player->block_delay_adjustment = 0;
1056
1057     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1058     if (player->block_last_field && element == EL_SP_MURPHY)
1059       player->block_delay_adjustment = 1;
1060
1061     /* special case 2: in game engines before 3.1.1, blocking was different */
1062     if (game.use_block_last_field_bug)
1063       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1064
1065     if (!options.network || player->connected)
1066     {
1067       player->active = TRUE;
1068
1069       /* remove potentially duplicate players */
1070       if (StorePlayer[jx][jy] == Feld[x][y])
1071         StorePlayer[jx][jy] = 0;
1072
1073       StorePlayer[x][y] = Feld[x][y];
1074
1075       if (options.debug)
1076       {
1077         printf("Player %d activated.\n", player->element_nr);
1078         printf("[Local player is %d and currently %s.]\n",
1079                local_player->element_nr,
1080                local_player->active ? "active" : "not active");
1081       }
1082     }
1083
1084     Feld[x][y] = EL_EMPTY;
1085
1086     player->jx = player->last_jx = x;
1087     player->jy = player->last_jy = y;
1088   }
1089 }
1090
1091 static void InitField(int x, int y, boolean init_game)
1092 {
1093   int element = Feld[x][y];
1094
1095   switch (element)
1096   {
1097     case EL_SP_MURPHY:
1098     case EL_PLAYER_1:
1099     case EL_PLAYER_2:
1100     case EL_PLAYER_3:
1101     case EL_PLAYER_4:
1102       InitPlayerField(x, y, element, init_game);
1103       break;
1104
1105     case EL_SOKOBAN_FIELD_PLAYER:
1106       element = Feld[x][y] = EL_PLAYER_1;
1107       InitField(x, y, init_game);
1108
1109       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1110       InitField(x, y, init_game);
1111       break;
1112
1113     case EL_SOKOBAN_FIELD_EMPTY:
1114       local_player->sokobanfields_still_needed++;
1115       break;
1116
1117     case EL_STONEBLOCK:
1118       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1119         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1120       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1121         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1122       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1123         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1124       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1125         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1126       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1127         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1128       break;
1129
1130     case EL_BUG:
1131     case EL_BUG_RIGHT:
1132     case EL_BUG_UP:
1133     case EL_BUG_LEFT:
1134     case EL_BUG_DOWN:
1135     case EL_SPACESHIP:
1136     case EL_SPACESHIP_RIGHT:
1137     case EL_SPACESHIP_UP:
1138     case EL_SPACESHIP_LEFT:
1139     case EL_SPACESHIP_DOWN:
1140     case EL_BD_BUTTERFLY:
1141     case EL_BD_BUTTERFLY_RIGHT:
1142     case EL_BD_BUTTERFLY_UP:
1143     case EL_BD_BUTTERFLY_LEFT:
1144     case EL_BD_BUTTERFLY_DOWN:
1145     case EL_BD_FIREFLY:
1146     case EL_BD_FIREFLY_RIGHT:
1147     case EL_BD_FIREFLY_UP:
1148     case EL_BD_FIREFLY_LEFT:
1149     case EL_BD_FIREFLY_DOWN:
1150     case EL_PACMAN_RIGHT:
1151     case EL_PACMAN_UP:
1152     case EL_PACMAN_LEFT:
1153     case EL_PACMAN_DOWN:
1154     case EL_YAMYAM:
1155     case EL_YAMYAM_LEFT:
1156     case EL_YAMYAM_RIGHT:
1157     case EL_YAMYAM_UP:
1158     case EL_YAMYAM_DOWN:
1159     case EL_DARK_YAMYAM:
1160     case EL_ROBOT:
1161     case EL_PACMAN:
1162     case EL_SP_SNIKSNAK:
1163     case EL_SP_ELECTRON:
1164     case EL_MOLE:
1165     case EL_MOLE_LEFT:
1166     case EL_MOLE_RIGHT:
1167     case EL_MOLE_UP:
1168     case EL_MOLE_DOWN:
1169       InitMovDir(x, y);
1170       break;
1171
1172     case EL_AMOEBA_FULL:
1173     case EL_BD_AMOEBA:
1174       InitAmoebaNr(x, y);
1175       break;
1176
1177     case EL_AMOEBA_DROP:
1178       if (y == lev_fieldy - 1)
1179       {
1180         Feld[x][y] = EL_AMOEBA_GROWING;
1181         Store[x][y] = EL_AMOEBA_WET;
1182       }
1183       break;
1184
1185     case EL_DYNAMITE_ACTIVE:
1186     case EL_SP_DISK_RED_ACTIVE:
1187     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1188     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1189     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1190     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1191       MovDelay[x][y] = 96;
1192       break;
1193
1194     case EL_EM_DYNAMITE_ACTIVE:
1195       MovDelay[x][y] = 32;
1196       break;
1197
1198     case EL_LAMP:
1199       local_player->lights_still_needed++;
1200       break;
1201
1202     case EL_PENGUIN:
1203       local_player->friends_still_needed++;
1204       break;
1205
1206     case EL_PIG:
1207     case EL_DRAGON:
1208       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1209       break;
1210
1211     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1212     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1213     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1214     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1215     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1216     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1217     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1218     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1219     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1220     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1221     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1222     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1223       if (init_game)
1224       {
1225         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1226         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1227         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1228
1229         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1230         {
1231           game.belt_dir[belt_nr] = belt_dir;
1232           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1233         }
1234         else    /* more than one switch -- set it like the first switch */
1235         {
1236           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1237         }
1238       }
1239       break;
1240
1241 #if !USE_BOTH_SWITCHGATE_SWITCHES
1242     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1243       if (init_game)
1244         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1245       break;
1246
1247     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1248       if (init_game)
1249         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1250       break;
1251 #endif
1252
1253     case EL_LIGHT_SWITCH_ACTIVE:
1254       if (init_game)
1255         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1256       break;
1257
1258     case EL_INVISIBLE_STEELWALL:
1259     case EL_INVISIBLE_WALL:
1260     case EL_INVISIBLE_SAND:
1261       if (game.light_time_left > 0 ||
1262           game.lenses_time_left > 0)
1263         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1264       break;
1265
1266     case EL_EMC_MAGIC_BALL:
1267       if (game.ball_state)
1268         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1269       break;
1270
1271     case EL_EMC_MAGIC_BALL_SWITCH:
1272       if (game.ball_state)
1273         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1274       break;
1275
1276     default:
1277       if (IS_CUSTOM_ELEMENT(element))
1278       {
1279         if (CAN_MOVE(element))
1280           InitMovDir(x, y);
1281
1282 #if USE_NEW_CUSTOM_VALUE
1283         if (!element_info[element].use_last_ce_value || init_game)
1284           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1285 #endif
1286       }
1287       else if (IS_GROUP_ELEMENT(element))
1288       {
1289         Feld[x][y] = get_element_from_group_element(element);
1290
1291         InitField(x, y, init_game);
1292       }
1293
1294       break;
1295   }
1296
1297   if (!init_game)
1298     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1299 }
1300
1301 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1302 {
1303   InitField(x, y, init_game);
1304
1305   /* not needed to call InitMovDir() -- already done by InitField()! */
1306   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1307       CAN_MOVE(Feld[x][y]))
1308     InitMovDir(x, y);
1309 }
1310
1311 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1312 {
1313   int old_element = Feld[x][y];
1314
1315   InitField(x, y, init_game);
1316
1317   /* not needed to call InitMovDir() -- already done by InitField()! */
1318   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1319       CAN_MOVE(old_element) &&
1320       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1321     InitMovDir(x, y);
1322
1323   /* this case is in fact a combination of not less than three bugs:
1324      first, it calls InitMovDir() for elements that can move, although this is
1325      already done by InitField(); then, it checks the element that was at this
1326      field _before_ the call to InitField() (which can change it); lastly, it
1327      was not called for "mole with direction" elements, which were treated as
1328      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1329   */
1330 }
1331
1332 #if 1
1333
1334 void DrawGameValue_Emeralds(int value)
1335 {
1336   struct TextPosInfo *pos = &game.panel.gems;
1337   int font_nr = FONT_TEXT_2;
1338   int font_width = getFontWidth(font_nr);
1339   int digits = pos->chars;
1340
1341   if (PANEL_DEACTIVATED(pos))
1342     return;
1343
1344   pos->width = digits * font_width;
1345
1346   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1347 }
1348
1349 void DrawGameValue_Dynamite(int value)
1350 {
1351   struct TextPosInfo *pos = &game.panel.inventory;
1352   int font_nr = FONT_TEXT_2;
1353   int font_width = getFontWidth(font_nr);
1354   int digits = pos->chars;
1355
1356   if (PANEL_DEACTIVATED(pos))
1357     return;
1358
1359   pos->width = digits * font_width;
1360
1361   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1362 }
1363
1364 void DrawGameValue_Score(int value)
1365 {
1366   struct TextPosInfo *pos = &game.panel.score;
1367   int font_nr = FONT_TEXT_2;
1368   int font_width = getFontWidth(font_nr);
1369   int digits = pos->chars;
1370
1371   if (PANEL_DEACTIVATED(pos))
1372     return;
1373
1374   pos->width = digits * font_width;
1375
1376   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1377 }
1378
1379 void DrawGameValue_Time(int value)
1380 {
1381   struct TextPosInfo *pos = &game.panel.time;
1382   static int last_value = -1;
1383   int digits1 = 3;
1384   int digits2 = 4;
1385   int digits = pos->chars;
1386   int font1_nr = FONT_TEXT_2;
1387   int font2_nr = FONT_TEXT_1;
1388   int font_nr = font1_nr;
1389   boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1390
1391   if (PANEL_DEACTIVATED(pos))
1392     return;
1393
1394   if (use_dynamic_digits)               /* use dynamic number of digits */
1395   {
1396     digits  = (value < 1000 ? digits1  : digits2);
1397     font_nr = (value < 1000 ? font1_nr : font2_nr);
1398   }
1399
1400   /* clear background if value just changed its size (dynamic digits only) */
1401   if (use_dynamic_digits && (last_value < 1000) != (value < 1000))
1402   {
1403     int width1 = digits1 * getFontWidth(font1_nr);
1404     int width2 = digits2 * getFontWidth(font2_nr);
1405     int max_width = MAX(width1, width2);
1406     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1407
1408     pos->width = max_width;
1409
1410     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1411                                max_width, max_height);
1412   }
1413
1414   pos->width = digits * getFontWidth(font_nr);
1415
1416   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1417
1418   last_value = value;
1419 }
1420
1421 void DrawGameValue_Level(int value)
1422 {
1423   struct TextPosInfo *pos = &game.panel.level;
1424   int digits1 = 2;
1425   int digits2 = 3;
1426   int digits = pos->chars;
1427   int font1_nr = FONT_TEXT_2;
1428   int font2_nr = FONT_TEXT_1;
1429   int font_nr = font1_nr;
1430   boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1431
1432   if (PANEL_DEACTIVATED(pos))
1433     return;
1434
1435   if (use_dynamic_digits)               /* use dynamic number of digits */
1436   {
1437     digits  = (level_nr < 100 ? digits1  : digits2);
1438     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1439   }
1440
1441   pos->width = digits * getFontWidth(font_nr);
1442
1443   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1444 }
1445
1446 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1447 {
1448   struct TextPosInfo *pos = &game.panel.keys;
1449   int base_key_graphic = EL_KEY_1;
1450   int i;
1451
1452   if (PANEL_DEACTIVATED(pos))
1453     return;
1454
1455   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1456     base_key_graphic = EL_EM_KEY_1;
1457
1458   pos->width = 4 * MINI_TILEX;
1459
1460   /* currently only 4 of 8 possible keys are displayed */
1461   for (i = 0; i < STD_NUM_KEYS; i++)
1462   {
1463     int src_x = DOOR_GFX_PAGEX5 + 18;
1464     int src_y = DOOR_GFX_PAGEY1 + 123;
1465     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1466     int dst_y = PANEL_YPOS(pos);
1467
1468     if (key[i])
1469       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1470     else
1471       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1472                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1473   }
1474 }
1475
1476 #else
1477
1478 void DrawGameValue_Emeralds(int value)
1479 {
1480   int font_nr = FONT_TEXT_2;
1481   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1482
1483   if (PANEL_DEACTIVATED(game.panel.gems))
1484     return;
1485
1486   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1487 }
1488
1489 void DrawGameValue_Dynamite(int value)
1490 {
1491   int font_nr = FONT_TEXT_2;
1492   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1493
1494   if (PANEL_DEACTIVATED(game.panel.inventory))
1495     return;
1496
1497   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1498 }
1499
1500 void DrawGameValue_Score(int value)
1501 {
1502   int font_nr = FONT_TEXT_2;
1503   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1504
1505   if (PANEL_DEACTIVATED(game.panel.score))
1506     return;
1507
1508   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1509 }
1510
1511 void DrawGameValue_Time(int value)
1512 {
1513   int font1_nr = FONT_TEXT_2;
1514 #if 1
1515   int font2_nr = FONT_TEXT_1;
1516 #else
1517   int font2_nr = FONT_LEVEL_NUMBER;
1518 #endif
1519   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1520   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1521
1522   if (PANEL_DEACTIVATED(game.panel.time))
1523     return;
1524
1525   /* clear background if value just changed its size */
1526   if (value == 999 || value == 1000)
1527     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1528
1529   if (value < 1000)
1530     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1531   else
1532     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1533 }
1534
1535 void DrawGameValue_Level(int value)
1536 {
1537   int font1_nr = FONT_TEXT_2;
1538 #if 1
1539   int font2_nr = FONT_TEXT_1;
1540 #else
1541   int font2_nr = FONT_LEVEL_NUMBER;
1542 #endif
1543
1544   if (PANEL_DEACTIVATED(game.panel.level))
1545     return;
1546
1547   if (level_nr < 100)
1548     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1549   else
1550     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1551 }
1552
1553 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1554 {
1555   int base_key_graphic = EL_KEY_1;
1556   int i;
1557
1558   if (PANEL_DEACTIVATED(game.panel.keys))
1559     return;
1560
1561   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1562     base_key_graphic = EL_EM_KEY_1;
1563
1564   /* currently only 4 of 8 possible keys are displayed */
1565   for (i = 0; i < STD_NUM_KEYS; i++)
1566   {
1567     int x = XX_KEYS + i * MINI_TILEX;
1568     int y = YY_KEYS;
1569
1570     if (key[i])
1571       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1572     else
1573       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1574                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1575   }
1576 }
1577
1578 #endif
1579
1580 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1581                        int key_bits)
1582 {
1583   int key[MAX_NUM_KEYS];
1584   int i;
1585
1586   /* prevent EM engine from updating time/score values parallel to GameWon() */
1587   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1588       local_player->LevelSolved)
1589     return;
1590
1591   for (i = 0; i < MAX_NUM_KEYS; i++)
1592     key[i] = key_bits & (1 << i);
1593
1594   DrawGameValue_Level(level_nr);
1595
1596   DrawGameValue_Emeralds(emeralds);
1597   DrawGameValue_Dynamite(dynamite);
1598   DrawGameValue_Score(score);
1599   DrawGameValue_Time(time);
1600
1601   DrawGameValue_Keys(key);
1602 }
1603
1604 void DrawGameDoorValues()
1605 {
1606   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1607   int dynamite_value = 0;
1608   int score_value = (local_player->LevelSolved ? local_player->score_final :
1609                      local_player->score);
1610   int gems_value = local_player->gems_still_needed;
1611   int key_bits = 0;
1612   int i, j;
1613
1614   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1615   {
1616     DrawGameDoorValues_EM();
1617
1618     return;
1619   }
1620
1621   if (game.centered_player_nr == -1)
1622   {
1623     for (i = 0; i < MAX_PLAYERS; i++)
1624     {
1625       for (j = 0; j < MAX_NUM_KEYS; j++)
1626         if (stored_player[i].key[j])
1627           key_bits |= (1 << j);
1628
1629       dynamite_value += stored_player[i].inventory_size;
1630     }
1631   }
1632   else
1633   {
1634     int player_nr = game.centered_player_nr;
1635
1636     for (i = 0; i < MAX_NUM_KEYS; i++)
1637       if (stored_player[player_nr].key[i])
1638         key_bits |= (1 << i);
1639
1640     dynamite_value = stored_player[player_nr].inventory_size;
1641   }
1642
1643   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1644                     key_bits);
1645 }
1646
1647
1648 /*
1649   =============================================================================
1650   InitGameEngine()
1651   -----------------------------------------------------------------------------
1652   initialize game engine due to level / tape version number
1653   =============================================================================
1654 */
1655
1656 static void InitGameEngine()
1657 {
1658   int i, j, k, l, x, y;
1659
1660   /* set game engine from tape file when re-playing, else from level file */
1661   game.engine_version = (tape.playing ? tape.engine_version :
1662                          level.game_version);
1663
1664   /* ---------------------------------------------------------------------- */
1665   /* set flags for bugs and changes according to active game engine version */
1666   /* ---------------------------------------------------------------------- */
1667
1668   /*
1669     Summary of bugfix/change:
1670     Fixed handling for custom elements that change when pushed by the player.
1671
1672     Fixed/changed in version:
1673     3.1.0
1674
1675     Description:
1676     Before 3.1.0, custom elements that "change when pushing" changed directly
1677     after the player started pushing them (until then handled in "DigField()").
1678     Since 3.1.0, these custom elements are not changed until the "pushing"
1679     move of the element is finished (now handled in "ContinueMoving()").
1680
1681     Affected levels/tapes:
1682     The first condition is generally needed for all levels/tapes before version
1683     3.1.0, which might use the old behaviour before it was changed; known tapes
1684     that are affected are some tapes from the level set "Walpurgis Gardens" by
1685     Jamie Cullen.
1686     The second condition is an exception from the above case and is needed for
1687     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1688     above (including some development versions of 3.1.0), but before it was
1689     known that this change would break tapes like the above and was fixed in
1690     3.1.1, so that the changed behaviour was active although the engine version
1691     while recording maybe was before 3.1.0. There is at least one tape that is
1692     affected by this exception, which is the tape for the one-level set "Bug
1693     Machine" by Juergen Bonhagen.
1694   */
1695
1696   game.use_change_when_pushing_bug =
1697     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1698      !(tape.playing &&
1699        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1700        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1701
1702   /*
1703     Summary of bugfix/change:
1704     Fixed handling for blocking the field the player leaves when moving.
1705
1706     Fixed/changed in version:
1707     3.1.1
1708
1709     Description:
1710     Before 3.1.1, when "block last field when moving" was enabled, the field
1711     the player is leaving when moving was blocked for the time of the move,
1712     and was directly unblocked afterwards. This resulted in the last field
1713     being blocked for exactly one less than the number of frames of one player
1714     move. Additionally, even when blocking was disabled, the last field was
1715     blocked for exactly one frame.
1716     Since 3.1.1, due to changes in player movement handling, the last field
1717     is not blocked at all when blocking is disabled. When blocking is enabled,
1718     the last field is blocked for exactly the number of frames of one player
1719     move. Additionally, if the player is Murphy, the hero of Supaplex, the
1720     last field is blocked for exactly one more than the number of frames of
1721     one player move.
1722
1723     Affected levels/tapes:
1724     (!!! yet to be determined -- probably many !!!)
1725   */
1726
1727   game.use_block_last_field_bug =
1728     (game.engine_version < VERSION_IDENT(3,1,1,0));
1729
1730   /*
1731     Summary of bugfix/change:
1732     Changed behaviour of CE changes with multiple changes per single frame.
1733
1734     Fixed/changed in version:
1735     3.2.0-6
1736
1737     Description:
1738     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1739     This resulted in race conditions where CEs seem to behave strange in some
1740     situations (where triggered CE changes were just skipped because there was
1741     already a CE change on that tile in the playfield in that engine frame).
1742     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1743     (The number of changes per frame must be limited in any case, because else
1744     it is easily possible to define CE changes that would result in an infinite
1745     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1746     should be set large enough so that it would only be reached in cases where
1747     the corresponding CE change conditions run into a loop. Therefore, it seems
1748     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1749     maximal number of change pages for custom elements.)
1750
1751     Affected levels/tapes:
1752     Probably many.
1753   */
1754
1755 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1756   game.max_num_changes_per_frame = 1;
1757 #else
1758   game.max_num_changes_per_frame =
1759     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1760 #endif
1761
1762   /* ---------------------------------------------------------------------- */
1763
1764   /* default scan direction: scan playfield from top/left to bottom/right */
1765   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1766
1767   /* dynamically adjust element properties according to game engine version */
1768   InitElementPropertiesEngine(game.engine_version);
1769
1770 #if 0
1771   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1772   printf("          tape version == %06d [%s] [file: %06d]\n",
1773          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1774          tape.file_version);
1775   printf("       => game.engine_version == %06d\n", game.engine_version);
1776 #endif
1777
1778   /* ---------- initialize player's initial move delay --------------------- */
1779
1780   /* dynamically adjust player properties according to level information */
1781   for (i = 0; i < MAX_PLAYERS; i++)
1782     game.initial_move_delay_value[i] =
1783       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1784
1785   /* dynamically adjust player properties according to game engine version */
1786   for (i = 0; i < MAX_PLAYERS; i++)
1787     game.initial_move_delay[i] =
1788       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1789        game.initial_move_delay_value[i] : 0);
1790
1791   /* ---------- initialize player's initial push delay --------------------- */
1792
1793   /* dynamically adjust player properties according to game engine version */
1794   game.initial_push_delay_value =
1795     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1796
1797   /* ---------- initialize changing elements ------------------------------- */
1798
1799   /* initialize changing elements information */
1800   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1801   {
1802     struct ElementInfo *ei = &element_info[i];
1803
1804     /* this pointer might have been changed in the level editor */
1805     ei->change = &ei->change_page[0];
1806
1807     if (!IS_CUSTOM_ELEMENT(i))
1808     {
1809       ei->change->target_element = EL_EMPTY_SPACE;
1810       ei->change->delay_fixed = 0;
1811       ei->change->delay_random = 0;
1812       ei->change->delay_frames = 1;
1813     }
1814
1815     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1816     {
1817       ei->has_change_event[j] = FALSE;
1818
1819       ei->event_page_nr[j] = 0;
1820       ei->event_page[j] = &ei->change_page[0];
1821     }
1822   }
1823
1824   /* add changing elements from pre-defined list */
1825   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1826   {
1827     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1828     struct ElementInfo *ei = &element_info[ch_delay->element];
1829
1830     ei->change->target_element       = ch_delay->target_element;
1831     ei->change->delay_fixed          = ch_delay->change_delay;
1832
1833     ei->change->pre_change_function  = ch_delay->pre_change_function;
1834     ei->change->change_function      = ch_delay->change_function;
1835     ei->change->post_change_function = ch_delay->post_change_function;
1836
1837     ei->change->can_change = TRUE;
1838     ei->change->can_change_or_has_action = TRUE;
1839
1840     ei->has_change_event[CE_DELAY] = TRUE;
1841
1842     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1843     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1844   }
1845
1846   /* ---------- initialize internal run-time variables ------------- */
1847
1848   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1849   {
1850     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1851
1852     for (j = 0; j < ei->num_change_pages; j++)
1853     {
1854       ei->change_page[j].can_change_or_has_action =
1855         (ei->change_page[j].can_change |
1856          ei->change_page[j].has_action);
1857     }
1858   }
1859
1860   /* add change events from custom element configuration */
1861   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1862   {
1863     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1864
1865     for (j = 0; j < ei->num_change_pages; j++)
1866     {
1867       if (!ei->change_page[j].can_change_or_has_action)
1868         continue;
1869
1870       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1871       {
1872         /* only add event page for the first page found with this event */
1873         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1874         {
1875           ei->has_change_event[k] = TRUE;
1876
1877           ei->event_page_nr[k] = j;
1878           ei->event_page[k] = &ei->change_page[j];
1879         }
1880       }
1881     }
1882   }
1883
1884   /* ---------- initialize run-time trigger player and element ------------- */
1885
1886   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1887   {
1888     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1889
1890     for (j = 0; j < ei->num_change_pages; j++)
1891     {
1892       ei->change_page[j].actual_trigger_element = EL_EMPTY;
1893       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1894       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1895       ei->change_page[j].actual_trigger_ce_value = 0;
1896       ei->change_page[j].actual_trigger_ce_score = 0;
1897     }
1898   }
1899
1900   /* ---------- initialize trigger events ---------------------------------- */
1901
1902   /* initialize trigger events information */
1903   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1904     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1905       trigger_events[i][j] = FALSE;
1906
1907   /* add trigger events from element change event properties */
1908   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1909   {
1910     struct ElementInfo *ei = &element_info[i];
1911
1912     for (j = 0; j < ei->num_change_pages; j++)
1913     {
1914       if (!ei->change_page[j].can_change_or_has_action)
1915         continue;
1916
1917       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1918       {
1919         int trigger_element = ei->change_page[j].trigger_element;
1920
1921         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1922         {
1923           if (ei->change_page[j].has_event[k])
1924           {
1925             if (IS_GROUP_ELEMENT(trigger_element))
1926             {
1927               struct ElementGroupInfo *group =
1928                 element_info[trigger_element].group;
1929
1930               for (l = 0; l < group->num_elements_resolved; l++)
1931                 trigger_events[group->element_resolved[l]][k] = TRUE;
1932             }
1933             else if (trigger_element == EL_ANY_ELEMENT)
1934               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1935                 trigger_events[l][k] = TRUE;
1936             else
1937               trigger_events[trigger_element][k] = TRUE;
1938           }
1939         }
1940       }
1941     }
1942   }
1943
1944   /* ---------- initialize push delay -------------------------------------- */
1945
1946   /* initialize push delay values to default */
1947   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1948   {
1949     if (!IS_CUSTOM_ELEMENT(i))
1950     {
1951       /* set default push delay values (corrected since version 3.0.7-1) */
1952       if (game.engine_version < VERSION_IDENT(3,0,7,1))
1953       {
1954         element_info[i].push_delay_fixed = 2;
1955         element_info[i].push_delay_random = 8;
1956       }
1957       else
1958       {
1959         element_info[i].push_delay_fixed = 8;
1960         element_info[i].push_delay_random = 8;
1961       }
1962     }
1963   }
1964
1965   /* set push delay value for certain elements from pre-defined list */
1966   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1967   {
1968     int e = push_delay_list[i].element;
1969
1970     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1971     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1972   }
1973
1974   /* set push delay value for Supaplex elements for newer engine versions */
1975   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1976   {
1977     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1978     {
1979       if (IS_SP_ELEMENT(i))
1980       {
1981         /* set SP push delay to just enough to push under a falling zonk */
1982         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1983
1984         element_info[i].push_delay_fixed  = delay;
1985         element_info[i].push_delay_random = 0;
1986       }
1987     }
1988   }
1989
1990   /* ---------- initialize move stepsize ----------------------------------- */
1991
1992   /* initialize move stepsize values to default */
1993   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1994     if (!IS_CUSTOM_ELEMENT(i))
1995       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1996
1997   /* set move stepsize value for certain elements from pre-defined list */
1998   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1999   {
2000     int e = move_stepsize_list[i].element;
2001
2002     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2003   }
2004
2005   /* ---------- initialize collect score ----------------------------------- */
2006
2007   /* initialize collect score values for custom elements from initial value */
2008   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2009     if (IS_CUSTOM_ELEMENT(i))
2010       element_info[i].collect_score = element_info[i].collect_score_initial;
2011
2012   /* ---------- initialize collect count ----------------------------------- */
2013
2014   /* initialize collect count values for non-custom elements */
2015   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2016     if (!IS_CUSTOM_ELEMENT(i))
2017       element_info[i].collect_count_initial = 0;
2018
2019   /* add collect count values for all elements from pre-defined list */
2020   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2021     element_info[collect_count_list[i].element].collect_count_initial =
2022       collect_count_list[i].count;
2023
2024   /* ---------- initialize access direction -------------------------------- */
2025
2026   /* initialize access direction values to default (access from every side) */
2027   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2028     if (!IS_CUSTOM_ELEMENT(i))
2029       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2030
2031   /* set access direction value for certain elements from pre-defined list */
2032   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2033     element_info[access_direction_list[i].element].access_direction =
2034       access_direction_list[i].direction;
2035
2036   /* ---------- initialize explosion content ------------------------------- */
2037   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2038   {
2039     if (IS_CUSTOM_ELEMENT(i))
2040       continue;
2041
2042     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2043     {
2044       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2045
2046       element_info[i].content.e[x][y] =
2047         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2048          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2049          i == EL_PLAYER_3 ? EL_EMERALD :
2050          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2051          i == EL_MOLE ? EL_EMERALD_RED :
2052          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2053          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2054          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2055          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2056          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2057          i == EL_WALL_EMERALD ? EL_EMERALD :
2058          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2059          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2060          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2061          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2062          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2063          i == EL_WALL_PEARL ? EL_PEARL :
2064          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2065          EL_EMPTY);
2066     }
2067   }
2068
2069   /* ---------- initialize recursion detection ------------------------------ */
2070   recursion_loop_depth = 0;
2071   recursion_loop_detected = FALSE;
2072   recursion_loop_element = EL_UNDEFINED;
2073 }
2074
2075 int get_num_special_action(int element, int action_first, int action_last)
2076 {
2077   int num_special_action = 0;
2078   int i, j;
2079
2080   for (i = action_first; i <= action_last; i++)
2081   {
2082     boolean found = FALSE;
2083
2084     for (j = 0; j < NUM_DIRECTIONS; j++)
2085       if (el_act_dir2img(element, i, j) !=
2086           el_act_dir2img(element, ACTION_DEFAULT, j))
2087         found = TRUE;
2088
2089     if (found)
2090       num_special_action++;
2091     else
2092       break;
2093   }
2094
2095   return num_special_action;
2096 }
2097
2098
2099 /*
2100   =============================================================================
2101   InitGame()
2102   -----------------------------------------------------------------------------
2103   initialize and start new game
2104   =============================================================================
2105 */
2106
2107 void InitGame()
2108 {
2109   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2110   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2111   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2112   boolean do_fading = (game_status == GAME_MODE_MAIN);
2113   int i, j, x, y;
2114
2115   game_status = GAME_MODE_PLAYING;
2116
2117   InitGameEngine();
2118
2119   /* don't play tapes over network */
2120   network_playing = (options.network && !tape.playing);
2121
2122   for (i = 0; i < MAX_PLAYERS; i++)
2123   {
2124     struct PlayerInfo *player = &stored_player[i];
2125
2126     player->index_nr = i;
2127     player->index_bit = (1 << i);
2128     player->element_nr = EL_PLAYER_1 + i;
2129
2130     player->present = FALSE;
2131     player->active = FALSE;
2132     player->killed = FALSE;
2133
2134     player->action = 0;
2135     player->effective_action = 0;
2136     player->programmed_action = 0;
2137
2138     player->score = 0;
2139     player->score_final = 0;
2140
2141     player->gems_still_needed = level.gems_needed;
2142     player->sokobanfields_still_needed = 0;
2143     player->lights_still_needed = 0;
2144     player->friends_still_needed = 0;
2145
2146     for (j = 0; j < MAX_NUM_KEYS; j++)
2147       player->key[j] = FALSE;
2148
2149     player->num_white_keys = 0;
2150
2151     player->dynabomb_count = 0;
2152     player->dynabomb_size = 1;
2153     player->dynabombs_left = 0;
2154     player->dynabomb_xl = FALSE;
2155
2156     player->MovDir = MV_NONE;
2157     player->MovPos = 0;
2158     player->GfxPos = 0;
2159     player->GfxDir = MV_NONE;
2160     player->GfxAction = ACTION_DEFAULT;
2161     player->Frame = 0;
2162     player->StepFrame = 0;
2163
2164     player->use_murphy = FALSE;
2165     player->artwork_element =
2166       (level.use_artwork_element[i] ? level.artwork_element[i] :
2167        player->element_nr);
2168
2169     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2170     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2171
2172     player->gravity = level.initial_player_gravity[i];
2173
2174     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2175
2176     player->actual_frame_counter = 0;
2177
2178     player->step_counter = 0;
2179
2180     player->last_move_dir = MV_NONE;
2181
2182     player->is_active = FALSE;
2183
2184     player->is_waiting = FALSE;
2185     player->is_moving = FALSE;
2186     player->is_auto_moving = FALSE;
2187     player->is_digging = FALSE;
2188     player->is_snapping = FALSE;
2189     player->is_collecting = FALSE;
2190     player->is_pushing = FALSE;
2191     player->is_switching = FALSE;
2192     player->is_dropping = FALSE;
2193     player->is_dropping_pressed = FALSE;
2194
2195     player->is_bored = FALSE;
2196     player->is_sleeping = FALSE;
2197
2198     player->frame_counter_bored = -1;
2199     player->frame_counter_sleeping = -1;
2200
2201     player->anim_delay_counter = 0;
2202     player->post_delay_counter = 0;
2203
2204     player->dir_waiting = MV_NONE;
2205     player->action_waiting = ACTION_DEFAULT;
2206     player->last_action_waiting = ACTION_DEFAULT;
2207     player->special_action_bored = ACTION_DEFAULT;
2208     player->special_action_sleeping = ACTION_DEFAULT;
2209
2210     player->switch_x = -1;
2211     player->switch_y = -1;
2212
2213     player->drop_x = -1;
2214     player->drop_y = -1;
2215
2216     player->show_envelope = 0;
2217
2218     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2219
2220     player->push_delay       = -1;      /* initialized when pushing starts */
2221     player->push_delay_value = game.initial_push_delay_value;
2222
2223     player->drop_delay = 0;
2224     player->drop_pressed_delay = 0;
2225
2226     player->last_jx = -1;
2227     player->last_jy = -1;
2228     player->jx = -1;
2229     player->jy = -1;
2230
2231     player->shield_normal_time_left = 0;
2232     player->shield_deadly_time_left = 0;
2233
2234     player->inventory_infinite_element = EL_UNDEFINED;
2235     player->inventory_size = 0;
2236
2237     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2238     SnapField(player, 0, 0);
2239
2240     player->LevelSolved = FALSE;
2241     player->GameOver = FALSE;
2242
2243     player->LevelSolved_GameEnd = FALSE;
2244     player->LevelSolved_SaveTape = FALSE;
2245     player->LevelSolved_SaveScore = FALSE;
2246   }
2247
2248   network_player_action_received = FALSE;
2249
2250 #if defined(NETWORK_AVALIABLE)
2251   /* initial null action */
2252   if (network_playing)
2253     SendToServer_MovePlayer(MV_NONE);
2254 #endif
2255
2256   ZX = ZY = -1;
2257   ExitX = ExitY = -1;
2258
2259   FrameCounter = 0;
2260   TimeFrames = 0;
2261   TimePlayed = 0;
2262   TimeLeft = level.time;
2263   TapeTime = 0;
2264
2265   ScreenMovDir = MV_NONE;
2266   ScreenMovPos = 0;
2267   ScreenGfxPos = 0;
2268
2269   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2270
2271   AllPlayersGone = FALSE;
2272
2273   game.yamyam_content_nr = 0;
2274   game.magic_wall_active = FALSE;
2275   game.magic_wall_time_left = 0;
2276   game.light_time_left = 0;
2277   game.timegate_time_left = 0;
2278   game.switchgate_pos = 0;
2279   game.wind_direction = level.wind_direction_initial;
2280
2281 #if !USE_PLAYER_GRAVITY
2282   game.gravity = FALSE;
2283   game.explosions_delayed = TRUE;
2284 #endif
2285
2286   game.lenses_time_left = 0;
2287   game.magnify_time_left = 0;
2288
2289   game.ball_state = level.ball_state_initial;
2290   game.ball_content_nr = 0;
2291
2292   game.envelope_active = FALSE;
2293
2294   /* set focus to local player for network games, else to all players */
2295   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2296   game.centered_player_nr_next = game.centered_player_nr;
2297   game.set_centered_player = FALSE;
2298
2299   if (network_playing && tape.recording)
2300   {
2301     /* store client dependent player focus when recording network games */
2302     tape.centered_player_nr_next = game.centered_player_nr_next;
2303     tape.set_centered_player = TRUE;
2304   }
2305
2306   for (i = 0; i < NUM_BELTS; i++)
2307   {
2308     game.belt_dir[i] = MV_NONE;
2309     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2310   }
2311
2312   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2313     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2314
2315   SCAN_PLAYFIELD(x, y)
2316   {
2317     Feld[x][y] = level.field[x][y];
2318     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2319     ChangeDelay[x][y] = 0;
2320     ChangePage[x][y] = -1;
2321 #if USE_NEW_CUSTOM_VALUE
2322     CustomValue[x][y] = 0;              /* initialized in InitField() */
2323 #endif
2324     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2325     AmoebaNr[x][y] = 0;
2326     WasJustMoving[x][y] = 0;
2327     WasJustFalling[x][y] = 0;
2328     CheckCollision[x][y] = 0;
2329     CheckImpact[x][y] = 0;
2330     Stop[x][y] = FALSE;
2331     Pushed[x][y] = FALSE;
2332
2333     ChangeCount[x][y] = 0;
2334     ChangeEvent[x][y] = -1;
2335
2336     ExplodePhase[x][y] = 0;
2337     ExplodeDelay[x][y] = 0;
2338     ExplodeField[x][y] = EX_TYPE_NONE;
2339
2340     RunnerVisit[x][y] = 0;
2341     PlayerVisit[x][y] = 0;
2342
2343     GfxFrame[x][y] = 0;
2344     GfxRandom[x][y] = INIT_GFX_RANDOM();
2345     GfxElement[x][y] = EL_UNDEFINED;
2346     GfxAction[x][y] = ACTION_DEFAULT;
2347     GfxDir[x][y] = MV_NONE;
2348   }
2349
2350   SCAN_PLAYFIELD(x, y)
2351   {
2352     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2353       emulate_bd = FALSE;
2354     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2355       emulate_sb = FALSE;
2356     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2357       emulate_sp = FALSE;
2358
2359     InitField(x, y, TRUE);
2360   }
2361
2362   InitBeltMovement();
2363
2364   for (i = 0; i < MAX_PLAYERS; i++)
2365   {
2366     struct PlayerInfo *player = &stored_player[i];
2367
2368     /* set number of special actions for bored and sleeping animation */
2369     player->num_special_action_bored =
2370       get_num_special_action(player->artwork_element,
2371                              ACTION_BORING_1, ACTION_BORING_LAST);
2372     player->num_special_action_sleeping =
2373       get_num_special_action(player->artwork_element,
2374                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2375   }
2376
2377   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2378                     emulate_sb ? EMU_SOKOBAN :
2379                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2380
2381 #if USE_NEW_ALL_SLIPPERY
2382   /* initialize type of slippery elements */
2383   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2384   {
2385     if (!IS_CUSTOM_ELEMENT(i))
2386     {
2387       /* default: elements slip down either to the left or right randomly */
2388       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2389
2390       /* SP style elements prefer to slip down on the left side */
2391       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2392         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2393
2394       /* BD style elements prefer to slip down on the left side */
2395       if (game.emulation == EMU_BOULDERDASH)
2396         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2397     }
2398   }
2399 #endif
2400
2401   /* initialize explosion and ignition delay */
2402   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2403   {
2404     if (!IS_CUSTOM_ELEMENT(i))
2405     {
2406       int num_phase = 8;
2407       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2408                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2409                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2410       int last_phase = (num_phase + 1) * delay;
2411       int half_phase = (num_phase / 2) * delay;
2412
2413       element_info[i].explosion_delay = last_phase - 1;
2414       element_info[i].ignition_delay = half_phase;
2415
2416       if (i == EL_BLACK_ORB)
2417         element_info[i].ignition_delay = 1;
2418     }
2419
2420 #if 0
2421     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2422       element_info[i].explosion_delay = 1;
2423
2424     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2425       element_info[i].ignition_delay = 1;
2426 #endif
2427   }
2428
2429   /* correct non-moving belts to start moving left */
2430   for (i = 0; i < NUM_BELTS; i++)
2431     if (game.belt_dir[i] == MV_NONE)
2432       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2433
2434   /* check if any connected player was not found in playfield */
2435   for (i = 0; i < MAX_PLAYERS; i++)
2436   {
2437     struct PlayerInfo *player = &stored_player[i];
2438
2439     if (player->connected && !player->present)
2440     {
2441       for (j = 0; j < MAX_PLAYERS; j++)
2442       {
2443         struct PlayerInfo *some_player = &stored_player[j];
2444         int jx = some_player->jx, jy = some_player->jy;
2445
2446         /* assign first free player found that is present in the playfield */
2447         if (some_player->present && !some_player->connected)
2448         {
2449           player->present = TRUE;
2450           player->active = TRUE;
2451
2452           some_player->present = FALSE;
2453           some_player->active = FALSE;
2454
2455           player->artwork_element = some_player->artwork_element;
2456
2457           player->block_last_field       = some_player->block_last_field;
2458           player->block_delay_adjustment = some_player->block_delay_adjustment;
2459
2460           StorePlayer[jx][jy] = player->element_nr;
2461           player->jx = player->last_jx = jx;
2462           player->jy = player->last_jy = jy;
2463
2464           break;
2465         }
2466       }
2467     }
2468   }
2469
2470   if (tape.playing)
2471   {
2472     /* when playing a tape, eliminate all players who do not participate */
2473
2474     for (i = 0; i < MAX_PLAYERS; i++)
2475     {
2476       if (stored_player[i].active && !tape.player_participates[i])
2477       {
2478         struct PlayerInfo *player = &stored_player[i];
2479         int jx = player->jx, jy = player->jy;
2480
2481         player->active = FALSE;
2482         StorePlayer[jx][jy] = 0;
2483         Feld[jx][jy] = EL_EMPTY;
2484       }
2485     }
2486   }
2487   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2488   {
2489     /* when in single player mode, eliminate all but the first active player */
2490
2491     for (i = 0; i < MAX_PLAYERS; i++)
2492     {
2493       if (stored_player[i].active)
2494       {
2495         for (j = i + 1; j < MAX_PLAYERS; j++)
2496         {
2497           if (stored_player[j].active)
2498           {
2499             struct PlayerInfo *player = &stored_player[j];
2500             int jx = player->jx, jy = player->jy;
2501
2502             player->active = FALSE;
2503             player->present = FALSE;
2504
2505             StorePlayer[jx][jy] = 0;
2506             Feld[jx][jy] = EL_EMPTY;
2507           }
2508         }
2509       }
2510     }
2511   }
2512
2513   /* when recording the game, store which players take part in the game */
2514   if (tape.recording)
2515   {
2516     for (i = 0; i < MAX_PLAYERS; i++)
2517       if (stored_player[i].active)
2518         tape.player_participates[i] = TRUE;
2519   }
2520
2521   if (options.debug)
2522   {
2523     for (i = 0; i < MAX_PLAYERS; i++)
2524     {
2525       struct PlayerInfo *player = &stored_player[i];
2526
2527       printf("Player %d: present == %d, connected == %d, active == %d.\n",
2528              i+1,
2529              player->present,
2530              player->connected,
2531              player->active);
2532       if (local_player == player)
2533         printf("Player  %d is local player.\n", i+1);
2534     }
2535   }
2536
2537   if (BorderElement == EL_EMPTY)
2538   {
2539     SBX_Left = 0;
2540     SBX_Right = lev_fieldx - SCR_FIELDX;
2541     SBY_Upper = 0;
2542     SBY_Lower = lev_fieldy - SCR_FIELDY;
2543   }
2544   else
2545   {
2546     SBX_Left = -1;
2547     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2548     SBY_Upper = -1;
2549     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2550   }
2551
2552   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2553     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2554
2555   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2556     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2557
2558   /* if local player not found, look for custom element that might create
2559      the player (make some assumptions about the right custom element) */
2560   if (!local_player->present)
2561   {
2562     int start_x = 0, start_y = 0;
2563     int found_rating = 0;
2564     int found_element = EL_UNDEFINED;
2565     int player_nr = local_player->index_nr;
2566
2567     SCAN_PLAYFIELD(x, y)
2568     {
2569       int element = Feld[x][y];
2570       int content;
2571       int xx, yy;
2572       boolean is_player;
2573
2574       if (level.use_start_element[player_nr] &&
2575           level.start_element[player_nr] == element &&
2576           found_rating < 4)
2577       {
2578         start_x = x;
2579         start_y = y;
2580
2581         found_rating = 4;
2582         found_element = element;
2583       }
2584
2585       if (!IS_CUSTOM_ELEMENT(element))
2586         continue;
2587
2588       if (CAN_CHANGE(element))
2589       {
2590         for (i = 0; i < element_info[element].num_change_pages; i++)
2591         {
2592           /* check for player created from custom element as single target */
2593           content = element_info[element].change_page[i].target_element;
2594           is_player = ELEM_IS_PLAYER(content);
2595
2596           if (is_player && (found_rating < 3 || element < found_element))
2597           {
2598             start_x = x;
2599             start_y = y;
2600
2601             found_rating = 3;
2602             found_element = element;
2603           }
2604         }
2605       }
2606
2607       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2608       {
2609         /* check for player created from custom element as explosion content */
2610         content = element_info[element].content.e[xx][yy];
2611         is_player = ELEM_IS_PLAYER(content);
2612
2613         if (is_player && (found_rating < 2 || element < found_element))
2614         {
2615           start_x = x + xx - 1;
2616           start_y = y + yy - 1;
2617
2618           found_rating = 2;
2619           found_element = element;
2620         }
2621
2622         if (!CAN_CHANGE(element))
2623           continue;
2624
2625         for (i = 0; i < element_info[element].num_change_pages; i++)
2626         {
2627           /* check for player created from custom element as extended target */
2628           content =
2629             element_info[element].change_page[i].target_content.e[xx][yy];
2630
2631           is_player = ELEM_IS_PLAYER(content);
2632
2633           if (is_player && (found_rating < 1 || element < found_element))
2634           {
2635             start_x = x + xx - 1;
2636             start_y = y + yy - 1;
2637
2638             found_rating = 1;
2639             found_element = element;
2640           }
2641         }
2642       }
2643     }
2644
2645     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
2646                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2647                 start_x - MIDPOSX);
2648
2649     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2650                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2651                 start_y - MIDPOSY);
2652   }
2653   else
2654   {
2655     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2656                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2657                 local_player->jx - MIDPOSX);
2658
2659     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2660                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2661                 local_player->jy - MIDPOSY);
2662   }
2663
2664   StopAnimation();
2665
2666   if (!game.restart_level)
2667     CloseDoor(DOOR_CLOSE_1);
2668
2669   if (do_fading)
2670     FadeOut(REDRAW_FIELD);
2671
2672   /* !!! FIX THIS (START) !!! */
2673   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2674   {
2675     InitGameEngine_EM();
2676
2677     /* blit playfield from scroll buffer to normal back buffer for fading in */
2678     BlitScreenToBitmap_EM(backbuffer);
2679   }
2680   else
2681   {
2682     DrawLevel();
2683     DrawAllPlayers();
2684
2685     /* after drawing the level, correct some elements */
2686     if (game.timegate_time_left == 0)
2687       CloseAllOpenTimegates();
2688
2689     /* blit playfield from scroll buffer to normal back buffer for fading in */
2690     if (setup.soft_scrolling)
2691       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2692
2693     redraw_mask |= REDRAW_FROM_BACKBUFFER;
2694   }
2695   /* !!! FIX THIS (END) !!! */
2696
2697   if (do_fading)
2698     FadeIn(REDRAW_FIELD);
2699
2700   BackToFront();
2701
2702   if (!game.restart_level)
2703   {
2704     /* copy default game door content to main double buffer */
2705     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2706                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2707   }
2708
2709   SetPanelBackground();
2710   SetDrawBackgroundMask(REDRAW_DOOR_1);
2711
2712   DrawGameDoorValues();
2713
2714   if (!game.restart_level)
2715   {
2716     UnmapGameButtons();
2717     UnmapTapeButtons();
2718     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2719     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2720     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2721     MapGameButtons();
2722     MapTapeButtons();
2723
2724     /* copy actual game door content to door double buffer for OpenDoor() */
2725     BlitBitmap(drawto, bitmap_db_door,
2726                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2727
2728     OpenDoor(DOOR_OPEN_ALL);
2729
2730     PlaySound(SND_GAME_STARTING);
2731
2732     if (setup.sound_music)
2733       PlayLevelMusic();
2734
2735     KeyboardAutoRepeatOffUnlessAutoplay();
2736
2737     if (options.debug)
2738     {
2739       for (i = 0; i < MAX_PLAYERS; i++)
2740         printf("Player %d %sactive.\n",
2741                i + 1, (stored_player[i].active ? "" : "not "));
2742     }
2743   }
2744
2745 #if 1
2746   UnmapAllGadgets();
2747
2748   MapGameButtons();
2749   MapTapeButtons();
2750 #endif
2751
2752   game.restart_level = FALSE;
2753 }
2754
2755 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2756 {
2757   /* this is used for non-R'n'D game engines to update certain engine values */
2758
2759   /* needed to determine if sounds are played within the visible screen area */
2760   scroll_x = actual_scroll_x;
2761   scroll_y = actual_scroll_y;
2762 }
2763
2764 void InitMovDir(int x, int y)
2765 {
2766   int i, element = Feld[x][y];
2767   static int xy[4][2] =
2768   {
2769     {  0, +1 },
2770     { +1,  0 },
2771     {  0, -1 },
2772     { -1,  0 }
2773   };
2774   static int direction[3][4] =
2775   {
2776     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
2777     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
2778     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
2779   };
2780
2781   switch (element)
2782   {
2783     case EL_BUG_RIGHT:
2784     case EL_BUG_UP:
2785     case EL_BUG_LEFT:
2786     case EL_BUG_DOWN:
2787       Feld[x][y] = EL_BUG;
2788       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2789       break;
2790
2791     case EL_SPACESHIP_RIGHT:
2792     case EL_SPACESHIP_UP:
2793     case EL_SPACESHIP_LEFT:
2794     case EL_SPACESHIP_DOWN:
2795       Feld[x][y] = EL_SPACESHIP;
2796       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2797       break;
2798
2799     case EL_BD_BUTTERFLY_RIGHT:
2800     case EL_BD_BUTTERFLY_UP:
2801     case EL_BD_BUTTERFLY_LEFT:
2802     case EL_BD_BUTTERFLY_DOWN:
2803       Feld[x][y] = EL_BD_BUTTERFLY;
2804       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2805       break;
2806
2807     case EL_BD_FIREFLY_RIGHT:
2808     case EL_BD_FIREFLY_UP:
2809     case EL_BD_FIREFLY_LEFT:
2810     case EL_BD_FIREFLY_DOWN:
2811       Feld[x][y] = EL_BD_FIREFLY;
2812       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2813       break;
2814
2815     case EL_PACMAN_RIGHT:
2816     case EL_PACMAN_UP:
2817     case EL_PACMAN_LEFT:
2818     case EL_PACMAN_DOWN:
2819       Feld[x][y] = EL_PACMAN;
2820       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2821       break;
2822
2823     case EL_YAMYAM_LEFT:
2824     case EL_YAMYAM_RIGHT:
2825     case EL_YAMYAM_UP:
2826     case EL_YAMYAM_DOWN:
2827       Feld[x][y] = EL_YAMYAM;
2828       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2829       break;
2830
2831     case EL_SP_SNIKSNAK:
2832       MovDir[x][y] = MV_UP;
2833       break;
2834
2835     case EL_SP_ELECTRON:
2836       MovDir[x][y] = MV_LEFT;
2837       break;
2838
2839     case EL_MOLE_LEFT:
2840     case EL_MOLE_RIGHT:
2841     case EL_MOLE_UP:
2842     case EL_MOLE_DOWN:
2843       Feld[x][y] = EL_MOLE;
2844       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2845       break;
2846
2847     default:
2848       if (IS_CUSTOM_ELEMENT(element))
2849       {
2850         struct ElementInfo *ei = &element_info[element];
2851         int move_direction_initial = ei->move_direction_initial;
2852         int move_pattern = ei->move_pattern;
2853
2854         if (move_direction_initial == MV_START_PREVIOUS)
2855         {
2856           if (MovDir[x][y] != MV_NONE)
2857             return;
2858
2859           move_direction_initial = MV_START_AUTOMATIC;
2860         }
2861
2862         if (move_direction_initial == MV_START_RANDOM)
2863           MovDir[x][y] = 1 << RND(4);
2864         else if (move_direction_initial & MV_ANY_DIRECTION)
2865           MovDir[x][y] = move_direction_initial;
2866         else if (move_pattern == MV_ALL_DIRECTIONS ||
2867                  move_pattern == MV_TURNING_LEFT ||
2868                  move_pattern == MV_TURNING_RIGHT ||
2869                  move_pattern == MV_TURNING_LEFT_RIGHT ||
2870                  move_pattern == MV_TURNING_RIGHT_LEFT ||
2871                  move_pattern == MV_TURNING_RANDOM)
2872           MovDir[x][y] = 1 << RND(4);
2873         else if (move_pattern == MV_HORIZONTAL)
2874           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2875         else if (move_pattern == MV_VERTICAL)
2876           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2877         else if (move_pattern & MV_ANY_DIRECTION)
2878           MovDir[x][y] = element_info[element].move_pattern;
2879         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2880                  move_pattern == MV_ALONG_RIGHT_SIDE)
2881         {
2882           /* use random direction as default start direction */
2883           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2884             MovDir[x][y] = 1 << RND(4);
2885
2886           for (i = 0; i < NUM_DIRECTIONS; i++)
2887           {
2888             int x1 = x + xy[i][0];
2889             int y1 = y + xy[i][1];
2890
2891             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2892             {
2893               if (move_pattern == MV_ALONG_RIGHT_SIDE)
2894                 MovDir[x][y] = direction[0][i];
2895               else
2896                 MovDir[x][y] = direction[1][i];
2897
2898               break;
2899             }
2900           }
2901         }                
2902       }
2903       else
2904       {
2905         MovDir[x][y] = 1 << RND(4);
2906
2907         if (element != EL_BUG &&
2908             element != EL_SPACESHIP &&
2909             element != EL_BD_BUTTERFLY &&
2910             element != EL_BD_FIREFLY)
2911           break;
2912
2913         for (i = 0; i < NUM_DIRECTIONS; i++)
2914         {
2915           int x1 = x + xy[i][0];
2916           int y1 = y + xy[i][1];
2917
2918           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2919           {
2920             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2921             {
2922               MovDir[x][y] = direction[0][i];
2923               break;
2924             }
2925             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2926                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2927             {
2928               MovDir[x][y] = direction[1][i];
2929               break;
2930             }
2931           }
2932         }
2933       }
2934       break;
2935   }
2936
2937   GfxDir[x][y] = MovDir[x][y];
2938 }
2939
2940 void InitAmoebaNr(int x, int y)
2941 {
2942   int i;
2943   int group_nr = AmoebeNachbarNr(x, y);
2944
2945   if (group_nr == 0)
2946   {
2947     for (i = 1; i < MAX_NUM_AMOEBA; i++)
2948     {
2949       if (AmoebaCnt[i] == 0)
2950       {
2951         group_nr = i;
2952         break;
2953       }
2954     }
2955   }
2956
2957   AmoebaNr[x][y] = group_nr;
2958   AmoebaCnt[group_nr]++;
2959   AmoebaCnt2[group_nr]++;
2960 }
2961
2962 static void PlayerWins(struct PlayerInfo *player)
2963 {
2964   player->LevelSolved = TRUE;
2965   player->GameOver = TRUE;
2966
2967   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2968                          level.native_em_level->lev->score : player->score);
2969 }
2970
2971 void GameWon()
2972 {
2973   static int time, time_final;
2974   static int score, score_final;
2975   static int game_over_delay = 0;
2976   int game_over_delay_value = 50;
2977
2978   if (!local_player->LevelSolved_GameEnd)
2979   {
2980     int i;
2981
2982     /* do not start end game actions before the player stops moving (to exit) */
2983     if (local_player->MovPos)
2984       return;
2985
2986     local_player->LevelSolved_GameEnd = TRUE;
2987     local_player->LevelSolved_SaveTape = tape.recording;
2988     local_player->LevelSolved_SaveScore = !tape.playing;
2989
2990     if (tape.auto_play)         /* tape might already be stopped here */
2991       tape.auto_play_level_solved = TRUE;
2992
2993 #if 1
2994     TapeStop();
2995 #endif
2996
2997     game_over_delay = game_over_delay_value;
2998
2999     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3000     score = score_final = local_player->score_final;
3001
3002     if (TimeLeft > 0)
3003     {
3004       time_final = 0;
3005       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3006     }
3007     else if (level.time == 0 && TimePlayed < 999)
3008     {
3009       time_final = 999;
3010       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3011     }
3012
3013     local_player->score_final = score_final;
3014
3015     if (level_editor_test_game)
3016     {
3017       time = time_final;
3018       score = score_final;
3019
3020       DrawGameValue_Time(time);
3021       DrawGameValue_Score(score);
3022     }
3023
3024     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3025     {
3026       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3027       {
3028         /* close exit door after last player */
3029         if ((AllPlayersGone &&
3030              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3031               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3032               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3033             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3034             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3035         {
3036           int element = Feld[ExitX][ExitY];
3037
3038 #if 0
3039           if (element == EL_EM_EXIT_OPEN ||
3040               element == EL_EM_STEEL_EXIT_OPEN)
3041           {
3042             Bang(ExitX, ExitY);
3043           }
3044           else
3045 #endif
3046           {
3047             Feld[ExitX][ExitY] =
3048               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3049                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3050                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3051                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3052                EL_EM_STEEL_EXIT_CLOSING);
3053
3054             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3055           }
3056         }
3057
3058         /* player disappears */
3059         DrawLevelField(ExitX, ExitY);
3060       }
3061
3062       for (i = 0; i < MAX_PLAYERS; i++)
3063       {
3064         struct PlayerInfo *player = &stored_player[i];
3065
3066         if (player->present)
3067         {
3068           RemovePlayer(player);
3069
3070           /* player disappears */
3071           DrawLevelField(player->jx, player->jy);
3072         }
3073       }
3074     }
3075
3076     PlaySound(SND_GAME_WINNING);
3077   }
3078
3079   if (game_over_delay > 0)
3080   {
3081     game_over_delay--;
3082
3083     return;
3084   }
3085
3086   if (time != time_final)
3087   {
3088     int time_to_go = ABS(time_final - time);
3089     int time_count_dir = (time < time_final ? +1 : -1);
3090     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3091
3092     time  += time_count_steps * time_count_dir;
3093     score += time_count_steps * level.score[SC_TIME_BONUS];
3094
3095     DrawGameValue_Time(time);
3096     DrawGameValue_Score(score);
3097
3098     if (time == time_final)
3099       StopSound(SND_GAME_LEVELTIME_BONUS);
3100     else if (setup.sound_loops)
3101       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3102     else
3103       PlaySound(SND_GAME_LEVELTIME_BONUS);
3104   }
3105 }
3106
3107 void GameEnd()
3108 {
3109   int hi_pos;
3110   boolean raise_level = FALSE;
3111
3112   CloseDoor(DOOR_CLOSE_1);
3113
3114   if (local_player->LevelSolved_SaveTape)
3115   {
3116 #if 0
3117     TapeStop();
3118 #endif
3119
3120 #if 1
3121     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3122 #else
3123     SaveTape(tape.level_nr);            /* ask to save tape */
3124 #endif
3125   }
3126
3127   if (level_editor_test_game)
3128   {
3129     game_status = GAME_MODE_MAIN;
3130
3131     DrawMainMenu();
3132
3133     return;
3134   }
3135
3136   if (!local_player->LevelSolved_SaveScore)
3137   {
3138     FadeOut(REDRAW_FIELD);
3139
3140     game_status = GAME_MODE_MAIN;
3141
3142     DrawAndFadeInMainMenu(REDRAW_FIELD);
3143
3144     return;
3145   }
3146
3147   if (level_nr == leveldir_current->handicap_level)
3148   {
3149     leveldir_current->handicap_level++;
3150     SaveLevelSetup_SeriesInfo();
3151   }
3152
3153   if (level_nr < leveldir_current->last_level)
3154     raise_level = TRUE;                 /* advance to next level */
3155
3156   if ((hi_pos = NewHiScore()) >= 0) 
3157   {
3158     game_status = GAME_MODE_SCORES;
3159
3160     DrawHallOfFame(hi_pos);
3161
3162     if (raise_level)
3163     {
3164       level_nr++;
3165       TapeErase();
3166     }
3167   }
3168   else
3169   {
3170     FadeOut(REDRAW_FIELD);
3171
3172     game_status = GAME_MODE_MAIN;
3173
3174     if (raise_level)
3175     {
3176       level_nr++;
3177       TapeErase();
3178     }
3179
3180     DrawAndFadeInMainMenu(REDRAW_FIELD);
3181   }
3182 }
3183
3184 int NewHiScore()
3185 {
3186   int k, l;
3187   int position = -1;
3188
3189   LoadScore(level_nr);
3190
3191   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3192       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3193     return -1;
3194
3195   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3196   {
3197     if (local_player->score_final > highscore[k].Score)
3198     {
3199       /* player has made it to the hall of fame */
3200
3201       if (k < MAX_SCORE_ENTRIES - 1)
3202       {
3203         int m = MAX_SCORE_ENTRIES - 1;
3204
3205 #ifdef ONE_PER_NAME
3206         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3207           if (strEqual(setup.player_name, highscore[l].Name))
3208             m = l;
3209         if (m == k)     /* player's new highscore overwrites his old one */
3210           goto put_into_list;
3211 #endif
3212
3213         for (l = m; l > k; l--)
3214         {
3215           strcpy(highscore[l].Name, highscore[l - 1].Name);
3216           highscore[l].Score = highscore[l - 1].Score;
3217         }
3218       }
3219
3220 #ifdef ONE_PER_NAME
3221       put_into_list:
3222 #endif
3223       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3224       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3225       highscore[k].Score = local_player->score_final; 
3226       position = k;
3227       break;
3228     }
3229
3230 #ifdef ONE_PER_NAME
3231     else if (!strncmp(setup.player_name, highscore[k].Name,
3232                       MAX_PLAYER_NAME_LEN))
3233       break;    /* player already there with a higher score */
3234 #endif
3235
3236   }
3237
3238   if (position >= 0) 
3239     SaveScore(level_nr);
3240
3241   return position;
3242 }
3243
3244 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3245 {
3246   int element = Feld[x][y];
3247   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3248   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3249   int horiz_move = (dx != 0);
3250   int sign = (horiz_move ? dx : dy);
3251   int step = sign * element_info[element].move_stepsize;
3252
3253   /* special values for move stepsize for spring and things on conveyor belt */
3254   if (horiz_move)
3255   {
3256     if (CAN_FALL(element) &&
3257         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3258       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3259     else if (element == EL_SPRING)
3260       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3261   }
3262
3263   return step;
3264 }
3265
3266 inline static int getElementMoveStepsize(int x, int y)
3267 {
3268   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3269 }
3270
3271 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3272 {
3273   if (player->GfxAction != action || player->GfxDir != dir)
3274   {
3275 #if 0
3276     printf("Player frame reset! (%d => %d, %d => %d)\n",
3277            player->GfxAction, action, player->GfxDir, dir);
3278 #endif
3279
3280     player->GfxAction = action;
3281     player->GfxDir = dir;
3282     player->Frame = 0;
3283     player->StepFrame = 0;
3284   }
3285 }
3286
3287 #if USE_GFX_RESET_GFX_ANIMATION
3288 static void ResetGfxFrame(int x, int y, boolean redraw)
3289 {
3290   int element = Feld[x][y];
3291   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3292   int last_gfx_frame = GfxFrame[x][y];
3293
3294   if (graphic_info[graphic].anim_global_sync)
3295     GfxFrame[x][y] = FrameCounter;
3296   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3297     GfxFrame[x][y] = CustomValue[x][y];
3298   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3299     GfxFrame[x][y] = element_info[element].collect_score;
3300   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3301     GfxFrame[x][y] = ChangeDelay[x][y];
3302
3303   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3304     DrawLevelGraphicAnimation(x, y, graphic);
3305 }
3306 #endif
3307
3308 static void ResetGfxAnimation(int x, int y)
3309 {
3310   GfxAction[x][y] = ACTION_DEFAULT;
3311   GfxDir[x][y] = MovDir[x][y];
3312   GfxFrame[x][y] = 0;
3313
3314 #if USE_GFX_RESET_GFX_ANIMATION
3315   ResetGfxFrame(x, y, FALSE);
3316 #endif
3317 }
3318
3319 static void ResetRandomAnimationValue(int x, int y)
3320 {
3321   GfxRandom[x][y] = INIT_GFX_RANDOM();
3322 }
3323
3324 void InitMovingField(int x, int y, int direction)
3325 {
3326   int element = Feld[x][y];
3327   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3328   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3329   int newx = x + dx;
3330   int newy = y + dy;
3331   boolean is_moving_before, is_moving_after;
3332 #if 0
3333   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3334 #endif
3335
3336   /* check if element was/is moving or being moved before/after mode change */
3337 #if 1
3338   is_moving_before = WasJustMoving[x][y];
3339 #else
3340   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3341 #endif
3342   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
3343
3344   /* reset animation only for moving elements which change direction of moving
3345      or which just started or stopped moving
3346      (else CEs with property "can move" / "not moving" are reset each frame) */
3347 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3348 #if 1
3349   if (is_moving_before != is_moving_after ||
3350       direction != MovDir[x][y])
3351     ResetGfxAnimation(x, y);
3352 #else
3353   if ((is_moving_before || is_moving_after) && !continues_moving)
3354     ResetGfxAnimation(x, y);
3355 #endif
3356 #else
3357   if (!continues_moving)
3358     ResetGfxAnimation(x, y);
3359 #endif
3360
3361   MovDir[x][y] = direction;
3362   GfxDir[x][y] = direction;
3363
3364 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3365   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3366                      direction == MV_DOWN && CAN_FALL(element) ?
3367                      ACTION_FALLING : ACTION_MOVING);
3368 #else
3369   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3370                      ACTION_FALLING : ACTION_MOVING);
3371 #endif
3372
3373   /* this is needed for CEs with property "can move" / "not moving" */
3374
3375   if (is_moving_after)
3376   {
3377     if (Feld[newx][newy] == EL_EMPTY)
3378       Feld[newx][newy] = EL_BLOCKED;
3379
3380     MovDir[newx][newy] = MovDir[x][y];
3381
3382 #if USE_NEW_CUSTOM_VALUE
3383     CustomValue[newx][newy] = CustomValue[x][y];
3384 #endif
3385
3386     GfxFrame[newx][newy] = GfxFrame[x][y];
3387     GfxRandom[newx][newy] = GfxRandom[x][y];
3388     GfxAction[newx][newy] = GfxAction[x][y];
3389     GfxDir[newx][newy] = GfxDir[x][y];
3390   }
3391 }
3392
3393 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3394 {
3395   int direction = MovDir[x][y];
3396   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3397   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3398
3399   *goes_to_x = newx;
3400   *goes_to_y = newy;
3401 }
3402
3403 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3404 {
3405   int oldx = x, oldy = y;
3406   int direction = MovDir[x][y];
3407
3408   if (direction == MV_LEFT)
3409     oldx++;
3410   else if (direction == MV_RIGHT)
3411     oldx--;
3412   else if (direction == MV_UP)
3413     oldy++;
3414   else if (direction == MV_DOWN)
3415     oldy--;
3416
3417   *comes_from_x = oldx;
3418   *comes_from_y = oldy;
3419 }
3420
3421 int MovingOrBlocked2Element(int x, int y)
3422 {
3423   int element = Feld[x][y];
3424
3425   if (element == EL_BLOCKED)
3426   {
3427     int oldx, oldy;
3428
3429     Blocked2Moving(x, y, &oldx, &oldy);
3430     return Feld[oldx][oldy];
3431   }
3432   else
3433     return element;
3434 }
3435
3436 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3437 {
3438   /* like MovingOrBlocked2Element(), but if element is moving
3439      and (x,y) is the field the moving element is just leaving,
3440      return EL_BLOCKED instead of the element value */
3441   int element = Feld[x][y];
3442
3443   if (IS_MOVING(x, y))
3444   {
3445     if (element == EL_BLOCKED)
3446     {
3447       int oldx, oldy;
3448
3449       Blocked2Moving(x, y, &oldx, &oldy);
3450       return Feld[oldx][oldy];
3451     }
3452     else
3453       return EL_BLOCKED;
3454   }
3455   else
3456     return element;
3457 }
3458
3459 static void RemoveField(int x, int y)
3460 {
3461   Feld[x][y] = EL_EMPTY;
3462
3463   MovPos[x][y] = 0;
3464   MovDir[x][y] = 0;
3465   MovDelay[x][y] = 0;
3466
3467 #if USE_NEW_CUSTOM_VALUE
3468   CustomValue[x][y] = 0;
3469 #endif
3470
3471   AmoebaNr[x][y] = 0;
3472   ChangeDelay[x][y] = 0;
3473   ChangePage[x][y] = -1;
3474   Pushed[x][y] = FALSE;
3475
3476 #if 0
3477   ExplodeField[x][y] = EX_TYPE_NONE;
3478 #endif
3479
3480   GfxElement[x][y] = EL_UNDEFINED;
3481   GfxAction[x][y] = ACTION_DEFAULT;
3482   GfxDir[x][y] = MV_NONE;
3483 }
3484
3485 void RemoveMovingField(int x, int y)
3486 {
3487   int oldx = x, oldy = y, newx = x, newy = y;
3488   int element = Feld[x][y];
3489   int next_element = EL_UNDEFINED;
3490
3491   if (element != EL_BLOCKED && !IS_MOVING(x, y))
3492     return;
3493
3494   if (IS_MOVING(x, y))
3495   {
3496     Moving2Blocked(x, y, &newx, &newy);
3497
3498     if (Feld[newx][newy] != EL_BLOCKED)
3499     {
3500       /* element is moving, but target field is not free (blocked), but
3501          already occupied by something different (example: acid pool);
3502          in this case, only remove the moving field, but not the target */
3503
3504       RemoveField(oldx, oldy);
3505
3506       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3507
3508       DrawLevelField(oldx, oldy);
3509
3510       return;
3511     }
3512   }
3513   else if (element == EL_BLOCKED)
3514   {
3515     Blocked2Moving(x, y, &oldx, &oldy);
3516     if (!IS_MOVING(oldx, oldy))
3517       return;
3518   }
3519
3520   if (element == EL_BLOCKED &&
3521       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3522        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3523        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3524        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3525        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3526        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3527     next_element = get_next_element(Feld[oldx][oldy]);
3528
3529   RemoveField(oldx, oldy);
3530   RemoveField(newx, newy);
3531
3532   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3533
3534   if (next_element != EL_UNDEFINED)
3535     Feld[oldx][oldy] = next_element;
3536
3537   DrawLevelField(oldx, oldy);
3538   DrawLevelField(newx, newy);
3539 }
3540
3541 void DrawDynamite(int x, int y)
3542 {
3543   int sx = SCREENX(x), sy = SCREENY(y);
3544   int graphic = el2img(Feld[x][y]);
3545   int frame;
3546
3547   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3548     return;
3549
3550   if (IS_WALKABLE_INSIDE(Back[x][y]))
3551     return;
3552
3553   if (Back[x][y])
3554     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3555   else if (Store[x][y])
3556     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3557
3558   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3559
3560   if (Back[x][y] || Store[x][y])
3561     DrawGraphicThruMask(sx, sy, graphic, frame);
3562   else
3563     DrawGraphic(sx, sy, graphic, frame);
3564 }
3565
3566 void CheckDynamite(int x, int y)
3567 {
3568   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
3569   {
3570     MovDelay[x][y]--;
3571
3572     if (MovDelay[x][y] != 0)
3573     {
3574       DrawDynamite(x, y);
3575       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3576
3577       return;
3578     }
3579   }
3580
3581   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3582
3583   Bang(x, y);
3584 }
3585
3586 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3587 {
3588   boolean num_checked_players = 0;
3589   int i;
3590
3591   for (i = 0; i < MAX_PLAYERS; i++)
3592   {
3593     if (stored_player[i].active)
3594     {
3595       int sx = stored_player[i].jx;
3596       int sy = stored_player[i].jy;
3597
3598       if (num_checked_players == 0)
3599       {
3600         *sx1 = *sx2 = sx;
3601         *sy1 = *sy2 = sy;
3602       }
3603       else
3604       {
3605         *sx1 = MIN(*sx1, sx);
3606         *sy1 = MIN(*sy1, sy);
3607         *sx2 = MAX(*sx2, sx);
3608         *sy2 = MAX(*sy2, sy);
3609       }
3610
3611       num_checked_players++;
3612     }
3613   }
3614 }
3615
3616 static boolean checkIfAllPlayersFitToScreen_RND()
3617 {
3618   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3619
3620   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3621
3622   return (sx2 - sx1 < SCR_FIELDX &&
3623           sy2 - sy1 < SCR_FIELDY);
3624 }
3625
3626 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3627 {
3628   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3629
3630   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3631
3632   *sx = (sx1 + sx2) / 2;
3633   *sy = (sy1 + sy2) / 2;
3634 }
3635
3636 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3637                         boolean center_screen, boolean quick_relocation)
3638 {
3639   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3640   boolean no_delay = (tape.warp_forward);
3641   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3642   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3643
3644   if (quick_relocation)
3645   {
3646     int offset = (setup.scroll_delay ? 3 : 0);
3647
3648     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3649     {
3650       if (center_screen)
3651       {
3652         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3653                     x > SBX_Right + MIDPOSX ? SBX_Right :
3654                     x - MIDPOSX);
3655
3656         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3657                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
3658                     y - MIDPOSY);
3659       }
3660       else
3661       {
3662         /* quick relocation (without scrolling), but do not center screen */
3663
3664         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
3665                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
3666                                old_x - MIDPOSX);
3667
3668         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3669                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3670                                old_y - MIDPOSY);
3671
3672         int offset_x = x + (scroll_x - center_scroll_x);
3673         int offset_y = y + (scroll_y - center_scroll_y);
3674
3675         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
3676                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3677                     offset_x - MIDPOSX);
3678
3679         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3680                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3681                     offset_y - MIDPOSY);
3682       }
3683     }
3684     else
3685     {
3686       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
3687           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3688         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3689
3690       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
3691           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3692         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3693
3694       /* don't scroll over playfield boundaries */
3695       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3696         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3697
3698       /* don't scroll over playfield boundaries */
3699       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3700         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3701     }
3702
3703     RedrawPlayfield(TRUE, 0,0,0,0);
3704   }
3705   else
3706   {
3707     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3708                      x > SBX_Right + MIDPOSX ? SBX_Right :
3709                      x - MIDPOSX);
3710
3711     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3712                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
3713                      y - MIDPOSY);
3714
3715     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3716
3717     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3718     {
3719       int dx = 0, dy = 0;
3720       int fx = FX, fy = FY;
3721
3722       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3723       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3724
3725       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3726         break;
3727
3728       scroll_x -= dx;
3729       scroll_y -= dy;
3730
3731       fx += dx * TILEX / 2;
3732       fy += dy * TILEY / 2;
3733
3734       ScrollLevel(dx, dy);
3735       DrawAllPlayers();
3736
3737       /* scroll in two steps of half tile size to make things smoother */
3738       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3739       FlushDisplay();
3740       Delay(wait_delay_value);
3741
3742       /* scroll second step to align at full tile size */
3743       BackToFront();
3744       Delay(wait_delay_value);
3745     }
3746
3747     DrawAllPlayers();
3748     BackToFront();
3749     Delay(wait_delay_value);
3750   }
3751 }
3752
3753 void RelocatePlayer(int jx, int jy, int el_player_raw)
3754 {
3755   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3756   int player_nr = GET_PLAYER_NR(el_player);
3757   struct PlayerInfo *player = &stored_player[player_nr];
3758   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3759   boolean no_delay = (tape.warp_forward);
3760   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3761   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3762   int old_jx = player->jx;
3763   int old_jy = player->jy;
3764   int old_element = Feld[old_jx][old_jy];
3765   int element = Feld[jx][jy];
3766   boolean player_relocated = (old_jx != jx || old_jy != jy);
3767
3768   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3769   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
3770   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3771   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
3772   int leave_side_horiz = move_dir_horiz;
3773   int leave_side_vert  = move_dir_vert;
3774   int enter_side = enter_side_horiz | enter_side_vert;
3775   int leave_side = leave_side_horiz | leave_side_vert;
3776
3777   if (player->GameOver)         /* do not reanimate dead player */
3778     return;
3779
3780   if (!player_relocated)        /* no need to relocate the player */
3781     return;
3782
3783   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
3784   {
3785     RemoveField(jx, jy);        /* temporarily remove newly placed player */
3786     DrawLevelField(jx, jy);
3787   }
3788
3789   if (player->present)
3790   {
3791     while (player->MovPos)
3792     {
3793       ScrollPlayer(player, SCROLL_GO_ON);
3794       ScrollScreen(NULL, SCROLL_GO_ON);
3795
3796       AdvanceFrameAndPlayerCounters(player->index_nr);
3797
3798       DrawPlayer(player);
3799
3800       BackToFront();
3801       Delay(wait_delay_value);
3802     }
3803
3804     DrawPlayer(player);         /* needed here only to cleanup last field */
3805     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
3806
3807     player->is_moving = FALSE;
3808   }
3809
3810   if (IS_CUSTOM_ELEMENT(old_element))
3811     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3812                                CE_LEFT_BY_PLAYER,
3813                                player->index_bit, leave_side);
3814
3815   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3816                                       CE_PLAYER_LEAVES_X,
3817                                       player->index_bit, leave_side);
3818
3819   Feld[jx][jy] = el_player;
3820   InitPlayerField(jx, jy, el_player, TRUE);
3821
3822   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3823   {
3824     Feld[jx][jy] = element;
3825     InitField(jx, jy, FALSE);
3826   }
3827
3828   /* only visually relocate centered player */
3829   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3830                      FALSE, level.instant_relocation);
3831
3832   TestIfPlayerTouchesBadThing(jx, jy);
3833   TestIfPlayerTouchesCustomElement(jx, jy);
3834
3835   if (IS_CUSTOM_ELEMENT(element))
3836     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3837                                player->index_bit, enter_side);
3838
3839   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3840                                       player->index_bit, enter_side);
3841 }
3842
3843 void Explode(int ex, int ey, int phase, int mode)
3844 {
3845   int x, y;
3846   int last_phase;
3847   int border_element;
3848
3849   /* !!! eliminate this variable !!! */
3850   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3851
3852   if (game.explosions_delayed)
3853   {
3854     ExplodeField[ex][ey] = mode;
3855     return;
3856   }
3857
3858   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
3859   {
3860     int center_element = Feld[ex][ey];
3861     int artwork_element, explosion_element;     /* set these values later */
3862
3863 #if 0
3864     /* --- This is only really needed (and now handled) in "Impact()". --- */
3865     /* do not explode moving elements that left the explode field in time */
3866     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3867         center_element == EL_EMPTY &&
3868         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3869       return;
3870 #endif
3871
3872 #if 0
3873     /* !!! at this place, the center element may be EL_BLOCKED !!! */
3874     if (mode == EX_TYPE_NORMAL ||
3875         mode == EX_TYPE_CENTER ||
3876         mode == EX_TYPE_CROSS)
3877       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3878 #endif
3879
3880     /* remove things displayed in background while burning dynamite */
3881     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3882       Back[ex][ey] = 0;
3883
3884     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3885     {
3886       /* put moving element to center field (and let it explode there) */
3887       center_element = MovingOrBlocked2Element(ex, ey);
3888       RemoveMovingField(ex, ey);
3889       Feld[ex][ey] = center_element;
3890     }
3891
3892     /* now "center_element" is finally determined -- set related values now */
3893     artwork_element = center_element;           /* for custom player artwork */
3894     explosion_element = center_element;         /* for custom player artwork */
3895
3896     if (IS_PLAYER(ex, ey))
3897     {
3898       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3899
3900       artwork_element = stored_player[player_nr].artwork_element;
3901
3902       if (level.use_explosion_element[player_nr])
3903       {
3904         explosion_element = level.explosion_element[player_nr];
3905         artwork_element = explosion_element;
3906       }
3907     }
3908
3909 #if 1
3910     if (mode == EX_TYPE_NORMAL ||
3911         mode == EX_TYPE_CENTER ||
3912         mode == EX_TYPE_CROSS)
3913       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3914 #endif
3915
3916     last_phase = element_info[explosion_element].explosion_delay + 1;
3917
3918     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3919     {
3920       int xx = x - ex + 1;
3921       int yy = y - ey + 1;
3922       int element;
3923
3924       if (!IN_LEV_FIELD(x, y) ||
3925           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3926           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
3927         continue;
3928
3929       element = Feld[x][y];
3930
3931       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3932       {
3933         element = MovingOrBlocked2Element(x, y);
3934
3935         if (!IS_EXPLOSION_PROOF(element))
3936           RemoveMovingField(x, y);
3937       }
3938
3939       /* indestructible elements can only explode in center (but not flames) */
3940       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3941                                            mode == EX_TYPE_BORDER)) ||
3942           element == EL_FLAMES)
3943         continue;
3944
3945       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3946          behaviour, for example when touching a yamyam that explodes to rocks
3947          with active deadly shield, a rock is created under the player !!! */
3948       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3949 #if 0
3950       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3951           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3952            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3953 #else
3954       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3955 #endif
3956       {
3957         if (IS_ACTIVE_BOMB(element))
3958         {
3959           /* re-activate things under the bomb like gate or penguin */
3960           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3961           Back[x][y] = 0;
3962         }
3963
3964         continue;
3965       }
3966
3967       /* save walkable background elements while explosion on same tile */
3968       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3969           (x != ex || y != ey || mode == EX_TYPE_BORDER))
3970         Back[x][y] = element;
3971
3972       /* ignite explodable elements reached by other explosion */
3973       if (element == EL_EXPLOSION)
3974         element = Store2[x][y];
3975
3976       if (AmoebaNr[x][y] &&
3977           (element == EL_AMOEBA_FULL ||
3978            element == EL_BD_AMOEBA ||
3979            element == EL_AMOEBA_GROWING))
3980       {
3981         AmoebaCnt[AmoebaNr[x][y]]--;
3982         AmoebaCnt2[AmoebaNr[x][y]]--;
3983       }
3984
3985       RemoveField(x, y);
3986
3987       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3988       {
3989         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3990
3991         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3992
3993         if (PLAYERINFO(ex, ey)->use_murphy)
3994           Store[x][y] = EL_EMPTY;
3995       }
3996
3997       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3998          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3999       else if (ELEM_IS_PLAYER(center_element))
4000         Store[x][y] = EL_EMPTY;
4001       else if (center_element == EL_YAMYAM)
4002         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4003       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4004         Store[x][y] = element_info[center_element].content.e[xx][yy];
4005 #if 1
4006       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4007          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4008          otherwise) -- FIX THIS !!! */
4009       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4010         Store[x][y] = element_info[element].content.e[1][1];
4011 #else
4012       else if (!CAN_EXPLODE(element))
4013         Store[x][y] = element_info[element].content.e[1][1];
4014 #endif
4015       else
4016         Store[x][y] = EL_EMPTY;
4017
4018       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4019           center_element == EL_AMOEBA_TO_DIAMOND)
4020         Store2[x][y] = element;
4021
4022       Feld[x][y] = EL_EXPLOSION;
4023       GfxElement[x][y] = artwork_element;
4024
4025       ExplodePhase[x][y] = 1;
4026       ExplodeDelay[x][y] = last_phase;
4027
4028       Stop[x][y] = TRUE;
4029     }
4030
4031     if (center_element == EL_YAMYAM)
4032       game.yamyam_content_nr =
4033         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4034
4035     return;
4036   }
4037
4038   if (Stop[ex][ey])
4039     return;
4040
4041   x = ex;
4042   y = ey;
4043
4044   if (phase == 1)
4045     GfxFrame[x][y] = 0;         /* restart explosion animation */
4046
4047   last_phase = ExplodeDelay[x][y];
4048
4049   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4050
4051 #ifdef DEBUG
4052
4053   /* activate this even in non-DEBUG version until cause for crash in
4054      getGraphicAnimationFrame() (see below) is found and eliminated */
4055
4056 #endif
4057 #if 1
4058
4059 #if 1
4060   /* this can happen if the player leaves an explosion just in time */
4061   if (GfxElement[x][y] == EL_UNDEFINED)
4062     GfxElement[x][y] = EL_EMPTY;
4063 #else
4064   if (GfxElement[x][y] == EL_UNDEFINED)
4065   {
4066     printf("\n\n");
4067     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4068     printf("Explode(): This should never happen!\n");
4069     printf("\n\n");
4070
4071     GfxElement[x][y] = EL_EMPTY;
4072   }
4073 #endif
4074
4075 #endif
4076
4077   border_element = Store2[x][y];
4078   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4079     border_element = StorePlayer[x][y];
4080
4081   if (phase == element_info[border_element].ignition_delay ||
4082       phase == last_phase)
4083   {
4084     boolean border_explosion = FALSE;
4085
4086     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4087         !PLAYER_EXPLOSION_PROTECTED(x, y))
4088     {
4089       KillPlayerUnlessExplosionProtected(x, y);
4090       border_explosion = TRUE;
4091     }
4092     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4093     {
4094       Feld[x][y] = Store2[x][y];
4095       Store2[x][y] = 0;
4096       Bang(x, y);
4097       border_explosion = TRUE;
4098     }
4099     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4100     {
4101       AmoebeUmwandeln(x, y);
4102       Store2[x][y] = 0;
4103       border_explosion = TRUE;
4104     }
4105
4106     /* if an element just explodes due to another explosion (chain-reaction),
4107        do not immediately end the new explosion when it was the last frame of
4108        the explosion (as it would be done in the following "if"-statement!) */
4109     if (border_explosion && phase == last_phase)
4110       return;
4111   }
4112
4113   if (phase == last_phase)
4114   {
4115     int element;
4116
4117     element = Feld[x][y] = Store[x][y];
4118     Store[x][y] = Store2[x][y] = 0;
4119     GfxElement[x][y] = EL_UNDEFINED;
4120
4121     /* player can escape from explosions and might therefore be still alive */
4122     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4123         element <= EL_PLAYER_IS_EXPLODING_4)
4124     {
4125       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4126       int explosion_element = EL_PLAYER_1 + player_nr;
4127       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4128       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4129
4130       if (level.use_explosion_element[player_nr])
4131         explosion_element = level.explosion_element[player_nr];
4132
4133       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4134                     element_info[explosion_element].content.e[xx][yy]);
4135     }
4136
4137     /* restore probably existing indestructible background element */
4138     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4139       element = Feld[x][y] = Back[x][y];
4140     Back[x][y] = 0;
4141
4142     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4143     GfxDir[x][y] = MV_NONE;
4144     ChangeDelay[x][y] = 0;
4145     ChangePage[x][y] = -1;
4146
4147 #if USE_NEW_CUSTOM_VALUE
4148     CustomValue[x][y] = 0;
4149 #endif
4150
4151     InitField_WithBug2(x, y, FALSE);
4152
4153     DrawLevelField(x, y);
4154
4155     TestIfElementTouchesCustomElement(x, y);
4156
4157     if (GFX_CRUMBLED(element))
4158       DrawLevelFieldCrumbledSandNeighbours(x, y);
4159
4160     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4161       StorePlayer[x][y] = 0;
4162
4163     if (ELEM_IS_PLAYER(element))
4164       RelocatePlayer(x, y, element);
4165   }
4166   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4167   {
4168     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4169     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4170
4171     if (phase == delay)
4172       DrawLevelFieldCrumbledSand(x, y);
4173
4174     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4175     {
4176       DrawLevelElement(x, y, Back[x][y]);
4177       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4178     }
4179     else if (IS_WALKABLE_UNDER(Back[x][y]))
4180     {
4181       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4182       DrawLevelElementThruMask(x, y, Back[x][y]);
4183     }
4184     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4185       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4186   }
4187 }
4188
4189 void DynaExplode(int ex, int ey)
4190 {
4191   int i, j;
4192   int dynabomb_element = Feld[ex][ey];
4193   int dynabomb_size = 1;
4194   boolean dynabomb_xl = FALSE;
4195   struct PlayerInfo *player;
4196   static int xy[4][2] =
4197   {
4198     { 0, -1 },
4199     { -1, 0 },
4200     { +1, 0 },
4201     { 0, +1 }
4202   };
4203
4204   if (IS_ACTIVE_BOMB(dynabomb_element))
4205   {
4206     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4207     dynabomb_size = player->dynabomb_size;
4208     dynabomb_xl = player->dynabomb_xl;
4209     player->dynabombs_left++;
4210   }
4211
4212   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4213
4214   for (i = 0; i < NUM_DIRECTIONS; i++)
4215   {
4216     for (j = 1; j <= dynabomb_size; j++)
4217     {
4218       int x = ex + j * xy[i][0];
4219       int y = ey + j * xy[i][1];
4220       int element;
4221
4222       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4223         break;
4224
4225       element = Feld[x][y];
4226
4227       /* do not restart explosions of fields with active bombs */
4228       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4229         continue;
4230
4231       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4232
4233       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4234           !IS_DIGGABLE(element) && !dynabomb_xl)
4235         break;
4236     }
4237   }
4238 }
4239
4240 void Bang(int x, int y)
4241 {
4242   int element = MovingOrBlocked2Element(x, y);
4243   int explosion_type = EX_TYPE_NORMAL;
4244
4245   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4246   {
4247     struct PlayerInfo *player = PLAYERINFO(x, y);
4248
4249     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4250                             player->element_nr);
4251
4252     if (level.use_explosion_element[player->index_nr])
4253     {
4254       int explosion_element = level.explosion_element[player->index_nr];
4255
4256       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4257         explosion_type = EX_TYPE_CROSS;
4258       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4259         explosion_type = EX_TYPE_CENTER;
4260     }
4261   }
4262
4263   switch (element)
4264   {
4265     case EL_BUG:
4266     case EL_SPACESHIP:
4267     case EL_BD_BUTTERFLY:
4268     case EL_BD_FIREFLY:
4269     case EL_YAMYAM:
4270     case EL_DARK_YAMYAM:
4271     case EL_ROBOT:
4272     case EL_PACMAN:
4273     case EL_MOLE:
4274       RaiseScoreElement(element);
4275       break;
4276
4277     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4278     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4279     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4280     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4281     case EL_DYNABOMB_INCREASE_NUMBER:
4282     case EL_DYNABOMB_INCREASE_SIZE:
4283     case EL_DYNABOMB_INCREASE_POWER:
4284       explosion_type = EX_TYPE_DYNA;
4285       break;
4286
4287     case EL_DC_LANDMINE:
4288 #if 0
4289     case EL_EM_EXIT_OPEN:
4290     case EL_EM_STEEL_EXIT_OPEN:
4291 #endif
4292       explosion_type = EX_TYPE_CENTER;
4293       break;
4294
4295     case EL_PENGUIN:
4296     case EL_LAMP:
4297     case EL_LAMP_ACTIVE:
4298     case EL_AMOEBA_TO_DIAMOND:
4299       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4300         explosion_type = EX_TYPE_CENTER;
4301       break;
4302
4303     default:
4304       if (element_info[element].explosion_type == EXPLODES_CROSS)
4305         explosion_type = EX_TYPE_CROSS;
4306       else if (element_info[element].explosion_type == EXPLODES_1X1)
4307         explosion_type = EX_TYPE_CENTER;
4308       break;
4309   }
4310
4311   if (explosion_type == EX_TYPE_DYNA)
4312     DynaExplode(x, y);
4313   else
4314     Explode(x, y, EX_PHASE_START, explosion_type);
4315
4316   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4317 }
4318
4319 void SplashAcid(int x, int y)
4320 {
4321   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4322       (!IN_LEV_FIELD(x - 1, y - 2) ||
4323        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4324     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4325
4326   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4327       (!IN_LEV_FIELD(x + 1, y - 2) ||
4328        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4329     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4330
4331   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4332 }
4333
4334 static void InitBeltMovement()
4335 {
4336   static int belt_base_element[4] =
4337   {
4338     EL_CONVEYOR_BELT_1_LEFT,
4339     EL_CONVEYOR_BELT_2_LEFT,
4340     EL_CONVEYOR_BELT_3_LEFT,
4341     EL_CONVEYOR_BELT_4_LEFT
4342   };
4343   static int belt_base_active_element[4] =
4344   {
4345     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4346     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4347     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4348     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4349   };
4350
4351   int x, y, i, j;
4352
4353   /* set frame order for belt animation graphic according to belt direction */
4354   for (i = 0; i < NUM_BELTS; i++)
4355   {
4356     int belt_nr = i;
4357
4358     for (j = 0; j < NUM_BELT_PARTS; j++)
4359     {
4360       int element = belt_base_active_element[belt_nr] + j;
4361       int graphic = el2img(element);
4362
4363       if (game.belt_dir[i] == MV_LEFT)
4364         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4365       else
4366         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4367     }
4368   }
4369
4370   SCAN_PLAYFIELD(x, y)
4371   {
4372     int element = Feld[x][y];
4373
4374     for (i = 0; i < NUM_BELTS; i++)
4375     {
4376       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4377       {
4378         int e_belt_nr = getBeltNrFromBeltElement(element);
4379         int belt_nr = i;
4380
4381         if (e_belt_nr == belt_nr)
4382         {
4383           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4384
4385           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4386         }
4387       }
4388     }
4389   }
4390 }
4391
4392 static void ToggleBeltSwitch(int x, int y)
4393 {
4394   static int belt_base_element[4] =
4395   {
4396     EL_CONVEYOR_BELT_1_LEFT,
4397     EL_CONVEYOR_BELT_2_LEFT,
4398     EL_CONVEYOR_BELT_3_LEFT,
4399     EL_CONVEYOR_BELT_4_LEFT
4400   };
4401   static int belt_base_active_element[4] =
4402   {
4403     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4404     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4405     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4406     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4407   };
4408   static int belt_base_switch_element[4] =
4409   {
4410     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4411     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4412     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4413     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4414   };
4415   static int belt_move_dir[4] =
4416   {
4417     MV_LEFT,
4418     MV_NONE,
4419     MV_RIGHT,
4420     MV_NONE,
4421   };
4422
4423   int element = Feld[x][y];
4424   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4425   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4426   int belt_dir = belt_move_dir[belt_dir_nr];
4427   int xx, yy, i;
4428
4429   if (!IS_BELT_SWITCH(element))
4430     return;
4431
4432   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4433   game.belt_dir[belt_nr] = belt_dir;
4434
4435   if (belt_dir_nr == 3)
4436     belt_dir_nr = 1;
4437
4438   /* set frame order for belt animation graphic according to belt direction */
4439   for (i = 0; i < NUM_BELT_PARTS; i++)
4440   {
4441     int element = belt_base_active_element[belt_nr] + i;
4442     int graphic = el2img(element);
4443
4444     if (belt_dir == MV_LEFT)
4445       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4446     else
4447       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4448   }
4449
4450   SCAN_PLAYFIELD(xx, yy)
4451   {
4452     int element = Feld[xx][yy];
4453
4454     if (IS_BELT_SWITCH(element))
4455     {
4456       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4457
4458       if (e_belt_nr == belt_nr)
4459       {
4460         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4461         DrawLevelField(xx, yy);
4462       }
4463     }
4464     else if (IS_BELT(element) && belt_dir != MV_NONE)
4465     {
4466       int e_belt_nr = getBeltNrFromBeltElement(element);
4467
4468       if (e_belt_nr == belt_nr)
4469       {
4470         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4471
4472         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4473         DrawLevelField(xx, yy);
4474       }
4475     }
4476     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4477     {
4478       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4479
4480       if (e_belt_nr == belt_nr)
4481       {
4482         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4483
4484         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4485         DrawLevelField(xx, yy);
4486       }
4487     }
4488   }
4489 }
4490
4491 static void ToggleSwitchgateSwitch(int x, int y)
4492 {
4493   int xx, yy;
4494
4495   game.switchgate_pos = !game.switchgate_pos;
4496
4497   SCAN_PLAYFIELD(xx, yy)
4498   {
4499     int element = Feld[xx][yy];
4500
4501 #if !USE_BOTH_SWITCHGATE_SWITCHES
4502     if (element == EL_SWITCHGATE_SWITCH_UP ||
4503         element == EL_SWITCHGATE_SWITCH_DOWN)
4504     {
4505       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4506       DrawLevelField(xx, yy);
4507     }
4508     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4509              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4510     {
4511       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4512       DrawLevelField(xx, yy);
4513     }
4514 #else
4515     if (element == EL_SWITCHGATE_SWITCH_UP)
4516     {
4517       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4518       DrawLevelField(xx, yy);
4519     }
4520     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4521     {
4522       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4523       DrawLevelField(xx, yy);
4524     }
4525     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4526     {
4527       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4528       DrawLevelField(xx, yy);
4529     }
4530     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4531     {
4532       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4533       DrawLevelField(xx, yy);
4534     }
4535 #endif
4536     else if (element == EL_SWITCHGATE_OPEN ||
4537              element == EL_SWITCHGATE_OPENING)
4538     {
4539       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4540
4541       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4542     }
4543     else if (element == EL_SWITCHGATE_CLOSED ||
4544              element == EL_SWITCHGATE_CLOSING)
4545     {
4546       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4547
4548       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4549     }
4550   }
4551 }
4552
4553 static int getInvisibleActiveFromInvisibleElement(int element)
4554 {
4555   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4556           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
4557           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
4558           element);
4559 }
4560
4561 static int getInvisibleFromInvisibleActiveElement(int element)
4562 {
4563   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4564           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
4565           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
4566           element);
4567 }
4568
4569 static void RedrawAllLightSwitchesAndInvisibleElements()
4570 {
4571   int x, y;
4572
4573   SCAN_PLAYFIELD(x, y)
4574   {
4575     int element = Feld[x][y];
4576
4577     if (element == EL_LIGHT_SWITCH &&
4578         game.light_time_left > 0)
4579     {
4580       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4581       DrawLevelField(x, y);
4582     }
4583     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4584              game.light_time_left == 0)
4585     {
4586       Feld[x][y] = EL_LIGHT_SWITCH;
4587       DrawLevelField(x, y);
4588     }
4589     else if (element == EL_EMC_DRIPPER &&
4590              game.light_time_left > 0)
4591     {
4592       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4593       DrawLevelField(x, y);
4594     }
4595     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4596              game.light_time_left == 0)
4597     {
4598       Feld[x][y] = EL_EMC_DRIPPER;
4599       DrawLevelField(x, y);
4600     }
4601     else if (element == EL_INVISIBLE_STEELWALL ||
4602              element == EL_INVISIBLE_WALL ||
4603              element == EL_INVISIBLE_SAND)
4604     {
4605       if (game.light_time_left > 0)
4606         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4607
4608       DrawLevelField(x, y);
4609
4610       /* uncrumble neighbour fields, if needed */
4611       if (element == EL_INVISIBLE_SAND)
4612         DrawLevelFieldCrumbledSandNeighbours(x, y);
4613     }
4614     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4615              element == EL_INVISIBLE_WALL_ACTIVE ||
4616              element == EL_INVISIBLE_SAND_ACTIVE)
4617     {
4618       if (game.light_time_left == 0)
4619         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4620
4621       DrawLevelField(x, y);
4622
4623       /* re-crumble neighbour fields, if needed */
4624       if (element == EL_INVISIBLE_SAND)
4625         DrawLevelFieldCrumbledSandNeighbours(x, y);
4626     }
4627   }
4628 }
4629
4630 static void RedrawAllInvisibleElementsForLenses()
4631 {
4632   int x, y;
4633
4634   SCAN_PLAYFIELD(x, y)
4635   {
4636     int element = Feld[x][y];
4637
4638     if (element == EL_EMC_DRIPPER &&
4639         game.lenses_time_left > 0)
4640     {
4641       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4642       DrawLevelField(x, y);
4643     }
4644     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4645              game.lenses_time_left == 0)
4646     {
4647       Feld[x][y] = EL_EMC_DRIPPER;
4648       DrawLevelField(x, y);
4649     }
4650     else if (element == EL_INVISIBLE_STEELWALL ||
4651              element == EL_INVISIBLE_WALL ||
4652              element == EL_INVISIBLE_SAND)
4653     {
4654       if (game.lenses_time_left > 0)
4655         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4656
4657       DrawLevelField(x, y);
4658
4659       /* uncrumble neighbour fields, if needed */
4660       if (element == EL_INVISIBLE_SAND)
4661         DrawLevelFieldCrumbledSandNeighbours(x, y);
4662     }
4663     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4664              element == EL_INVISIBLE_WALL_ACTIVE ||
4665              element == EL_INVISIBLE_SAND_ACTIVE)
4666     {
4667       if (game.lenses_time_left == 0)
4668         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4669
4670       DrawLevelField(x, y);
4671
4672       /* re-crumble neighbour fields, if needed */
4673       if (element == EL_INVISIBLE_SAND)
4674         DrawLevelFieldCrumbledSandNeighbours(x, y);
4675     }
4676   }
4677 }
4678
4679 static void RedrawAllInvisibleElementsForMagnifier()
4680 {
4681   int x, y;
4682
4683   SCAN_PLAYFIELD(x, y)
4684   {
4685     int element = Feld[x][y];
4686
4687     if (element == EL_EMC_FAKE_GRASS &&
4688         game.magnify_time_left > 0)
4689     {
4690       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4691       DrawLevelField(x, y);
4692     }
4693     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4694              game.magnify_time_left == 0)
4695     {
4696       Feld[x][y] = EL_EMC_FAKE_GRASS;
4697       DrawLevelField(x, y);
4698     }
4699     else if (IS_GATE_GRAY(element) &&
4700              game.magnify_time_left > 0)
4701     {
4702       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4703                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4704                     IS_EM_GATE_GRAY(element) ?
4705                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4706                     IS_EMC_GATE_GRAY(element) ?
4707                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4708                     element);
4709       DrawLevelField(x, y);
4710     }
4711     else if (IS_GATE_GRAY_ACTIVE(element) &&
4712              game.magnify_time_left == 0)
4713     {
4714       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4715                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4716                     IS_EM_GATE_GRAY_ACTIVE(element) ?
4717                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4718                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
4719                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4720                     element);
4721       DrawLevelField(x, y);
4722     }
4723   }
4724 }
4725
4726 static void ToggleLightSwitch(int x, int y)
4727 {
4728   int element = Feld[x][y];
4729
4730   game.light_time_left =
4731     (element == EL_LIGHT_SWITCH ?
4732      level.time_light * FRAMES_PER_SECOND : 0);
4733
4734   RedrawAllLightSwitchesAndInvisibleElements();
4735 }
4736
4737 static void ActivateTimegateSwitch(int x, int y)
4738 {
4739   int xx, yy;
4740
4741   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4742
4743   SCAN_PLAYFIELD(xx, yy)
4744   {
4745     int element = Feld[xx][yy];
4746
4747     if (element == EL_TIMEGATE_CLOSED ||
4748         element == EL_TIMEGATE_CLOSING)
4749     {
4750       Feld[xx][yy] = EL_TIMEGATE_OPENING;
4751       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4752     }
4753
4754     /*
4755     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4756     {
4757       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4758       DrawLevelField(xx, yy);
4759     }
4760     */
4761
4762   }
4763
4764 #if 1
4765   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4766                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4767 #else
4768   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4769 #endif
4770 }
4771
4772 void Impact(int x, int y)
4773 {
4774   boolean last_line = (y == lev_fieldy - 1);
4775   boolean object_hit = FALSE;
4776   boolean impact = (last_line || object_hit);
4777   int element = Feld[x][y];
4778   int smashed = EL_STEELWALL;
4779
4780   if (!last_line)       /* check if element below was hit */
4781   {
4782     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4783       return;
4784
4785     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4786                                          MovDir[x][y + 1] != MV_DOWN ||
4787                                          MovPos[x][y + 1] <= TILEY / 2));
4788
4789     /* do not smash moving elements that left the smashed field in time */
4790     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4791         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4792       object_hit = FALSE;
4793
4794 #if USE_QUICKSAND_IMPACT_BUGFIX
4795     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4796     {
4797       RemoveMovingField(x, y + 1);
4798       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4799       Feld[x][y + 2] = EL_ROCK;
4800       DrawLevelField(x, y + 2);
4801
4802       object_hit = TRUE;
4803     }
4804
4805     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
4806     {
4807       RemoveMovingField(x, y + 1);
4808       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
4809       Feld[x][y + 2] = EL_ROCK;
4810       DrawLevelField(x, y + 2);
4811
4812       object_hit = TRUE;
4813     }
4814 #endif
4815
4816     if (object_hit)
4817       smashed = MovingOrBlocked2Element(x, y + 1);
4818
4819     impact = (last_line || object_hit);
4820   }
4821
4822   if (!last_line && smashed == EL_ACID) /* element falls into acid */
4823   {
4824     SplashAcid(x, y + 1);
4825     return;
4826   }
4827
4828   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4829   /* only reset graphic animation if graphic really changes after impact */
4830   if (impact &&
4831       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4832   {
4833     ResetGfxAnimation(x, y);
4834     DrawLevelField(x, y);
4835   }
4836
4837   if (impact && CAN_EXPLODE_IMPACT(element))
4838   {
4839     Bang(x, y);
4840     return;
4841   }
4842   else if (impact && element == EL_PEARL &&
4843            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
4844   {
4845     ResetGfxAnimation(x, y);
4846
4847     Feld[x][y] = EL_PEARL_BREAKING;
4848     PlayLevelSound(x, y, SND_PEARL_BREAKING);
4849     return;
4850   }
4851   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4852   {
4853     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4854
4855     return;
4856   }
4857
4858   if (impact && element == EL_AMOEBA_DROP)
4859   {
4860     if (object_hit && IS_PLAYER(x, y + 1))
4861       KillPlayerUnlessEnemyProtected(x, y + 1);
4862     else if (object_hit && smashed == EL_PENGUIN)
4863       Bang(x, y + 1);
4864     else
4865     {
4866       Feld[x][y] = EL_AMOEBA_GROWING;
4867       Store[x][y] = EL_AMOEBA_WET;
4868
4869       ResetRandomAnimationValue(x, y);
4870     }
4871     return;
4872   }
4873
4874   if (object_hit)               /* check which object was hit */
4875   {
4876     if ((CAN_PASS_MAGIC_WALL(element) && 
4877          (smashed == EL_MAGIC_WALL ||
4878           smashed == EL_BD_MAGIC_WALL)) ||
4879         (CAN_PASS_DC_MAGIC_WALL(element) &&
4880          smashed == EL_DC_MAGIC_WALL))
4881     {
4882       int xx, yy;
4883       int activated_magic_wall =
4884         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4885          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
4886          EL_DC_MAGIC_WALL_ACTIVE);
4887
4888       /* activate magic wall / mill */
4889       SCAN_PLAYFIELD(xx, yy)
4890       {
4891         if (Feld[xx][yy] == smashed)
4892           Feld[xx][yy] = activated_magic_wall;
4893       }
4894
4895       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4896       game.magic_wall_active = TRUE;
4897
4898       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4899                             SND_MAGIC_WALL_ACTIVATING :
4900                             smashed == EL_BD_MAGIC_WALL ?
4901                             SND_BD_MAGIC_WALL_ACTIVATING :
4902                             SND_DC_MAGIC_WALL_ACTIVATING));
4903     }
4904
4905     if (IS_PLAYER(x, y + 1))
4906     {
4907       if (CAN_SMASH_PLAYER(element))
4908       {
4909         KillPlayerUnlessEnemyProtected(x, y + 1);
4910         return;
4911       }
4912     }
4913     else if (smashed == EL_PENGUIN)
4914     {
4915       if (CAN_SMASH_PLAYER(element))
4916       {
4917         Bang(x, y + 1);
4918         return;
4919       }
4920     }
4921     else if (element == EL_BD_DIAMOND)
4922     {
4923       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4924       {
4925         Bang(x, y + 1);
4926         return;
4927       }
4928     }
4929     else if (((element == EL_SP_INFOTRON ||
4930                element == EL_SP_ZONK) &&
4931               (smashed == EL_SP_SNIKSNAK ||
4932                smashed == EL_SP_ELECTRON ||
4933                smashed == EL_SP_DISK_ORANGE)) ||
4934              (element == EL_SP_INFOTRON &&
4935               smashed == EL_SP_DISK_YELLOW))
4936     {
4937       Bang(x, y + 1);
4938       return;
4939     }
4940     else if (CAN_SMASH_EVERYTHING(element))
4941     {
4942       if (IS_CLASSIC_ENEMY(smashed) ||
4943           CAN_EXPLODE_SMASHED(smashed))
4944       {
4945         Bang(x, y + 1);
4946         return;
4947       }
4948       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4949       {
4950         if (smashed == EL_LAMP ||
4951             smashed == EL_LAMP_ACTIVE)
4952         {
4953           Bang(x, y + 1);
4954           return;
4955         }
4956         else if (smashed == EL_NUT)
4957         {
4958           Feld[x][y + 1] = EL_NUT_BREAKING;
4959           PlayLevelSound(x, y, SND_NUT_BREAKING);
4960           RaiseScoreElement(EL_NUT);
4961           return;
4962         }
4963         else if (smashed == EL_PEARL)
4964         {
4965           ResetGfxAnimation(x, y);
4966
4967           Feld[x][y + 1] = EL_PEARL_BREAKING;
4968           PlayLevelSound(x, y, SND_PEARL_BREAKING);
4969           return;
4970         }
4971         else if (smashed == EL_DIAMOND)
4972         {
4973           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4974           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4975           return;
4976         }
4977         else if (IS_BELT_SWITCH(smashed))
4978         {
4979           ToggleBeltSwitch(x, y + 1);
4980         }
4981         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4982                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
4983                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
4984                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
4985         {
4986           ToggleSwitchgateSwitch(x, y + 1);
4987         }
4988         else if (smashed == EL_LIGHT_SWITCH ||
4989                  smashed == EL_LIGHT_SWITCH_ACTIVE)
4990         {
4991           ToggleLightSwitch(x, y + 1);
4992         }
4993         else
4994         {
4995 #if 0
4996           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4997 #endif
4998
4999           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5000
5001           CheckElementChangeBySide(x, y + 1, smashed, element,
5002                                    CE_SWITCHED, CH_SIDE_TOP);
5003           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5004                                             CH_SIDE_TOP);
5005         }
5006       }
5007       else
5008       {
5009         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5010       }
5011     }
5012   }
5013
5014   /* play sound of magic wall / mill */
5015   if (!last_line &&
5016       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5017        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5018        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5019   {
5020     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5021       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5022     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5023       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5024     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5025       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5026
5027     return;
5028   }
5029
5030   /* play sound of object that hits the ground */
5031   if (last_line || object_hit)
5032     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5033 }
5034
5035 inline static void TurnRoundExt(int x, int y)
5036 {
5037   static struct
5038   {
5039     int dx, dy;
5040   } move_xy[] =
5041   {
5042     {  0,  0 },
5043     { -1,  0 },
5044     { +1,  0 },
5045     {  0,  0 },
5046     {  0, -1 },
5047     {  0,  0 }, { 0, 0 }, { 0, 0 },
5048     {  0, +1 }
5049   };
5050   static struct
5051   {
5052     int left, right, back;
5053   } turn[] =
5054   {
5055     { 0,        0,              0        },
5056     { MV_DOWN,  MV_UP,          MV_RIGHT },
5057     { MV_UP,    MV_DOWN,        MV_LEFT  },
5058     { 0,        0,              0        },
5059     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5060     { 0,        0,              0        },
5061     { 0,        0,              0        },
5062     { 0,        0,              0        },
5063     { MV_RIGHT, MV_LEFT,        MV_UP    }
5064   };
5065
5066   int element = Feld[x][y];
5067   int move_pattern = element_info[element].move_pattern;
5068
5069   int old_move_dir = MovDir[x][y];
5070   int left_dir  = turn[old_move_dir].left;
5071   int right_dir = turn[old_move_dir].right;
5072   int back_dir  = turn[old_move_dir].back;
5073
5074   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5075   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5076   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5077   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5078
5079   int left_x  = x + left_dx,  left_y  = y + left_dy;
5080   int right_x = x + right_dx, right_y = y + right_dy;
5081   int move_x  = x + move_dx,  move_y  = y + move_dy;
5082
5083   int xx, yy;
5084
5085   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5086   {
5087     TestIfBadThingTouchesOtherBadThing(x, y);
5088
5089     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5090       MovDir[x][y] = right_dir;
5091     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5092       MovDir[x][y] = left_dir;
5093
5094     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5095       MovDelay[x][y] = 9;
5096     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5097       MovDelay[x][y] = 1;
5098   }
5099   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5100   {
5101     TestIfBadThingTouchesOtherBadThing(x, y);
5102
5103     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5104       MovDir[x][y] = left_dir;
5105     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5106       MovDir[x][y] = right_dir;
5107
5108     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5109       MovDelay[x][y] = 9;
5110     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5111       MovDelay[x][y] = 1;
5112   }
5113   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5114   {
5115     TestIfBadThingTouchesOtherBadThing(x, y);
5116
5117     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5118       MovDir[x][y] = left_dir;
5119     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5120       MovDir[x][y] = right_dir;
5121
5122     if (MovDir[x][y] != old_move_dir)
5123       MovDelay[x][y] = 9;
5124   }
5125   else if (element == EL_YAMYAM)
5126   {
5127     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5128     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5129
5130     if (can_turn_left && can_turn_right)
5131       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5132     else if (can_turn_left)
5133       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5134     else if (can_turn_right)
5135       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5136     else
5137       MovDir[x][y] = back_dir;
5138
5139     MovDelay[x][y] = 16 + 16 * RND(3);
5140   }
5141   else if (element == EL_DARK_YAMYAM)
5142   {
5143     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5144                                                          left_x, left_y);
5145     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5146                                                          right_x, right_y);
5147
5148     if (can_turn_left && can_turn_right)
5149       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5150     else if (can_turn_left)
5151       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5152     else if (can_turn_right)
5153       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5154     else
5155       MovDir[x][y] = back_dir;
5156
5157     MovDelay[x][y] = 16 + 16 * RND(3);
5158   }
5159   else if (element == EL_PACMAN)
5160   {
5161     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5162     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5163
5164     if (can_turn_left && can_turn_right)
5165       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5166     else if (can_turn_left)
5167       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5168     else if (can_turn_right)
5169       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5170     else
5171       MovDir[x][y] = back_dir;
5172
5173     MovDelay[x][y] = 6 + RND(40);
5174   }
5175   else if (element == EL_PIG)
5176   {
5177     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5178     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5179     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5180     boolean should_turn_left, should_turn_right, should_move_on;
5181     int rnd_value = 24;
5182     int rnd = RND(rnd_value);
5183
5184     should_turn_left = (can_turn_left &&
5185                         (!can_move_on ||
5186                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5187                                                    y + back_dy + left_dy)));
5188     should_turn_right = (can_turn_right &&
5189                          (!can_move_on ||
5190                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5191                                                     y + back_dy + right_dy)));
5192     should_move_on = (can_move_on &&
5193                       (!can_turn_left ||
5194                        !can_turn_right ||
5195                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5196                                                  y + move_dy + left_dy) ||
5197                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5198                                                  y + move_dy + right_dy)));
5199
5200     if (should_turn_left || should_turn_right || should_move_on)
5201     {
5202       if (should_turn_left && should_turn_right && should_move_on)
5203         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5204                         rnd < 2 * rnd_value / 3 ? right_dir :
5205                         old_move_dir);
5206       else if (should_turn_left && should_turn_right)
5207         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5208       else if (should_turn_left && should_move_on)
5209         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5210       else if (should_turn_right && should_move_on)
5211         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5212       else if (should_turn_left)
5213         MovDir[x][y] = left_dir;
5214       else if (should_turn_right)
5215         MovDir[x][y] = right_dir;
5216       else if (should_move_on)
5217         MovDir[x][y] = old_move_dir;
5218     }
5219     else if (can_move_on && rnd > rnd_value / 8)
5220       MovDir[x][y] = old_move_dir;
5221     else if (can_turn_left && can_turn_right)
5222       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5223     else if (can_turn_left && rnd > rnd_value / 8)
5224       MovDir[x][y] = left_dir;
5225     else if (can_turn_right && rnd > rnd_value/8)
5226       MovDir[x][y] = right_dir;
5227     else
5228       MovDir[x][y] = back_dir;
5229
5230     xx = x + move_xy[MovDir[x][y]].dx;
5231     yy = y + move_xy[MovDir[x][y]].dy;
5232
5233     if (!IN_LEV_FIELD(xx, yy) ||
5234         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5235       MovDir[x][y] = old_move_dir;
5236
5237     MovDelay[x][y] = 0;
5238   }
5239   else if (element == EL_DRAGON)
5240   {
5241     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5242     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5243     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5244     int rnd_value = 24;
5245     int rnd = RND(rnd_value);
5246
5247     if (can_move_on && rnd > rnd_value / 8)
5248       MovDir[x][y] = old_move_dir;
5249     else if (can_turn_left && can_turn_right)
5250       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5251     else if (can_turn_left && rnd > rnd_value / 8)
5252       MovDir[x][y] = left_dir;
5253     else if (can_turn_right && rnd > rnd_value / 8)
5254       MovDir[x][y] = right_dir;
5255     else
5256       MovDir[x][y] = back_dir;
5257
5258     xx = x + move_xy[MovDir[x][y]].dx;
5259     yy = y + move_xy[MovDir[x][y]].dy;
5260
5261     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5262       MovDir[x][y] = old_move_dir;
5263
5264     MovDelay[x][y] = 0;
5265   }
5266   else if (element == EL_MOLE)
5267   {
5268     boolean can_move_on =
5269       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5270                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5271                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5272     if (!can_move_on)
5273     {
5274       boolean can_turn_left =
5275         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5276                               IS_AMOEBOID(Feld[left_x][left_y])));
5277
5278       boolean can_turn_right =
5279         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5280                               IS_AMOEBOID(Feld[right_x][right_y])));
5281
5282       if (can_turn_left && can_turn_right)
5283         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5284       else if (can_turn_left)
5285         MovDir[x][y] = left_dir;
5286       else
5287         MovDir[x][y] = right_dir;
5288     }
5289
5290     if (MovDir[x][y] != old_move_dir)
5291       MovDelay[x][y] = 9;
5292   }
5293   else if (element == EL_BALLOON)
5294   {
5295     MovDir[x][y] = game.wind_direction;
5296     MovDelay[x][y] = 0;
5297   }
5298   else if (element == EL_SPRING)
5299   {
5300 #if USE_NEW_SPRING_BUMPER
5301     if (MovDir[x][y] & MV_HORIZONTAL)
5302     {
5303       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5304           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5305       {
5306         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5307         ResetGfxAnimation(move_x, move_y);
5308         DrawLevelField(move_x, move_y);
5309
5310         MovDir[x][y] = back_dir;
5311       }
5312       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5313                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5314         MovDir[x][y] = MV_NONE;
5315     }
5316 #else
5317     if (MovDir[x][y] & MV_HORIZONTAL &&
5318         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5319          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5320       MovDir[x][y] = MV_NONE;
5321 #endif
5322
5323     MovDelay[x][y] = 0;
5324   }
5325   else if (element == EL_ROBOT ||
5326            element == EL_SATELLITE ||
5327            element == EL_PENGUIN ||
5328            element == EL_EMC_ANDROID)
5329   {
5330     int attr_x = -1, attr_y = -1;
5331
5332     if (AllPlayersGone)
5333     {
5334       attr_x = ExitX;
5335       attr_y = ExitY;
5336     }
5337     else
5338     {
5339       int i;
5340
5341       for (i = 0; i < MAX_PLAYERS; i++)
5342       {
5343         struct PlayerInfo *player = &stored_player[i];
5344         int jx = player->jx, jy = player->jy;
5345
5346         if (!player->active)
5347           continue;
5348
5349         if (attr_x == -1 ||
5350             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5351         {
5352           attr_x = jx;
5353           attr_y = jy;
5354         }
5355       }
5356     }
5357
5358     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5359         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5360          game.engine_version < VERSION_IDENT(3,1,0,0)))
5361     {
5362       attr_x = ZX;
5363       attr_y = ZY;
5364     }
5365
5366     if (element == EL_PENGUIN)
5367     {
5368       int i;
5369       static int xy[4][2] =
5370       {
5371         { 0, -1 },
5372         { -1, 0 },
5373         { +1, 0 },
5374         { 0, +1 }
5375       };
5376
5377       for (i = 0; i < NUM_DIRECTIONS; i++)
5378       {
5379         int ex = x + xy[i][0];
5380         int ey = y + xy[i][1];
5381
5382         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5383                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5384                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5385                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5386         {
5387           attr_x = ex;
5388           attr_y = ey;
5389           break;
5390         }
5391       }
5392     }
5393
5394     MovDir[x][y] = MV_NONE;
5395     if (attr_x < x)
5396       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5397     else if (attr_x > x)
5398       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5399     if (attr_y < y)
5400       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5401     else if (attr_y > y)
5402       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5403
5404     if (element == EL_ROBOT)
5405     {
5406       int newx, newy;
5407
5408       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5409         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5410       Moving2Blocked(x, y, &newx, &newy);
5411
5412       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5413         MovDelay[x][y] = 8 + 8 * !RND(3);
5414       else
5415         MovDelay[x][y] = 16;
5416     }
5417     else if (element == EL_PENGUIN)
5418     {
5419       int newx, newy;
5420
5421       MovDelay[x][y] = 1;
5422
5423       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5424       {
5425         boolean first_horiz = RND(2);
5426         int new_move_dir = MovDir[x][y];
5427
5428         MovDir[x][y] =
5429           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5430         Moving2Blocked(x, y, &newx, &newy);
5431
5432         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5433           return;
5434
5435         MovDir[x][y] =
5436           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5437         Moving2Blocked(x, y, &newx, &newy);
5438
5439         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5440           return;
5441
5442         MovDir[x][y] = old_move_dir;
5443         return;
5444       }
5445     }
5446     else if (element == EL_SATELLITE)
5447     {
5448       int newx, newy;
5449
5450       MovDelay[x][y] = 1;
5451
5452       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5453       {
5454         boolean first_horiz = RND(2);
5455         int new_move_dir = MovDir[x][y];
5456
5457         MovDir[x][y] =
5458           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5459         Moving2Blocked(x, y, &newx, &newy);
5460
5461         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5462           return;
5463
5464         MovDir[x][y] =
5465           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5466         Moving2Blocked(x, y, &newx, &newy);
5467
5468         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5469           return;
5470
5471         MovDir[x][y] = old_move_dir;
5472         return;
5473       }
5474     }
5475     else if (element == EL_EMC_ANDROID)
5476     {
5477       static int check_pos[16] =
5478       {
5479         -1,             /*  0 => (invalid)          */
5480         7,              /*  1 => MV_LEFT            */
5481         3,              /*  2 => MV_RIGHT           */
5482         -1,             /*  3 => (invalid)          */
5483         1,              /*  4 =>            MV_UP   */
5484         0,              /*  5 => MV_LEFT  | MV_UP   */
5485         2,              /*  6 => MV_RIGHT | MV_UP   */
5486         -1,             /*  7 => (invalid)          */
5487         5,              /*  8 =>            MV_DOWN */
5488         6,              /*  9 => MV_LEFT  | MV_DOWN */
5489         4,              /* 10 => MV_RIGHT | MV_DOWN */
5490         -1,             /* 11 => (invalid)          */
5491         -1,             /* 12 => (invalid)          */
5492         -1,             /* 13 => (invalid)          */
5493         -1,             /* 14 => (invalid)          */
5494         -1,             /* 15 => (invalid)          */
5495       };
5496       static struct
5497       {
5498         int dx, dy;
5499         int dir;
5500       } check_xy[8] =
5501       {
5502         { -1, -1,       MV_LEFT  | MV_UP   },
5503         {  0, -1,                  MV_UP   },
5504         { +1, -1,       MV_RIGHT | MV_UP   },
5505         { +1,  0,       MV_RIGHT           },
5506         { +1, +1,       MV_RIGHT | MV_DOWN },
5507         {  0, +1,                  MV_DOWN },
5508         { -1, +1,       MV_LEFT  | MV_DOWN },
5509         { -1,  0,       MV_LEFT            },
5510       };
5511       int start_pos, check_order;
5512       boolean can_clone = FALSE;
5513       int i;
5514
5515       /* check if there is any free field around current position */
5516       for (i = 0; i < 8; i++)
5517       {
5518         int newx = x + check_xy[i].dx;
5519         int newy = y + check_xy[i].dy;
5520
5521         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5522         {
5523           can_clone = TRUE;
5524
5525           break;
5526         }
5527       }
5528
5529       if (can_clone)            /* randomly find an element to clone */
5530       {
5531         can_clone = FALSE;
5532
5533         start_pos = check_pos[RND(8)];
5534         check_order = (RND(2) ? -1 : +1);
5535
5536         for (i = 0; i < 8; i++)
5537         {
5538           int pos_raw = start_pos + i * check_order;
5539           int pos = (pos_raw + 8) % 8;
5540           int newx = x + check_xy[pos].dx;
5541           int newy = y + check_xy[pos].dy;
5542
5543           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5544           {
5545             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5546             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5547
5548             Store[x][y] = Feld[newx][newy];
5549
5550             can_clone = TRUE;
5551
5552             break;
5553           }
5554         }
5555       }
5556
5557       if (can_clone)            /* randomly find a direction to move */
5558       {
5559         can_clone = FALSE;
5560
5561         start_pos = check_pos[RND(8)];
5562         check_order = (RND(2) ? -1 : +1);
5563
5564         for (i = 0; i < 8; i++)
5565         {
5566           int pos_raw = start_pos + i * check_order;
5567           int pos = (pos_raw + 8) % 8;
5568           int newx = x + check_xy[pos].dx;
5569           int newy = y + check_xy[pos].dy;
5570           int new_move_dir = check_xy[pos].dir;
5571
5572           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5573           {
5574             MovDir[x][y] = new_move_dir;
5575             MovDelay[x][y] = level.android_clone_time * 8 + 1;
5576
5577             can_clone = TRUE;
5578
5579             break;
5580           }
5581         }
5582       }
5583
5584       if (can_clone)            /* cloning and moving successful */
5585         return;
5586
5587       /* cannot clone -- try to move towards player */
5588
5589       start_pos = check_pos[MovDir[x][y] & 0x0f];
5590       check_order = (RND(2) ? -1 : +1);
5591
5592       for (i = 0; i < 3; i++)
5593       {
5594         /* first check start_pos, then previous/next or (next/previous) pos */
5595         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5596         int pos = (pos_raw + 8) % 8;
5597         int newx = x + check_xy[pos].dx;
5598         int newy = y + check_xy[pos].dy;
5599         int new_move_dir = check_xy[pos].dir;
5600
5601         if (IS_PLAYER(newx, newy))
5602           break;
5603
5604         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5605         {
5606           MovDir[x][y] = new_move_dir;
5607           MovDelay[x][y] = level.android_move_time * 8 + 1;
5608
5609           break;
5610         }
5611       }
5612     }
5613   }
5614   else if (move_pattern == MV_TURNING_LEFT ||
5615            move_pattern == MV_TURNING_RIGHT ||
5616            move_pattern == MV_TURNING_LEFT_RIGHT ||
5617            move_pattern == MV_TURNING_RIGHT_LEFT ||
5618            move_pattern == MV_TURNING_RANDOM ||
5619            move_pattern == MV_ALL_DIRECTIONS)
5620   {
5621     boolean can_turn_left =
5622       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5623     boolean can_turn_right =
5624       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5625
5626     if (element_info[element].move_stepsize == 0)       /* "not moving" */
5627       return;
5628
5629     if (move_pattern == MV_TURNING_LEFT)
5630       MovDir[x][y] = left_dir;
5631     else if (move_pattern == MV_TURNING_RIGHT)
5632       MovDir[x][y] = right_dir;
5633     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5634       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5635     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5636       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5637     else if (move_pattern == MV_TURNING_RANDOM)
5638       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5639                       can_turn_right && !can_turn_left ? right_dir :
5640                       RND(2) ? left_dir : right_dir);
5641     else if (can_turn_left && can_turn_right)
5642       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5643     else if (can_turn_left)
5644       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5645     else if (can_turn_right)
5646       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5647     else
5648       MovDir[x][y] = back_dir;
5649
5650     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5651   }
5652   else if (move_pattern == MV_HORIZONTAL ||
5653            move_pattern == MV_VERTICAL)
5654   {
5655     if (move_pattern & old_move_dir)
5656       MovDir[x][y] = back_dir;
5657     else if (move_pattern == MV_HORIZONTAL)
5658       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5659     else if (move_pattern == MV_VERTICAL)
5660       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5661
5662     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5663   }
5664   else if (move_pattern & MV_ANY_DIRECTION)
5665   {
5666     MovDir[x][y] = move_pattern;
5667     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5668   }
5669   else if (move_pattern & MV_WIND_DIRECTION)
5670   {
5671     MovDir[x][y] = game.wind_direction;
5672     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5673   }
5674   else if (move_pattern == MV_ALONG_LEFT_SIDE)
5675   {
5676     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5677       MovDir[x][y] = left_dir;
5678     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5679       MovDir[x][y] = right_dir;
5680
5681     if (MovDir[x][y] != old_move_dir)
5682       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5683   }
5684   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5685   {
5686     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5687       MovDir[x][y] = right_dir;
5688     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5689       MovDir[x][y] = left_dir;
5690
5691     if (MovDir[x][y] != old_move_dir)
5692       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5693   }
5694   else if (move_pattern == MV_TOWARDS_PLAYER ||
5695            move_pattern == MV_AWAY_FROM_PLAYER)
5696   {
5697     int attr_x = -1, attr_y = -1;
5698     int newx, newy;
5699     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5700
5701     if (AllPlayersGone)
5702     {
5703       attr_x = ExitX;
5704       attr_y = ExitY;
5705     }
5706     else
5707     {
5708       int i;
5709
5710       for (i = 0; i < MAX_PLAYERS; i++)
5711       {
5712         struct PlayerInfo *player = &stored_player[i];
5713         int jx = player->jx, jy = player->jy;
5714
5715         if (!player->active)
5716           continue;
5717
5718         if (attr_x == -1 ||
5719             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5720         {
5721           attr_x = jx;
5722           attr_y = jy;
5723         }
5724       }
5725     }
5726
5727     MovDir[x][y] = MV_NONE;
5728     if (attr_x < x)
5729       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5730     else if (attr_x > x)
5731       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5732     if (attr_y < y)
5733       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5734     else if (attr_y > y)
5735       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5736
5737     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5738
5739     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5740     {
5741       boolean first_horiz = RND(2);
5742       int new_move_dir = MovDir[x][y];
5743
5744       if (element_info[element].move_stepsize == 0)     /* "not moving" */
5745       {
5746         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5747         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5748
5749         return;
5750       }
5751
5752       MovDir[x][y] =
5753         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5754       Moving2Blocked(x, y, &newx, &newy);
5755
5756       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5757         return;
5758
5759       MovDir[x][y] =
5760         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5761       Moving2Blocked(x, y, &newx, &newy);
5762
5763       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5764         return;
5765
5766       MovDir[x][y] = old_move_dir;
5767     }
5768   }
5769   else if (move_pattern == MV_WHEN_PUSHED ||
5770            move_pattern == MV_WHEN_DROPPED)
5771   {
5772     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5773       MovDir[x][y] = MV_NONE;
5774
5775     MovDelay[x][y] = 0;
5776   }
5777   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5778   {
5779     static int test_xy[7][2] =
5780     {
5781       { 0, -1 },
5782       { -1, 0 },
5783       { +1, 0 },
5784       { 0, +1 },
5785       { 0, -1 },
5786       { -1, 0 },
5787       { +1, 0 },
5788     };
5789     static int test_dir[7] =
5790     {
5791       MV_UP,
5792       MV_LEFT,
5793       MV_RIGHT,
5794       MV_DOWN,
5795       MV_UP,
5796       MV_LEFT,
5797       MV_RIGHT,
5798     };
5799     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5800     int move_preference = -1000000;     /* start with very low preference */
5801     int new_move_dir = MV_NONE;
5802     int start_test = RND(4);
5803     int i;
5804
5805     for (i = 0; i < NUM_DIRECTIONS; i++)
5806     {
5807       int move_dir = test_dir[start_test + i];
5808       int move_dir_preference;
5809
5810       xx = x + test_xy[start_test + i][0];
5811       yy = y + test_xy[start_test + i][1];
5812
5813       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5814           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5815       {
5816         new_move_dir = move_dir;
5817
5818         break;
5819       }
5820
5821       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5822         continue;
5823
5824       move_dir_preference = -1 * RunnerVisit[xx][yy];
5825       if (hunter_mode && PlayerVisit[xx][yy] > 0)
5826         move_dir_preference = PlayerVisit[xx][yy];
5827
5828       if (move_dir_preference > move_preference)
5829       {
5830         /* prefer field that has not been visited for the longest time */
5831         move_preference = move_dir_preference;
5832         new_move_dir = move_dir;
5833       }
5834       else if (move_dir_preference == move_preference &&
5835                move_dir == old_move_dir)
5836       {
5837         /* prefer last direction when all directions are preferred equally */
5838         move_preference = move_dir_preference;
5839         new_move_dir = move_dir;
5840       }
5841     }
5842
5843     MovDir[x][y] = new_move_dir;
5844     if (old_move_dir != new_move_dir)
5845       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5846   }
5847 }
5848
5849 static void TurnRound(int x, int y)
5850 {
5851   int direction = MovDir[x][y];
5852
5853   TurnRoundExt(x, y);
5854
5855   GfxDir[x][y] = MovDir[x][y];
5856
5857   if (direction != MovDir[x][y])
5858     GfxFrame[x][y] = 0;
5859
5860   if (MovDelay[x][y])
5861     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5862
5863   ResetGfxFrame(x, y, FALSE);
5864 }
5865
5866 static boolean JustBeingPushed(int x, int y)
5867 {
5868   int i;
5869
5870   for (i = 0; i < MAX_PLAYERS; i++)
5871   {
5872     struct PlayerInfo *player = &stored_player[i];
5873
5874     if (player->active && player->is_pushing && player->MovPos)
5875     {
5876       int next_jx = player->jx + (player->jx - player->last_jx);
5877       int next_jy = player->jy + (player->jy - player->last_jy);
5878
5879       if (x == next_jx && y == next_jy)
5880         return TRUE;
5881     }
5882   }
5883
5884   return FALSE;
5885 }
5886
5887 void StartMoving(int x, int y)
5888 {
5889   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
5890   int element = Feld[x][y];
5891
5892   if (Stop[x][y])
5893     return;
5894
5895   if (MovDelay[x][y] == 0)
5896     GfxAction[x][y] = ACTION_DEFAULT;
5897
5898   if (CAN_FALL(element) && y < lev_fieldy - 1)
5899   {
5900     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5901         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5902       if (JustBeingPushed(x, y))
5903         return;
5904
5905     if (element == EL_QUICKSAND_FULL)
5906     {
5907       if (IS_FREE(x, y + 1))
5908       {
5909         InitMovingField(x, y, MV_DOWN);
5910         started_moving = TRUE;
5911
5912         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5913 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5914         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5915           Store[x][y] = EL_ROCK;
5916 #else
5917         Store[x][y] = EL_ROCK;
5918 #endif
5919
5920         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5921       }
5922       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5923       {
5924         if (!MovDelay[x][y])
5925           MovDelay[x][y] = TILEY + 1;
5926
5927         if (MovDelay[x][y])
5928         {
5929           MovDelay[x][y]--;
5930           if (MovDelay[x][y])
5931             return;
5932         }
5933
5934         Feld[x][y] = EL_QUICKSAND_EMPTY;
5935         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5936         Store[x][y + 1] = Store[x][y];
5937         Store[x][y] = 0;
5938
5939         PlayLevelSoundAction(x, y, ACTION_FILLING);
5940       }
5941     }
5942     else if (element == EL_QUICKSAND_FAST_FULL)
5943     {
5944       if (IS_FREE(x, y + 1))
5945       {
5946         InitMovingField(x, y, MV_DOWN);
5947         started_moving = TRUE;
5948
5949         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
5950 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5951         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5952           Store[x][y] = EL_ROCK;
5953 #else
5954         Store[x][y] = EL_ROCK;
5955 #endif
5956
5957         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5958       }
5959       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5960       {
5961         if (!MovDelay[x][y])
5962           MovDelay[x][y] = TILEY + 1;
5963
5964         if (MovDelay[x][y])
5965         {
5966           MovDelay[x][y]--;
5967           if (MovDelay[x][y])
5968             return;
5969         }
5970
5971         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
5972         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
5973         Store[x][y + 1] = Store[x][y];
5974         Store[x][y] = 0;
5975
5976         PlayLevelSoundAction(x, y, ACTION_FILLING);
5977       }
5978     }
5979     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5980              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5981     {
5982       InitMovingField(x, y, MV_DOWN);
5983       started_moving = TRUE;
5984
5985       Feld[x][y] = EL_QUICKSAND_FILLING;
5986       Store[x][y] = element;
5987
5988       PlayLevelSoundAction(x, y, ACTION_FILLING);
5989     }
5990     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5991              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5992     {
5993       InitMovingField(x, y, MV_DOWN);
5994       started_moving = TRUE;
5995
5996       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
5997       Store[x][y] = element;
5998
5999       PlayLevelSoundAction(x, y, ACTION_FILLING);
6000     }
6001     else if (element == EL_MAGIC_WALL_FULL)
6002     {
6003       if (IS_FREE(x, y + 1))
6004       {
6005         InitMovingField(x, y, MV_DOWN);
6006         started_moving = TRUE;
6007
6008         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6009         Store[x][y] = EL_CHANGED(Store[x][y]);
6010       }
6011       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6012       {
6013         if (!MovDelay[x][y])
6014           MovDelay[x][y] = TILEY/4 + 1;
6015
6016         if (MovDelay[x][y])
6017         {
6018           MovDelay[x][y]--;
6019           if (MovDelay[x][y])
6020             return;
6021         }
6022
6023         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6024         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6025         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6026         Store[x][y] = 0;
6027       }
6028     }
6029     else if (element == EL_BD_MAGIC_WALL_FULL)
6030     {
6031       if (IS_FREE(x, y + 1))
6032       {
6033         InitMovingField(x, y, MV_DOWN);
6034         started_moving = TRUE;
6035
6036         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6037         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6038       }
6039       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6040       {
6041         if (!MovDelay[x][y])
6042           MovDelay[x][y] = TILEY/4 + 1;
6043
6044         if (MovDelay[x][y])
6045         {
6046           MovDelay[x][y]--;
6047           if (MovDelay[x][y])
6048             return;
6049         }
6050
6051         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6052         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6053         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6054         Store[x][y] = 0;
6055       }
6056     }
6057     else if (element == EL_DC_MAGIC_WALL_FULL)
6058     {
6059       if (IS_FREE(x, y + 1))
6060       {
6061         InitMovingField(x, y, MV_DOWN);
6062         started_moving = TRUE;
6063
6064         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6065         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6066       }
6067       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6068       {
6069         if (!MovDelay[x][y])
6070           MovDelay[x][y] = TILEY/4 + 1;
6071
6072         if (MovDelay[x][y])
6073         {
6074           MovDelay[x][y]--;
6075           if (MovDelay[x][y])
6076             return;
6077         }
6078
6079         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6080         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6081         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6082         Store[x][y] = 0;
6083       }
6084     }
6085     else if ((CAN_PASS_MAGIC_WALL(element) &&
6086               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6087                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6088              (CAN_PASS_DC_MAGIC_WALL(element) &&
6089               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6090
6091     {
6092       InitMovingField(x, y, MV_DOWN);
6093       started_moving = TRUE;
6094
6095       Feld[x][y] =
6096         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6097          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6098          EL_DC_MAGIC_WALL_FILLING);
6099       Store[x][y] = element;
6100     }
6101     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6102     {
6103       SplashAcid(x, y + 1);
6104
6105       InitMovingField(x, y, MV_DOWN);
6106       started_moving = TRUE;
6107
6108       Store[x][y] = EL_ACID;
6109     }
6110     else if (
6111 #if USE_FIX_IMPACT_COLLISION
6112              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6113               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6114 #else
6115              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6116               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6117 #endif
6118              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6119               CAN_FALL(element) && WasJustFalling[x][y] &&
6120               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6121
6122              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6123               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6124               (Feld[x][y + 1] == EL_BLOCKED)))
6125     {
6126       /* this is needed for a special case not covered by calling "Impact()"
6127          from "ContinueMoving()": if an element moves to a tile directly below
6128          another element which was just falling on that tile (which was empty
6129          in the previous frame), the falling element above would just stop
6130          instead of smashing the element below (in previous version, the above
6131          element was just checked for "moving" instead of "falling", resulting
6132          in incorrect smashes caused by horizontal movement of the above
6133          element; also, the case of the player being the element to smash was
6134          simply not covered here... :-/ ) */
6135
6136       CheckCollision[x][y] = 0;
6137       CheckImpact[x][y] = 0;
6138
6139       Impact(x, y);
6140     }
6141     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6142     {
6143       if (MovDir[x][y] == MV_NONE)
6144       {
6145         InitMovingField(x, y, MV_DOWN);
6146         started_moving = TRUE;
6147       }
6148     }
6149     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6150     {
6151       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6152         MovDir[x][y] = MV_DOWN;
6153
6154       InitMovingField(x, y, MV_DOWN);
6155       started_moving = TRUE;
6156     }
6157     else if (element == EL_AMOEBA_DROP)
6158     {
6159       Feld[x][y] = EL_AMOEBA_GROWING;
6160       Store[x][y] = EL_AMOEBA_WET;
6161     }
6162     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6163               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6164              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6165              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6166     {
6167       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6168                                 (IS_FREE(x - 1, y + 1) ||
6169                                  Feld[x - 1][y + 1] == EL_ACID));
6170       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6171                                 (IS_FREE(x + 1, y + 1) ||
6172                                  Feld[x + 1][y + 1] == EL_ACID));
6173       boolean can_fall_any  = (can_fall_left || can_fall_right);
6174       boolean can_fall_both = (can_fall_left && can_fall_right);
6175       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6176
6177 #if USE_NEW_ALL_SLIPPERY
6178       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6179       {
6180         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6181           can_fall_right = FALSE;
6182         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6183           can_fall_left = FALSE;
6184         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6185           can_fall_right = FALSE;
6186         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6187           can_fall_left = FALSE;
6188
6189         can_fall_any  = (can_fall_left || can_fall_right);
6190         can_fall_both = FALSE;
6191       }
6192 #else
6193       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6194       {
6195         if (slippery_type == SLIPPERY_ONLY_LEFT)
6196           can_fall_right = FALSE;
6197         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6198           can_fall_left = FALSE;
6199         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6200           can_fall_right = FALSE;
6201         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6202           can_fall_left = FALSE;
6203
6204         can_fall_any  = (can_fall_left || can_fall_right);
6205         can_fall_both = (can_fall_left && can_fall_right);
6206       }
6207 #endif
6208
6209 #if USE_NEW_ALL_SLIPPERY
6210 #else
6211 #if USE_NEW_SP_SLIPPERY
6212       /* !!! better use the same properties as for custom elements here !!! */
6213       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6214                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6215       {
6216         can_fall_right = FALSE;         /* slip down on left side */
6217         can_fall_both = FALSE;
6218       }
6219 #endif
6220 #endif
6221
6222 #if USE_NEW_ALL_SLIPPERY
6223       if (can_fall_both)
6224       {
6225         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6226           can_fall_right = FALSE;       /* slip down on left side */
6227         else
6228           can_fall_left = !(can_fall_right = RND(2));
6229
6230         can_fall_both = FALSE;
6231       }
6232 #else
6233       if (can_fall_both)
6234       {
6235         if (game.emulation == EMU_BOULDERDASH ||
6236             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6237           can_fall_right = FALSE;       /* slip down on left side */
6238         else
6239           can_fall_left = !(can_fall_right = RND(2));
6240
6241         can_fall_both = FALSE;
6242       }
6243 #endif
6244
6245       if (can_fall_any)
6246       {
6247         /* if not determined otherwise, prefer left side for slipping down */
6248         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6249         started_moving = TRUE;
6250       }
6251     }
6252 #if 0
6253     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6254 #else
6255     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6256 #endif
6257     {
6258       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6259       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6260       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6261       int belt_dir = game.belt_dir[belt_nr];
6262
6263       if ((belt_dir == MV_LEFT  && left_is_free) ||
6264           (belt_dir == MV_RIGHT && right_is_free))
6265       {
6266         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6267
6268         InitMovingField(x, y, belt_dir);
6269         started_moving = TRUE;
6270
6271         Pushed[x][y] = TRUE;
6272         Pushed[nextx][y] = TRUE;
6273
6274         GfxAction[x][y] = ACTION_DEFAULT;
6275       }
6276       else
6277       {
6278         MovDir[x][y] = 0;       /* if element was moving, stop it */
6279       }
6280     }
6281   }
6282
6283   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6284 #if 0
6285   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6286 #else
6287   if (CAN_MOVE(element) && !started_moving)
6288 #endif
6289   {
6290     int move_pattern = element_info[element].move_pattern;
6291     int newx, newy;
6292
6293 #if 0
6294 #if DEBUG
6295     if (MovDir[x][y] == MV_NONE)
6296     {
6297       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6298              x, y, element, element_info[element].token_name);
6299       printf("StartMoving(): This should never happen!\n");
6300     }
6301 #endif
6302 #endif
6303
6304     Moving2Blocked(x, y, &newx, &newy);
6305
6306     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6307       return;
6308
6309     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6310         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6311     {
6312       WasJustMoving[x][y] = 0;
6313       CheckCollision[x][y] = 0;
6314
6315       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6316
6317       if (Feld[x][y] != element)        /* element has changed */
6318         return;
6319     }
6320
6321     if (!MovDelay[x][y])        /* start new movement phase */
6322     {
6323       /* all objects that can change their move direction after each step
6324          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6325
6326       if (element != EL_YAMYAM &&
6327           element != EL_DARK_YAMYAM &&
6328           element != EL_PACMAN &&
6329           !(move_pattern & MV_ANY_DIRECTION) &&
6330           move_pattern != MV_TURNING_LEFT &&
6331           move_pattern != MV_TURNING_RIGHT &&
6332           move_pattern != MV_TURNING_LEFT_RIGHT &&
6333           move_pattern != MV_TURNING_RIGHT_LEFT &&
6334           move_pattern != MV_TURNING_RANDOM)
6335       {
6336         TurnRound(x, y);
6337
6338         if (MovDelay[x][y] && (element == EL_BUG ||
6339                                element == EL_SPACESHIP ||
6340                                element == EL_SP_SNIKSNAK ||
6341                                element == EL_SP_ELECTRON ||
6342                                element == EL_MOLE))
6343           DrawLevelField(x, y);
6344       }
6345     }
6346
6347     if (MovDelay[x][y])         /* wait some time before next movement */
6348     {
6349       MovDelay[x][y]--;
6350
6351       if (element == EL_ROBOT ||
6352           element == EL_YAMYAM ||
6353           element == EL_DARK_YAMYAM)
6354       {
6355         DrawLevelElementAnimationIfNeeded(x, y, element);
6356         PlayLevelSoundAction(x, y, ACTION_WAITING);
6357       }
6358       else if (element == EL_SP_ELECTRON)
6359         DrawLevelElementAnimationIfNeeded(x, y, element);
6360       else if (element == EL_DRAGON)
6361       {
6362         int i;
6363         int dir = MovDir[x][y];
6364         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6365         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6366         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6367                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6368                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6369                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6370         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6371
6372         GfxAction[x][y] = ACTION_ATTACKING;
6373
6374         if (IS_PLAYER(x, y))
6375           DrawPlayerField(x, y);
6376         else
6377           DrawLevelField(x, y);
6378
6379         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6380
6381         for (i = 1; i <= 3; i++)
6382         {
6383           int xx = x + i * dx;
6384           int yy = y + i * dy;
6385           int sx = SCREENX(xx);
6386           int sy = SCREENY(yy);
6387           int flame_graphic = graphic + (i - 1);
6388
6389           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6390             break;
6391
6392           if (MovDelay[x][y])
6393           {
6394             int flamed = MovingOrBlocked2Element(xx, yy);
6395
6396             /* !!! */
6397 #if 0
6398             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6399               Bang(xx, yy);
6400             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6401               RemoveMovingField(xx, yy);
6402             else
6403               RemoveField(xx, yy);
6404 #else
6405             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6406               Bang(xx, yy);
6407             else
6408               RemoveMovingField(xx, yy);
6409 #endif
6410
6411             ChangeDelay[xx][yy] = 0;
6412
6413             Feld[xx][yy] = EL_FLAMES;
6414
6415             if (IN_SCR_FIELD(sx, sy))
6416             {
6417               DrawLevelFieldCrumbledSand(xx, yy);
6418               DrawGraphic(sx, sy, flame_graphic, frame);
6419             }
6420           }
6421           else
6422           {
6423             if (Feld[xx][yy] == EL_FLAMES)
6424               Feld[xx][yy] = EL_EMPTY;
6425             DrawLevelField(xx, yy);
6426           }
6427         }
6428       }
6429
6430       if (MovDelay[x][y])       /* element still has to wait some time */
6431       {
6432         PlayLevelSoundAction(x, y, ACTION_WAITING);
6433
6434         return;
6435       }
6436     }
6437
6438     /* now make next step */
6439
6440     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6441
6442     if (DONT_COLLIDE_WITH(element) &&
6443         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6444         !PLAYER_ENEMY_PROTECTED(newx, newy))
6445     {
6446       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6447
6448       return;
6449     }
6450
6451     else if (CAN_MOVE_INTO_ACID(element) &&
6452              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6453              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6454              (MovDir[x][y] == MV_DOWN ||
6455               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6456     {
6457       SplashAcid(newx, newy);
6458       Store[x][y] = EL_ACID;
6459     }
6460     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6461     {
6462       if (Feld[newx][newy] == EL_EXIT_OPEN ||
6463           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6464           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6465           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6466       {
6467         RemoveField(x, y);
6468         DrawLevelField(x, y);
6469
6470         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6471         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6472           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6473
6474         local_player->friends_still_needed--;
6475         if (!local_player->friends_still_needed &&
6476             !local_player->GameOver && AllPlayersGone)
6477           PlayerWins(local_player);
6478
6479         return;
6480       }
6481       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6482       {
6483         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6484           DrawLevelField(newx, newy);
6485         else
6486           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6487       }
6488       else if (!IS_FREE(newx, newy))
6489       {
6490         GfxAction[x][y] = ACTION_WAITING;
6491
6492         if (IS_PLAYER(x, y))
6493           DrawPlayerField(x, y);
6494         else
6495           DrawLevelField(x, y);
6496
6497         return;
6498       }
6499     }
6500     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6501     {
6502       if (IS_FOOD_PIG(Feld[newx][newy]))
6503       {
6504         if (IS_MOVING(newx, newy))
6505           RemoveMovingField(newx, newy);
6506         else
6507         {
6508           Feld[newx][newy] = EL_EMPTY;
6509           DrawLevelField(newx, newy);
6510         }
6511
6512         PlayLevelSound(x, y, SND_PIG_DIGGING);
6513       }
6514       else if (!IS_FREE(newx, newy))
6515       {
6516         if (IS_PLAYER(x, y))
6517           DrawPlayerField(x, y);
6518         else
6519           DrawLevelField(x, y);
6520
6521         return;
6522       }
6523     }
6524     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6525     {
6526       if (Store[x][y] != EL_EMPTY)
6527       {
6528         boolean can_clone = FALSE;
6529         int xx, yy;
6530
6531         /* check if element to clone is still there */
6532         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6533         {
6534           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6535           {
6536             can_clone = TRUE;
6537
6538             break;
6539           }
6540         }
6541
6542         /* cannot clone or target field not free anymore -- do not clone */
6543         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6544           Store[x][y] = EL_EMPTY;
6545       }
6546
6547       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6548       {
6549         if (IS_MV_DIAGONAL(MovDir[x][y]))
6550         {
6551           int diagonal_move_dir = MovDir[x][y];
6552           int stored = Store[x][y];
6553           int change_delay = 8;
6554           int graphic;
6555
6556           /* android is moving diagonally */
6557
6558           CreateField(x, y, EL_DIAGONAL_SHRINKING);
6559
6560           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6561           GfxElement[x][y] = EL_EMC_ANDROID;
6562           GfxAction[x][y] = ACTION_SHRINKING;
6563           GfxDir[x][y] = diagonal_move_dir;
6564           ChangeDelay[x][y] = change_delay;
6565
6566           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6567                                    GfxDir[x][y]);
6568
6569           DrawLevelGraphicAnimation(x, y, graphic);
6570           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6571
6572           if (Feld[newx][newy] == EL_ACID)
6573           {
6574             SplashAcid(newx, newy);
6575
6576             return;
6577           }
6578
6579           CreateField(newx, newy, EL_DIAGONAL_GROWING);
6580
6581           Store[newx][newy] = EL_EMC_ANDROID;
6582           GfxElement[newx][newy] = EL_EMC_ANDROID;
6583           GfxAction[newx][newy] = ACTION_GROWING;
6584           GfxDir[newx][newy] = diagonal_move_dir;
6585           ChangeDelay[newx][newy] = change_delay;
6586
6587           graphic = el_act_dir2img(GfxElement[newx][newy],
6588                                    GfxAction[newx][newy], GfxDir[newx][newy]);
6589
6590           DrawLevelGraphicAnimation(newx, newy, graphic);
6591           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6592
6593           return;
6594         }
6595         else
6596         {
6597           Feld[newx][newy] = EL_EMPTY;
6598           DrawLevelField(newx, newy);
6599
6600           PlayLevelSoundAction(x, y, ACTION_DIGGING);
6601         }
6602       }
6603       else if (!IS_FREE(newx, newy))
6604       {
6605 #if 0
6606         if (IS_PLAYER(x, y))
6607           DrawPlayerField(x, y);
6608         else
6609           DrawLevelField(x, y);
6610 #endif
6611
6612         return;
6613       }
6614     }
6615     else if (IS_CUSTOM_ELEMENT(element) &&
6616              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6617     {
6618       int new_element = Feld[newx][newy];
6619
6620       if (!IS_FREE(newx, newy))
6621       {
6622         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6623                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6624                       ACTION_BREAKING);
6625
6626         /* no element can dig solid indestructible elements */
6627         if (IS_INDESTRUCTIBLE(new_element) &&
6628             !IS_DIGGABLE(new_element) &&
6629             !IS_COLLECTIBLE(new_element))
6630           return;
6631
6632         if (AmoebaNr[newx][newy] &&
6633             (new_element == EL_AMOEBA_FULL ||
6634              new_element == EL_BD_AMOEBA ||
6635              new_element == EL_AMOEBA_GROWING))
6636         {
6637           AmoebaCnt[AmoebaNr[newx][newy]]--;
6638           AmoebaCnt2[AmoebaNr[newx][newy]]--;
6639         }
6640
6641         if (IS_MOVING(newx, newy))
6642           RemoveMovingField(newx, newy);
6643         else
6644         {
6645           RemoveField(newx, newy);
6646           DrawLevelField(newx, newy);
6647         }
6648
6649         /* if digged element was about to explode, prevent the explosion */
6650         ExplodeField[newx][newy] = EX_TYPE_NONE;
6651
6652         PlayLevelSoundAction(x, y, action);
6653       }
6654
6655       Store[newx][newy] = EL_EMPTY;
6656 #if 1
6657       /* this makes it possible to leave the removed element again */
6658       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6659         Store[newx][newy] = new_element;
6660 #else
6661       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6662       {
6663         int move_leave_element = element_info[element].move_leave_element;
6664
6665         /* this makes it possible to leave the removed element again */
6666         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6667                              new_element : move_leave_element);
6668       }
6669 #endif
6670
6671       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6672       {
6673         RunnerVisit[x][y] = FrameCounter;
6674         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6675       }
6676     }
6677     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6678     {
6679       if (!IS_FREE(newx, newy))
6680       {
6681         if (IS_PLAYER(x, y))
6682           DrawPlayerField(x, y);
6683         else
6684           DrawLevelField(x, y);
6685
6686         return;
6687       }
6688       else
6689       {
6690         boolean wanna_flame = !RND(10);
6691         int dx = newx - x, dy = newy - y;
6692         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6693         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6694         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6695                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6696         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6697                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6698
6699         if ((wanna_flame ||
6700              IS_CLASSIC_ENEMY(element1) ||
6701              IS_CLASSIC_ENEMY(element2)) &&
6702             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6703             element1 != EL_FLAMES && element2 != EL_FLAMES)
6704         {
6705           ResetGfxAnimation(x, y);
6706           GfxAction[x][y] = ACTION_ATTACKING;
6707
6708           if (IS_PLAYER(x, y))
6709             DrawPlayerField(x, y);
6710           else
6711             DrawLevelField(x, y);
6712
6713           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6714
6715           MovDelay[x][y] = 50;
6716
6717           /* !!! */
6718 #if 0
6719           RemoveField(newx, newy);
6720 #endif
6721           Feld[newx][newy] = EL_FLAMES;
6722           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6723           {
6724 #if 0
6725             RemoveField(newx1, newy1);
6726 #endif
6727             Feld[newx1][newy1] = EL_FLAMES;
6728           }
6729           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6730           {
6731 #if 0
6732             RemoveField(newx2, newy2);
6733 #endif
6734             Feld[newx2][newy2] = EL_FLAMES;
6735           }
6736
6737           return;
6738         }
6739       }
6740     }
6741     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6742              Feld[newx][newy] == EL_DIAMOND)
6743     {
6744       if (IS_MOVING(newx, newy))
6745         RemoveMovingField(newx, newy);
6746       else
6747       {
6748         Feld[newx][newy] = EL_EMPTY;
6749         DrawLevelField(newx, newy);
6750       }
6751
6752       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6753     }
6754     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6755              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6756     {
6757       if (AmoebaNr[newx][newy])
6758       {
6759         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6760         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6761             Feld[newx][newy] == EL_BD_AMOEBA)
6762           AmoebaCnt[AmoebaNr[newx][newy]]--;
6763       }
6764
6765 #if 0
6766       /* !!! test !!! */
6767       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6768       {
6769         RemoveMovingField(newx, newy);
6770       }
6771 #else
6772       if (IS_MOVING(newx, newy))
6773       {
6774         RemoveMovingField(newx, newy);
6775       }
6776 #endif
6777       else
6778       {
6779         Feld[newx][newy] = EL_EMPTY;
6780         DrawLevelField(newx, newy);
6781       }
6782
6783       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6784     }
6785     else if ((element == EL_PACMAN || element == EL_MOLE)
6786              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6787     {
6788       if (AmoebaNr[newx][newy])
6789       {
6790         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6791         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6792             Feld[newx][newy] == EL_BD_AMOEBA)
6793           AmoebaCnt[AmoebaNr[newx][newy]]--;
6794       }
6795
6796       if (element == EL_MOLE)
6797       {
6798         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6799         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6800
6801         ResetGfxAnimation(x, y);
6802         GfxAction[x][y] = ACTION_DIGGING;
6803         DrawLevelField(x, y);
6804
6805         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6806
6807         return;                         /* wait for shrinking amoeba */
6808       }
6809       else      /* element == EL_PACMAN */
6810       {
6811         Feld[newx][newy] = EL_EMPTY;
6812         DrawLevelField(newx, newy);
6813         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6814       }
6815     }
6816     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6817              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6818               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6819     {
6820       /* wait for shrinking amoeba to completely disappear */
6821       return;
6822     }
6823     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6824     {
6825       /* object was running against a wall */
6826
6827       TurnRound(x, y);
6828
6829 #if 0
6830       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6831       if (move_pattern & MV_ANY_DIRECTION &&
6832           move_pattern == MovDir[x][y])
6833       {
6834         int blocking_element =
6835           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6836
6837         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6838                                  MovDir[x][y]);
6839
6840         element = Feld[x][y];   /* element might have changed */
6841       }
6842 #endif
6843
6844       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
6845         DrawLevelElementAnimation(x, y, element);
6846
6847       if (DONT_TOUCH(element))
6848         TestIfBadThingTouchesPlayer(x, y);
6849
6850       return;
6851     }
6852
6853     InitMovingField(x, y, MovDir[x][y]);
6854
6855     PlayLevelSoundAction(x, y, ACTION_MOVING);
6856   }
6857
6858   if (MovDir[x][y])
6859     ContinueMoving(x, y);
6860 }
6861
6862 void ContinueMoving(int x, int y)
6863 {
6864   int element = Feld[x][y];
6865   struct ElementInfo *ei = &element_info[element];
6866   int direction = MovDir[x][y];
6867   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6868   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
6869   int newx = x + dx, newy = y + dy;
6870   int stored = Store[x][y];
6871   int stored_new = Store[newx][newy];
6872   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
6873   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6874   boolean last_line = (newy == lev_fieldy - 1);
6875
6876   MovPos[x][y] += getElementMoveStepsize(x, y);
6877
6878   if (pushed_by_player) /* special case: moving object pushed by player */
6879     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6880
6881   if (ABS(MovPos[x][y]) < TILEX)
6882   {
6883     DrawLevelField(x, y);
6884
6885     return;     /* element is still moving */
6886   }
6887
6888   /* element reached destination field */
6889
6890   Feld[x][y] = EL_EMPTY;
6891   Feld[newx][newy] = element;
6892   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6893
6894   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6895   {
6896     element = Feld[newx][newy] = EL_ACID;
6897   }
6898   else if (element == EL_MOLE)
6899   {
6900     Feld[x][y] = EL_SAND;
6901
6902     DrawLevelFieldCrumbledSandNeighbours(x, y);
6903   }
6904   else if (element == EL_QUICKSAND_FILLING)
6905   {
6906     element = Feld[newx][newy] = get_next_element(element);
6907     Store[newx][newy] = Store[x][y];
6908   }
6909   else if (element == EL_QUICKSAND_EMPTYING)
6910   {
6911     Feld[x][y] = get_next_element(element);
6912     element = Feld[newx][newy] = Store[x][y];
6913   }
6914   else if (element == EL_QUICKSAND_FAST_FILLING)
6915   {
6916     element = Feld[newx][newy] = get_next_element(element);
6917     Store[newx][newy] = Store[x][y];
6918   }
6919   else if (element == EL_QUICKSAND_FAST_EMPTYING)
6920   {
6921     Feld[x][y] = get_next_element(element);
6922     element = Feld[newx][newy] = Store[x][y];
6923   }
6924   else if (element == EL_MAGIC_WALL_FILLING)
6925   {
6926     element = Feld[newx][newy] = get_next_element(element);
6927     if (!game.magic_wall_active)
6928       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6929     Store[newx][newy] = Store[x][y];
6930   }
6931   else if (element == EL_MAGIC_WALL_EMPTYING)
6932   {
6933     Feld[x][y] = get_next_element(element);
6934     if (!game.magic_wall_active)
6935       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6936     element = Feld[newx][newy] = Store[x][y];
6937
6938 #if USE_NEW_CUSTOM_VALUE
6939     InitField(newx, newy, FALSE);
6940 #endif
6941   }
6942   else if (element == EL_BD_MAGIC_WALL_FILLING)
6943   {
6944     element = Feld[newx][newy] = get_next_element(element);
6945     if (!game.magic_wall_active)
6946       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6947     Store[newx][newy] = Store[x][y];
6948   }
6949   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6950   {
6951     Feld[x][y] = get_next_element(element);
6952     if (!game.magic_wall_active)
6953       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6954     element = Feld[newx][newy] = Store[x][y];
6955
6956 #if USE_NEW_CUSTOM_VALUE
6957     InitField(newx, newy, FALSE);
6958 #endif
6959   }
6960   else if (element == EL_DC_MAGIC_WALL_FILLING)
6961   {
6962     element = Feld[newx][newy] = get_next_element(element);
6963     if (!game.magic_wall_active)
6964       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
6965     Store[newx][newy] = Store[x][y];
6966   }
6967   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
6968   {
6969     Feld[x][y] = get_next_element(element);
6970     if (!game.magic_wall_active)
6971       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
6972     element = Feld[newx][newy] = Store[x][y];
6973
6974 #if USE_NEW_CUSTOM_VALUE
6975     InitField(newx, newy, FALSE);
6976 #endif
6977   }
6978   else if (element == EL_AMOEBA_DROPPING)
6979   {
6980     Feld[x][y] = get_next_element(element);
6981     element = Feld[newx][newy] = Store[x][y];
6982   }
6983   else if (element == EL_SOKOBAN_OBJECT)
6984   {
6985     if (Back[x][y])
6986       Feld[x][y] = Back[x][y];
6987
6988     if (Back[newx][newy])
6989       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6990
6991     Back[x][y] = Back[newx][newy] = 0;
6992   }
6993
6994   Store[x][y] = EL_EMPTY;
6995   MovPos[x][y] = 0;
6996   MovDir[x][y] = 0;
6997   MovDelay[x][y] = 0;
6998
6999   MovDelay[newx][newy] = 0;
7000
7001   if (CAN_CHANGE_OR_HAS_ACTION(element))
7002   {
7003     /* copy element change control values to new field */
7004     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7005     ChangePage[newx][newy]  = ChangePage[x][y];
7006     ChangeCount[newx][newy] = ChangeCount[x][y];
7007     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7008   }
7009
7010 #if USE_NEW_CUSTOM_VALUE
7011     CustomValue[newx][newy] = CustomValue[x][y];
7012 #endif
7013
7014   ChangeDelay[x][y] = 0;
7015   ChangePage[x][y] = -1;
7016   ChangeCount[x][y] = 0;
7017   ChangeEvent[x][y] = -1;
7018
7019 #if USE_NEW_CUSTOM_VALUE
7020   CustomValue[x][y] = 0;
7021 #endif
7022
7023   /* copy animation control values to new field */
7024   GfxFrame[newx][newy]  = GfxFrame[x][y];
7025   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7026   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7027   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7028
7029   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7030
7031   /* some elements can leave other elements behind after moving */
7032 #if 1
7033   if (ei->move_leave_element != EL_EMPTY &&
7034       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7035       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7036 #else
7037   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7038       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7039       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7040 #endif
7041   {
7042     int move_leave_element = ei->move_leave_element;
7043
7044 #if 1
7045 #if 1
7046     /* this makes it possible to leave the removed element again */
7047     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7048       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7049 #else
7050     /* this makes it possible to leave the removed element again */
7051     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7052       move_leave_element = stored;
7053 #endif
7054 #else
7055     /* this makes it possible to leave the removed element again */
7056     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7057         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7058       move_leave_element = stored;
7059 #endif
7060
7061     Feld[x][y] = move_leave_element;
7062
7063     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7064       MovDir[x][y] = direction;
7065
7066     InitField(x, y, FALSE);
7067
7068     if (GFX_CRUMBLED(Feld[x][y]))
7069       DrawLevelFieldCrumbledSandNeighbours(x, y);
7070
7071     if (ELEM_IS_PLAYER(move_leave_element))
7072       RelocatePlayer(x, y, move_leave_element);
7073   }
7074
7075   /* do this after checking for left-behind element */
7076   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7077
7078   if (!CAN_MOVE(element) ||
7079       (CAN_FALL(element) && direction == MV_DOWN &&
7080        (element == EL_SPRING ||
7081         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7082         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7083     GfxDir[x][y] = MovDir[newx][newy] = 0;
7084
7085   DrawLevelField(x, y);
7086   DrawLevelField(newx, newy);
7087
7088   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7089
7090   /* prevent pushed element from moving on in pushed direction */
7091   if (pushed_by_player && CAN_MOVE(element) &&
7092       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7093       !(element_info[element].move_pattern & direction))
7094     TurnRound(newx, newy);
7095
7096   /* prevent elements on conveyor belt from moving on in last direction */
7097   if (pushed_by_conveyor && CAN_FALL(element) &&
7098       direction & MV_HORIZONTAL)
7099     MovDir[newx][newy] = 0;
7100
7101   if (!pushed_by_player)
7102   {
7103     int nextx = newx + dx, nexty = newy + dy;
7104     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7105
7106     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7107
7108     if (CAN_FALL(element) && direction == MV_DOWN)
7109       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7110
7111     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7112       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7113
7114 #if USE_FIX_IMPACT_COLLISION
7115     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7116       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7117 #endif
7118   }
7119
7120   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7121   {
7122     TestIfBadThingTouchesPlayer(newx, newy);
7123     TestIfBadThingTouchesFriend(newx, newy);
7124
7125     if (!IS_CUSTOM_ELEMENT(element))
7126       TestIfBadThingTouchesOtherBadThing(newx, newy);
7127   }
7128   else if (element == EL_PENGUIN)
7129     TestIfFriendTouchesBadThing(newx, newy);
7130
7131   /* give the player one last chance (one more frame) to move away */
7132   if (CAN_FALL(element) && direction == MV_DOWN &&
7133       (last_line || (!IS_FREE(x, newy + 1) &&
7134                      (!IS_PLAYER(x, newy + 1) ||
7135                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7136     Impact(x, newy);
7137
7138   if (pushed_by_player && !game.use_change_when_pushing_bug)
7139   {
7140     int push_side = MV_DIR_OPPOSITE(direction);
7141     struct PlayerInfo *player = PLAYERINFO(x, y);
7142
7143     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7144                                player->index_bit, push_side);
7145     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7146                                         player->index_bit, push_side);
7147   }
7148
7149   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7150     MovDelay[newx][newy] = 1;
7151
7152   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7153
7154   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7155
7156 #if 0
7157   if (ChangePage[newx][newy] != -1)             /* delayed change */
7158   {
7159     int page = ChangePage[newx][newy];
7160     struct ElementChangeInfo *change = &ei->change_page[page];
7161
7162     ChangePage[newx][newy] = -1;
7163
7164     if (change->can_change)
7165     {
7166       if (ChangeElement(newx, newy, element, page))
7167       {
7168         if (change->post_change_function)
7169           change->post_change_function(newx, newy);
7170       }
7171     }
7172
7173     if (change->has_action)
7174       ExecuteCustomElementAction(newx, newy, element, page);
7175   }
7176 #endif
7177
7178   TestIfElementHitsCustomElement(newx, newy, direction);
7179   TestIfPlayerTouchesCustomElement(newx, newy);
7180   TestIfElementTouchesCustomElement(newx, newy);
7181
7182   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7183       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7184     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7185                              MV_DIR_OPPOSITE(direction));
7186 }
7187
7188 int AmoebeNachbarNr(int ax, int ay)
7189 {
7190   int i;
7191   int element = Feld[ax][ay];
7192   int group_nr = 0;
7193   static int xy[4][2] =
7194   {
7195     { 0, -1 },
7196     { -1, 0 },
7197     { +1, 0 },
7198     { 0, +1 }
7199   };
7200
7201   for (i = 0; i < NUM_DIRECTIONS; i++)
7202   {
7203     int x = ax + xy[i][0];
7204     int y = ay + xy[i][1];
7205
7206     if (!IN_LEV_FIELD(x, y))
7207       continue;
7208
7209     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7210       group_nr = AmoebaNr[x][y];
7211   }
7212
7213   return group_nr;
7214 }
7215
7216 void AmoebenVereinigen(int ax, int ay)
7217 {
7218   int i, x, y, xx, yy;
7219   int new_group_nr = AmoebaNr[ax][ay];
7220   static int xy[4][2] =
7221   {
7222     { 0, -1 },
7223     { -1, 0 },
7224     { +1, 0 },
7225     { 0, +1 }
7226   };
7227
7228   if (new_group_nr == 0)
7229     return;
7230
7231   for (i = 0; i < NUM_DIRECTIONS; i++)
7232   {
7233     x = ax + xy[i][0];
7234     y = ay + xy[i][1];
7235
7236     if (!IN_LEV_FIELD(x, y))
7237       continue;
7238
7239     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7240          Feld[x][y] == EL_BD_AMOEBA ||
7241          Feld[x][y] == EL_AMOEBA_DEAD) &&
7242         AmoebaNr[x][y] != new_group_nr)
7243     {
7244       int old_group_nr = AmoebaNr[x][y];
7245
7246       if (old_group_nr == 0)
7247         return;
7248
7249       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7250       AmoebaCnt[old_group_nr] = 0;
7251       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7252       AmoebaCnt2[old_group_nr] = 0;
7253
7254       SCAN_PLAYFIELD(xx, yy)
7255       {
7256         if (AmoebaNr[xx][yy] == old_group_nr)
7257           AmoebaNr[xx][yy] = new_group_nr;
7258       }
7259     }
7260   }
7261 }
7262
7263 void AmoebeUmwandeln(int ax, int ay)
7264 {
7265   int i, x, y;
7266
7267   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7268   {
7269     int group_nr = AmoebaNr[ax][ay];
7270
7271 #ifdef DEBUG
7272     if (group_nr == 0)
7273     {
7274       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7275       printf("AmoebeUmwandeln(): This should never happen!\n");
7276       return;
7277     }
7278 #endif
7279
7280     SCAN_PLAYFIELD(x, y)
7281     {
7282       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7283       {
7284         AmoebaNr[x][y] = 0;
7285         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7286       }
7287     }
7288
7289     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7290                             SND_AMOEBA_TURNING_TO_GEM :
7291                             SND_AMOEBA_TURNING_TO_ROCK));
7292     Bang(ax, ay);
7293   }
7294   else
7295   {
7296     static int xy[4][2] =
7297     {
7298       { 0, -1 },
7299       { -1, 0 },
7300       { +1, 0 },
7301       { 0, +1 }
7302     };
7303
7304     for (i = 0; i < NUM_DIRECTIONS; i++)
7305     {
7306       x = ax + xy[i][0];
7307       y = ay + xy[i][1];
7308
7309       if (!IN_LEV_FIELD(x, y))
7310         continue;
7311
7312       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7313       {
7314         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7315                               SND_AMOEBA_TURNING_TO_GEM :
7316                               SND_AMOEBA_TURNING_TO_ROCK));
7317         Bang(x, y);
7318       }
7319     }
7320   }
7321 }
7322
7323 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7324 {
7325   int x, y;
7326   int group_nr = AmoebaNr[ax][ay];
7327   boolean done = FALSE;
7328
7329 #ifdef DEBUG
7330   if (group_nr == 0)
7331   {
7332     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7333     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7334     return;
7335   }
7336 #endif
7337
7338   SCAN_PLAYFIELD(x, y)
7339   {
7340     if (AmoebaNr[x][y] == group_nr &&
7341         (Feld[x][y] == EL_AMOEBA_DEAD ||
7342          Feld[x][y] == EL_BD_AMOEBA ||
7343          Feld[x][y] == EL_AMOEBA_GROWING))
7344     {
7345       AmoebaNr[x][y] = 0;
7346       Feld[x][y] = new_element;
7347       InitField(x, y, FALSE);
7348       DrawLevelField(x, y);
7349       done = TRUE;
7350     }
7351   }
7352
7353   if (done)
7354     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7355                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7356                             SND_BD_AMOEBA_TURNING_TO_GEM));
7357 }
7358
7359 void AmoebeWaechst(int x, int y)
7360 {
7361   static unsigned long sound_delay = 0;
7362   static unsigned long sound_delay_value = 0;
7363
7364   if (!MovDelay[x][y])          /* start new growing cycle */
7365   {
7366     MovDelay[x][y] = 7;
7367
7368     if (DelayReached(&sound_delay, sound_delay_value))
7369     {
7370       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7371       sound_delay_value = 30;
7372     }
7373   }
7374
7375   if (MovDelay[x][y])           /* wait some time before growing bigger */
7376   {
7377     MovDelay[x][y]--;
7378     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7379     {
7380       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7381                                            6 - MovDelay[x][y]);
7382
7383       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7384     }
7385
7386     if (!MovDelay[x][y])
7387     {
7388       Feld[x][y] = Store[x][y];
7389       Store[x][y] = 0;
7390       DrawLevelField(x, y);
7391     }
7392   }
7393 }
7394
7395 void AmoebaDisappearing(int x, int y)
7396 {
7397   static unsigned long sound_delay = 0;
7398   static unsigned long sound_delay_value = 0;
7399
7400   if (!MovDelay[x][y])          /* start new shrinking cycle */
7401   {
7402     MovDelay[x][y] = 7;
7403
7404     if (DelayReached(&sound_delay, sound_delay_value))
7405       sound_delay_value = 30;
7406   }
7407
7408   if (MovDelay[x][y])           /* wait some time before shrinking */
7409   {
7410     MovDelay[x][y]--;
7411     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7412     {
7413       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7414                                            6 - MovDelay[x][y]);
7415
7416       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7417     }
7418
7419     if (!MovDelay[x][y])
7420     {
7421       Feld[x][y] = EL_EMPTY;
7422       DrawLevelField(x, y);
7423
7424       /* don't let mole enter this field in this cycle;
7425          (give priority to objects falling to this field from above) */
7426       Stop[x][y] = TRUE;
7427     }
7428   }
7429 }
7430
7431 void AmoebeAbleger(int ax, int ay)
7432 {
7433   int i;
7434   int element = Feld[ax][ay];
7435   int graphic = el2img(element);
7436   int newax = ax, neway = ay;
7437   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7438   static int xy[4][2] =
7439   {
7440     { 0, -1 },
7441     { -1, 0 },
7442     { +1, 0 },
7443     { 0, +1 }
7444   };
7445
7446   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7447   {
7448     Feld[ax][ay] = EL_AMOEBA_DEAD;
7449     DrawLevelField(ax, ay);
7450     return;
7451   }
7452
7453   if (IS_ANIMATED(graphic))
7454     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7455
7456   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7457     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7458
7459   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7460   {
7461     MovDelay[ax][ay]--;
7462     if (MovDelay[ax][ay])
7463       return;
7464   }
7465
7466   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7467   {
7468     int start = RND(4);
7469     int x = ax + xy[start][0];
7470     int y = ay + xy[start][1];
7471
7472     if (!IN_LEV_FIELD(x, y))
7473       return;
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     }
7483
7484     if (newax == ax && neway == ay)
7485       return;
7486   }
7487   else                          /* normal or "filled" (BD style) amoeba */
7488   {
7489     int start = RND(4);
7490     boolean waiting_for_player = FALSE;
7491
7492     for (i = 0; i < NUM_DIRECTIONS; i++)
7493     {
7494       int j = (start + i) % 4;
7495       int x = ax + xy[j][0];
7496       int y = ay + xy[j][1];
7497
7498       if (!IN_LEV_FIELD(x, y))
7499         continue;
7500
7501       if (IS_FREE(x, y) ||
7502           CAN_GROW_INTO(Feld[x][y]) ||
7503           Feld[x][y] == EL_QUICKSAND_EMPTY ||
7504           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7505       {
7506         newax = x;
7507         neway = y;
7508         break;
7509       }
7510       else if (IS_PLAYER(x, y))
7511         waiting_for_player = TRUE;
7512     }
7513
7514     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7515     {
7516       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7517       {
7518         Feld[ax][ay] = EL_AMOEBA_DEAD;
7519         DrawLevelField(ax, ay);
7520         AmoebaCnt[AmoebaNr[ax][ay]]--;
7521
7522         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7523         {
7524           if (element == EL_AMOEBA_FULL)
7525             AmoebeUmwandeln(ax, ay);
7526           else if (element == EL_BD_AMOEBA)
7527             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7528         }
7529       }
7530       return;
7531     }
7532     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7533     {
7534       /* amoeba gets larger by growing in some direction */
7535
7536       int new_group_nr = AmoebaNr[ax][ay];
7537
7538 #ifdef DEBUG
7539   if (new_group_nr == 0)
7540   {
7541     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7542     printf("AmoebeAbleger(): This should never happen!\n");
7543     return;
7544   }
7545 #endif
7546
7547       AmoebaNr[newax][neway] = new_group_nr;
7548       AmoebaCnt[new_group_nr]++;
7549       AmoebaCnt2[new_group_nr]++;
7550
7551       /* if amoeba touches other amoeba(s) after growing, unify them */
7552       AmoebenVereinigen(newax, neway);
7553
7554       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7555       {
7556         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7557         return;
7558       }
7559     }
7560   }
7561
7562   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7563       (neway == lev_fieldy - 1 && newax != ax))
7564   {
7565     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7566     Store[newax][neway] = element;
7567   }
7568   else if (neway == ay || element == EL_EMC_DRIPPER)
7569   {
7570     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7571
7572     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7573   }
7574   else
7575   {
7576     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7577     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7578     Store[ax][ay] = EL_AMOEBA_DROP;
7579     ContinueMoving(ax, ay);
7580     return;
7581   }
7582
7583   DrawLevelField(newax, neway);
7584 }
7585
7586 void Life(int ax, int ay)
7587 {
7588   int x1, y1, x2, y2;
7589   int life_time = 40;
7590   int element = Feld[ax][ay];
7591   int graphic = el2img(element);
7592   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7593                          level.biomaze);
7594   boolean changed = FALSE;
7595
7596   if (IS_ANIMATED(graphic))
7597     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7598
7599   if (Stop[ax][ay])
7600     return;
7601
7602   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7603     MovDelay[ax][ay] = life_time;
7604
7605   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7606   {
7607     MovDelay[ax][ay]--;
7608     if (MovDelay[ax][ay])
7609       return;
7610   }
7611
7612   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7613   {
7614     int xx = ax+x1, yy = ay+y1;
7615     int nachbarn = 0;
7616
7617     if (!IN_LEV_FIELD(xx, yy))
7618       continue;
7619
7620     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7621     {
7622       int x = xx+x2, y = yy+y2;
7623
7624       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7625         continue;
7626
7627       if (((Feld[x][y] == element ||
7628             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7629            !Stop[x][y]) ||
7630           (IS_FREE(x, y) && Stop[x][y]))
7631         nachbarn++;
7632     }
7633
7634     if (xx == ax && yy == ay)           /* field in the middle */
7635     {
7636       if (nachbarn < life_parameter[0] ||
7637           nachbarn > life_parameter[1])
7638       {
7639         Feld[xx][yy] = EL_EMPTY;
7640         if (!Stop[xx][yy])
7641           DrawLevelField(xx, yy);
7642         Stop[xx][yy] = TRUE;
7643         changed = TRUE;
7644       }
7645     }
7646     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7647     {                                   /* free border field */
7648       if (nachbarn >= life_parameter[2] &&
7649           nachbarn <= life_parameter[3])
7650       {
7651         Feld[xx][yy] = element;
7652         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7653         if (!Stop[xx][yy])
7654           DrawLevelField(xx, yy);
7655         Stop[xx][yy] = TRUE;
7656         changed = TRUE;
7657       }
7658     }
7659   }
7660
7661   if (changed)
7662     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7663                    SND_GAME_OF_LIFE_GROWING);
7664 }
7665
7666 static void InitRobotWheel(int x, int y)
7667 {
7668   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7669 }
7670
7671 static void RunRobotWheel(int x, int y)
7672 {
7673   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7674 }
7675
7676 static void StopRobotWheel(int x, int y)
7677 {
7678   if (ZX == x && ZY == y)
7679     ZX = ZY = -1;
7680 }
7681
7682 static void InitTimegateWheel(int x, int y)
7683 {
7684   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7685 }
7686
7687 static void RunTimegateWheel(int x, int y)
7688 {
7689   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7690 }
7691
7692 static void InitMagicBallDelay(int x, int y)
7693 {
7694 #if 1
7695   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7696 #else
7697   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7698 #endif
7699 }
7700
7701 static void ActivateMagicBall(int bx, int by)
7702 {
7703   int x, y;
7704
7705   if (level.ball_random)
7706   {
7707     int pos_border = RND(8);    /* select one of the eight border elements */
7708     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7709     int xx = pos_content % 3;
7710     int yy = pos_content / 3;
7711
7712     x = bx - 1 + xx;
7713     y = by - 1 + yy;
7714
7715     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7716       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7717   }
7718   else
7719   {
7720     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7721     {
7722       int xx = x - bx + 1;
7723       int yy = y - by + 1;
7724
7725       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7726         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7727     }
7728   }
7729
7730   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7731 }
7732
7733 void CheckExit(int x, int y)
7734 {
7735   if (local_player->gems_still_needed > 0 ||
7736       local_player->sokobanfields_still_needed > 0 ||
7737       local_player->lights_still_needed > 0)
7738   {
7739     int element = Feld[x][y];
7740     int graphic = el2img(element);
7741
7742     if (IS_ANIMATED(graphic))
7743       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7744
7745     return;
7746   }
7747
7748   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7749     return;
7750
7751   Feld[x][y] = EL_EXIT_OPENING;
7752
7753   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7754 }
7755
7756 void CheckExitEM(int x, int y)
7757 {
7758   if (local_player->gems_still_needed > 0 ||
7759       local_player->sokobanfields_still_needed > 0 ||
7760       local_player->lights_still_needed > 0)
7761   {
7762     int element = Feld[x][y];
7763     int graphic = el2img(element);
7764
7765     if (IS_ANIMATED(graphic))
7766       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7767
7768     return;
7769   }
7770
7771   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7772     return;
7773
7774   Feld[x][y] = EL_EM_EXIT_OPENING;
7775
7776   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7777 }
7778
7779 void CheckExitSteel(int x, int y)
7780 {
7781   if (local_player->gems_still_needed > 0 ||
7782       local_player->sokobanfields_still_needed > 0 ||
7783       local_player->lights_still_needed > 0)
7784   {
7785     int element = Feld[x][y];
7786     int graphic = el2img(element);
7787
7788     if (IS_ANIMATED(graphic))
7789       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7790
7791     return;
7792   }
7793
7794   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7795     return;
7796
7797   Feld[x][y] = EL_STEEL_EXIT_OPENING;
7798
7799   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7800 }
7801
7802 void CheckExitSteelEM(int x, int y)
7803 {
7804   if (local_player->gems_still_needed > 0 ||
7805       local_player->sokobanfields_still_needed > 0 ||
7806       local_player->lights_still_needed > 0)
7807   {
7808     int element = Feld[x][y];
7809     int graphic = el2img(element);
7810
7811     if (IS_ANIMATED(graphic))
7812       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7813
7814     return;
7815   }
7816
7817   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7818     return;
7819
7820   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7821
7822   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7823 }
7824
7825 void CheckExitSP(int x, int y)
7826 {
7827   if (local_player->gems_still_needed > 0)
7828   {
7829     int element = Feld[x][y];
7830     int graphic = el2img(element);
7831
7832     if (IS_ANIMATED(graphic))
7833       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7834
7835     return;
7836   }
7837
7838   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7839     return;
7840
7841   Feld[x][y] = EL_SP_EXIT_OPENING;
7842
7843   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7844 }
7845
7846 static void CloseAllOpenTimegates()
7847 {
7848   int x, y;
7849
7850   SCAN_PLAYFIELD(x, y)
7851   {
7852     int element = Feld[x][y];
7853
7854     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7855     {
7856       Feld[x][y] = EL_TIMEGATE_CLOSING;
7857
7858       PlayLevelSoundAction(x, y, ACTION_CLOSING);
7859     }
7860   }
7861 }
7862
7863 void DrawTwinkleOnField(int x, int y)
7864 {
7865   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7866     return;
7867
7868   if (Feld[x][y] == EL_BD_DIAMOND)
7869     return;
7870
7871   if (MovDelay[x][y] == 0)      /* next animation frame */
7872     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7873
7874   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7875   {
7876     MovDelay[x][y]--;
7877
7878     if (setup.direct_draw && MovDelay[x][y])
7879       SetDrawtoField(DRAW_BUFFERED);
7880
7881     DrawLevelElementAnimation(x, y, Feld[x][y]);
7882
7883     if (MovDelay[x][y] != 0)
7884     {
7885       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7886                                            10 - MovDelay[x][y]);
7887
7888       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7889
7890       if (setup.direct_draw)
7891       {
7892         int dest_x, dest_y;
7893
7894         dest_x = FX + SCREENX(x) * TILEX;
7895         dest_y = FY + SCREENY(y) * TILEY;
7896
7897         BlitBitmap(drawto_field, window,
7898                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7899         SetDrawtoField(DRAW_DIRECT);
7900       }
7901     }
7902   }
7903 }
7904
7905 void MauerWaechst(int x, int y)
7906 {
7907   int delay = 6;
7908
7909   if (!MovDelay[x][y])          /* next animation frame */
7910     MovDelay[x][y] = 3 * delay;
7911
7912   if (MovDelay[x][y])           /* wait some time before next frame */
7913   {
7914     MovDelay[x][y]--;
7915
7916     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7917     {
7918       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7919       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7920
7921       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7922     }
7923
7924     if (!MovDelay[x][y])
7925     {
7926       if (MovDir[x][y] == MV_LEFT)
7927       {
7928         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7929           DrawLevelField(x - 1, y);
7930       }
7931       else if (MovDir[x][y] == MV_RIGHT)
7932       {
7933         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7934           DrawLevelField(x + 1, y);
7935       }
7936       else if (MovDir[x][y] == MV_UP)
7937       {
7938         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7939           DrawLevelField(x, y - 1);
7940       }
7941       else
7942       {
7943         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7944           DrawLevelField(x, y + 1);
7945       }
7946
7947       Feld[x][y] = Store[x][y];
7948       Store[x][y] = 0;
7949       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7950       DrawLevelField(x, y);
7951     }
7952   }
7953 }
7954
7955 void MauerAbleger(int ax, int ay)
7956 {
7957   int element = Feld[ax][ay];
7958   int graphic = el2img(element);
7959   boolean oben_frei = FALSE, unten_frei = FALSE;
7960   boolean links_frei = FALSE, rechts_frei = FALSE;
7961   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7962   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7963   boolean new_wall = FALSE;
7964
7965   if (IS_ANIMATED(graphic))
7966     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7967
7968   if (!MovDelay[ax][ay])        /* start building new wall */
7969     MovDelay[ax][ay] = 6;
7970
7971   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7972   {
7973     MovDelay[ax][ay]--;
7974     if (MovDelay[ax][ay])
7975       return;
7976   }
7977
7978   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7979     oben_frei = TRUE;
7980   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7981     unten_frei = TRUE;
7982   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7983     links_frei = TRUE;
7984   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7985     rechts_frei = TRUE;
7986
7987   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7988       element == EL_EXPANDABLE_WALL_ANY)
7989   {
7990     if (oben_frei)
7991     {
7992       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7993       Store[ax][ay-1] = element;
7994       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7995       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7996         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7997                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7998       new_wall = TRUE;
7999     }
8000     if (unten_frei)
8001     {
8002       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8003       Store[ax][ay+1] = element;
8004       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8005       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8006         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8007                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8008       new_wall = TRUE;
8009     }
8010   }
8011
8012   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8013       element == EL_EXPANDABLE_WALL_ANY ||
8014       element == EL_EXPANDABLE_WALL ||
8015       element == EL_BD_EXPANDABLE_WALL)
8016   {
8017     if (links_frei)
8018     {
8019       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8020       Store[ax-1][ay] = element;
8021       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8022       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8023         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8024                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8025       new_wall = TRUE;
8026     }
8027
8028     if (rechts_frei)
8029     {
8030       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8031       Store[ax+1][ay] = element;
8032       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8033       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8034         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8035                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8036       new_wall = TRUE;
8037     }
8038   }
8039
8040   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8041     DrawLevelField(ax, ay);
8042
8043   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8044     oben_massiv = TRUE;
8045   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8046     unten_massiv = TRUE;
8047   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8048     links_massiv = TRUE;
8049   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8050     rechts_massiv = TRUE;
8051
8052   if (((oben_massiv && unten_massiv) ||
8053        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8054        element == EL_EXPANDABLE_WALL) &&
8055       ((links_massiv && rechts_massiv) ||
8056        element == EL_EXPANDABLE_WALL_VERTICAL))
8057     Feld[ax][ay] = EL_WALL;
8058
8059   if (new_wall)
8060     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8061 }
8062
8063 void MauerAblegerStahl(int ax, int ay)
8064 {
8065   int element = Feld[ax][ay];
8066   int graphic = el2img(element);
8067   boolean oben_frei = FALSE, unten_frei = FALSE;
8068   boolean links_frei = FALSE, rechts_frei = FALSE;
8069   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8070   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8071   boolean new_wall = FALSE;
8072
8073   if (IS_ANIMATED(graphic))
8074     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8075
8076   if (!MovDelay[ax][ay])        /* start building new wall */
8077     MovDelay[ax][ay] = 6;
8078
8079   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8080   {
8081     MovDelay[ax][ay]--;
8082     if (MovDelay[ax][ay])
8083       return;
8084   }
8085
8086   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8087     oben_frei = TRUE;
8088   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8089     unten_frei = TRUE;
8090   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8091     links_frei = TRUE;
8092   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8093     rechts_frei = TRUE;
8094
8095   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8096       element == EL_EXPANDABLE_STEELWALL_ANY)
8097   {
8098     if (oben_frei)
8099     {
8100       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8101       Store[ax][ay-1] = element;
8102       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8103       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8104         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8105                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8106       new_wall = TRUE;
8107     }
8108     if (unten_frei)
8109     {
8110       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8111       Store[ax][ay+1] = element;
8112       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8113       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8114         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8115                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8116       new_wall = TRUE;
8117     }
8118   }
8119
8120   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8121       element == EL_EXPANDABLE_STEELWALL_ANY)
8122   {
8123     if (links_frei)
8124     {
8125       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8126       Store[ax-1][ay] = element;
8127       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8128       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8129         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8130                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8131       new_wall = TRUE;
8132     }
8133
8134     if (rechts_frei)
8135     {
8136       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8137       Store[ax+1][ay] = element;
8138       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8139       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8140         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8141                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8142       new_wall = TRUE;
8143     }
8144   }
8145
8146   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8147     oben_massiv = TRUE;
8148   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8149     unten_massiv = TRUE;
8150   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8151     links_massiv = TRUE;
8152   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8153     rechts_massiv = TRUE;
8154
8155   if (((oben_massiv && unten_massiv) ||
8156        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8157       ((links_massiv && rechts_massiv) ||
8158        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8159     Feld[ax][ay] = EL_WALL;
8160
8161   if (new_wall)
8162     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8163 }
8164
8165 void CheckForDragon(int x, int y)
8166 {
8167   int i, j;
8168   boolean dragon_found = FALSE;
8169   static int xy[4][2] =
8170   {
8171     { 0, -1 },
8172     { -1, 0 },
8173     { +1, 0 },
8174     { 0, +1 }
8175   };
8176
8177   for (i = 0; i < NUM_DIRECTIONS; i++)
8178   {
8179     for (j = 0; j < 4; j++)
8180     {
8181       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8182
8183       if (IN_LEV_FIELD(xx, yy) &&
8184           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8185       {
8186         if (Feld[xx][yy] == EL_DRAGON)
8187           dragon_found = TRUE;
8188       }
8189       else
8190         break;
8191     }
8192   }
8193
8194   if (!dragon_found)
8195   {
8196     for (i = 0; i < NUM_DIRECTIONS; i++)
8197     {
8198       for (j = 0; j < 3; j++)
8199       {
8200         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8201   
8202         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8203         {
8204           Feld[xx][yy] = EL_EMPTY;
8205           DrawLevelField(xx, yy);
8206         }
8207         else
8208           break;
8209       }
8210     }
8211   }
8212 }
8213
8214 static void InitBuggyBase(int x, int y)
8215 {
8216   int element = Feld[x][y];
8217   int activating_delay = FRAMES_PER_SECOND / 4;
8218
8219   ChangeDelay[x][y] =
8220     (element == EL_SP_BUGGY_BASE ?
8221      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8222      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8223      activating_delay :
8224      element == EL_SP_BUGGY_BASE_ACTIVE ?
8225      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8226 }
8227
8228 static void WarnBuggyBase(int x, int y)
8229 {
8230   int i;
8231   static int xy[4][2] =
8232   {
8233     { 0, -1 },
8234     { -1, 0 },
8235     { +1, 0 },
8236     { 0, +1 }
8237   };
8238
8239   for (i = 0; i < NUM_DIRECTIONS; i++)
8240   {
8241     int xx = x + xy[i][0];
8242     int yy = y + xy[i][1];
8243
8244     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8245     {
8246       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8247
8248       break;
8249     }
8250   }
8251 }
8252
8253 static void InitTrap(int x, int y)
8254 {
8255   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8256 }
8257
8258 static void ActivateTrap(int x, int y)
8259 {
8260   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8261 }
8262
8263 static void ChangeActiveTrap(int x, int y)
8264 {
8265   int graphic = IMG_TRAP_ACTIVE;
8266
8267   /* if new animation frame was drawn, correct crumbled sand border */
8268   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8269     DrawLevelFieldCrumbledSand(x, y);
8270 }
8271
8272 static int getSpecialActionElement(int element, int number, int base_element)
8273 {
8274   return (element != EL_EMPTY ? element :
8275           number != -1 ? base_element + number - 1 :
8276           EL_EMPTY);
8277 }
8278
8279 static int getModifiedActionNumber(int value_old, int operator, int operand,
8280                                    int value_min, int value_max)
8281 {
8282   int value_new = (operator == CA_MODE_SET      ? operand :
8283                    operator == CA_MODE_ADD      ? value_old + operand :
8284                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8285                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8286                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8287                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8288                    value_old);
8289
8290   return (value_new < value_min ? value_min :
8291           value_new > value_max ? value_max :
8292           value_new);
8293 }
8294
8295 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8296 {
8297   struct ElementInfo *ei = &element_info[element];
8298   struct ElementChangeInfo *change = &ei->change_page[page];
8299   int target_element = change->target_element;
8300   int action_type = change->action_type;
8301   int action_mode = change->action_mode;
8302   int action_arg = change->action_arg;
8303   int i;
8304
8305   if (!change->has_action)
8306     return;
8307
8308   /* ---------- determine action paramater values -------------------------- */
8309
8310   int level_time_value =
8311     (level.time > 0 ? TimeLeft :
8312      TimePlayed);
8313
8314   int action_arg_element =
8315     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8316      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8317      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8318      EL_EMPTY);
8319
8320   int action_arg_direction =
8321     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8322      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8323      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8324      change->actual_trigger_side :
8325      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8326      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8327      MV_NONE);
8328
8329   int action_arg_number_min =
8330     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8331      CA_ARG_MIN);
8332
8333   int action_arg_number_max =
8334     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8335      action_type == CA_SET_LEVEL_GEMS ? 999 :
8336      action_type == CA_SET_LEVEL_TIME ? 9999 :
8337      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8338      action_type == CA_SET_CE_VALUE ? 9999 :
8339      action_type == CA_SET_CE_SCORE ? 9999 :
8340      CA_ARG_MAX);
8341
8342   int action_arg_number_reset =
8343     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8344      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8345      action_type == CA_SET_LEVEL_TIME ? level.time :
8346      action_type == CA_SET_LEVEL_SCORE ? 0 :
8347 #if USE_NEW_CUSTOM_VALUE
8348      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8349 #else
8350      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8351 #endif
8352      action_type == CA_SET_CE_SCORE ? 0 :
8353      0);
8354
8355   int action_arg_number =
8356     (action_arg <= CA_ARG_MAX ? action_arg :
8357      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8358      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8359      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8360      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8361      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8362      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8363 #if USE_NEW_CUSTOM_VALUE
8364      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8365 #else
8366      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8367 #endif
8368      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8369      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8370      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8371      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8372      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8373      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8374      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8375      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8376      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8377      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8378      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8379      -1);
8380
8381   int action_arg_number_old =
8382     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8383      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8384      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8385      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8386      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8387      0);
8388
8389   int action_arg_number_new =
8390     getModifiedActionNumber(action_arg_number_old,
8391                             action_mode, action_arg_number,
8392                             action_arg_number_min, action_arg_number_max);
8393
8394   int trigger_player_bits =
8395     (change->actual_trigger_player >= EL_PLAYER_1 &&
8396      change->actual_trigger_player <= EL_PLAYER_4 ?
8397      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8398      PLAYER_BITS_ANY);
8399
8400   int action_arg_player_bits =
8401     (action_arg >= CA_ARG_PLAYER_1 &&
8402      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8403      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8404      PLAYER_BITS_ANY);
8405
8406   /* ---------- execute action  -------------------------------------------- */
8407
8408   switch (action_type)
8409   {
8410     case CA_NO_ACTION:
8411     {
8412       return;
8413     }
8414
8415     /* ---------- level actions  ------------------------------------------- */
8416
8417     case CA_RESTART_LEVEL:
8418     {
8419       game.restart_level = TRUE;
8420
8421       break;
8422     }
8423
8424     case CA_SHOW_ENVELOPE:
8425     {
8426       int element = getSpecialActionElement(action_arg_element,
8427                                             action_arg_number, EL_ENVELOPE_1);
8428
8429       if (IS_ENVELOPE(element))
8430         local_player->show_envelope = element;
8431
8432       break;
8433     }
8434
8435     case CA_SET_LEVEL_TIME:
8436     {
8437       if (level.time > 0)       /* only modify limited time value */
8438       {
8439         TimeLeft = action_arg_number_new;
8440
8441         DrawGameValue_Time(TimeLeft);
8442
8443         if (!TimeLeft && setup.time_limit)
8444           for (i = 0; i < MAX_PLAYERS; i++)
8445             KillPlayer(&stored_player[i]);
8446       }
8447
8448       break;
8449     }
8450
8451     case CA_SET_LEVEL_SCORE:
8452     {
8453       local_player->score = action_arg_number_new;
8454
8455       DrawGameValue_Score(local_player->score);
8456
8457       break;
8458     }
8459
8460     case CA_SET_LEVEL_GEMS:
8461     {
8462       local_player->gems_still_needed = action_arg_number_new;
8463
8464       DrawGameValue_Emeralds(local_player->gems_still_needed);
8465
8466       break;
8467     }
8468
8469 #if !USE_PLAYER_GRAVITY
8470     case CA_SET_LEVEL_GRAVITY:
8471     {
8472       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
8473                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
8474                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8475                       game.gravity);
8476       break;
8477     }
8478 #endif
8479
8480     case CA_SET_LEVEL_WIND:
8481     {
8482       game.wind_direction = action_arg_direction;
8483
8484       break;
8485     }
8486
8487     /* ---------- player actions  ------------------------------------------ */
8488
8489     case CA_MOVE_PLAYER:
8490     {
8491       /* automatically move to the next field in specified direction */
8492       for (i = 0; i < MAX_PLAYERS; i++)
8493         if (trigger_player_bits & (1 << i))
8494           stored_player[i].programmed_action = action_arg_direction;
8495
8496       break;
8497     }
8498
8499     case CA_EXIT_PLAYER:
8500     {
8501       for (i = 0; i < MAX_PLAYERS; i++)
8502         if (action_arg_player_bits & (1 << i))
8503           PlayerWins(&stored_player[i]);
8504
8505       break;
8506     }
8507
8508     case CA_KILL_PLAYER:
8509     {
8510       for (i = 0; i < MAX_PLAYERS; i++)
8511         if (action_arg_player_bits & (1 << i))
8512           KillPlayer(&stored_player[i]);
8513
8514       break;
8515     }
8516
8517     case CA_SET_PLAYER_KEYS:
8518     {
8519       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8520       int element = getSpecialActionElement(action_arg_element,
8521                                             action_arg_number, EL_KEY_1);
8522
8523       if (IS_KEY(element))
8524       {
8525         for (i = 0; i < MAX_PLAYERS; i++)
8526         {
8527           if (trigger_player_bits & (1 << i))
8528           {
8529             stored_player[i].key[KEY_NR(element)] = key_state;
8530
8531             DrawGameDoorValues();
8532           }
8533         }
8534       }
8535
8536       break;
8537     }
8538
8539     case CA_SET_PLAYER_SPEED:
8540     {
8541       for (i = 0; i < MAX_PLAYERS; i++)
8542       {
8543         if (trigger_player_bits & (1 << i))
8544         {
8545           int move_stepsize = TILEX / stored_player[i].move_delay_value;
8546
8547           if (action_arg == CA_ARG_SPEED_FASTER &&
8548               stored_player[i].cannot_move)
8549           {
8550             action_arg_number = STEPSIZE_VERY_SLOW;
8551           }
8552           else if (action_arg == CA_ARG_SPEED_SLOWER ||
8553                    action_arg == CA_ARG_SPEED_FASTER)
8554           {
8555             action_arg_number = 2;
8556             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8557                            CA_MODE_MULTIPLY);
8558           }
8559           else if (action_arg == CA_ARG_NUMBER_RESET)
8560           {
8561             action_arg_number = level.initial_player_stepsize[i];
8562           }
8563
8564           move_stepsize =
8565             getModifiedActionNumber(move_stepsize,
8566                                     action_mode,
8567                                     action_arg_number,
8568                                     action_arg_number_min,
8569                                     action_arg_number_max);
8570
8571           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8572         }
8573       }
8574
8575       break;
8576     }
8577
8578     case CA_SET_PLAYER_SHIELD:
8579     {
8580       for (i = 0; i < MAX_PLAYERS; i++)
8581       {
8582         if (trigger_player_bits & (1 << i))
8583         {
8584           if (action_arg == CA_ARG_SHIELD_OFF)
8585           {
8586             stored_player[i].shield_normal_time_left = 0;
8587             stored_player[i].shield_deadly_time_left = 0;
8588           }
8589           else if (action_arg == CA_ARG_SHIELD_NORMAL)
8590           {
8591             stored_player[i].shield_normal_time_left = 999999;
8592           }
8593           else if (action_arg == CA_ARG_SHIELD_DEADLY)
8594           {
8595             stored_player[i].shield_normal_time_left = 999999;
8596             stored_player[i].shield_deadly_time_left = 999999;
8597           }
8598         }
8599       }
8600
8601       break;
8602     }
8603
8604 #if USE_PLAYER_GRAVITY
8605     case CA_SET_PLAYER_GRAVITY:
8606     {
8607       for (i = 0; i < MAX_PLAYERS; i++)
8608       {
8609         if (trigger_player_bits & (1 << i))
8610         {
8611           stored_player[i].gravity =
8612             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
8613              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
8614              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8615              stored_player[i].gravity);
8616         }
8617       }
8618
8619       break;
8620     }
8621 #endif
8622
8623     case CA_SET_PLAYER_ARTWORK:
8624     {
8625       for (i = 0; i < MAX_PLAYERS; i++)
8626       {
8627         if (trigger_player_bits & (1 << i))
8628         {
8629           int artwork_element = action_arg_element;
8630
8631           if (action_arg == CA_ARG_ELEMENT_RESET)
8632             artwork_element =
8633               (level.use_artwork_element[i] ? level.artwork_element[i] :
8634                stored_player[i].element_nr);
8635
8636 #if USE_GFX_RESET_PLAYER_ARTWORK
8637           if (stored_player[i].artwork_element != artwork_element)
8638             stored_player[i].Frame = 0;
8639 #endif
8640
8641           stored_player[i].artwork_element = artwork_element;
8642
8643           SetPlayerWaiting(&stored_player[i], FALSE);
8644
8645           /* set number of special actions for bored and sleeping animation */
8646           stored_player[i].num_special_action_bored =
8647             get_num_special_action(artwork_element,
8648                                    ACTION_BORING_1, ACTION_BORING_LAST);
8649           stored_player[i].num_special_action_sleeping =
8650             get_num_special_action(artwork_element,
8651                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8652         }
8653       }
8654
8655       break;
8656     }
8657
8658     /* ---------- CE actions  ---------------------------------------------- */
8659
8660     case CA_SET_CE_VALUE:
8661     {
8662 #if USE_NEW_CUSTOM_VALUE
8663       int last_ce_value = CustomValue[x][y];
8664
8665       CustomValue[x][y] = action_arg_number_new;
8666
8667       if (CustomValue[x][y] != last_ce_value)
8668       {
8669         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8670         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8671
8672         if (CustomValue[x][y] == 0)
8673         {
8674           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8675           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8676         }
8677       }
8678 #endif
8679
8680       break;
8681     }
8682
8683     case CA_SET_CE_SCORE:
8684     {
8685 #if USE_NEW_CUSTOM_VALUE
8686       int last_ce_score = ei->collect_score;
8687
8688       ei->collect_score = action_arg_number_new;
8689
8690       if (ei->collect_score != last_ce_score)
8691       {
8692         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8693         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8694
8695         if (ei->collect_score == 0)
8696         {
8697           int xx, yy;
8698
8699           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8700           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8701
8702           /*
8703             This is a very special case that seems to be a mixture between
8704             CheckElementChange() and CheckTriggeredElementChange(): while
8705             the first one only affects single elements that are triggered
8706             directly, the second one affects multiple elements in the playfield
8707             that are triggered indirectly by another element. This is a third
8708             case: Changing the CE score always affects multiple identical CEs,
8709             so every affected CE must be checked, not only the single CE for
8710             which the CE score was changed in the first place (as every instance
8711             of that CE shares the same CE score, and therefore also can change)!
8712           */
8713           SCAN_PLAYFIELD(xx, yy)
8714           {
8715             if (Feld[xx][yy] == element)
8716               CheckElementChange(xx, yy, element, EL_UNDEFINED,
8717                                  CE_SCORE_GETS_ZERO);
8718           }
8719         }
8720       }
8721 #endif
8722
8723       break;
8724     }
8725
8726     /* ---------- engine actions  ------------------------------------------ */
8727
8728     case CA_SET_ENGINE_SCAN_MODE:
8729     {
8730       InitPlayfieldScanMode(action_arg);
8731
8732       break;
8733     }
8734
8735     default:
8736       break;
8737   }
8738 }
8739
8740 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8741 {
8742   int old_element = Feld[x][y];
8743   int new_element = get_element_from_group_element(element);
8744   int previous_move_direction = MovDir[x][y];
8745 #if USE_NEW_CUSTOM_VALUE
8746   int last_ce_value = CustomValue[x][y];
8747 #endif
8748   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8749   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8750   boolean add_player_onto_element = (new_element_is_player &&
8751 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8752                                      /* this breaks SnakeBite when a snake is
8753                                         halfway through a door that closes */
8754                                      /* NOW FIXED AT LEVEL INIT IN files.c */
8755                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
8756 #endif
8757                                      IS_WALKABLE(old_element));
8758
8759 #if 0
8760   /* check if element under the player changes from accessible to unaccessible
8761      (needed for special case of dropping element which then changes) */
8762   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8763       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8764   {
8765     Bang(x, y);
8766
8767     return;
8768   }
8769 #endif
8770
8771   if (!add_player_onto_element)
8772   {
8773     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8774       RemoveMovingField(x, y);
8775     else
8776       RemoveField(x, y);
8777
8778     Feld[x][y] = new_element;
8779
8780 #if !USE_GFX_RESET_GFX_ANIMATION
8781     ResetGfxAnimation(x, y);
8782     ResetRandomAnimationValue(x, y);
8783 #endif
8784
8785     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8786       MovDir[x][y] = previous_move_direction;
8787
8788 #if USE_NEW_CUSTOM_VALUE
8789     if (element_info[new_element].use_last_ce_value)
8790       CustomValue[x][y] = last_ce_value;
8791 #endif
8792
8793     InitField_WithBug1(x, y, FALSE);
8794
8795     new_element = Feld[x][y];   /* element may have changed */
8796
8797 #if USE_GFX_RESET_GFX_ANIMATION
8798     ResetGfxAnimation(x, y);
8799     ResetRandomAnimationValue(x, y);
8800 #endif
8801
8802     DrawLevelField(x, y);
8803
8804     if (GFX_CRUMBLED(new_element))
8805       DrawLevelFieldCrumbledSandNeighbours(x, y);
8806   }
8807
8808 #if 1
8809   /* check if element under the player changes from accessible to unaccessible
8810      (needed for special case of dropping element which then changes) */
8811   /* (must be checked after creating new element for walkable group elements) */
8812 #if USE_FIX_KILLED_BY_NON_WALKABLE
8813   if (IS_PLAYER(x, y) && !player_explosion_protected &&
8814       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8815   {
8816     Bang(x, y);
8817
8818     return;
8819   }
8820 #else
8821   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8822       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8823   {
8824     Bang(x, y);
8825
8826     return;
8827   }
8828 #endif
8829 #endif
8830
8831   /* "ChangeCount" not set yet to allow "entered by player" change one time */
8832   if (new_element_is_player)
8833     RelocatePlayer(x, y, new_element);
8834
8835   if (is_change)
8836     ChangeCount[x][y]++;        /* count number of changes in the same frame */
8837
8838   TestIfBadThingTouchesPlayer(x, y);
8839   TestIfPlayerTouchesCustomElement(x, y);
8840   TestIfElementTouchesCustomElement(x, y);
8841 }
8842
8843 static void CreateField(int x, int y, int element)
8844 {
8845   CreateFieldExt(x, y, element, FALSE);
8846 }
8847
8848 static void CreateElementFromChange(int x, int y, int element)
8849 {
8850   element = GET_VALID_RUNTIME_ELEMENT(element);
8851
8852 #if USE_STOP_CHANGED_ELEMENTS
8853   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8854   {
8855     int old_element = Feld[x][y];
8856
8857     /* prevent changed element from moving in same engine frame
8858        unless both old and new element can either fall or move */
8859     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8860         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8861       Stop[x][y] = TRUE;
8862   }
8863 #endif
8864
8865   CreateFieldExt(x, y, element, TRUE);
8866 }
8867
8868 static boolean ChangeElement(int x, int y, int element, int page)
8869 {
8870   struct ElementInfo *ei = &element_info[element];
8871   struct ElementChangeInfo *change = &ei->change_page[page];
8872   int ce_value = CustomValue[x][y];
8873   int ce_score = ei->collect_score;
8874   int target_element;
8875   int old_element = Feld[x][y];
8876
8877   /* always use default change event to prevent running into a loop */
8878   if (ChangeEvent[x][y] == -1)
8879     ChangeEvent[x][y] = CE_DELAY;
8880
8881   if (ChangeEvent[x][y] == CE_DELAY)
8882   {
8883     /* reset actual trigger element, trigger player and action element */
8884     change->actual_trigger_element = EL_EMPTY;
8885     change->actual_trigger_player = EL_PLAYER_1;
8886     change->actual_trigger_side = CH_SIDE_NONE;
8887     change->actual_trigger_ce_value = 0;
8888     change->actual_trigger_ce_score = 0;
8889   }
8890
8891   /* do not change elements more than a specified maximum number of changes */
8892   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8893     return FALSE;
8894
8895   ChangeCount[x][y]++;          /* count number of changes in the same frame */
8896
8897   if (change->explode)
8898   {
8899     Bang(x, y);
8900
8901     return TRUE;
8902   }
8903
8904   if (change->use_target_content)
8905   {
8906     boolean complete_replace = TRUE;
8907     boolean can_replace[3][3];
8908     int xx, yy;
8909
8910     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8911     {
8912       boolean is_empty;
8913       boolean is_walkable;
8914       boolean is_diggable;
8915       boolean is_collectible;
8916       boolean is_removable;
8917       boolean is_destructible;
8918       int ex = x + xx - 1;
8919       int ey = y + yy - 1;
8920       int content_element = change->target_content.e[xx][yy];
8921       int e;
8922
8923       can_replace[xx][yy] = TRUE;
8924
8925       if (ex == x && ey == y)   /* do not check changing element itself */
8926         continue;
8927
8928       if (content_element == EL_EMPTY_SPACE)
8929       {
8930         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8931
8932         continue;
8933       }
8934
8935       if (!IN_LEV_FIELD(ex, ey))
8936       {
8937         can_replace[xx][yy] = FALSE;
8938         complete_replace = FALSE;
8939
8940         continue;
8941       }
8942
8943       e = Feld[ex][ey];
8944
8945       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8946         e = MovingOrBlocked2Element(ex, ey);
8947
8948       is_empty = (IS_FREE(ex, ey) ||
8949                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8950
8951       is_walkable     = (is_empty || IS_WALKABLE(e));
8952       is_diggable     = (is_empty || IS_DIGGABLE(e));
8953       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8954       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8955       is_removable    = (is_diggable || is_collectible);
8956
8957       can_replace[xx][yy] =
8958         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
8959           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
8960           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
8961           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
8962           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
8963           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8964          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8965
8966       if (!can_replace[xx][yy])
8967         complete_replace = FALSE;
8968     }
8969
8970     if (!change->only_if_complete || complete_replace)
8971     {
8972       boolean something_has_changed = FALSE;
8973
8974       if (change->only_if_complete && change->use_random_replace &&
8975           RND(100) < change->random_percentage)
8976         return FALSE;
8977
8978       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8979       {
8980         int ex = x + xx - 1;
8981         int ey = y + yy - 1;
8982         int content_element;
8983
8984         if (can_replace[xx][yy] && (!change->use_random_replace ||
8985                                     RND(100) < change->random_percentage))
8986         {
8987           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8988             RemoveMovingField(ex, ey);
8989
8990           ChangeEvent[ex][ey] = ChangeEvent[x][y];
8991
8992           content_element = change->target_content.e[xx][yy];
8993           target_element = GET_TARGET_ELEMENT(element, content_element, change,
8994                                               ce_value, ce_score);
8995
8996           CreateElementFromChange(ex, ey, target_element);
8997
8998           something_has_changed = TRUE;
8999
9000           /* for symmetry reasons, freeze newly created border elements */
9001           if (ex != x || ey != y)
9002             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9003         }
9004       }
9005
9006       if (something_has_changed)
9007       {
9008         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9009         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9010       }
9011     }
9012   }
9013   else
9014   {
9015     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9016                                         ce_value, ce_score);
9017
9018     if (element == EL_DIAGONAL_GROWING ||
9019         element == EL_DIAGONAL_SHRINKING)
9020     {
9021       target_element = Store[x][y];
9022
9023       Store[x][y] = EL_EMPTY;
9024     }
9025
9026     CreateElementFromChange(x, y, target_element);
9027
9028     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9029     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9030   }
9031
9032   /* this uses direct change before indirect change */
9033   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9034
9035   return TRUE;
9036 }
9037
9038 #if USE_NEW_DELAYED_ACTION
9039
9040 static void HandleElementChange(int x, int y, int page)
9041 {
9042   int element = MovingOrBlocked2Element(x, y);
9043   struct ElementInfo *ei = &element_info[element];
9044   struct ElementChangeInfo *change = &ei->change_page[page];
9045
9046 #ifdef DEBUG
9047   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9048       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9049   {
9050     printf("\n\n");
9051     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9052            x, y, element, element_info[element].token_name);
9053     printf("HandleElementChange(): This should never happen!\n");
9054     printf("\n\n");
9055   }
9056 #endif
9057
9058   /* this can happen with classic bombs on walkable, changing elements */
9059   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9060   {
9061 #if 0
9062     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9063       ChangeDelay[x][y] = 0;
9064 #endif
9065
9066     return;
9067   }
9068
9069   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9070   {
9071     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9072
9073     if (change->can_change)
9074     {
9075       ResetGfxAnimation(x, y);
9076       ResetRandomAnimationValue(x, y);
9077
9078       if (change->pre_change_function)
9079         change->pre_change_function(x, y);
9080     }
9081   }
9082
9083   ChangeDelay[x][y]--;
9084
9085   if (ChangeDelay[x][y] != 0)           /* continue element change */
9086   {
9087     if (change->can_change)
9088     {
9089       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9090
9091       if (IS_ANIMATED(graphic))
9092         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9093
9094       if (change->change_function)
9095         change->change_function(x, y);
9096     }
9097   }
9098   else                                  /* finish element change */
9099   {
9100     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9101     {
9102       page = ChangePage[x][y];
9103       ChangePage[x][y] = -1;
9104
9105       change = &ei->change_page[page];
9106     }
9107
9108     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9109     {
9110       ChangeDelay[x][y] = 1;            /* try change after next move step */
9111       ChangePage[x][y] = page;          /* remember page to use for change */
9112
9113       return;
9114     }
9115
9116     if (change->can_change)
9117     {
9118       if (ChangeElement(x, y, element, page))
9119       {
9120         if (change->post_change_function)
9121           change->post_change_function(x, y);
9122       }
9123     }
9124
9125     if (change->has_action)
9126       ExecuteCustomElementAction(x, y, element, page);
9127   }
9128 }
9129
9130 #else
9131
9132 static void HandleElementChange(int x, int y, int page)
9133 {
9134   int element = MovingOrBlocked2Element(x, y);
9135   struct ElementInfo *ei = &element_info[element];
9136   struct ElementChangeInfo *change = &ei->change_page[page];
9137
9138 #ifdef DEBUG
9139   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9140   {
9141     printf("\n\n");
9142     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9143            x, y, element, element_info[element].token_name);
9144     printf("HandleElementChange(): This should never happen!\n");
9145     printf("\n\n");
9146   }
9147 #endif
9148
9149   /* this can happen with classic bombs on walkable, changing elements */
9150   if (!CAN_CHANGE(element))
9151   {
9152 #if 0
9153     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9154       ChangeDelay[x][y] = 0;
9155 #endif
9156
9157     return;
9158   }
9159
9160   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9161   {
9162     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9163
9164     ResetGfxAnimation(x, y);
9165     ResetRandomAnimationValue(x, y);
9166
9167     if (change->pre_change_function)
9168       change->pre_change_function(x, y);
9169   }
9170
9171   ChangeDelay[x][y]--;
9172
9173   if (ChangeDelay[x][y] != 0)           /* continue element change */
9174   {
9175     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9176
9177     if (IS_ANIMATED(graphic))
9178       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9179
9180     if (change->change_function)
9181       change->change_function(x, y);
9182   }
9183   else                                  /* finish element change */
9184   {
9185     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9186     {
9187       page = ChangePage[x][y];
9188       ChangePage[x][y] = -1;
9189
9190       change = &ei->change_page[page];
9191     }
9192
9193     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9194     {
9195       ChangeDelay[x][y] = 1;            /* try change after next move step */
9196       ChangePage[x][y] = page;          /* remember page to use for change */
9197
9198       return;
9199     }
9200
9201     if (ChangeElement(x, y, element, page))
9202     {
9203       if (change->post_change_function)
9204         change->post_change_function(x, y);
9205     }
9206   }
9207 }
9208
9209 #endif
9210
9211 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9212                                               int trigger_element,
9213                                               int trigger_event,
9214                                               int trigger_player,
9215                                               int trigger_side,
9216                                               int trigger_page)
9217 {
9218   boolean change_done_any = FALSE;
9219   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9220   int i;
9221
9222   if (!(trigger_events[trigger_element][trigger_event]))
9223     return FALSE;
9224
9225 #if 0
9226   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9227          trigger_event, recursion_loop_depth, recursion_loop_detected,
9228          recursion_loop_element, EL_NAME(recursion_loop_element));
9229 #endif
9230
9231   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9232
9233   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9234   {
9235     int element = EL_CUSTOM_START + i;
9236     boolean change_done = FALSE;
9237     int p;
9238
9239     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9240         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9241       continue;
9242
9243     for (p = 0; p < element_info[element].num_change_pages; p++)
9244     {
9245       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9246
9247       if (change->can_change_or_has_action &&
9248           change->has_event[trigger_event] &&
9249           change->trigger_side & trigger_side &&
9250           change->trigger_player & trigger_player &&
9251           change->trigger_page & trigger_page_bits &&
9252           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9253       {
9254         change->actual_trigger_element = trigger_element;
9255         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9256         change->actual_trigger_side = trigger_side;
9257         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9258         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9259
9260         if ((change->can_change && !change_done) || change->has_action)
9261         {
9262           int x, y;
9263
9264           SCAN_PLAYFIELD(x, y)
9265           {
9266             if (Feld[x][y] == element)
9267             {
9268               if (change->can_change && !change_done)
9269               {
9270                 ChangeDelay[x][y] = 1;
9271                 ChangeEvent[x][y] = trigger_event;
9272
9273                 HandleElementChange(x, y, p);
9274               }
9275 #if USE_NEW_DELAYED_ACTION
9276               else if (change->has_action)
9277               {
9278                 ExecuteCustomElementAction(x, y, element, p);
9279                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9280               }
9281 #else
9282               if (change->has_action)
9283               {
9284                 ExecuteCustomElementAction(x, y, element, p);
9285                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9286               }
9287 #endif
9288             }
9289           }
9290
9291           if (change->can_change)
9292           {
9293             change_done = TRUE;
9294             change_done_any = TRUE;
9295           }
9296         }
9297       }
9298     }
9299   }
9300
9301   RECURSION_LOOP_DETECTION_END();
9302
9303   return change_done_any;
9304 }
9305
9306 static boolean CheckElementChangeExt(int x, int y,
9307                                      int element,
9308                                      int trigger_element,
9309                                      int trigger_event,
9310                                      int trigger_player,
9311                                      int trigger_side)
9312 {
9313   boolean change_done = FALSE;
9314   int p;
9315
9316   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9317       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9318     return FALSE;
9319
9320   if (Feld[x][y] == EL_BLOCKED)
9321   {
9322     Blocked2Moving(x, y, &x, &y);
9323     element = Feld[x][y];
9324   }
9325
9326 #if 0
9327   /* check if element has already changed */
9328   if (Feld[x][y] != element)
9329     return FALSE;
9330 #else
9331   /* check if element has already changed or is about to change after moving */
9332   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9333        Feld[x][y] != element) ||
9334
9335       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9336        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9337         ChangePage[x][y] != -1)))
9338     return FALSE;
9339 #endif
9340
9341 #if 0
9342   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9343          trigger_event, recursion_loop_depth, recursion_loop_detected,
9344          recursion_loop_element, EL_NAME(recursion_loop_element));
9345 #endif
9346
9347   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9348
9349   for (p = 0; p < element_info[element].num_change_pages; p++)
9350   {
9351     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9352
9353     /* check trigger element for all events where the element that is checked
9354        for changing interacts with a directly adjacent element -- this is
9355        different to element changes that affect other elements to change on the
9356        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9357     boolean check_trigger_element =
9358       (trigger_event == CE_TOUCHING_X ||
9359        trigger_event == CE_HITTING_X ||
9360        trigger_event == CE_HIT_BY_X ||
9361 #if 1
9362        /* this one was forgotten until 3.2.3 */
9363        trigger_event == CE_DIGGING_X);
9364 #endif
9365
9366     if (change->can_change_or_has_action &&
9367         change->has_event[trigger_event] &&
9368         change->trigger_side & trigger_side &&
9369         change->trigger_player & trigger_player &&
9370         (!check_trigger_element ||
9371          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9372     {
9373       change->actual_trigger_element = trigger_element;
9374       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9375       change->actual_trigger_side = trigger_side;
9376       change->actual_trigger_ce_value = CustomValue[x][y];
9377       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9378
9379       /* special case: trigger element not at (x,y) position for some events */
9380       if (check_trigger_element)
9381       {
9382         static struct
9383         {
9384           int dx, dy;
9385         } move_xy[] =
9386           {
9387             {  0,  0 },
9388             { -1,  0 },
9389             { +1,  0 },
9390             {  0,  0 },
9391             {  0, -1 },
9392             {  0,  0 }, { 0, 0 }, { 0, 0 },
9393             {  0, +1 }
9394           };
9395
9396         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9397         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9398
9399         change->actual_trigger_ce_value = CustomValue[xx][yy];
9400         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9401       }
9402
9403       if (change->can_change && !change_done)
9404       {
9405         ChangeDelay[x][y] = 1;
9406         ChangeEvent[x][y] = trigger_event;
9407
9408         HandleElementChange(x, y, p);
9409
9410         change_done = TRUE;
9411       }
9412 #if USE_NEW_DELAYED_ACTION
9413       else if (change->has_action)
9414       {
9415         ExecuteCustomElementAction(x, y, element, p);
9416         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9417       }
9418 #else
9419       if (change->has_action)
9420       {
9421         ExecuteCustomElementAction(x, y, element, p);
9422         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9423       }
9424 #endif
9425     }
9426   }
9427
9428   RECURSION_LOOP_DETECTION_END();
9429
9430   return change_done;
9431 }
9432
9433 static void PlayPlayerSound(struct PlayerInfo *player)
9434 {
9435   int jx = player->jx, jy = player->jy;
9436   int sound_element = player->artwork_element;
9437   int last_action = player->last_action_waiting;
9438   int action = player->action_waiting;
9439
9440   if (player->is_waiting)
9441   {
9442     if (action != last_action)
9443       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9444     else
9445       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9446   }
9447   else
9448   {
9449     if (action != last_action)
9450       StopSound(element_info[sound_element].sound[last_action]);
9451
9452     if (last_action == ACTION_SLEEPING)
9453       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9454   }
9455 }
9456
9457 static void PlayAllPlayersSound()
9458 {
9459   int i;
9460
9461   for (i = 0; i < MAX_PLAYERS; i++)
9462     if (stored_player[i].active)
9463       PlayPlayerSound(&stored_player[i]);
9464 }
9465
9466 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9467 {
9468   boolean last_waiting = player->is_waiting;
9469   int move_dir = player->MovDir;
9470
9471   player->dir_waiting = move_dir;
9472   player->last_action_waiting = player->action_waiting;
9473
9474   if (is_waiting)
9475   {
9476     if (!last_waiting)          /* not waiting -> waiting */
9477     {
9478       player->is_waiting = TRUE;
9479
9480       player->frame_counter_bored =
9481         FrameCounter +
9482         game.player_boring_delay_fixed +
9483         GetSimpleRandom(game.player_boring_delay_random);
9484       player->frame_counter_sleeping =
9485         FrameCounter +
9486         game.player_sleeping_delay_fixed +
9487         GetSimpleRandom(game.player_sleeping_delay_random);
9488
9489       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9490     }
9491
9492     if (game.player_sleeping_delay_fixed +
9493         game.player_sleeping_delay_random > 0 &&
9494         player->anim_delay_counter == 0 &&
9495         player->post_delay_counter == 0 &&
9496         FrameCounter >= player->frame_counter_sleeping)
9497       player->is_sleeping = TRUE;
9498     else if (game.player_boring_delay_fixed +
9499              game.player_boring_delay_random > 0 &&
9500              FrameCounter >= player->frame_counter_bored)
9501       player->is_bored = TRUE;
9502
9503     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9504                               player->is_bored ? ACTION_BORING :
9505                               ACTION_WAITING);
9506
9507     if (player->is_sleeping && player->use_murphy)
9508     {
9509       /* special case for sleeping Murphy when leaning against non-free tile */
9510
9511       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9512           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9513            !IS_MOVING(player->jx - 1, player->jy)))
9514         move_dir = MV_LEFT;
9515       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9516                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9517                 !IS_MOVING(player->jx + 1, player->jy)))
9518         move_dir = MV_RIGHT;
9519       else
9520         player->is_sleeping = FALSE;
9521
9522       player->dir_waiting = move_dir;
9523     }
9524
9525     if (player->is_sleeping)
9526     {
9527       if (player->num_special_action_sleeping > 0)
9528       {
9529         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9530         {
9531           int last_special_action = player->special_action_sleeping;
9532           int num_special_action = player->num_special_action_sleeping;
9533           int special_action =
9534             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9535              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9536              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9537              last_special_action + 1 : ACTION_SLEEPING);
9538           int special_graphic =
9539             el_act_dir2img(player->artwork_element, special_action, move_dir);
9540
9541           player->anim_delay_counter =
9542             graphic_info[special_graphic].anim_delay_fixed +
9543             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9544           player->post_delay_counter =
9545             graphic_info[special_graphic].post_delay_fixed +
9546             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9547
9548           player->special_action_sleeping = special_action;
9549         }
9550
9551         if (player->anim_delay_counter > 0)
9552         {
9553           player->action_waiting = player->special_action_sleeping;
9554           player->anim_delay_counter--;
9555         }
9556         else if (player->post_delay_counter > 0)
9557         {
9558           player->post_delay_counter--;
9559         }
9560       }
9561     }
9562     else if (player->is_bored)
9563     {
9564       if (player->num_special_action_bored > 0)
9565       {
9566         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9567         {
9568           int special_action =
9569             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9570           int special_graphic =
9571             el_act_dir2img(player->artwork_element, special_action, move_dir);
9572
9573           player->anim_delay_counter =
9574             graphic_info[special_graphic].anim_delay_fixed +
9575             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9576           player->post_delay_counter =
9577             graphic_info[special_graphic].post_delay_fixed +
9578             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9579
9580           player->special_action_bored = special_action;
9581         }
9582
9583         if (player->anim_delay_counter > 0)
9584         {
9585           player->action_waiting = player->special_action_bored;
9586           player->anim_delay_counter--;
9587         }
9588         else if (player->post_delay_counter > 0)
9589         {
9590           player->post_delay_counter--;
9591         }
9592       }
9593     }
9594   }
9595   else if (last_waiting)        /* waiting -> not waiting */
9596   {
9597     player->is_waiting = FALSE;
9598     player->is_bored = FALSE;
9599     player->is_sleeping = FALSE;
9600
9601     player->frame_counter_bored = -1;
9602     player->frame_counter_sleeping = -1;
9603
9604     player->anim_delay_counter = 0;
9605     player->post_delay_counter = 0;
9606
9607     player->dir_waiting = player->MovDir;
9608     player->action_waiting = ACTION_DEFAULT;
9609
9610     player->special_action_bored = ACTION_DEFAULT;
9611     player->special_action_sleeping = ACTION_DEFAULT;
9612   }
9613 }
9614
9615 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9616 {
9617   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9618   int left      = player_action & JOY_LEFT;
9619   int right     = player_action & JOY_RIGHT;
9620   int up        = player_action & JOY_UP;
9621   int down      = player_action & JOY_DOWN;
9622   int button1   = player_action & JOY_BUTTON_1;
9623   int button2   = player_action & JOY_BUTTON_2;
9624   int dx        = (left ? -1 : right ? 1 : 0);
9625   int dy        = (up   ? -1 : down  ? 1 : 0);
9626
9627   if (!player->active || tape.pausing)
9628     return 0;
9629
9630   if (player_action)
9631   {
9632     if (button1)
9633       snapped = SnapField(player, dx, dy);
9634     else
9635     {
9636       if (button2)
9637         dropped = DropElement(player);
9638
9639       moved = MovePlayer(player, dx, dy);
9640     }
9641
9642     if (tape.single_step && tape.recording && !tape.pausing)
9643     {
9644       if (button1 || (dropped && !moved))
9645       {
9646         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9647         SnapField(player, 0, 0);                /* stop snapping */
9648       }
9649     }
9650
9651     SetPlayerWaiting(player, FALSE);
9652
9653     return player_action;
9654   }
9655   else
9656   {
9657     /* no actions for this player (no input at player's configured device) */
9658
9659     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9660     SnapField(player, 0, 0);
9661     CheckGravityMovementWhenNotMoving(player);
9662
9663     if (player->MovPos == 0)
9664       SetPlayerWaiting(player, TRUE);
9665
9666     if (player->MovPos == 0)    /* needed for tape.playing */
9667       player->is_moving = FALSE;
9668
9669     player->is_dropping = FALSE;
9670     player->is_dropping_pressed = FALSE;
9671     player->drop_pressed_delay = 0;
9672
9673     return 0;
9674   }
9675 }
9676
9677 static void CheckLevelTime()
9678 {
9679   int i;
9680
9681   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9682   {
9683     if (level.native_em_level->lev->home == 0)  /* all players at home */
9684     {
9685       PlayerWins(local_player);
9686
9687       AllPlayersGone = TRUE;
9688
9689       level.native_em_level->lev->home = -1;
9690     }
9691
9692     if (level.native_em_level->ply[0]->alive == 0 &&
9693         level.native_em_level->ply[1]->alive == 0 &&
9694         level.native_em_level->ply[2]->alive == 0 &&
9695         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9696       AllPlayersGone = TRUE;
9697   }
9698
9699   if (TimeFrames >= FRAMES_PER_SECOND)
9700   {
9701     TimeFrames = 0;
9702     TapeTime++;
9703
9704     for (i = 0; i < MAX_PLAYERS; i++)
9705     {
9706       struct PlayerInfo *player = &stored_player[i];
9707
9708       if (SHIELD_ON(player))
9709       {
9710         player->shield_normal_time_left--;
9711
9712         if (player->shield_deadly_time_left > 0)
9713           player->shield_deadly_time_left--;
9714       }
9715     }
9716
9717     if (!local_player->LevelSolved && !level.use_step_counter)
9718     {
9719       TimePlayed++;
9720
9721       if (TimeLeft > 0)
9722       {
9723         TimeLeft--;
9724
9725         if (TimeLeft <= 10 && setup.time_limit)
9726           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9727
9728         DrawGameValue_Time(TimeLeft);
9729
9730         if (!TimeLeft && setup.time_limit)
9731         {
9732           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9733             level.native_em_level->lev->killed_out_of_time = TRUE;
9734           else
9735             for (i = 0; i < MAX_PLAYERS; i++)
9736               KillPlayer(&stored_player[i]);
9737         }
9738       }
9739       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9740         DrawGameValue_Time(TimePlayed);
9741
9742       level.native_em_level->lev->time =
9743         (level.time == 0 ? TimePlayed : TimeLeft);
9744     }
9745
9746     if (tape.recording || tape.playing)
9747       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9748   }
9749 }
9750
9751 void AdvanceFrameAndPlayerCounters(int player_nr)
9752 {
9753   int i;
9754
9755   /* advance frame counters (global frame counter and time frame counter) */
9756   FrameCounter++;
9757   TimeFrames++;
9758
9759   /* advance player counters (counters for move delay, move animation etc.) */
9760   for (i = 0; i < MAX_PLAYERS; i++)
9761   {
9762     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9763     int move_delay_value = stored_player[i].move_delay_value;
9764     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9765
9766     if (!advance_player_counters)       /* not all players may be affected */
9767       continue;
9768
9769 #if USE_NEW_PLAYER_ANIM
9770     if (move_frames == 0)       /* less than one move per game frame */
9771     {
9772       int stepsize = TILEX / move_delay_value;
9773       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9774       int count = (stored_player[i].is_moving ?
9775                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9776
9777       if (count % delay == 0)
9778         move_frames = 1;
9779     }
9780 #endif
9781
9782     stored_player[i].Frame += move_frames;
9783
9784     if (stored_player[i].MovPos != 0)
9785       stored_player[i].StepFrame += move_frames;
9786
9787     if (stored_player[i].move_delay > 0)
9788       stored_player[i].move_delay--;
9789
9790     /* due to bugs in previous versions, counter must count up, not down */
9791     if (stored_player[i].push_delay != -1)
9792       stored_player[i].push_delay++;
9793
9794     if (stored_player[i].drop_delay > 0)
9795       stored_player[i].drop_delay--;
9796
9797     if (stored_player[i].is_dropping_pressed)
9798       stored_player[i].drop_pressed_delay++;
9799   }
9800 }
9801
9802 void StartGameActions(boolean init_network_game, boolean record_tape,
9803                       long random_seed)
9804 {
9805   unsigned long new_random_seed = InitRND(random_seed);
9806
9807   if (record_tape)
9808     TapeStartRecording(new_random_seed);
9809
9810 #if defined(NETWORK_AVALIABLE)
9811   if (init_network_game)
9812   {
9813     SendToServer_StartPlaying();
9814
9815     return;
9816   }
9817 #endif
9818
9819   InitGame();
9820 }
9821
9822 void GameActions()
9823 {
9824   static unsigned long game_frame_delay = 0;
9825   unsigned long game_frame_delay_value;
9826   byte *recorded_player_action;
9827   byte summarized_player_action = 0;
9828   byte tape_action[MAX_PLAYERS];
9829   int i;
9830
9831   /* detect endless loops, caused by custom element programming */
9832   if (recursion_loop_detected && recursion_loop_depth == 0)
9833   {
9834     char *message = getStringCat3("Internal Error ! Element ",
9835                                   EL_NAME(recursion_loop_element),
9836                                   " caused endless loop ! Quit the game ?");
9837
9838     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9839           EL_NAME(recursion_loop_element));
9840
9841     RequestQuitGameExt(FALSE, level_editor_test_game, message);
9842
9843     recursion_loop_detected = FALSE;    /* if game should be continued */
9844
9845     free(message);
9846
9847     return;
9848   }
9849
9850   if (game.restart_level)
9851     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9852
9853   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9854   {
9855     if (level.native_em_level->lev->home == 0)  /* all players at home */
9856     {
9857       PlayerWins(local_player);
9858
9859       AllPlayersGone = TRUE;
9860
9861       level.native_em_level->lev->home = -1;
9862     }
9863
9864     if (level.native_em_level->ply[0]->alive == 0 &&
9865         level.native_em_level->ply[1]->alive == 0 &&
9866         level.native_em_level->ply[2]->alive == 0 &&
9867         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9868       AllPlayersGone = TRUE;
9869   }
9870
9871   if (local_player->LevelSolved)
9872     GameWon();
9873
9874   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9875     TapeStop();
9876
9877   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9878     return;
9879
9880   game_frame_delay_value =
9881     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9882
9883   if (tape.playing && tape.warp_forward && !tape.pausing)
9884     game_frame_delay_value = 0;
9885
9886   /* ---------- main game synchronization point ---------- */
9887
9888   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9889
9890   if (network_playing && !network_player_action_received)
9891   {
9892     /* try to get network player actions in time */
9893
9894 #if defined(NETWORK_AVALIABLE)
9895     /* last chance to get network player actions without main loop delay */
9896     HandleNetworking();
9897 #endif
9898
9899     /* game was quit by network peer */
9900     if (game_status != GAME_MODE_PLAYING)
9901       return;
9902
9903     if (!network_player_action_received)
9904       return;           /* failed to get network player actions in time */
9905
9906     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9907   }
9908
9909   if (tape.pausing)
9910     return;
9911
9912   /* at this point we know that we really continue executing the game */
9913
9914   network_player_action_received = FALSE;
9915
9916   /* when playing tape, read previously recorded player input from tape data */
9917   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9918
9919 #if 1
9920   /* TapePlayAction() may return NULL when toggling to "pause before death" */
9921   if (tape.pausing)
9922     return;
9923 #endif
9924
9925   if (tape.set_centered_player)
9926   {
9927     game.centered_player_nr_next = tape.centered_player_nr_next;
9928     game.set_centered_player = TRUE;
9929   }
9930
9931   for (i = 0; i < MAX_PLAYERS; i++)
9932   {
9933     summarized_player_action |= stored_player[i].action;
9934
9935     if (!network_playing)
9936       stored_player[i].effective_action = stored_player[i].action;
9937   }
9938
9939 #if defined(NETWORK_AVALIABLE)
9940   if (network_playing)
9941     SendToServer_MovePlayer(summarized_player_action);
9942 #endif
9943
9944   if (!options.network && !setup.team_mode)
9945     local_player->effective_action = summarized_player_action;
9946
9947   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9948   {
9949     for (i = 0; i < MAX_PLAYERS; i++)
9950       stored_player[i].effective_action =
9951         (i == game.centered_player_nr ? summarized_player_action : 0);
9952   }
9953
9954   if (recorded_player_action != NULL)
9955     for (i = 0; i < MAX_PLAYERS; i++)
9956       stored_player[i].effective_action = recorded_player_action[i];
9957
9958   for (i = 0; i < MAX_PLAYERS; i++)
9959   {
9960     tape_action[i] = stored_player[i].effective_action;
9961
9962     /* (this can only happen in the R'n'D game engine) */
9963     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9964       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9965   }
9966
9967   /* only record actions from input devices, but not programmed actions */
9968   if (tape.recording)
9969     TapeRecordAction(tape_action);
9970
9971   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9972   {
9973     GameActions_EM_Main();
9974   }
9975   else
9976   {
9977     GameActions_RND();
9978   }
9979 }
9980
9981 void GameActions_EM_Main()
9982 {
9983   byte effective_action[MAX_PLAYERS];
9984   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9985   int i;
9986
9987   for (i = 0; i < MAX_PLAYERS; i++)
9988     effective_action[i] = stored_player[i].effective_action;
9989
9990   GameActions_EM(effective_action, warp_mode);
9991
9992   CheckLevelTime();
9993
9994   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9995 }
9996
9997 void GameActions_RND()
9998 {
9999   int magic_wall_x = 0, magic_wall_y = 0;
10000   int i, x, y, element, graphic;
10001
10002   InitPlayfieldScanModeVars();
10003
10004 #if USE_ONE_MORE_CHANGE_PER_FRAME
10005   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10006   {
10007     SCAN_PLAYFIELD(x, y)
10008     {
10009       ChangeCount[x][y] = 0;
10010       ChangeEvent[x][y] = -1;
10011     }
10012   }
10013 #endif
10014
10015   if (game.set_centered_player)
10016   {
10017     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10018
10019     /* switching to "all players" only possible if all players fit to screen */
10020     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10021     {
10022       game.centered_player_nr_next = game.centered_player_nr;
10023       game.set_centered_player = FALSE;
10024     }
10025
10026     /* do not switch focus to non-existing (or non-active) player */
10027     if (game.centered_player_nr_next >= 0 &&
10028         !stored_player[game.centered_player_nr_next].active)
10029     {
10030       game.centered_player_nr_next = game.centered_player_nr;
10031       game.set_centered_player = FALSE;
10032     }
10033   }
10034
10035   if (game.set_centered_player &&
10036       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10037   {
10038     int sx, sy;
10039
10040     if (game.centered_player_nr_next == -1)
10041     {
10042       setScreenCenteredToAllPlayers(&sx, &sy);
10043     }
10044     else
10045     {
10046       sx = stored_player[game.centered_player_nr_next].jx;
10047       sy = stored_player[game.centered_player_nr_next].jy;
10048     }
10049
10050     game.centered_player_nr = game.centered_player_nr_next;
10051     game.set_centered_player = FALSE;
10052
10053     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10054     DrawGameDoorValues();
10055   }
10056
10057   for (i = 0; i < MAX_PLAYERS; i++)
10058   {
10059     int actual_player_action = stored_player[i].effective_action;
10060
10061 #if 1
10062     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10063        - rnd_equinox_tetrachloride 048
10064        - rnd_equinox_tetrachloride_ii 096
10065        - rnd_emanuel_schmieg 002
10066        - doctor_sloan_ww 001, 020
10067     */
10068     if (stored_player[i].MovPos == 0)
10069       CheckGravityMovement(&stored_player[i]);
10070 #endif
10071
10072     /* overwrite programmed action with tape action */
10073     if (stored_player[i].programmed_action)
10074       actual_player_action = stored_player[i].programmed_action;
10075
10076     PlayerActions(&stored_player[i], actual_player_action);
10077
10078     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10079   }
10080
10081   ScrollScreen(NULL, SCROLL_GO_ON);
10082
10083   /* for backwards compatibility, the following code emulates a fixed bug that
10084      occured when pushing elements (causing elements that just made their last
10085      pushing step to already (if possible) make their first falling step in the
10086      same game frame, which is bad); this code is also needed to use the famous
10087      "spring push bug" which is used in older levels and might be wanted to be
10088      used also in newer levels, but in this case the buggy pushing code is only
10089      affecting the "spring" element and no other elements */
10090
10091   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10092   {
10093     for (i = 0; i < MAX_PLAYERS; i++)
10094     {
10095       struct PlayerInfo *player = &stored_player[i];
10096       int x = player->jx;
10097       int y = player->jy;
10098
10099       if (player->active && player->is_pushing && player->is_moving &&
10100           IS_MOVING(x, y) &&
10101           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10102            Feld[x][y] == EL_SPRING))
10103       {
10104         ContinueMoving(x, y);
10105
10106         /* continue moving after pushing (this is actually a bug) */
10107         if (!IS_MOVING(x, y))
10108         {
10109           Stop[x][y] = FALSE;
10110         }
10111       }
10112     }
10113   }
10114
10115   SCAN_PLAYFIELD(x, y)
10116   {
10117     ChangeCount[x][y] = 0;
10118     ChangeEvent[x][y] = -1;
10119
10120     /* this must be handled before main playfield loop */
10121     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10122     {
10123       MovDelay[x][y]--;
10124       if (MovDelay[x][y] <= 0)
10125         RemoveField(x, y);
10126     }
10127
10128 #if USE_NEW_SNAP_DELAY
10129     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10130     {
10131       MovDelay[x][y]--;
10132       if (MovDelay[x][y] <= 0)
10133       {
10134         RemoveField(x, y);
10135         DrawLevelField(x, y);
10136
10137         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10138       }
10139     }
10140 #endif
10141
10142 #if DEBUG
10143     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10144     {
10145       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10146       printf("GameActions(): This should never happen!\n");
10147
10148       ChangePage[x][y] = -1;
10149     }
10150 #endif
10151
10152     Stop[x][y] = FALSE;
10153     if (WasJustMoving[x][y] > 0)
10154       WasJustMoving[x][y]--;
10155     if (WasJustFalling[x][y] > 0)
10156       WasJustFalling[x][y]--;
10157     if (CheckCollision[x][y] > 0)
10158       CheckCollision[x][y]--;
10159     if (CheckImpact[x][y] > 0)
10160       CheckImpact[x][y]--;
10161
10162     GfxFrame[x][y]++;
10163
10164     /* reset finished pushing action (not done in ContinueMoving() to allow
10165        continuous pushing animation for elements with zero push delay) */
10166     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10167     {
10168       ResetGfxAnimation(x, y);
10169       DrawLevelField(x, y);
10170     }
10171
10172 #if DEBUG
10173     if (IS_BLOCKED(x, y))
10174     {
10175       int oldx, oldy;
10176
10177       Blocked2Moving(x, y, &oldx, &oldy);
10178       if (!IS_MOVING(oldx, oldy))
10179       {
10180         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10181         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10182         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10183         printf("GameActions(): This should never happen!\n");
10184       }
10185     }
10186 #endif
10187   }
10188
10189   SCAN_PLAYFIELD(x, y)
10190   {
10191     element = Feld[x][y];
10192     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10193
10194     ResetGfxFrame(x, y, TRUE);
10195
10196     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10197         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10198       ResetRandomAnimationValue(x, y);
10199
10200     SetRandomAnimationValue(x, y);
10201
10202     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10203
10204     if (IS_INACTIVE(element))
10205     {
10206       if (IS_ANIMATED(graphic))
10207         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10208
10209       continue;
10210     }
10211
10212     /* this may take place after moving, so 'element' may have changed */
10213     if (IS_CHANGING(x, y) &&
10214         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10215     {
10216       int page = element_info[element].event_page_nr[CE_DELAY];
10217
10218 #if 1
10219       HandleElementChange(x, y, page);
10220 #else
10221       if (CAN_CHANGE(element))
10222         HandleElementChange(x, y, page);
10223
10224       if (HAS_ACTION(element))
10225         ExecuteCustomElementAction(x, y, element, page);
10226 #endif
10227
10228       element = Feld[x][y];
10229       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10230     }
10231
10232     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10233     {
10234       StartMoving(x, y);
10235
10236       element = Feld[x][y];
10237       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10238
10239       if (IS_ANIMATED(graphic) &&
10240           !IS_MOVING(x, y) &&
10241           !Stop[x][y])
10242         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10243
10244       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10245         DrawTwinkleOnField(x, y);
10246     }
10247     else if ((element == EL_ACID ||
10248               element == EL_EXIT_OPEN ||
10249               element == EL_EM_EXIT_OPEN ||
10250               element == EL_SP_EXIT_OPEN ||
10251               element == EL_STEEL_EXIT_OPEN ||
10252               element == EL_EM_STEEL_EXIT_OPEN ||
10253               element == EL_SP_TERMINAL ||
10254               element == EL_SP_TERMINAL_ACTIVE ||
10255               element == EL_EXTRA_TIME ||
10256               element == EL_SHIELD_NORMAL ||
10257               element == EL_SHIELD_DEADLY) &&
10258              IS_ANIMATED(graphic))
10259       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10260     else if (IS_MOVING(x, y))
10261       ContinueMoving(x, y);
10262     else if (IS_ACTIVE_BOMB(element))
10263       CheckDynamite(x, y);
10264     else if (element == EL_AMOEBA_GROWING)
10265       AmoebeWaechst(x, y);
10266     else if (element == EL_AMOEBA_SHRINKING)
10267       AmoebaDisappearing(x, y);
10268
10269 #if !USE_NEW_AMOEBA_CODE
10270     else if (IS_AMOEBALIVE(element))
10271       AmoebeAbleger(x, y);
10272 #endif
10273
10274     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10275       Life(x, y);
10276     else if (element == EL_EXIT_CLOSED)
10277       CheckExit(x, y);
10278     else if (element == EL_EM_EXIT_CLOSED)
10279       CheckExitEM(x, y);
10280     else if (element == EL_STEEL_EXIT_CLOSED)
10281       CheckExitSteel(x, y);
10282     else if (element == EL_EM_STEEL_EXIT_CLOSED)
10283       CheckExitSteelEM(x, y);
10284     else if (element == EL_SP_EXIT_CLOSED)
10285       CheckExitSP(x, y);
10286     else if (element == EL_EXPANDABLE_WALL_GROWING ||
10287              element == EL_EXPANDABLE_STEELWALL_GROWING)
10288       MauerWaechst(x, y);
10289     else if (element == EL_EXPANDABLE_WALL ||
10290              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10291              element == EL_EXPANDABLE_WALL_VERTICAL ||
10292              element == EL_EXPANDABLE_WALL_ANY ||
10293              element == EL_BD_EXPANDABLE_WALL)
10294       MauerAbleger(x, y);
10295     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10296              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10297              element == EL_EXPANDABLE_STEELWALL_ANY)
10298       MauerAblegerStahl(x, y);
10299     else if (element == EL_FLAMES)
10300       CheckForDragon(x, y);
10301     else if (element == EL_EXPLOSION)
10302       ; /* drawing of correct explosion animation is handled separately */
10303     else if (element == EL_ELEMENT_SNAPPING ||
10304              element == EL_DIAGONAL_SHRINKING ||
10305              element == EL_DIAGONAL_GROWING)
10306     {
10307       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10308
10309       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10310     }
10311     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10312       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10313
10314     if (IS_BELT_ACTIVE(element))
10315       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10316
10317     if (game.magic_wall_active)
10318     {
10319       int jx = local_player->jx, jy = local_player->jy;
10320
10321       /* play the element sound at the position nearest to the player */
10322       if ((element == EL_MAGIC_WALL_FULL ||
10323            element == EL_MAGIC_WALL_ACTIVE ||
10324            element == EL_MAGIC_WALL_EMPTYING ||
10325            element == EL_BD_MAGIC_WALL_FULL ||
10326            element == EL_BD_MAGIC_WALL_ACTIVE ||
10327            element == EL_BD_MAGIC_WALL_EMPTYING ||
10328            element == EL_DC_MAGIC_WALL_FULL ||
10329            element == EL_DC_MAGIC_WALL_ACTIVE ||
10330            element == EL_DC_MAGIC_WALL_EMPTYING) &&
10331           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10332       {
10333         magic_wall_x = x;
10334         magic_wall_y = y;
10335       }
10336     }
10337   }
10338
10339 #if USE_NEW_AMOEBA_CODE
10340   /* new experimental amoeba growth stuff */
10341   if (!(FrameCounter % 8))
10342   {
10343     static unsigned long random = 1684108901;
10344
10345     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10346     {
10347       x = RND(lev_fieldx);
10348       y = RND(lev_fieldy);
10349       element = Feld[x][y];
10350
10351       if (!IS_PLAYER(x,y) &&
10352           (element == EL_EMPTY ||
10353            CAN_GROW_INTO(element) ||
10354            element == EL_QUICKSAND_EMPTY ||
10355            element == EL_QUICKSAND_FAST_EMPTY ||
10356            element == EL_ACID_SPLASH_LEFT ||
10357            element == EL_ACID_SPLASH_RIGHT))
10358       {
10359         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10360             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10361             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10362             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10363           Feld[x][y] = EL_AMOEBA_DROP;
10364       }
10365
10366       random = random * 129 + 1;
10367     }
10368   }
10369 #endif
10370
10371 #if 0
10372   if (game.explosions_delayed)
10373 #endif
10374   {
10375     game.explosions_delayed = FALSE;
10376
10377     SCAN_PLAYFIELD(x, y)
10378     {
10379       element = Feld[x][y];
10380
10381       if (ExplodeField[x][y])
10382         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10383       else if (element == EL_EXPLOSION)
10384         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10385
10386       ExplodeField[x][y] = EX_TYPE_NONE;
10387     }
10388
10389     game.explosions_delayed = TRUE;
10390   }
10391
10392   if (game.magic_wall_active)
10393   {
10394     if (!(game.magic_wall_time_left % 4))
10395     {
10396       int element = Feld[magic_wall_x][magic_wall_y];
10397
10398       if (element == EL_BD_MAGIC_WALL_FULL ||
10399           element == EL_BD_MAGIC_WALL_ACTIVE ||
10400           element == EL_BD_MAGIC_WALL_EMPTYING)
10401         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10402       else if (element == EL_DC_MAGIC_WALL_FULL ||
10403                element == EL_DC_MAGIC_WALL_ACTIVE ||
10404                element == EL_DC_MAGIC_WALL_EMPTYING)
10405         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10406       else
10407         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10408     }
10409
10410     if (game.magic_wall_time_left > 0)
10411     {
10412       game.magic_wall_time_left--;
10413       if (!game.magic_wall_time_left)
10414       {
10415         SCAN_PLAYFIELD(x, y)
10416         {
10417           element = Feld[x][y];
10418
10419           if (element == EL_MAGIC_WALL_ACTIVE ||
10420               element == EL_MAGIC_WALL_FULL)
10421           {
10422             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10423             DrawLevelField(x, y);
10424           }
10425           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10426                    element == EL_BD_MAGIC_WALL_FULL)
10427           {
10428             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10429             DrawLevelField(x, y);
10430           }
10431           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10432                    element == EL_DC_MAGIC_WALL_FULL)
10433           {
10434             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10435             DrawLevelField(x, y);
10436           }
10437         }
10438
10439         game.magic_wall_active = FALSE;
10440       }
10441     }
10442   }
10443
10444   if (game.light_time_left > 0)
10445   {
10446     game.light_time_left--;
10447
10448     if (game.light_time_left == 0)
10449       RedrawAllLightSwitchesAndInvisibleElements();
10450   }
10451
10452   if (game.timegate_time_left > 0)
10453   {
10454     game.timegate_time_left--;
10455
10456     if (game.timegate_time_left == 0)
10457       CloseAllOpenTimegates();
10458   }
10459
10460   if (game.lenses_time_left > 0)
10461   {
10462     game.lenses_time_left--;
10463
10464     if (game.lenses_time_left == 0)
10465       RedrawAllInvisibleElementsForLenses();
10466   }
10467
10468   if (game.magnify_time_left > 0)
10469   {
10470     game.magnify_time_left--;
10471
10472     if (game.magnify_time_left == 0)
10473       RedrawAllInvisibleElementsForMagnifier();
10474   }
10475
10476   for (i = 0; i < MAX_PLAYERS; i++)
10477   {
10478     struct PlayerInfo *player = &stored_player[i];
10479
10480     if (SHIELD_ON(player))
10481     {
10482       if (player->shield_deadly_time_left)
10483         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10484       else if (player->shield_normal_time_left)
10485         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10486     }
10487   }
10488
10489   CheckLevelTime();
10490
10491   DrawAllPlayers();
10492   PlayAllPlayersSound();
10493
10494   if (options.debug)                    /* calculate frames per second */
10495   {
10496     static unsigned long fps_counter = 0;
10497     static int fps_frames = 0;
10498     unsigned long fps_delay_ms = Counter() - fps_counter;
10499
10500     fps_frames++;
10501
10502     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10503     {
10504       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10505
10506       fps_frames = 0;
10507       fps_counter = Counter();
10508     }
10509
10510     redraw_mask |= REDRAW_FPS;
10511   }
10512
10513   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10514
10515   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10516   {
10517     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10518
10519     local_player->show_envelope = 0;
10520   }
10521
10522   /* use random number generator in every frame to make it less predictable */
10523   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10524     RND(1);
10525 }
10526
10527 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10528 {
10529   int min_x = x, min_y = y, max_x = x, max_y = y;
10530   int i;
10531
10532   for (i = 0; i < MAX_PLAYERS; i++)
10533   {
10534     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10535
10536     if (!stored_player[i].active || &stored_player[i] == player)
10537       continue;
10538
10539     min_x = MIN(min_x, jx);
10540     min_y = MIN(min_y, jy);
10541     max_x = MAX(max_x, jx);
10542     max_y = MAX(max_y, jy);
10543   }
10544
10545   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10546 }
10547
10548 static boolean AllPlayersInVisibleScreen()
10549 {
10550   int i;
10551
10552   for (i = 0; i < MAX_PLAYERS; i++)
10553   {
10554     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10555
10556     if (!stored_player[i].active)
10557       continue;
10558
10559     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10560       return FALSE;
10561   }
10562
10563   return TRUE;
10564 }
10565
10566 void ScrollLevel(int dx, int dy)
10567 {
10568   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10569   int x, y;
10570
10571 #if 1
10572   BlitBitmap(drawto_field, bitmap_db_field2,
10573              FX + TILEX * (dx == -1) - softscroll_offset,
10574              FY + TILEY * (dy == -1) - softscroll_offset,
10575              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10576              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10577              FX + TILEX * (dx == 1) - softscroll_offset,
10578              FY + TILEY * (dy == 1) - softscroll_offset);
10579   BlitBitmap(bitmap_db_field2, drawto_field,
10580              FX + TILEX * (dx == 1) - softscroll_offset,
10581              FY + TILEY * (dy == 1) - softscroll_offset,
10582              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10583              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10584              FX + TILEX * (dx == 1) - softscroll_offset,
10585              FY + TILEY * (dy == 1) - softscroll_offset);
10586 #else
10587   BlitBitmap(drawto_field, drawto_field,
10588              FX + TILEX * (dx == -1) - softscroll_offset,
10589              FY + TILEY * (dy == -1) - softscroll_offset,
10590              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
10591              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
10592              FX + TILEX * (dx == 1) - softscroll_offset,
10593              FY + TILEY * (dy == 1) - softscroll_offset);
10594 #endif
10595
10596   if (dx)
10597   {
10598     x = (dx == 1 ? BX1 : BX2);
10599     for (y = BY1; y <= BY2; y++)
10600       DrawScreenField(x, y);
10601   }
10602
10603   if (dy)
10604   {
10605     y = (dy == 1 ? BY1 : BY2);
10606     for (x = BX1; x <= BX2; x++)
10607       DrawScreenField(x, y);
10608   }
10609
10610   redraw_mask |= REDRAW_FIELD;
10611 }
10612
10613 static boolean canFallDown(struct PlayerInfo *player)
10614 {
10615   int jx = player->jx, jy = player->jy;
10616
10617   return (IN_LEV_FIELD(jx, jy + 1) &&
10618           (IS_FREE(jx, jy + 1) ||
10619            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10620           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10621           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10622 }
10623
10624 static boolean canPassField(int x, int y, int move_dir)
10625 {
10626   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10627   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10628   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10629   int nextx = x + dx;
10630   int nexty = y + dy;
10631   int element = Feld[x][y];
10632
10633   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10634           !CAN_MOVE(element) &&
10635           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10636           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10637           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10638 }
10639
10640 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10641 {
10642   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10643   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10644   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10645   int newx = x + dx;
10646   int newy = y + dy;
10647
10648   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10649           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10650           (IS_DIGGABLE(Feld[newx][newy]) ||
10651            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10652            canPassField(newx, newy, move_dir)));
10653 }
10654
10655 static void CheckGravityMovement(struct PlayerInfo *player)
10656 {
10657 #if USE_PLAYER_GRAVITY
10658   if (player->gravity && !player->programmed_action)
10659 #else
10660   if (game.gravity && !player->programmed_action)
10661 #endif
10662   {
10663     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10664     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10665     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10666     int jx = player->jx, jy = player->jy;
10667     boolean player_is_moving_to_valid_field =
10668       (!player_is_snapping &&
10669        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10670         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10671     boolean player_can_fall_down = canFallDown(player);
10672
10673     if (player_can_fall_down &&
10674         !player_is_moving_to_valid_field)
10675       player->programmed_action = MV_DOWN;
10676   }
10677 }
10678
10679 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10680 {
10681   return CheckGravityMovement(player);
10682
10683 #if USE_PLAYER_GRAVITY
10684   if (player->gravity && !player->programmed_action)
10685 #else
10686   if (game.gravity && !player->programmed_action)
10687 #endif
10688   {
10689     int jx = player->jx, jy = player->jy;
10690     boolean field_under_player_is_free =
10691       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10692     boolean player_is_standing_on_valid_field =
10693       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10694        (IS_WALKABLE(Feld[jx][jy]) &&
10695         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10696
10697     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10698       player->programmed_action = MV_DOWN;
10699   }
10700 }
10701
10702 /*
10703   MovePlayerOneStep()
10704   -----------------------------------------------------------------------------
10705   dx, dy:               direction (non-diagonal) to try to move the player to
10706   real_dx, real_dy:     direction as read from input device (can be diagonal)
10707 */
10708
10709 boolean MovePlayerOneStep(struct PlayerInfo *player,
10710                           int dx, int dy, int real_dx, int real_dy)
10711 {
10712   int jx = player->jx, jy = player->jy;
10713   int new_jx = jx + dx, new_jy = jy + dy;
10714 #if !USE_FIXED_DONT_RUN_INTO
10715   int element;
10716 #endif
10717   int can_move;
10718   boolean player_can_move = !player->cannot_move;
10719
10720   if (!player->active || (!dx && !dy))
10721     return MP_NO_ACTION;
10722
10723   player->MovDir = (dx < 0 ? MV_LEFT :
10724                     dx > 0 ? MV_RIGHT :
10725                     dy < 0 ? MV_UP :
10726                     dy > 0 ? MV_DOWN :  MV_NONE);
10727
10728   if (!IN_LEV_FIELD(new_jx, new_jy))
10729     return MP_NO_ACTION;
10730
10731   if (!player_can_move)
10732   {
10733     if (player->MovPos == 0)
10734     {
10735       player->is_moving = FALSE;
10736       player->is_digging = FALSE;
10737       player->is_collecting = FALSE;
10738       player->is_snapping = FALSE;
10739       player->is_pushing = FALSE;
10740     }
10741   }
10742
10743 #if 1
10744   if (!options.network && game.centered_player_nr == -1 &&
10745       !AllPlayersInSight(player, new_jx, new_jy))
10746     return MP_NO_ACTION;
10747 #else
10748   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10749     return MP_NO_ACTION;
10750 #endif
10751
10752 #if !USE_FIXED_DONT_RUN_INTO
10753   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10754
10755   /* (moved to DigField()) */
10756   if (player_can_move && DONT_RUN_INTO(element))
10757   {
10758     if (element == EL_ACID && dx == 0 && dy == 1)
10759     {
10760       SplashAcid(new_jx, new_jy);
10761       Feld[jx][jy] = EL_PLAYER_1;
10762       InitMovingField(jx, jy, MV_DOWN);
10763       Store[jx][jy] = EL_ACID;
10764       ContinueMoving(jx, jy);
10765       BuryPlayer(player);
10766     }
10767     else
10768       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10769
10770     return MP_MOVING;
10771   }
10772 #endif
10773
10774   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10775   if (can_move != MP_MOVING)
10776     return can_move;
10777
10778   /* check if DigField() has caused relocation of the player */
10779   if (player->jx != jx || player->jy != jy)
10780     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10781
10782   StorePlayer[jx][jy] = 0;
10783   player->last_jx = jx;
10784   player->last_jy = jy;
10785   player->jx = new_jx;
10786   player->jy = new_jy;
10787   StorePlayer[new_jx][new_jy] = player->element_nr;
10788
10789   if (player->move_delay_value_next != -1)
10790   {
10791     player->move_delay_value = player->move_delay_value_next;
10792     player->move_delay_value_next = -1;
10793   }
10794
10795   player->MovPos =
10796     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10797
10798   player->step_counter++;
10799
10800   PlayerVisit[jx][jy] = FrameCounter;
10801
10802 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10803   player->is_moving = TRUE;
10804 #endif
10805
10806 #if 1
10807   /* should better be called in MovePlayer(), but this breaks some tapes */
10808   ScrollPlayer(player, SCROLL_INIT);
10809 #endif
10810
10811   return MP_MOVING;
10812 }
10813
10814 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10815 {
10816   int jx = player->jx, jy = player->jy;
10817   int old_jx = jx, old_jy = jy;
10818   int moved = MP_NO_ACTION;
10819
10820   if (!player->active)
10821     return FALSE;
10822
10823   if (!dx && !dy)
10824   {
10825     if (player->MovPos == 0)
10826     {
10827       player->is_moving = FALSE;
10828       player->is_digging = FALSE;
10829       player->is_collecting = FALSE;
10830       player->is_snapping = FALSE;
10831       player->is_pushing = FALSE;
10832     }
10833
10834     return FALSE;
10835   }
10836
10837   if (player->move_delay > 0)
10838     return FALSE;
10839
10840   player->move_delay = -1;              /* set to "uninitialized" value */
10841
10842   /* store if player is automatically moved to next field */
10843   player->is_auto_moving = (player->programmed_action != MV_NONE);
10844
10845   /* remove the last programmed player action */
10846   player->programmed_action = 0;
10847
10848   if (player->MovPos)
10849   {
10850     /* should only happen if pre-1.2 tape recordings are played */
10851     /* this is only for backward compatibility */
10852
10853     int original_move_delay_value = player->move_delay_value;
10854
10855 #if DEBUG
10856     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10857            tape.counter);
10858 #endif
10859
10860     /* scroll remaining steps with finest movement resolution */
10861     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10862
10863     while (player->MovPos)
10864     {
10865       ScrollPlayer(player, SCROLL_GO_ON);
10866       ScrollScreen(NULL, SCROLL_GO_ON);
10867
10868       AdvanceFrameAndPlayerCounters(player->index_nr);
10869
10870       DrawAllPlayers();
10871       BackToFront();
10872     }
10873
10874     player->move_delay_value = original_move_delay_value;
10875   }
10876
10877   player->is_active = FALSE;
10878
10879   if (player->last_move_dir & MV_HORIZONTAL)
10880   {
10881     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10882       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10883   }
10884   else
10885   {
10886     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10887       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10888   }
10889
10890 #if USE_FIXED_BORDER_RUNNING_GFX
10891   if (!moved && !player->is_active)
10892   {
10893     player->is_moving = FALSE;
10894     player->is_digging = FALSE;
10895     player->is_collecting = FALSE;
10896     player->is_snapping = FALSE;
10897     player->is_pushing = FALSE;
10898   }
10899 #endif
10900
10901   jx = player->jx;
10902   jy = player->jy;
10903
10904 #if 1
10905   if (moved & MP_MOVING && !ScreenMovPos &&
10906       (player->index_nr == game.centered_player_nr ||
10907        game.centered_player_nr == -1))
10908 #else
10909   if (moved & MP_MOVING && !ScreenMovPos &&
10910       (player == local_player || !options.network))
10911 #endif
10912   {
10913     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10914     int offset = (setup.scroll_delay ? 3 : 0);
10915
10916     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10917     {
10918       /* actual player has left the screen -- scroll in that direction */
10919       if (jx != old_jx)         /* player has moved horizontally */
10920         scroll_x += (jx - old_jx);
10921       else                      /* player has moved vertically */
10922         scroll_y += (jy - old_jy);
10923     }
10924     else
10925     {
10926       if (jx != old_jx)         /* player has moved horizontally */
10927       {
10928         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10929             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10930           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10931
10932         /* don't scroll over playfield boundaries */
10933         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10934           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10935
10936         /* don't scroll more than one field at a time */
10937         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10938
10939         /* don't scroll against the player's moving direction */
10940         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10941             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10942           scroll_x = old_scroll_x;
10943       }
10944       else                      /* player has moved vertically */
10945       {
10946         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10947             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10948           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10949
10950         /* don't scroll over playfield boundaries */
10951         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10952           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10953
10954         /* don't scroll more than one field at a time */
10955         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10956
10957         /* don't scroll against the player's moving direction */
10958         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10959             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10960           scroll_y = old_scroll_y;
10961       }
10962     }
10963
10964     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10965     {
10966 #if 1
10967       if (!options.network && game.centered_player_nr == -1 &&
10968           !AllPlayersInVisibleScreen())
10969       {
10970         scroll_x = old_scroll_x;
10971         scroll_y = old_scroll_y;
10972       }
10973       else
10974 #else
10975       if (!options.network && !AllPlayersInVisibleScreen())
10976       {
10977         scroll_x = old_scroll_x;
10978         scroll_y = old_scroll_y;
10979       }
10980       else
10981 #endif
10982       {
10983         ScrollScreen(player, SCROLL_INIT);
10984         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10985       }
10986     }
10987   }
10988
10989   player->StepFrame = 0;
10990
10991   if (moved & MP_MOVING)
10992   {
10993     if (old_jx != jx && old_jy == jy)
10994       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10995     else if (old_jx == jx && old_jy != jy)
10996       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10997
10998     DrawLevelField(jx, jy);     /* for "crumbled sand" */
10999
11000     player->last_move_dir = player->MovDir;
11001     player->is_moving = TRUE;
11002     player->is_snapping = FALSE;
11003     player->is_switching = FALSE;
11004     player->is_dropping = FALSE;
11005     player->is_dropping_pressed = FALSE;
11006     player->drop_pressed_delay = 0;
11007
11008 #if 0
11009     /* should better be called here than above, but this breaks some tapes */
11010     ScrollPlayer(player, SCROLL_INIT);
11011 #endif
11012   }
11013   else
11014   {
11015     CheckGravityMovementWhenNotMoving(player);
11016
11017     player->is_moving = FALSE;
11018
11019     /* at this point, the player is allowed to move, but cannot move right now
11020        (e.g. because of something blocking the way) -- ensure that the player
11021        is also allowed to move in the next frame (in old versions before 3.1.1,
11022        the player was forced to wait again for eight frames before next try) */
11023
11024     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11025       player->move_delay = 0;   /* allow direct movement in the next frame */
11026   }
11027
11028   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11029     player->move_delay = player->move_delay_value;
11030
11031   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11032   {
11033     TestIfPlayerTouchesBadThing(jx, jy);
11034     TestIfPlayerTouchesCustomElement(jx, jy);
11035   }
11036
11037   if (!player->active)
11038     RemovePlayer(player);
11039
11040   return moved;
11041 }
11042
11043 void ScrollPlayer(struct PlayerInfo *player, int mode)
11044 {
11045   int jx = player->jx, jy = player->jy;
11046   int last_jx = player->last_jx, last_jy = player->last_jy;
11047   int move_stepsize = TILEX / player->move_delay_value;
11048
11049 #if USE_NEW_PLAYER_SPEED
11050   if (!player->active)
11051     return;
11052
11053   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11054     return;
11055 #else
11056   if (!player->active || player->MovPos == 0)
11057     return;
11058 #endif
11059
11060   if (mode == SCROLL_INIT)
11061   {
11062     player->actual_frame_counter = FrameCounter;
11063     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11064
11065     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11066         Feld[last_jx][last_jy] == EL_EMPTY)
11067     {
11068       int last_field_block_delay = 0;   /* start with no blocking at all */
11069       int block_delay_adjustment = player->block_delay_adjustment;
11070
11071       /* if player blocks last field, add delay for exactly one move */
11072       if (player->block_last_field)
11073       {
11074         last_field_block_delay += player->move_delay_value;
11075
11076         /* when blocking enabled, prevent moving up despite gravity */
11077 #if USE_PLAYER_GRAVITY
11078         if (player->gravity && player->MovDir == MV_UP)
11079           block_delay_adjustment = -1;
11080 #else
11081         if (game.gravity && player->MovDir == MV_UP)
11082           block_delay_adjustment = -1;
11083 #endif
11084       }
11085
11086       /* add block delay adjustment (also possible when not blocking) */
11087       last_field_block_delay += block_delay_adjustment;
11088
11089       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11090       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11091     }
11092
11093 #if USE_NEW_PLAYER_SPEED
11094     if (player->MovPos != 0)    /* player has not yet reached destination */
11095       return;
11096 #else
11097     return;
11098 #endif
11099   }
11100   else if (!FrameReached(&player->actual_frame_counter, 1))
11101     return;
11102
11103 #if USE_NEW_PLAYER_SPEED
11104   if (player->MovPos != 0)
11105   {
11106     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11107     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11108
11109     /* before DrawPlayer() to draw correct player graphic for this case */
11110     if (player->MovPos == 0)
11111       CheckGravityMovement(player);
11112   }
11113 #else
11114   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11115   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11116
11117   /* before DrawPlayer() to draw correct player graphic for this case */
11118   if (player->MovPos == 0)
11119     CheckGravityMovement(player);
11120 #endif
11121
11122   if (player->MovPos == 0)      /* player reached destination field */
11123   {
11124     if (player->move_delay_reset_counter > 0)
11125     {
11126       player->move_delay_reset_counter--;
11127
11128       if (player->move_delay_reset_counter == 0)
11129       {
11130         /* continue with normal speed after quickly moving through gate */
11131         HALVE_PLAYER_SPEED(player);
11132
11133         /* be able to make the next move without delay */
11134         player->move_delay = 0;
11135       }
11136     }
11137
11138     player->last_jx = jx;
11139     player->last_jy = jy;
11140
11141     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11142         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11143         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11144         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11145         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11146         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11147     {
11148       DrawPlayer(player);       /* needed here only to cleanup last field */
11149       RemovePlayer(player);
11150
11151       if (local_player->friends_still_needed == 0 ||
11152           IS_SP_ELEMENT(Feld[jx][jy]))
11153         PlayerWins(player);
11154     }
11155
11156     /* this breaks one level: "machine", level 000 */
11157     {
11158       int move_direction = player->MovDir;
11159       int enter_side = MV_DIR_OPPOSITE(move_direction);
11160       int leave_side = move_direction;
11161       int old_jx = last_jx;
11162       int old_jy = last_jy;
11163       int old_element = Feld[old_jx][old_jy];
11164       int new_element = Feld[jx][jy];
11165
11166       if (IS_CUSTOM_ELEMENT(old_element))
11167         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11168                                    CE_LEFT_BY_PLAYER,
11169                                    player->index_bit, leave_side);
11170
11171       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11172                                           CE_PLAYER_LEAVES_X,
11173                                           player->index_bit, leave_side);
11174
11175       if (IS_CUSTOM_ELEMENT(new_element))
11176         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11177                                    player->index_bit, enter_side);
11178
11179       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11180                                           CE_PLAYER_ENTERS_X,
11181                                           player->index_bit, enter_side);
11182
11183       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11184                                         CE_MOVE_OF_X, move_direction);
11185     }
11186
11187     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11188     {
11189       TestIfPlayerTouchesBadThing(jx, jy);
11190       TestIfPlayerTouchesCustomElement(jx, jy);
11191
11192       /* needed because pushed element has not yet reached its destination,
11193          so it would trigger a change event at its previous field location */
11194       if (!player->is_pushing)
11195         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
11196
11197       if (!player->active)
11198         RemovePlayer(player);
11199     }
11200
11201     if (!local_player->LevelSolved && level.use_step_counter)
11202     {
11203       int i;
11204
11205       TimePlayed++;
11206
11207       if (TimeLeft > 0)
11208       {
11209         TimeLeft--;
11210
11211         if (TimeLeft <= 10 && setup.time_limit)
11212           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11213
11214         DrawGameValue_Time(TimeLeft);
11215
11216         if (!TimeLeft && setup.time_limit)
11217           for (i = 0; i < MAX_PLAYERS; i++)
11218             KillPlayer(&stored_player[i]);
11219       }
11220       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11221         DrawGameValue_Time(TimePlayed);
11222     }
11223
11224     if (tape.single_step && tape.recording && !tape.pausing &&
11225         !player->programmed_action)
11226       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11227   }
11228 }
11229
11230 void ScrollScreen(struct PlayerInfo *player, int mode)
11231 {
11232   static unsigned long screen_frame_counter = 0;
11233
11234   if (mode == SCROLL_INIT)
11235   {
11236     /* set scrolling step size according to actual player's moving speed */
11237     ScrollStepSize = TILEX / player->move_delay_value;
11238
11239     screen_frame_counter = FrameCounter;
11240     ScreenMovDir = player->MovDir;
11241     ScreenMovPos = player->MovPos;
11242     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11243     return;
11244   }
11245   else if (!FrameReached(&screen_frame_counter, 1))
11246     return;
11247
11248   if (ScreenMovPos)
11249   {
11250     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11251     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11252     redraw_mask |= REDRAW_FIELD;
11253   }
11254   else
11255     ScreenMovDir = MV_NONE;
11256 }
11257
11258 void TestIfPlayerTouchesCustomElement(int x, int y)
11259 {
11260   static int xy[4][2] =
11261   {
11262     { 0, -1 },
11263     { -1, 0 },
11264     { +1, 0 },
11265     { 0, +1 }
11266   };
11267   static int trigger_sides[4][2] =
11268   {
11269     /* center side       border side */
11270     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11271     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11272     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11273     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11274   };
11275   static int touch_dir[4] =
11276   {
11277     MV_LEFT | MV_RIGHT,
11278     MV_UP   | MV_DOWN,
11279     MV_UP   | MV_DOWN,
11280     MV_LEFT | MV_RIGHT
11281   };
11282   int center_element = Feld[x][y];      /* should always be non-moving! */
11283   int i;
11284
11285   for (i = 0; i < NUM_DIRECTIONS; i++)
11286   {
11287     int xx = x + xy[i][0];
11288     int yy = y + xy[i][1];
11289     int center_side = trigger_sides[i][0];
11290     int border_side = trigger_sides[i][1];
11291     int border_element;
11292
11293     if (!IN_LEV_FIELD(xx, yy))
11294       continue;
11295
11296     if (IS_PLAYER(x, y))
11297     {
11298       struct PlayerInfo *player = PLAYERINFO(x, y);
11299
11300       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11301         border_element = Feld[xx][yy];          /* may be moving! */
11302       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11303         border_element = Feld[xx][yy];
11304       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11305         border_element = MovingOrBlocked2Element(xx, yy);
11306       else
11307         continue;               /* center and border element do not touch */
11308
11309       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11310                                  player->index_bit, border_side);
11311       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11312                                           CE_PLAYER_TOUCHES_X,
11313                                           player->index_bit, border_side);
11314     }
11315     else if (IS_PLAYER(xx, yy))
11316     {
11317       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11318
11319       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11320       {
11321         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11322           continue;             /* center and border element do not touch */
11323       }
11324
11325       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11326                                  player->index_bit, center_side);
11327       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11328                                           CE_PLAYER_TOUCHES_X,
11329                                           player->index_bit, center_side);
11330       break;
11331     }
11332   }
11333 }
11334
11335 #if USE_ELEMENT_TOUCHING_BUGFIX
11336
11337 void TestIfElementTouchesCustomElement(int x, int y)
11338 {
11339   static int xy[4][2] =
11340   {
11341     { 0, -1 },
11342     { -1, 0 },
11343     { +1, 0 },
11344     { 0, +1 }
11345   };
11346   static int trigger_sides[4][2] =
11347   {
11348     /* center side      border side */
11349     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11350     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11351     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11352     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11353   };
11354   static int touch_dir[4] =
11355   {
11356     MV_LEFT | MV_RIGHT,
11357     MV_UP   | MV_DOWN,
11358     MV_UP   | MV_DOWN,
11359     MV_LEFT | MV_RIGHT
11360   };
11361   boolean change_center_element = FALSE;
11362   int center_element = Feld[x][y];      /* should always be non-moving! */
11363   int border_element_old[NUM_DIRECTIONS];
11364   int i;
11365
11366   for (i = 0; i < NUM_DIRECTIONS; i++)
11367   {
11368     int xx = x + xy[i][0];
11369     int yy = y + xy[i][1];
11370     int border_element;
11371
11372     border_element_old[i] = -1;
11373
11374     if (!IN_LEV_FIELD(xx, yy))
11375       continue;
11376
11377     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11378       border_element = Feld[xx][yy];    /* may be moving! */
11379     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11380       border_element = Feld[xx][yy];
11381     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11382       border_element = MovingOrBlocked2Element(xx, yy);
11383     else
11384       continue;                 /* center and border element do not touch */
11385
11386     border_element_old[i] = border_element;
11387   }
11388
11389   for (i = 0; i < NUM_DIRECTIONS; i++)
11390   {
11391     int xx = x + xy[i][0];
11392     int yy = y + xy[i][1];
11393     int center_side = trigger_sides[i][0];
11394     int border_element = border_element_old[i];
11395
11396     if (border_element == -1)
11397       continue;
11398
11399     /* check for change of border element */
11400     CheckElementChangeBySide(xx, yy, border_element, center_element,
11401                              CE_TOUCHING_X, center_side);
11402   }
11403
11404   for (i = 0; i < NUM_DIRECTIONS; i++)
11405   {
11406     int border_side = trigger_sides[i][1];
11407     int border_element = border_element_old[i];
11408
11409     if (border_element == -1)
11410       continue;
11411
11412     /* check for change of center element (but change it only once) */
11413     if (!change_center_element)
11414       change_center_element =
11415         CheckElementChangeBySide(x, y, center_element, border_element,
11416                                  CE_TOUCHING_X, border_side);
11417   }
11418 }
11419
11420 #else
11421
11422 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11423 {
11424   static int xy[4][2] =
11425   {
11426     { 0, -1 },
11427     { -1, 0 },
11428     { +1, 0 },
11429     { 0, +1 }
11430   };
11431   static int trigger_sides[4][2] =
11432   {
11433     /* center side      border side */
11434     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11435     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11436     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11437     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11438   };
11439   static int touch_dir[4] =
11440   {
11441     MV_LEFT | MV_RIGHT,
11442     MV_UP   | MV_DOWN,
11443     MV_UP   | MV_DOWN,
11444     MV_LEFT | MV_RIGHT
11445   };
11446   boolean change_center_element = FALSE;
11447   int center_element = Feld[x][y];      /* should always be non-moving! */
11448   int i;
11449
11450   for (i = 0; i < NUM_DIRECTIONS; i++)
11451   {
11452     int xx = x + xy[i][0];
11453     int yy = y + xy[i][1];
11454     int center_side = trigger_sides[i][0];
11455     int border_side = trigger_sides[i][1];
11456     int border_element;
11457
11458     if (!IN_LEV_FIELD(xx, yy))
11459       continue;
11460
11461     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11462       border_element = Feld[xx][yy];    /* may be moving! */
11463     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11464       border_element = Feld[xx][yy];
11465     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11466       border_element = MovingOrBlocked2Element(xx, yy);
11467     else
11468       continue;                 /* center and border element do not touch */
11469
11470     /* check for change of center element (but change it only once) */
11471     if (!change_center_element)
11472       change_center_element =
11473         CheckElementChangeBySide(x, y, center_element, border_element,
11474                                  CE_TOUCHING_X, border_side);
11475
11476     /* check for change of border element */
11477     CheckElementChangeBySide(xx, yy, border_element, center_element,
11478                              CE_TOUCHING_X, center_side);
11479   }
11480 }
11481
11482 #endif
11483
11484 void TestIfElementHitsCustomElement(int x, int y, int direction)
11485 {
11486   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11487   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11488   int hitx = x + dx, hity = y + dy;
11489   int hitting_element = Feld[x][y];
11490   int touched_element;
11491
11492   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11493     return;
11494
11495   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11496                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11497
11498   if (IN_LEV_FIELD(hitx, hity))
11499   {
11500     int opposite_direction = MV_DIR_OPPOSITE(direction);
11501     int hitting_side = direction;
11502     int touched_side = opposite_direction;
11503     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11504                           MovDir[hitx][hity] != direction ||
11505                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11506
11507     object_hit = TRUE;
11508
11509     if (object_hit)
11510     {
11511       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11512                                CE_HITTING_X, touched_side);
11513
11514       CheckElementChangeBySide(hitx, hity, touched_element,
11515                                hitting_element, CE_HIT_BY_X, hitting_side);
11516
11517       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11518                                CE_HIT_BY_SOMETHING, opposite_direction);
11519     }
11520   }
11521
11522   /* "hitting something" is also true when hitting the playfield border */
11523   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11524                            CE_HITTING_SOMETHING, direction);
11525 }
11526
11527 #if 0
11528 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11529 {
11530   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11531   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11532   int hitx = x + dx, hity = y + dy;
11533   int hitting_element = Feld[x][y];
11534   int touched_element;
11535 #if 0
11536   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11537                         !IS_FREE(hitx, hity) &&
11538                         (!IS_MOVING(hitx, hity) ||
11539                          MovDir[hitx][hity] != direction ||
11540                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11541 #endif
11542
11543   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11544     return;
11545
11546 #if 0
11547   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11548     return;
11549 #endif
11550
11551   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11552                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11553
11554   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11555                            EP_CAN_SMASH_EVERYTHING, direction);
11556
11557   if (IN_LEV_FIELD(hitx, hity))
11558   {
11559     int opposite_direction = MV_DIR_OPPOSITE(direction);
11560     int hitting_side = direction;
11561     int touched_side = opposite_direction;
11562 #if 0
11563     int touched_element = MovingOrBlocked2Element(hitx, hity);
11564 #endif
11565 #if 1
11566     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11567                           MovDir[hitx][hity] != direction ||
11568                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11569
11570     object_hit = TRUE;
11571 #endif
11572
11573     if (object_hit)
11574     {
11575       int i;
11576
11577       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11578                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11579
11580       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11581                                CE_OTHER_IS_SMASHING, touched_side);
11582
11583       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11584                                CE_OTHER_GETS_SMASHED, hitting_side);
11585     }
11586   }
11587 }
11588 #endif
11589
11590 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11591 {
11592   int i, kill_x = -1, kill_y = -1;
11593
11594   int bad_element = -1;
11595   static int test_xy[4][2] =
11596   {
11597     { 0, -1 },
11598     { -1, 0 },
11599     { +1, 0 },
11600     { 0, +1 }
11601   };
11602   static int test_dir[4] =
11603   {
11604     MV_UP,
11605     MV_LEFT,
11606     MV_RIGHT,
11607     MV_DOWN
11608   };
11609
11610   for (i = 0; i < NUM_DIRECTIONS; i++)
11611   {
11612     int test_x, test_y, test_move_dir, test_element;
11613
11614     test_x = good_x + test_xy[i][0];
11615     test_y = good_y + test_xy[i][1];
11616
11617     if (!IN_LEV_FIELD(test_x, test_y))
11618       continue;
11619
11620     test_move_dir =
11621       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11622
11623     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11624
11625     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11626        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11627     */
11628     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11629         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11630     {
11631       kill_x = test_x;
11632       kill_y = test_y;
11633       bad_element = test_element;
11634
11635       break;
11636     }
11637   }
11638
11639   if (kill_x != -1 || kill_y != -1)
11640   {
11641     if (IS_PLAYER(good_x, good_y))
11642     {
11643       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11644
11645       if (player->shield_deadly_time_left > 0 &&
11646           !IS_INDESTRUCTIBLE(bad_element))
11647         Bang(kill_x, kill_y);
11648       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11649         KillPlayer(player);
11650     }
11651     else
11652       Bang(good_x, good_y);
11653   }
11654 }
11655
11656 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11657 {
11658   int i, kill_x = -1, kill_y = -1;
11659   int bad_element = Feld[bad_x][bad_y];
11660   static int test_xy[4][2] =
11661   {
11662     { 0, -1 },
11663     { -1, 0 },
11664     { +1, 0 },
11665     { 0, +1 }
11666   };
11667   static int touch_dir[4] =
11668   {
11669     MV_LEFT | MV_RIGHT,
11670     MV_UP   | MV_DOWN,
11671     MV_UP   | MV_DOWN,
11672     MV_LEFT | MV_RIGHT
11673   };
11674   static int test_dir[4] =
11675   {
11676     MV_UP,
11677     MV_LEFT,
11678     MV_RIGHT,
11679     MV_DOWN
11680   };
11681
11682   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11683     return;
11684
11685   for (i = 0; i < NUM_DIRECTIONS; i++)
11686   {
11687     int test_x, test_y, test_move_dir, test_element;
11688
11689     test_x = bad_x + test_xy[i][0];
11690     test_y = bad_y + test_xy[i][1];
11691     if (!IN_LEV_FIELD(test_x, test_y))
11692       continue;
11693
11694     test_move_dir =
11695       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11696
11697     test_element = Feld[test_x][test_y];
11698
11699     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11700        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11701     */
11702     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11703         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11704     {
11705       /* good thing is player or penguin that does not move away */
11706       if (IS_PLAYER(test_x, test_y))
11707       {
11708         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11709
11710         if (bad_element == EL_ROBOT && player->is_moving)
11711           continue;     /* robot does not kill player if he is moving */
11712
11713         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11714         {
11715           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11716             continue;           /* center and border element do not touch */
11717         }
11718
11719         kill_x = test_x;
11720         kill_y = test_y;
11721         break;
11722       }
11723       else if (test_element == EL_PENGUIN)
11724       {
11725         kill_x = test_x;
11726         kill_y = test_y;
11727         break;
11728       }
11729     }
11730   }
11731
11732   if (kill_x != -1 || kill_y != -1)
11733   {
11734     if (IS_PLAYER(kill_x, kill_y))
11735     {
11736       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11737
11738       if (player->shield_deadly_time_left > 0 &&
11739           !IS_INDESTRUCTIBLE(bad_element))
11740         Bang(bad_x, bad_y);
11741       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11742         KillPlayer(player);
11743     }
11744     else
11745       Bang(kill_x, kill_y);
11746   }
11747 }
11748
11749 void TestIfPlayerTouchesBadThing(int x, int y)
11750 {
11751   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11752 }
11753
11754 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11755 {
11756   TestIfGoodThingHitsBadThing(x, y, move_dir);
11757 }
11758
11759 void TestIfBadThingTouchesPlayer(int x, int y)
11760 {
11761   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11762 }
11763
11764 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11765 {
11766   TestIfBadThingHitsGoodThing(x, y, move_dir);
11767 }
11768
11769 void TestIfFriendTouchesBadThing(int x, int y)
11770 {
11771   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11772 }
11773
11774 void TestIfBadThingTouchesFriend(int x, int y)
11775 {
11776   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11777 }
11778
11779 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11780 {
11781   int i, kill_x = bad_x, kill_y = bad_y;
11782   static int xy[4][2] =
11783   {
11784     { 0, -1 },
11785     { -1, 0 },
11786     { +1, 0 },
11787     { 0, +1 }
11788   };
11789
11790   for (i = 0; i < NUM_DIRECTIONS; i++)
11791   {
11792     int x, y, element;
11793
11794     x = bad_x + xy[i][0];
11795     y = bad_y + xy[i][1];
11796     if (!IN_LEV_FIELD(x, y))
11797       continue;
11798
11799     element = Feld[x][y];
11800     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11801         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11802     {
11803       kill_x = x;
11804       kill_y = y;
11805       break;
11806     }
11807   }
11808
11809   if (kill_x != bad_x || kill_y != bad_y)
11810     Bang(bad_x, bad_y);
11811 }
11812
11813 void KillPlayer(struct PlayerInfo *player)
11814 {
11815   int jx = player->jx, jy = player->jy;
11816
11817   if (!player->active)
11818     return;
11819
11820   /* the following code was introduced to prevent an infinite loop when calling
11821      -> Bang()
11822      -> CheckTriggeredElementChangeExt()
11823      -> ExecuteCustomElementAction()
11824      -> KillPlayer()
11825      -> (infinitely repeating the above sequence of function calls)
11826      which occurs when killing the player while having a CE with the setting
11827      "kill player X when explosion of <player X>"; the solution using a new
11828      field "player->killed" was chosen for backwards compatibility, although
11829      clever use of the fields "player->active" etc. would probably also work */
11830 #if 1
11831   if (player->killed)
11832     return;
11833 #endif
11834
11835   player->killed = TRUE;
11836
11837   /* remove accessible field at the player's position */
11838   Feld[jx][jy] = EL_EMPTY;
11839
11840   /* deactivate shield (else Bang()/Explode() would not work right) */
11841   player->shield_normal_time_left = 0;
11842   player->shield_deadly_time_left = 0;
11843
11844   Bang(jx, jy);
11845   BuryPlayer(player);
11846 }
11847
11848 static void KillPlayerUnlessEnemyProtected(int x, int y)
11849 {
11850   if (!PLAYER_ENEMY_PROTECTED(x, y))
11851     KillPlayer(PLAYERINFO(x, y));
11852 }
11853
11854 static void KillPlayerUnlessExplosionProtected(int x, int y)
11855 {
11856   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11857     KillPlayer(PLAYERINFO(x, y));
11858 }
11859
11860 void BuryPlayer(struct PlayerInfo *player)
11861 {
11862   int jx = player->jx, jy = player->jy;
11863
11864   if (!player->active)
11865     return;
11866
11867   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11868   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11869
11870   player->GameOver = TRUE;
11871   RemovePlayer(player);
11872 }
11873
11874 void RemovePlayer(struct PlayerInfo *player)
11875 {
11876   int jx = player->jx, jy = player->jy;
11877   int i, found = FALSE;
11878
11879   player->present = FALSE;
11880   player->active = FALSE;
11881
11882   if (!ExplodeField[jx][jy])
11883     StorePlayer[jx][jy] = 0;
11884
11885   if (player->is_moving)
11886     DrawLevelField(player->last_jx, player->last_jy);
11887
11888   for (i = 0; i < MAX_PLAYERS; i++)
11889     if (stored_player[i].active)
11890       found = TRUE;
11891
11892   if (!found)
11893     AllPlayersGone = TRUE;
11894
11895   ExitX = ZX = jx;
11896   ExitY = ZY = jy;
11897 }
11898
11899 #if USE_NEW_SNAP_DELAY
11900 static void setFieldForSnapping(int x, int y, int element, int direction)
11901 {
11902   struct ElementInfo *ei = &element_info[element];
11903   int direction_bit = MV_DIR_TO_BIT(direction);
11904   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11905   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11906                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11907
11908   Feld[x][y] = EL_ELEMENT_SNAPPING;
11909   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11910
11911   ResetGfxAnimation(x, y);
11912
11913   GfxElement[x][y] = element;
11914   GfxAction[x][y] = action;
11915   GfxDir[x][y] = direction;
11916   GfxFrame[x][y] = -1;
11917 }
11918 #endif
11919
11920 /*
11921   =============================================================================
11922   checkDiagonalPushing()
11923   -----------------------------------------------------------------------------
11924   check if diagonal input device direction results in pushing of object
11925   (by checking if the alternative direction is walkable, diggable, ...)
11926   =============================================================================
11927 */
11928
11929 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11930                                     int x, int y, int real_dx, int real_dy)
11931 {
11932   int jx, jy, dx, dy, xx, yy;
11933
11934   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11935     return TRUE;
11936
11937   /* diagonal direction: check alternative direction */
11938   jx = player->jx;
11939   jy = player->jy;
11940   dx = x - jx;
11941   dy = y - jy;
11942   xx = jx + (dx == 0 ? real_dx : 0);
11943   yy = jy + (dy == 0 ? real_dy : 0);
11944
11945   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11946 }
11947
11948 /*
11949   =============================================================================
11950   DigField()
11951   -----------------------------------------------------------------------------
11952   x, y:                 field next to player (non-diagonal) to try to dig to
11953   real_dx, real_dy:     direction as read from input device (can be diagonal)
11954   =============================================================================
11955 */
11956
11957 int DigField(struct PlayerInfo *player,
11958              int oldx, int oldy, int x, int y,
11959              int real_dx, int real_dy, int mode)
11960 {
11961   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11962   boolean player_was_pushing = player->is_pushing;
11963   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11964   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11965   int jx = oldx, jy = oldy;
11966   int dx = x - jx, dy = y - jy;
11967   int nextx = x + dx, nexty = y + dy;
11968   int move_direction = (dx == -1 ? MV_LEFT  :
11969                         dx == +1 ? MV_RIGHT :
11970                         dy == -1 ? MV_UP    :
11971                         dy == +1 ? MV_DOWN  : MV_NONE);
11972   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11973   int dig_side = MV_DIR_OPPOSITE(move_direction);
11974   int old_element = Feld[jx][jy];
11975 #if USE_FIXED_DONT_RUN_INTO
11976   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11977 #else
11978   int element;
11979 #endif
11980   int collect_count;
11981
11982   if (is_player)                /* function can also be called by EL_PENGUIN */
11983   {
11984     if (player->MovPos == 0)
11985     {
11986       player->is_digging = FALSE;
11987       player->is_collecting = FALSE;
11988     }
11989
11990     if (player->MovPos == 0)    /* last pushing move finished */
11991       player->is_pushing = FALSE;
11992
11993     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11994     {
11995       player->is_switching = FALSE;
11996       player->push_delay = -1;
11997
11998       return MP_NO_ACTION;
11999     }
12000   }
12001
12002 #if !USE_FIXED_DONT_RUN_INTO
12003   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12004     return MP_NO_ACTION;
12005 #endif
12006
12007   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12008     old_element = Back[jx][jy];
12009
12010   /* in case of element dropped at player position, check background */
12011   else if (Back[jx][jy] != EL_EMPTY &&
12012            game.engine_version >= VERSION_IDENT(2,2,0,0))
12013     old_element = Back[jx][jy];
12014
12015   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12016     return MP_NO_ACTION;        /* field has no opening in this direction */
12017
12018   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12019     return MP_NO_ACTION;        /* field has no opening in this direction */
12020
12021 #if USE_FIXED_DONT_RUN_INTO
12022   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12023   {
12024     SplashAcid(x, y);
12025
12026     Feld[jx][jy] = player->artwork_element;
12027     InitMovingField(jx, jy, MV_DOWN);
12028     Store[jx][jy] = EL_ACID;
12029     ContinueMoving(jx, jy);
12030     BuryPlayer(player);
12031
12032     return MP_DONT_RUN_INTO;
12033   }
12034 #endif
12035
12036 #if USE_FIXED_DONT_RUN_INTO
12037   if (player_can_move && DONT_RUN_INTO(element))
12038   {
12039     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12040
12041     return MP_DONT_RUN_INTO;
12042   }
12043 #endif
12044
12045 #if USE_FIXED_DONT_RUN_INTO
12046   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12047     return MP_NO_ACTION;
12048 #endif
12049
12050 #if !USE_FIXED_DONT_RUN_INTO
12051   element = Feld[x][y];
12052 #endif
12053
12054   collect_count = element_info[element].collect_count_initial;
12055
12056   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12057     return MP_NO_ACTION;
12058
12059   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12060     player_can_move = player_can_move_or_snap;
12061
12062   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12063       game.engine_version >= VERSION_IDENT(2,2,0,0))
12064   {
12065     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12066                                player->index_bit, dig_side);
12067     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12068                                         player->index_bit, dig_side);
12069
12070     if (element == EL_DC_LANDMINE)
12071       Bang(x, y);
12072
12073     if (Feld[x][y] != element)          /* field changed by snapping */
12074       return MP_ACTION;
12075
12076     return MP_NO_ACTION;
12077   }
12078
12079 #if USE_PLAYER_GRAVITY
12080   if (player->gravity && is_player && !player->is_auto_moving &&
12081       canFallDown(player) && move_direction != MV_DOWN &&
12082       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12083     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12084 #else
12085   if (game.gravity && is_player && !player->is_auto_moving &&
12086       canFallDown(player) && move_direction != MV_DOWN &&
12087       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12088     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12089 #endif
12090
12091   if (player_can_move &&
12092       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12093   {
12094     int sound_element = SND_ELEMENT(element);
12095     int sound_action = ACTION_WALKING;
12096
12097     if (IS_RND_GATE(element))
12098     {
12099       if (!player->key[RND_GATE_NR(element)])
12100         return MP_NO_ACTION;
12101     }
12102     else if (IS_RND_GATE_GRAY(element))
12103     {
12104       if (!player->key[RND_GATE_GRAY_NR(element)])
12105         return MP_NO_ACTION;
12106     }
12107     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12108     {
12109       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12110         return MP_NO_ACTION;
12111     }
12112     else if (element == EL_EXIT_OPEN ||
12113              element == EL_EM_EXIT_OPEN ||
12114              element == EL_STEEL_EXIT_OPEN ||
12115              element == EL_EM_STEEL_EXIT_OPEN ||
12116              element == EL_SP_EXIT_OPEN ||
12117              element == EL_SP_EXIT_OPENING)
12118     {
12119       sound_action = ACTION_PASSING;    /* player is passing exit */
12120     }
12121     else if (element == EL_EMPTY)
12122     {
12123       sound_action = ACTION_MOVING;             /* nothing to walk on */
12124     }
12125
12126     /* play sound from background or player, whatever is available */
12127     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12128       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12129     else
12130       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12131   }
12132   else if (player_can_move &&
12133            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12134   {
12135     if (!ACCESS_FROM(element, opposite_direction))
12136       return MP_NO_ACTION;      /* field not accessible from this direction */
12137
12138     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12139       return MP_NO_ACTION;
12140
12141     if (IS_EM_GATE(element))
12142     {
12143       if (!player->key[EM_GATE_NR(element)])
12144         return MP_NO_ACTION;
12145     }
12146     else if (IS_EM_GATE_GRAY(element))
12147     {
12148       if (!player->key[EM_GATE_GRAY_NR(element)])
12149         return MP_NO_ACTION;
12150     }
12151     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12152     {
12153       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12154         return MP_NO_ACTION;
12155     }
12156     else if (IS_EMC_GATE(element))
12157     {
12158       if (!player->key[EMC_GATE_NR(element)])
12159         return MP_NO_ACTION;
12160     }
12161     else if (IS_EMC_GATE_GRAY(element))
12162     {
12163       if (!player->key[EMC_GATE_GRAY_NR(element)])
12164         return MP_NO_ACTION;
12165     }
12166     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12167     {
12168       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12169         return MP_NO_ACTION;
12170     }
12171     else if (element == EL_DC_GATE_WHITE ||
12172              element == EL_DC_GATE_WHITE_GRAY ||
12173              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12174     {
12175       if (player->num_white_keys == 0)
12176         return MP_NO_ACTION;
12177
12178       player->num_white_keys--;
12179     }
12180     else if (IS_SP_PORT(element))
12181     {
12182       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12183           element == EL_SP_GRAVITY_PORT_RIGHT ||
12184           element == EL_SP_GRAVITY_PORT_UP ||
12185           element == EL_SP_GRAVITY_PORT_DOWN)
12186 #if USE_PLAYER_GRAVITY
12187         player->gravity = !player->gravity;
12188 #else
12189         game.gravity = !game.gravity;
12190 #endif
12191       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12192                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12193                element == EL_SP_GRAVITY_ON_PORT_UP ||
12194                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12195 #if USE_PLAYER_GRAVITY
12196         player->gravity = TRUE;
12197 #else
12198         game.gravity = TRUE;
12199 #endif
12200       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12201                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12202                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12203                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12204 #if USE_PLAYER_GRAVITY
12205         player->gravity = FALSE;
12206 #else
12207         game.gravity = FALSE;
12208 #endif
12209     }
12210
12211     /* automatically move to the next field with double speed */
12212     player->programmed_action = move_direction;
12213
12214     if (player->move_delay_reset_counter == 0)
12215     {
12216       player->move_delay_reset_counter = 2;     /* two double speed steps */
12217
12218       DOUBLE_PLAYER_SPEED(player);
12219     }
12220
12221     PlayLevelSoundAction(x, y, ACTION_PASSING);
12222   }
12223   else if (player_can_move_or_snap && IS_DIGGABLE(element))
12224   {
12225     RemoveField(x, y);
12226
12227     if (mode != DF_SNAP)
12228     {
12229       GfxElement[x][y] = GFX_ELEMENT(element);
12230       player->is_digging = TRUE;
12231     }
12232
12233     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12234
12235     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12236                                         player->index_bit, dig_side);
12237
12238     if (mode == DF_SNAP)
12239     {
12240 #if USE_NEW_SNAP_DELAY
12241       if (level.block_snap_field)
12242         setFieldForSnapping(x, y, element, move_direction);
12243       else
12244         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12245 #else
12246       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12247 #endif
12248
12249       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12250                                           player->index_bit, dig_side);
12251     }
12252   }
12253   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12254   {
12255     RemoveField(x, y);
12256
12257     if (is_player && mode != DF_SNAP)
12258     {
12259       GfxElement[x][y] = element;
12260       player->is_collecting = TRUE;
12261     }
12262
12263     if (element == EL_SPEED_PILL)
12264     {
12265       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12266     }
12267     else if (element == EL_EXTRA_TIME && level.time > 0)
12268     {
12269       TimeLeft += level.extra_time;
12270       DrawGameValue_Time(TimeLeft);
12271     }
12272     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12273     {
12274       player->shield_normal_time_left += level.shield_normal_time;
12275       if (element == EL_SHIELD_DEADLY)
12276         player->shield_deadly_time_left += level.shield_deadly_time;
12277     }
12278     else if (element == EL_DYNAMITE ||
12279              element == EL_EM_DYNAMITE ||
12280              element == EL_SP_DISK_RED)
12281     {
12282       if (player->inventory_size < MAX_INVENTORY_SIZE)
12283         player->inventory_element[player->inventory_size++] = element;
12284
12285       DrawGameDoorValues();
12286     }
12287     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12288     {
12289       player->dynabomb_count++;
12290       player->dynabombs_left++;
12291     }
12292     else if (element == EL_DYNABOMB_INCREASE_SIZE)
12293     {
12294       player->dynabomb_size++;
12295     }
12296     else if (element == EL_DYNABOMB_INCREASE_POWER)
12297     {
12298       player->dynabomb_xl = TRUE;
12299     }
12300     else if (IS_KEY(element))
12301     {
12302       player->key[KEY_NR(element)] = TRUE;
12303
12304       DrawGameDoorValues();
12305     }
12306     else if (element == EL_DC_KEY_WHITE)
12307     {
12308       player->num_white_keys++;
12309
12310       /* display white keys? */
12311       /* DrawGameDoorValues(); */
12312     }
12313     else if (IS_ENVELOPE(element))
12314     {
12315       player->show_envelope = element;
12316     }
12317     else if (element == EL_EMC_LENSES)
12318     {
12319       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12320
12321       RedrawAllInvisibleElementsForLenses();
12322     }
12323     else if (element == EL_EMC_MAGNIFIER)
12324     {
12325       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12326
12327       RedrawAllInvisibleElementsForMagnifier();
12328     }
12329     else if (IS_DROPPABLE(element) ||
12330              IS_THROWABLE(element))     /* can be collected and dropped */
12331     {
12332       int i;
12333
12334       if (collect_count == 0)
12335         player->inventory_infinite_element = element;
12336       else
12337         for (i = 0; i < collect_count; i++)
12338           if (player->inventory_size < MAX_INVENTORY_SIZE)
12339             player->inventory_element[player->inventory_size++] = element;
12340
12341       DrawGameDoorValues();
12342     }
12343     else if (collect_count > 0)
12344     {
12345       local_player->gems_still_needed -= collect_count;
12346       if (local_player->gems_still_needed < 0)
12347         local_player->gems_still_needed = 0;
12348
12349       DrawGameValue_Emeralds(local_player->gems_still_needed);
12350     }
12351
12352     RaiseScoreElement(element);
12353     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12354
12355     if (is_player)
12356       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12357                                           player->index_bit, dig_side);
12358
12359     if (mode == DF_SNAP)
12360     {
12361 #if USE_NEW_SNAP_DELAY
12362       if (level.block_snap_field)
12363         setFieldForSnapping(x, y, element, move_direction);
12364       else
12365         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12366 #else
12367       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12368 #endif
12369
12370       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12371                                           player->index_bit, dig_side);
12372     }
12373   }
12374   else if (player_can_move_or_snap && IS_PUSHABLE(element))
12375   {
12376     if (mode == DF_SNAP && element != EL_BD_ROCK)
12377       return MP_NO_ACTION;
12378
12379     if (CAN_FALL(element) && dy)
12380       return MP_NO_ACTION;
12381
12382     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12383         !(element == EL_SPRING && level.use_spring_bug))
12384       return MP_NO_ACTION;
12385
12386     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12387         ((move_direction & MV_VERTICAL &&
12388           ((element_info[element].move_pattern & MV_LEFT &&
12389             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12390            (element_info[element].move_pattern & MV_RIGHT &&
12391             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12392          (move_direction & MV_HORIZONTAL &&
12393           ((element_info[element].move_pattern & MV_UP &&
12394             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12395            (element_info[element].move_pattern & MV_DOWN &&
12396             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12397       return MP_NO_ACTION;
12398
12399     /* do not push elements already moving away faster than player */
12400     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12401         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12402       return MP_NO_ACTION;
12403
12404     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12405     {
12406       if (player->push_delay_value == -1 || !player_was_pushing)
12407         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12408     }
12409     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12410     {
12411       if (player->push_delay_value == -1)
12412         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12413     }
12414     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12415     {
12416       if (!player->is_pushing)
12417         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12418     }
12419
12420     player->is_pushing = TRUE;
12421     player->is_active = TRUE;
12422
12423     if (!(IN_LEV_FIELD(nextx, nexty) &&
12424           (IS_FREE(nextx, nexty) ||
12425            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12426             IS_SB_ELEMENT(element)))))
12427       return MP_NO_ACTION;
12428
12429     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12430       return MP_NO_ACTION;
12431
12432     if (player->push_delay == -1)       /* new pushing; restart delay */
12433       player->push_delay = 0;
12434
12435     if (player->push_delay < player->push_delay_value &&
12436         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12437         element != EL_SPRING && element != EL_BALLOON)
12438     {
12439       /* make sure that there is no move delay before next try to push */
12440       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12441         player->move_delay = 0;
12442
12443       return MP_NO_ACTION;
12444     }
12445
12446     if (IS_SB_ELEMENT(element))
12447     {
12448       if (element == EL_SOKOBAN_FIELD_FULL)
12449       {
12450         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12451         local_player->sokobanfields_still_needed++;
12452       }
12453
12454       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12455       {
12456         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12457         local_player->sokobanfields_still_needed--;
12458       }
12459
12460       Feld[x][y] = EL_SOKOBAN_OBJECT;
12461
12462       if (Back[x][y] == Back[nextx][nexty])
12463         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12464       else if (Back[x][y] != 0)
12465         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12466                                     ACTION_EMPTYING);
12467       else
12468         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12469                                     ACTION_FILLING);
12470
12471       if (local_player->sokobanfields_still_needed == 0 &&
12472           game.emulation == EMU_SOKOBAN)
12473       {
12474         PlayerWins(player);
12475
12476         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12477       }
12478     }
12479     else
12480       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12481
12482     InitMovingField(x, y, move_direction);
12483     GfxAction[x][y] = ACTION_PUSHING;
12484
12485     if (mode == DF_SNAP)
12486       ContinueMoving(x, y);
12487     else
12488       MovPos[x][y] = (dx != 0 ? dx : dy);
12489
12490     Pushed[x][y] = TRUE;
12491     Pushed[nextx][nexty] = TRUE;
12492
12493     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12494       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12495     else
12496       player->push_delay_value = -1;    /* get new value later */
12497
12498     /* check for element change _after_ element has been pushed */
12499     if (game.use_change_when_pushing_bug)
12500     {
12501       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12502                                  player->index_bit, dig_side);
12503       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12504                                           player->index_bit, dig_side);
12505     }
12506   }
12507   else if (IS_SWITCHABLE(element))
12508   {
12509     if (PLAYER_SWITCHING(player, x, y))
12510     {
12511       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12512                                           player->index_bit, dig_side);
12513
12514       return MP_ACTION;
12515     }
12516
12517     player->is_switching = TRUE;
12518     player->switch_x = x;
12519     player->switch_y = y;
12520
12521     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12522
12523     if (element == EL_ROBOT_WHEEL)
12524     {
12525       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12526       ZX = x;
12527       ZY = y;
12528
12529       DrawLevelField(x, y);
12530     }
12531     else if (element == EL_SP_TERMINAL)
12532     {
12533       int xx, yy;
12534
12535       SCAN_PLAYFIELD(xx, yy)
12536       {
12537         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12538           Bang(xx, yy);
12539         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12540           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12541       }
12542     }
12543     else if (IS_BELT_SWITCH(element))
12544     {
12545       ToggleBeltSwitch(x, y);
12546     }
12547     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12548              element == EL_SWITCHGATE_SWITCH_DOWN ||
12549              element == EL_DC_SWITCHGATE_SWITCH_UP ||
12550              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12551     {
12552       ToggleSwitchgateSwitch(x, y);
12553     }
12554     else if (element == EL_LIGHT_SWITCH ||
12555              element == EL_LIGHT_SWITCH_ACTIVE)
12556     {
12557       ToggleLightSwitch(x, y);
12558     }
12559     else if (element == EL_TIMEGATE_SWITCH ||
12560              element == EL_DC_TIMEGATE_SWITCH)
12561     {
12562       ActivateTimegateSwitch(x, y);
12563     }
12564     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12565              element == EL_BALLOON_SWITCH_RIGHT ||
12566              element == EL_BALLOON_SWITCH_UP    ||
12567              element == EL_BALLOON_SWITCH_DOWN  ||
12568              element == EL_BALLOON_SWITCH_NONE  ||
12569              element == EL_BALLOON_SWITCH_ANY)
12570     {
12571       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12572                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12573                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12574                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12575                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12576                              move_direction);
12577     }
12578     else if (element == EL_LAMP)
12579     {
12580       Feld[x][y] = EL_LAMP_ACTIVE;
12581       local_player->lights_still_needed--;
12582
12583       ResetGfxAnimation(x, y);
12584       DrawLevelField(x, y);
12585     }
12586     else if (element == EL_TIME_ORB_FULL)
12587     {
12588       Feld[x][y] = EL_TIME_ORB_EMPTY;
12589
12590       if (level.time > 0 || level.use_time_orb_bug)
12591       {
12592         TimeLeft += level.time_orb_time;
12593         DrawGameValue_Time(TimeLeft);
12594       }
12595
12596       ResetGfxAnimation(x, y);
12597       DrawLevelField(x, y);
12598     }
12599     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12600              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12601     {
12602       int xx, yy;
12603
12604       game.ball_state = !game.ball_state;
12605
12606       SCAN_PLAYFIELD(xx, yy)
12607       {
12608         int e = Feld[xx][yy];
12609
12610         if (game.ball_state)
12611         {
12612           if (e == EL_EMC_MAGIC_BALL)
12613             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12614           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12615             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12616         }
12617         else
12618         {
12619           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12620             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12621           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12622             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12623         }
12624       }
12625     }
12626
12627     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12628                                         player->index_bit, dig_side);
12629
12630     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12631                                         player->index_bit, dig_side);
12632
12633     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12634                                         player->index_bit, dig_side);
12635
12636     return MP_ACTION;
12637   }
12638   else
12639   {
12640     if (!PLAYER_SWITCHING(player, x, y))
12641     {
12642       player->is_switching = TRUE;
12643       player->switch_x = x;
12644       player->switch_y = y;
12645
12646       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12647                                  player->index_bit, dig_side);
12648       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12649                                           player->index_bit, dig_side);
12650
12651       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12652                                  player->index_bit, dig_side);
12653       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12654                                           player->index_bit, dig_side);
12655     }
12656
12657     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12658                                player->index_bit, dig_side);
12659     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12660                                         player->index_bit, dig_side);
12661
12662     return MP_NO_ACTION;
12663   }
12664
12665   player->push_delay = -1;
12666
12667   if (is_player)                /* function can also be called by EL_PENGUIN */
12668   {
12669     if (Feld[x][y] != element)          /* really digged/collected something */
12670     {
12671       player->is_collecting = !player->is_digging;
12672       player->is_active = TRUE;
12673     }
12674   }
12675
12676   return MP_MOVING;
12677 }
12678
12679 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12680 {
12681   int jx = player->jx, jy = player->jy;
12682   int x = jx + dx, y = jy + dy;
12683   int snap_direction = (dx == -1 ? MV_LEFT  :
12684                         dx == +1 ? MV_RIGHT :
12685                         dy == -1 ? MV_UP    :
12686                         dy == +1 ? MV_DOWN  : MV_NONE);
12687   boolean can_continue_snapping = (level.continuous_snapping &&
12688                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12689
12690   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12691     return FALSE;
12692
12693   if (!player->active || !IN_LEV_FIELD(x, y))
12694     return FALSE;
12695
12696   if (dx && dy)
12697     return FALSE;
12698
12699   if (!dx && !dy)
12700   {
12701     if (player->MovPos == 0)
12702       player->is_pushing = FALSE;
12703
12704     player->is_snapping = FALSE;
12705
12706     if (player->MovPos == 0)
12707     {
12708       player->is_moving = FALSE;
12709       player->is_digging = FALSE;
12710       player->is_collecting = FALSE;
12711     }
12712
12713     return FALSE;
12714   }
12715
12716 #if USE_NEW_CONTINUOUS_SNAPPING
12717   /* prevent snapping with already pressed snap key when not allowed */
12718   if (player->is_snapping && !can_continue_snapping)
12719     return FALSE;
12720 #else
12721   if (player->is_snapping)
12722     return FALSE;
12723 #endif
12724
12725   player->MovDir = snap_direction;
12726
12727   if (player->MovPos == 0)
12728   {
12729     player->is_moving = FALSE;
12730     player->is_digging = FALSE;
12731     player->is_collecting = FALSE;
12732   }
12733
12734   player->is_dropping = FALSE;
12735   player->is_dropping_pressed = FALSE;
12736   player->drop_pressed_delay = 0;
12737
12738   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12739     return FALSE;
12740
12741   player->is_snapping = TRUE;
12742   player->is_active = TRUE;
12743
12744   if (player->MovPos == 0)
12745   {
12746     player->is_moving = FALSE;
12747     player->is_digging = FALSE;
12748     player->is_collecting = FALSE;
12749   }
12750
12751   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12752     DrawLevelField(player->last_jx, player->last_jy);
12753
12754   DrawLevelField(x, y);
12755
12756   return TRUE;
12757 }
12758
12759 boolean DropElement(struct PlayerInfo *player)
12760 {
12761   int old_element, new_element;
12762   int dropx = player->jx, dropy = player->jy;
12763   int drop_direction = player->MovDir;
12764   int drop_side = drop_direction;
12765   int drop_element = (player->inventory_size > 0 ?
12766                       player->inventory_element[player->inventory_size - 1] :
12767                       player->inventory_infinite_element != EL_UNDEFINED ?
12768                       player->inventory_infinite_element :
12769                       player->dynabombs_left > 0 ?
12770                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12771                       EL_UNDEFINED);
12772
12773   player->is_dropping_pressed = TRUE;
12774
12775   /* do not drop an element on top of another element; when holding drop key
12776      pressed without moving, dropped element must move away before the next
12777      element can be dropped (this is especially important if the next element
12778      is dynamite, which can be placed on background for historical reasons) */
12779   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12780     return MP_ACTION;
12781
12782   if (IS_THROWABLE(drop_element))
12783   {
12784     dropx += GET_DX_FROM_DIR(drop_direction);
12785     dropy += GET_DY_FROM_DIR(drop_direction);
12786
12787     if (!IN_LEV_FIELD(dropx, dropy))
12788       return FALSE;
12789   }
12790
12791   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12792   new_element = drop_element;           /* default: no change when dropping */
12793
12794   /* check if player is active, not moving and ready to drop */
12795   if (!player->active || player->MovPos || player->drop_delay > 0)
12796     return FALSE;
12797
12798   /* check if player has anything that can be dropped */
12799   if (new_element == EL_UNDEFINED)
12800     return FALSE;
12801
12802   /* check if drop key was pressed long enough for EM style dynamite */
12803   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12804     return FALSE;
12805
12806   /* check if anything can be dropped at the current position */
12807   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12808     return FALSE;
12809
12810   /* collected custom elements can only be dropped on empty fields */
12811   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12812     return FALSE;
12813
12814   if (old_element != EL_EMPTY)
12815     Back[dropx][dropy] = old_element;   /* store old element on this field */
12816
12817   ResetGfxAnimation(dropx, dropy);
12818   ResetRandomAnimationValue(dropx, dropy);
12819
12820   if (player->inventory_size > 0 ||
12821       player->inventory_infinite_element != EL_UNDEFINED)
12822   {
12823     if (player->inventory_size > 0)
12824     {
12825       player->inventory_size--;
12826
12827       DrawGameDoorValues();
12828
12829       if (new_element == EL_DYNAMITE)
12830         new_element = EL_DYNAMITE_ACTIVE;
12831       else if (new_element == EL_EM_DYNAMITE)
12832         new_element = EL_EM_DYNAMITE_ACTIVE;
12833       else if (new_element == EL_SP_DISK_RED)
12834         new_element = EL_SP_DISK_RED_ACTIVE;
12835     }
12836
12837     Feld[dropx][dropy] = new_element;
12838
12839     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12840       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12841                           el2img(Feld[dropx][dropy]), 0);
12842
12843     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12844
12845     /* needed if previous element just changed to "empty" in the last frame */
12846     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12847
12848     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12849                                player->index_bit, drop_side);
12850     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12851                                         CE_PLAYER_DROPS_X,
12852                                         player->index_bit, drop_side);
12853
12854     TestIfElementTouchesCustomElement(dropx, dropy);
12855   }
12856   else          /* player is dropping a dyna bomb */
12857   {
12858     player->dynabombs_left--;
12859
12860     Feld[dropx][dropy] = new_element;
12861
12862     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12863       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12864                           el2img(Feld[dropx][dropy]), 0);
12865
12866     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12867   }
12868
12869   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12870     InitField_WithBug1(dropx, dropy, FALSE);
12871
12872   new_element = Feld[dropx][dropy];     /* element might have changed */
12873
12874   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12875       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12876   {
12877     int move_direction, nextx, nexty;
12878
12879     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12880       MovDir[dropx][dropy] = drop_direction;
12881
12882     move_direction = MovDir[dropx][dropy];
12883     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12884     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12885
12886     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12887
12888 #if USE_FIX_IMPACT_COLLISION
12889     /* do not cause impact style collision by dropping elements that can fall */
12890     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12891 #else
12892     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12893 #endif
12894   }
12895
12896   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12897   player->is_dropping = TRUE;
12898
12899   player->drop_pressed_delay = 0;
12900   player->is_dropping_pressed = FALSE;
12901
12902   player->drop_x = dropx;
12903   player->drop_y = dropy;
12904
12905   return TRUE;
12906 }
12907
12908 /* ------------------------------------------------------------------------- */
12909 /* game sound playing functions                                              */
12910 /* ------------------------------------------------------------------------- */
12911
12912 static int *loop_sound_frame = NULL;
12913 static int *loop_sound_volume = NULL;
12914
12915 void InitPlayLevelSound()
12916 {
12917   int num_sounds = getSoundListSize();
12918
12919   checked_free(loop_sound_frame);
12920   checked_free(loop_sound_volume);
12921
12922   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12923   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12924 }
12925
12926 static void PlayLevelSound(int x, int y, int nr)
12927 {
12928   int sx = SCREENX(x), sy = SCREENY(y);
12929   int volume, stereo_position;
12930   int max_distance = 8;
12931   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12932
12933   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12934       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12935     return;
12936
12937   if (!IN_LEV_FIELD(x, y) ||
12938       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12939       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12940     return;
12941
12942   volume = SOUND_MAX_VOLUME;
12943
12944   if (!IN_SCR_FIELD(sx, sy))
12945   {
12946     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12947     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12948
12949     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12950   }
12951
12952   stereo_position = (SOUND_MAX_LEFT +
12953                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12954                      (SCR_FIELDX + 2 * max_distance));
12955
12956   if (IS_LOOP_SOUND(nr))
12957   {
12958     /* This assures that quieter loop sounds do not overwrite louder ones,
12959        while restarting sound volume comparison with each new game frame. */
12960
12961     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12962       return;
12963
12964     loop_sound_volume[nr] = volume;
12965     loop_sound_frame[nr] = FrameCounter;
12966   }
12967
12968   PlaySoundExt(nr, volume, stereo_position, type);
12969 }
12970
12971 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12972 {
12973   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12974                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12975                  y < LEVELY(BY1) ? LEVELY(BY1) :
12976                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12977                  sound_action);
12978 }
12979
12980 static void PlayLevelSoundAction(int x, int y, int action)
12981 {
12982   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12983 }
12984
12985 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12986 {
12987   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12988
12989   if (sound_effect != SND_UNDEFINED)
12990     PlayLevelSound(x, y, sound_effect);
12991 }
12992
12993 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12994                                               int action)
12995 {
12996   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12997
12998   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12999     PlayLevelSound(x, y, sound_effect);
13000 }
13001
13002 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13003 {
13004   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13005
13006   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13007     PlayLevelSound(x, y, sound_effect);
13008 }
13009
13010 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13011 {
13012   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13013
13014   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13015     StopSound(sound_effect);
13016 }
13017
13018 static void PlayLevelMusic()
13019 {
13020   if (levelset.music[level_nr] != MUS_UNDEFINED)
13021     PlayMusic(levelset.music[level_nr]);        /* from config file */
13022   else
13023     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13024 }
13025
13026 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13027 {
13028   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13029   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13030   int x = xx - 1 - offset;
13031   int y = yy - 1 - offset;
13032
13033   switch (sample)
13034   {
13035     case SAMPLE_blank:
13036       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13037       break;
13038
13039     case SAMPLE_roll:
13040       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13041       break;
13042
13043     case SAMPLE_stone:
13044       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13045       break;
13046
13047     case SAMPLE_nut:
13048       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13049       break;
13050
13051     case SAMPLE_crack:
13052       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13053       break;
13054
13055     case SAMPLE_bug:
13056       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13057       break;
13058
13059     case SAMPLE_tank:
13060       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13061       break;
13062
13063     case SAMPLE_android_clone:
13064       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13065       break;
13066
13067     case SAMPLE_android_move:
13068       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13069       break;
13070
13071     case SAMPLE_spring:
13072       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13073       break;
13074
13075     case SAMPLE_slurp:
13076       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13077       break;
13078
13079     case SAMPLE_eater:
13080       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13081       break;
13082
13083     case SAMPLE_eater_eat:
13084       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13085       break;
13086
13087     case SAMPLE_alien:
13088       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13089       break;
13090
13091     case SAMPLE_collect:
13092       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13093       break;
13094
13095     case SAMPLE_diamond:
13096       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13097       break;
13098
13099     case SAMPLE_squash:
13100       /* !!! CHECK THIS !!! */
13101 #if 1
13102       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13103 #else
13104       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13105 #endif
13106       break;
13107
13108     case SAMPLE_wonderfall:
13109       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13110       break;
13111
13112     case SAMPLE_drip:
13113       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13114       break;
13115
13116     case SAMPLE_push:
13117       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13118       break;
13119
13120     case SAMPLE_dirt:
13121       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13122       break;
13123
13124     case SAMPLE_acid:
13125       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13126       break;
13127
13128     case SAMPLE_ball:
13129       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13130       break;
13131
13132     case SAMPLE_grow:
13133       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13134       break;
13135
13136     case SAMPLE_wonder:
13137       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13138       break;
13139
13140     case SAMPLE_door:
13141       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13142       break;
13143
13144     case SAMPLE_exit_open:
13145       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13146       break;
13147
13148     case SAMPLE_exit_leave:
13149       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13150       break;
13151
13152     case SAMPLE_dynamite:
13153       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13154       break;
13155
13156     case SAMPLE_tick:
13157       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13158       break;
13159
13160     case SAMPLE_press:
13161       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13162       break;
13163
13164     case SAMPLE_wheel:
13165       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13166       break;
13167
13168     case SAMPLE_boom:
13169       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13170       break;
13171
13172     case SAMPLE_die:
13173       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13174       break;
13175
13176     case SAMPLE_time:
13177       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13178       break;
13179
13180     default:
13181       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13182       break;
13183   }
13184 }
13185
13186 #if 0
13187 void ChangeTime(int value)
13188 {
13189   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13190
13191   *time += value;
13192
13193   /* EMC game engine uses value from time counter of RND game engine */
13194   level.native_em_level->lev->time = *time;
13195
13196   DrawGameValue_Time(*time);
13197 }
13198
13199 void RaiseScore(int value)
13200 {
13201   /* EMC game engine and RND game engine have separate score counters */
13202   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13203                 &level.native_em_level->lev->score : &local_player->score);
13204
13205   *score += value;
13206
13207   DrawGameValue_Score(*score);
13208 }
13209 #endif
13210
13211 void RaiseScore(int value)
13212 {
13213   local_player->score += value;
13214
13215   DrawGameValue_Score(local_player->score);
13216 }
13217
13218 void RaiseScoreElement(int element)
13219 {
13220   switch (element)
13221   {
13222     case EL_EMERALD:
13223     case EL_BD_DIAMOND:
13224     case EL_EMERALD_YELLOW:
13225     case EL_EMERALD_RED:
13226     case EL_EMERALD_PURPLE:
13227     case EL_SP_INFOTRON:
13228       RaiseScore(level.score[SC_EMERALD]);
13229       break;
13230     case EL_DIAMOND:
13231       RaiseScore(level.score[SC_DIAMOND]);
13232       break;
13233     case EL_CRYSTAL:
13234       RaiseScore(level.score[SC_CRYSTAL]);
13235       break;
13236     case EL_PEARL:
13237       RaiseScore(level.score[SC_PEARL]);
13238       break;
13239     case EL_BUG:
13240     case EL_BD_BUTTERFLY:
13241     case EL_SP_ELECTRON:
13242       RaiseScore(level.score[SC_BUG]);
13243       break;
13244     case EL_SPACESHIP:
13245     case EL_BD_FIREFLY:
13246     case EL_SP_SNIKSNAK:
13247       RaiseScore(level.score[SC_SPACESHIP]);
13248       break;
13249     case EL_YAMYAM:
13250     case EL_DARK_YAMYAM:
13251       RaiseScore(level.score[SC_YAMYAM]);
13252       break;
13253     case EL_ROBOT:
13254       RaiseScore(level.score[SC_ROBOT]);
13255       break;
13256     case EL_PACMAN:
13257       RaiseScore(level.score[SC_PACMAN]);
13258       break;
13259     case EL_NUT:
13260       RaiseScore(level.score[SC_NUT]);
13261       break;
13262     case EL_DYNAMITE:
13263     case EL_EM_DYNAMITE:
13264     case EL_SP_DISK_RED:
13265     case EL_DYNABOMB_INCREASE_NUMBER:
13266     case EL_DYNABOMB_INCREASE_SIZE:
13267     case EL_DYNABOMB_INCREASE_POWER:
13268       RaiseScore(level.score[SC_DYNAMITE]);
13269       break;
13270     case EL_SHIELD_NORMAL:
13271     case EL_SHIELD_DEADLY:
13272       RaiseScore(level.score[SC_SHIELD]);
13273       break;
13274     case EL_EXTRA_TIME:
13275       RaiseScore(level.extra_time_score);
13276       break;
13277     case EL_KEY_1:
13278     case EL_KEY_2:
13279     case EL_KEY_3:
13280     case EL_KEY_4:
13281     case EL_EM_KEY_1:
13282     case EL_EM_KEY_2:
13283     case EL_EM_KEY_3:
13284     case EL_EM_KEY_4:
13285     case EL_EMC_KEY_5:
13286     case EL_EMC_KEY_6:
13287     case EL_EMC_KEY_7:
13288     case EL_EMC_KEY_8:
13289     case EL_DC_KEY_WHITE:
13290       RaiseScore(level.score[SC_KEY]);
13291       break;
13292     default:
13293       RaiseScore(element_info[element].collect_score);
13294       break;
13295   }
13296 }
13297
13298 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13299 {
13300   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13301   {
13302 #if defined(NETWORK_AVALIABLE)
13303     if (options.network)
13304       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13305     else
13306 #endif
13307     {
13308       if (quick_quit)
13309       {
13310         game_status = GAME_MODE_MAIN;
13311
13312         DrawMainMenu();
13313       }
13314       else
13315       {
13316         FadeOut(REDRAW_FIELD);
13317
13318         game_status = GAME_MODE_MAIN;
13319
13320         DrawAndFadeInMainMenu(REDRAW_FIELD);
13321       }
13322     }
13323   }
13324   else          /* continue playing the game */
13325   {
13326     if (tape.playing && tape.deactivate_display)
13327       TapeDeactivateDisplayOff(TRUE);
13328
13329     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13330
13331     if (tape.playing && tape.deactivate_display)
13332       TapeDeactivateDisplayOn();
13333   }
13334 }
13335
13336 void RequestQuitGame(boolean ask_if_really_quit)
13337 {
13338   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13339   boolean skip_request = AllPlayersGone || quick_quit;
13340
13341   RequestQuitGameExt(skip_request, quick_quit,
13342                      "Do you really want to quit the game ?");
13343 }
13344
13345
13346 /* ------------------------------------------------------------------------- */
13347 /* random generator functions                                                */
13348 /* ------------------------------------------------------------------------- */
13349
13350 unsigned int InitEngineRandom_RND(long seed)
13351 {
13352   game.num_random_calls = 0;
13353
13354 #if 0
13355   unsigned int rnd_seed = InitEngineRandom(seed);
13356
13357   printf("::: START RND: %d\n", rnd_seed);
13358
13359   return rnd_seed;
13360 #else
13361
13362   return InitEngineRandom(seed);
13363
13364 #endif
13365
13366 }
13367
13368 unsigned int RND(int max)
13369 {
13370   if (max > 0)
13371   {
13372     game.num_random_calls++;
13373
13374     return GetEngineRandom(max);
13375   }
13376
13377   return 0;
13378 }
13379
13380
13381 /* ------------------------------------------------------------------------- */
13382 /* game engine snapshot handling functions                                   */
13383 /* ------------------------------------------------------------------------- */
13384
13385 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
13386
13387 struct EngineSnapshotInfo
13388 {
13389   /* runtime values for custom element collect score */
13390   int collect_score[NUM_CUSTOM_ELEMENTS];
13391
13392   /* runtime values for group element choice position */
13393   int choice_pos[NUM_GROUP_ELEMENTS];
13394
13395   /* runtime values for belt position animations */
13396   int belt_graphic[4 * NUM_BELT_PARTS];
13397   int belt_anim_mode[4 * NUM_BELT_PARTS];
13398 };
13399
13400 struct EngineSnapshotNodeInfo
13401 {
13402   void *buffer_orig;
13403   void *buffer_copy;
13404   int size;
13405 };
13406
13407 static struct EngineSnapshotInfo engine_snapshot_rnd;
13408 static ListNode *engine_snapshot_list = NULL;
13409 static char *snapshot_level_identifier = NULL;
13410 static int snapshot_level_nr = -1;
13411
13412 void FreeEngineSnapshot()
13413 {
13414   while (engine_snapshot_list != NULL)
13415     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13416                        checked_free);
13417
13418   setString(&snapshot_level_identifier, NULL);
13419   snapshot_level_nr = -1;
13420 }
13421
13422 static void SaveEngineSnapshotValues_RND()
13423 {
13424   static int belt_base_active_element[4] =
13425   {
13426     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13427     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13428     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13429     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13430   };
13431   int i, j;
13432
13433   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13434   {
13435     int element = EL_CUSTOM_START + i;
13436
13437     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13438   }
13439
13440   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13441   {
13442     int element = EL_GROUP_START + i;
13443
13444     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13445   }
13446
13447   for (i = 0; i < 4; i++)
13448   {
13449     for (j = 0; j < NUM_BELT_PARTS; j++)
13450     {
13451       int element = belt_base_active_element[i] + j;
13452       int graphic = el2img(element);
13453       int anim_mode = graphic_info[graphic].anim_mode;
13454
13455       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13456       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13457     }
13458   }
13459 }
13460
13461 static void LoadEngineSnapshotValues_RND()
13462 {
13463   unsigned long num_random_calls = game.num_random_calls;
13464   int i, j;
13465
13466   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13467   {
13468     int element = EL_CUSTOM_START + i;
13469
13470     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13471   }
13472
13473   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13474   {
13475     int element = EL_GROUP_START + i;
13476
13477     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13478   }
13479
13480   for (i = 0; i < 4; i++)
13481   {
13482     for (j = 0; j < NUM_BELT_PARTS; j++)
13483     {
13484       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13485       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13486
13487       graphic_info[graphic].anim_mode = anim_mode;
13488     }
13489   }
13490
13491   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13492   {
13493     InitRND(tape.random_seed);
13494     for (i = 0; i < num_random_calls; i++)
13495       RND(1);
13496   }
13497
13498   if (game.num_random_calls != num_random_calls)
13499   {
13500     Error(ERR_RETURN, "number of random calls out of sync");
13501     Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
13502     Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
13503     Error(ERR_EXIT, "this should not happen -- please debug");
13504   }
13505 }
13506
13507 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13508 {
13509   struct EngineSnapshotNodeInfo *bi =
13510     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13511
13512   bi->buffer_orig = buffer;
13513   bi->buffer_copy = checked_malloc(size);
13514   bi->size = size;
13515
13516   memcpy(bi->buffer_copy, buffer, size);
13517
13518   addNodeToList(&engine_snapshot_list, NULL, bi);
13519 }
13520
13521 void SaveEngineSnapshot()
13522 {
13523   FreeEngineSnapshot();         /* free previous snapshot, if needed */
13524
13525   if (level_editor_test_game)   /* do not save snapshots from editor */
13526     return;
13527
13528   /* copy some special values to a structure better suited for the snapshot */
13529
13530   SaveEngineSnapshotValues_RND();
13531   SaveEngineSnapshotValues_EM();
13532
13533   /* save values stored in special snapshot structure */
13534
13535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13536   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13537
13538   /* save further RND engine values */
13539
13540   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13541   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13542   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13543
13544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13545   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13547   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13548
13549   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13551   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13554
13555   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13558
13559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13560
13561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13562
13563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13565
13566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13570   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13572   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13574   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13577   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13583   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13584
13585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13587
13588   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13589   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13590   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13591
13592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13594
13595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13599   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13600
13601   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13603
13604   /* save level identification information */
13605
13606   setString(&snapshot_level_identifier, leveldir_current->identifier);
13607   snapshot_level_nr = level_nr;
13608
13609 #if 0
13610   ListNode *node = engine_snapshot_list;
13611   int num_bytes = 0;
13612
13613   while (node != NULL)
13614   {
13615     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13616
13617     node = node->next;
13618   }
13619
13620   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13621 #endif
13622 }
13623
13624 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13625 {
13626   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13627 }
13628
13629 void LoadEngineSnapshot()
13630 {
13631   ListNode *node = engine_snapshot_list;
13632
13633   if (engine_snapshot_list == NULL)
13634     return;
13635
13636   while (node != NULL)
13637   {
13638     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13639
13640     node = node->next;
13641   }
13642
13643   /* restore special values from snapshot structure */
13644
13645   LoadEngineSnapshotValues_RND();
13646   LoadEngineSnapshotValues_EM();
13647 }
13648
13649 boolean CheckEngineSnapshot()
13650 {
13651   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13652           snapshot_level_nr == level_nr);
13653 }
13654
13655
13656 /* ---------- new game button stuff ---------------------------------------- */
13657
13658 /* graphic position values for game buttons */
13659 #define GAME_BUTTON_XSIZE       30
13660 #define GAME_BUTTON_YSIZE       30
13661 #define GAME_BUTTON_XPOS        5
13662 #define GAME_BUTTON_YPOS        215
13663 #define SOUND_BUTTON_XPOS       5
13664 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13665
13666 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13667 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13668 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13669 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13670 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13671 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13672
13673 static struct
13674 {
13675   int x, y;
13676   int gadget_id;
13677   char *infotext;
13678 } gamebutton_info[NUM_GAME_BUTTONS] =
13679 {
13680   {
13681     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13682     GAME_CTRL_ID_STOP,
13683     "stop game"
13684   },
13685   {
13686     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13687     GAME_CTRL_ID_PAUSE,
13688     "pause game"
13689   },
13690   {
13691     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13692     GAME_CTRL_ID_PLAY,
13693     "play game"
13694   },
13695   {
13696     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13697     SOUND_CTRL_ID_MUSIC,
13698     "background music on/off"
13699   },
13700   {
13701     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13702     SOUND_CTRL_ID_LOOPS,
13703     "sound loops on/off"
13704   },
13705   {
13706     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13707     SOUND_CTRL_ID_SIMPLE,
13708     "normal sounds on/off"
13709   }
13710 };
13711
13712 void CreateGameButtons()
13713 {
13714   int i;
13715
13716   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13717   {
13718     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13719     struct GadgetInfo *gi;
13720     int button_type;
13721     boolean checked;
13722     unsigned long event_mask;
13723     int gd_xoffset, gd_yoffset;
13724     int gd_x1, gd_x2, gd_y1, gd_y2;
13725     int id = i;
13726
13727     gd_xoffset = gamebutton_info[i].x;
13728     gd_yoffset = gamebutton_info[i].y;
13729     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13730     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13731
13732     if (id == GAME_CTRL_ID_STOP ||
13733         id == GAME_CTRL_ID_PAUSE ||
13734         id == GAME_CTRL_ID_PLAY)
13735     {
13736       button_type = GD_TYPE_NORMAL_BUTTON;
13737       checked = FALSE;
13738       event_mask = GD_EVENT_RELEASED;
13739       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13740       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13741     }
13742     else
13743     {
13744       button_type = GD_TYPE_CHECK_BUTTON;
13745       checked =
13746         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13747          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13748          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13749       event_mask = GD_EVENT_PRESSED;
13750       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
13751       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13752     }
13753
13754     gi = CreateGadget(GDI_CUSTOM_ID, id,
13755                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
13756                       GDI_X, DX + gd_xoffset,
13757                       GDI_Y, DY + gd_yoffset,
13758                       GDI_WIDTH, GAME_BUTTON_XSIZE,
13759                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
13760                       GDI_TYPE, button_type,
13761                       GDI_STATE, GD_BUTTON_UNPRESSED,
13762                       GDI_CHECKED, checked,
13763                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13764                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13765                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13766                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13767                       GDI_EVENT_MASK, event_mask,
13768                       GDI_CALLBACK_ACTION, HandleGameButtons,
13769                       GDI_END);
13770
13771     if (gi == NULL)
13772       Error(ERR_EXIT, "cannot create gadget");
13773
13774     game_gadget[id] = gi;
13775   }
13776 }
13777
13778 void FreeGameButtons()
13779 {
13780   int i;
13781
13782   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13783     FreeGadget(game_gadget[i]);
13784 }
13785
13786 static void MapGameButtons()
13787 {
13788   int i;
13789
13790   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13791     MapGadget(game_gadget[i]);
13792 }
13793
13794 void UnmapGameButtons()
13795 {
13796   int i;
13797
13798   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13799     UnmapGadget(game_gadget[i]);
13800 }
13801
13802 static void HandleGameButtons(struct GadgetInfo *gi)
13803 {
13804   int id = gi->custom_id;
13805
13806   if (game_status != GAME_MODE_PLAYING)
13807     return;
13808
13809   switch (id)
13810   {
13811     case GAME_CTRL_ID_STOP:
13812       if (tape.playing)
13813         TapeStop();
13814       else
13815         RequestQuitGame(TRUE);
13816       break;
13817
13818     case GAME_CTRL_ID_PAUSE:
13819       if (options.network)
13820       {
13821 #if defined(NETWORK_AVALIABLE)
13822         if (tape.pausing)
13823           SendToServer_ContinuePlaying();
13824         else
13825           SendToServer_PausePlaying();
13826 #endif
13827       }
13828       else
13829         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13830       break;
13831
13832     case GAME_CTRL_ID_PLAY:
13833       if (tape.pausing)
13834       {
13835 #if defined(NETWORK_AVALIABLE)
13836         if (options.network)
13837           SendToServer_ContinuePlaying();
13838         else
13839 #endif
13840         {
13841           tape.pausing = FALSE;
13842           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13843         }
13844       }
13845       break;
13846
13847     case SOUND_CTRL_ID_MUSIC:
13848       if (setup.sound_music)
13849       { 
13850         setup.sound_music = FALSE;
13851         FadeMusic();
13852       }
13853       else if (audio.music_available)
13854       { 
13855         setup.sound = setup.sound_music = TRUE;
13856
13857         SetAudioMode(setup.sound);
13858
13859         PlayLevelMusic();
13860       }
13861       break;
13862
13863     case SOUND_CTRL_ID_LOOPS:
13864       if (setup.sound_loops)
13865         setup.sound_loops = FALSE;
13866       else if (audio.loops_available)
13867       {
13868         setup.sound = setup.sound_loops = TRUE;
13869         SetAudioMode(setup.sound);
13870       }
13871       break;
13872
13873     case SOUND_CTRL_ID_SIMPLE:
13874       if (setup.sound_simple)
13875         setup.sound_simple = FALSE;
13876       else if (audio.sound_available)
13877       {
13878         setup.sound = setup.sound_simple = TRUE;
13879         SetAudioMode(setup.sound);
13880       }
13881       break;
13882
13883     default:
13884       break;
13885   }
13886 }