rnd-20070124-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 #define PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
89 #define PANEL_XPOS(p)           (ALIGNED_XPOS((p).x, (p).width, (p).align))
90 #define PANEL_YPOS(p)           ((p).y)
91
92 /* special positions in the game control window (relative to control window) */
93 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
94 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
95 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
96 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
97 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
98 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
99 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
100 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
101 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
102 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
103 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
104 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
105 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
106 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
107 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
108 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
109
110 /* special positions in the game control window (relative to main window) */
111 #define DX_LEVEL1               (DX + XX_LEVEL1)
112 #define DX_LEVEL2               (DX + XX_LEVEL2)
113 #define DX_LEVEL                (DX + XX_LEVEL)
114 #define DY_LEVEL                (DY + YY_LEVEL)
115 #define DX_EMERALDS             (DX + XX_EMERALDS)
116 #define DY_EMERALDS             (DY + YY_EMERALDS)
117 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
118 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
119 #define DX_KEYS                 (DX + XX_KEYS)
120 #define DY_KEYS                 (DY + YY_KEYS)
121 #define DX_SCORE                (DX + XX_SCORE)
122 #define DY_SCORE                (DY + YY_SCORE)
123 #define DX_TIME1                (DX + XX_TIME1)
124 #define DX_TIME2                (DX + XX_TIME2)
125 #define DX_TIME                 (DX + XX_TIME)
126 #define DY_TIME                 (DY + YY_TIME)
127
128 /* values for delayed check of falling and moving elements and for collision */
129 #define CHECK_DELAY_MOVING      3
130 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
131 #define CHECK_DELAY_COLLISION   2
132 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
133
134 /* values for initial player move delay (initial delay counter value) */
135 #define INITIAL_MOVE_DELAY_OFF  -1
136 #define INITIAL_MOVE_DELAY_ON   0
137
138 /* values for player movement speed (which is in fact a delay value) */
139 #define MOVE_DELAY_MIN_SPEED    32
140 #define MOVE_DELAY_NORMAL_SPEED 8
141 #define MOVE_DELAY_HIGH_SPEED   4
142 #define MOVE_DELAY_MAX_SPEED    1
143
144 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
145 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
146
147 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
148 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
149
150 /* values for other actions */
151 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
152 #define MOVE_STEPSIZE_MIN       (1)
153 #define MOVE_STEPSIZE_MAX       (TILEX)
154
155 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
156 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
157
158 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
159
160 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
161                                  RND(element_info[e].push_delay_random))
162 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
163                                  RND(element_info[e].drop_delay_random))
164 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
165                                  RND(element_info[e].move_delay_random))
166 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
167                                     (element_info[e].move_delay_random))
168 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
169                                  RND(element_info[e].ce_value_random_initial))
170 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
171 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
172                                  RND((c)->delay_random * (c)->delay_frames))
173 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
174                                  RND((c)->delay_random))
175
176
177 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
178          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
179
180 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
181         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
182          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
183          (be) + (e) - EL_SELF)
184
185 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
186         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
187          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
188          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
189          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
190          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
191          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
192          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
193          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
194          (e))
195
196 #define CAN_GROW_INTO(e)                                                \
197         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
198
199 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
200                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
201                                         (condition)))
202
203 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
204                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
205                                         (CAN_MOVE_INTO_ACID(e) &&       \
206                                          Feld[x][y] == EL_ACID) ||      \
207                                         (condition)))
208
209 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
210                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
211                                         (CAN_MOVE_INTO_ACID(e) &&       \
212                                          Feld[x][y] == EL_ACID) ||      \
213                                         (condition)))
214
215 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
216                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
217                                         (condition) ||                  \
218                                         (CAN_MOVE_INTO_ACID(e) &&       \
219                                          Feld[x][y] == EL_ACID) ||      \
220                                         (DONT_COLLIDE_WITH(e) &&        \
221                                          IS_PLAYER(x, y) &&             \
222                                          !PLAYER_ENEMY_PROTECTED(x, y))))
223
224 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
225         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
226
227 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
228         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
229
230 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
231         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
232
233 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
234         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
235                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
236
237 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
238         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
239
240 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
241         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
242
243 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
244         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
245
246 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
247         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
248
249 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
250         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
251
252 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
253         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
254                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
255                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
256                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
257                                                  IS_FOOD_PENGUIN(Feld[x][y])))
258 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
259         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
260
261 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
262         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
263
264 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
265         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
266
267 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
268         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
269                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
270
271 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
272
273 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
274                 (!IS_PLAYER(x, y) &&                                    \
275                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
276
277 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
278         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
279
280 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
281 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
282
283 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
284 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
285 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
286 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
287
288 /* game button identifiers */
289 #define GAME_CTRL_ID_STOP               0
290 #define GAME_CTRL_ID_PAUSE              1
291 #define GAME_CTRL_ID_PLAY               2
292 #define SOUND_CTRL_ID_MUSIC             3
293 #define SOUND_CTRL_ID_LOOPS             4
294 #define SOUND_CTRL_ID_SIMPLE            5
295
296 #define NUM_GAME_BUTTONS                6
297
298
299 /* forward declaration for internal use */
300
301 static void CreateField(int, int, int);
302
303 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
304 static void AdvanceFrameAndPlayerCounters(int);
305
306 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
307 static boolean MovePlayer(struct PlayerInfo *, int, int);
308 static void ScrollPlayer(struct PlayerInfo *, int);
309 static void ScrollScreen(struct PlayerInfo *, int);
310
311 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
312
313 static void InitBeltMovement(void);
314 static void CloseAllOpenTimegates(void);
315 static void CheckGravityMovement(struct PlayerInfo *);
316 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
317 static void KillPlayerUnlessEnemyProtected(int, int);
318 static void KillPlayerUnlessExplosionProtected(int, int);
319
320 static void TestIfPlayerTouchesCustomElement(int, int);
321 static void TestIfElementTouchesCustomElement(int, int);
322 static void TestIfElementHitsCustomElement(int, int, int);
323 #if 0
324 static void TestIfElementSmashesCustomElement(int, int, int);
325 #endif
326
327 static void HandleElementChange(int, int, int);
328 static void ExecuteCustomElementAction(int, int, int, int);
329 static boolean ChangeElement(int, int, int, int);
330
331 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
332 #define CheckTriggeredElementChange(x, y, e, ev)                        \
333         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
334 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
335         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
336 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
337         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
338 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
339         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
340
341 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
342 #define CheckElementChange(x, y, e, te, ev)                             \
343         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
344 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
345         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
346 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
347         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
348
349 static void PlayLevelSound(int, int, int);
350 static void PlayLevelSoundNearest(int, int, int);
351 static void PlayLevelSoundAction(int, int, int);
352 static void PlayLevelSoundElementAction(int, int, int, int);
353 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
354 static void PlayLevelSoundActionIfLoop(int, int, int);
355 static void StopLevelSoundActionIfLoop(int, int, int);
356 static void PlayLevelMusic();
357
358 static void MapGameButtons();
359 static void HandleGameButtons(struct GadgetInfo *);
360
361 int AmoebeNachbarNr(int, int);
362 void AmoebeUmwandeln(int, int);
363 void ContinueMoving(int, int);
364 void Bang(int, int);
365 void InitMovDir(int, int);
366 void InitAmoebaNr(int, int);
367 int NewHiScore(void);
368
369 void TestIfGoodThingHitsBadThing(int, int, int);
370 void TestIfBadThingHitsGoodThing(int, int, int);
371 void TestIfPlayerTouchesBadThing(int, int);
372 void TestIfPlayerRunsIntoBadThing(int, int, int);
373 void TestIfBadThingTouchesPlayer(int, int);
374 void TestIfBadThingRunsIntoPlayer(int, int, int);
375 void TestIfFriendTouchesBadThing(int, int);
376 void TestIfBadThingTouchesFriend(int, int);
377 void TestIfBadThingTouchesOtherBadThing(int, int);
378
379 void KillPlayer(struct PlayerInfo *);
380 void BuryPlayer(struct PlayerInfo *);
381 void RemovePlayer(struct PlayerInfo *);
382
383 boolean SnapField(struct PlayerInfo *, int, int);
384 boolean DropElement(struct PlayerInfo *);
385
386 static int getInvisibleActiveFromInvisibleElement(int);
387 static int getInvisibleFromInvisibleActiveElement(int);
388
389 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
390
391 /* for detection of endless loops, caused by custom element programming */
392 /* (using maximal playfield width x 10 is just a rough approximation) */
393 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
394
395 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
396 {                                                                       \
397   if (recursion_loop_detected)                                          \
398     return (rc);                                                        \
399                                                                         \
400   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
401   {                                                                     \
402     recursion_loop_detected = TRUE;                                     \
403     recursion_loop_element = (e);                                       \
404   }                                                                     \
405                                                                         \
406   recursion_loop_depth++;                                               \
407 }
408
409 #define RECURSION_LOOP_DETECTION_END()                                  \
410 {                                                                       \
411   recursion_loop_depth--;                                               \
412 }
413
414 static int recursion_loop_depth;
415 static boolean recursion_loop_detected;
416 static boolean recursion_loop_element;
417
418
419 /* ------------------------------------------------------------------------- */
420 /* definition of elements that automatically change to other elements after  */
421 /* a specified time, eventually calling a function when changing             */
422 /* ------------------------------------------------------------------------- */
423
424 /* forward declaration for changer functions */
425 static void InitBuggyBase(int, int);
426 static void WarnBuggyBase(int, int);
427
428 static void InitTrap(int, int);
429 static void ActivateTrap(int, int);
430 static void ChangeActiveTrap(int, int);
431
432 static void InitRobotWheel(int, int);
433 static void RunRobotWheel(int, int);
434 static void StopRobotWheel(int, int);
435
436 static void InitTimegateWheel(int, int);
437 static void RunTimegateWheel(int, int);
438
439 static void InitMagicBallDelay(int, int);
440 static void ActivateMagicBall(int, int);
441
442 struct ChangingElementInfo
443 {
444   int element;
445   int target_element;
446   int change_delay;
447   void (*pre_change_function)(int x, int y);
448   void (*change_function)(int x, int y);
449   void (*post_change_function)(int x, int y);
450 };
451
452 static struct ChangingElementInfo change_delay_list[] =
453 {
454   {
455     EL_NUT_BREAKING,
456     EL_EMERALD,
457     6,
458     NULL,
459     NULL,
460     NULL
461   },
462   {
463     EL_PEARL_BREAKING,
464     EL_EMPTY,
465     8,
466     NULL,
467     NULL,
468     NULL
469   },
470   {
471     EL_EXIT_OPENING,
472     EL_EXIT_OPEN,
473     29,
474     NULL,
475     NULL,
476     NULL
477   },
478   {
479     EL_EXIT_CLOSING,
480     EL_EXIT_CLOSED,
481     29,
482     NULL,
483     NULL,
484     NULL
485   },
486   {
487     EL_STEEL_EXIT_OPENING,
488     EL_STEEL_EXIT_OPEN,
489     29,
490     NULL,
491     NULL,
492     NULL
493   },
494   {
495     EL_STEEL_EXIT_CLOSING,
496     EL_STEEL_EXIT_CLOSED,
497     29,
498     NULL,
499     NULL,
500     NULL
501   },
502   {
503     EL_EM_EXIT_OPENING,
504     EL_EM_EXIT_OPEN,
505     29,
506     NULL,
507     NULL,
508     NULL
509   },
510   {
511     EL_EM_EXIT_CLOSING,
512 #if 1
513     EL_EMPTY,
514 #else
515     EL_EM_EXIT_CLOSED,
516 #endif
517     29,
518     NULL,
519     NULL,
520     NULL
521   },
522   {
523     EL_EM_STEEL_EXIT_OPENING,
524     EL_EM_STEEL_EXIT_OPEN,
525     29,
526     NULL,
527     NULL,
528     NULL
529   },
530   {
531     EL_EM_STEEL_EXIT_CLOSING,
532 #if 1
533     EL_STEELWALL,
534 #else
535     EL_EM_STEEL_EXIT_CLOSED,
536 #endif
537     29,
538     NULL,
539     NULL,
540     NULL
541   },
542   {
543     EL_SP_EXIT_OPENING,
544     EL_SP_EXIT_OPEN,
545     29,
546     NULL,
547     NULL,
548     NULL
549   },
550   {
551     EL_SP_EXIT_CLOSING,
552     EL_SP_EXIT_CLOSED,
553     29,
554     NULL,
555     NULL,
556     NULL
557   },
558   {
559     EL_SWITCHGATE_OPENING,
560     EL_SWITCHGATE_OPEN,
561     29,
562     NULL,
563     NULL,
564     NULL
565   },
566   {
567     EL_SWITCHGATE_CLOSING,
568     EL_SWITCHGATE_CLOSED,
569     29,
570     NULL,
571     NULL,
572     NULL
573   },
574   {
575     EL_TIMEGATE_OPENING,
576     EL_TIMEGATE_OPEN,
577     29,
578     NULL,
579     NULL,
580     NULL
581   },
582   {
583     EL_TIMEGATE_CLOSING,
584     EL_TIMEGATE_CLOSED,
585     29,
586     NULL,
587     NULL,
588     NULL
589   },
590
591   {
592     EL_ACID_SPLASH_LEFT,
593     EL_EMPTY,
594     8,
595     NULL,
596     NULL,
597     NULL
598   },
599   {
600     EL_ACID_SPLASH_RIGHT,
601     EL_EMPTY,
602     8,
603     NULL,
604     NULL,
605     NULL
606   },
607   {
608     EL_SP_BUGGY_BASE,
609     EL_SP_BUGGY_BASE_ACTIVATING,
610     0,
611     InitBuggyBase,
612     NULL,
613     NULL
614   },
615   {
616     EL_SP_BUGGY_BASE_ACTIVATING,
617     EL_SP_BUGGY_BASE_ACTIVE,
618     0,
619     InitBuggyBase,
620     NULL,
621     NULL
622   },
623   {
624     EL_SP_BUGGY_BASE_ACTIVE,
625     EL_SP_BUGGY_BASE,
626     0,
627     InitBuggyBase,
628     WarnBuggyBase,
629     NULL
630   },
631   {
632     EL_TRAP,
633     EL_TRAP_ACTIVE,
634     0,
635     InitTrap,
636     NULL,
637     ActivateTrap
638   },
639   {
640     EL_TRAP_ACTIVE,
641     EL_TRAP,
642     31,
643     NULL,
644     ChangeActiveTrap,
645     NULL
646   },
647   {
648     EL_ROBOT_WHEEL_ACTIVE,
649     EL_ROBOT_WHEEL,
650     0,
651     InitRobotWheel,
652     RunRobotWheel,
653     StopRobotWheel
654   },
655   {
656     EL_TIMEGATE_SWITCH_ACTIVE,
657     EL_TIMEGATE_SWITCH,
658     0,
659     InitTimegateWheel,
660     RunTimegateWheel,
661     NULL
662   },
663   {
664     EL_DC_TIMEGATE_SWITCH_ACTIVE,
665     EL_DC_TIMEGATE_SWITCH,
666     0,
667     InitTimegateWheel,
668     RunTimegateWheel,
669     NULL
670   },
671   {
672     EL_EMC_MAGIC_BALL_ACTIVE,
673     EL_EMC_MAGIC_BALL_ACTIVE,
674     0,
675     InitMagicBallDelay,
676     NULL,
677     ActivateMagicBall
678   },
679   {
680     EL_EMC_SPRING_BUMPER_ACTIVE,
681     EL_EMC_SPRING_BUMPER,
682     8,
683     NULL,
684     NULL,
685     NULL
686   },
687   {
688     EL_DIAGONAL_SHRINKING,
689     EL_UNDEFINED,
690     0,
691     NULL,
692     NULL,
693     NULL
694   },
695   {
696     EL_DIAGONAL_GROWING,
697     EL_UNDEFINED,
698     0,
699     NULL,
700     NULL,
701     NULL,
702   },
703
704   {
705     EL_UNDEFINED,
706     EL_UNDEFINED,
707     -1,
708     NULL,
709     NULL,
710     NULL
711   }
712 };
713
714 struct
715 {
716   int element;
717   int push_delay_fixed, push_delay_random;
718 }
719 push_delay_list[] =
720 {
721   { EL_SPRING,                  0, 0 },
722   { EL_BALLOON,                 0, 0 },
723
724   { EL_SOKOBAN_OBJECT,          2, 0 },
725   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
726   { EL_SATELLITE,               2, 0 },
727   { EL_SP_DISK_YELLOW,          2, 0 },
728
729   { EL_UNDEFINED,               0, 0 },
730 };
731
732 struct
733 {
734   int element;
735   int move_stepsize;
736 }
737 move_stepsize_list[] =
738 {
739   { EL_AMOEBA_DROP,             2 },
740   { EL_AMOEBA_DROPPING,         2 },
741   { EL_QUICKSAND_FILLING,       1 },
742   { EL_QUICKSAND_EMPTYING,      1 },
743   { EL_QUICKSAND_FAST_FILLING,  2 },
744   { EL_QUICKSAND_FAST_EMPTYING, 2 },
745   { EL_MAGIC_WALL_FILLING,      2 },
746   { EL_MAGIC_WALL_EMPTYING,     2 },
747   { EL_BD_MAGIC_WALL_FILLING,   2 },
748   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
749   { EL_DC_MAGIC_WALL_FILLING,   2 },
750   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
751
752   { EL_UNDEFINED,               0 },
753 };
754
755 struct
756 {
757   int element;
758   int count;
759 }
760 collect_count_list[] =
761 {
762   { EL_EMERALD,                 1 },
763   { EL_BD_DIAMOND,              1 },
764   { EL_EMERALD_YELLOW,          1 },
765   { EL_EMERALD_RED,             1 },
766   { EL_EMERALD_PURPLE,          1 },
767   { EL_DIAMOND,                 3 },
768   { EL_SP_INFOTRON,             1 },
769   { EL_PEARL,                   5 },
770   { EL_CRYSTAL,                 8 },
771
772   { EL_UNDEFINED,               0 },
773 };
774
775 struct
776 {
777   int element;
778   int direction;
779 }
780 access_direction_list[] =
781 {
782   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
783   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
784   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
785   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
786   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
787   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
788   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
789   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
790   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
791   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
792   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
793
794   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
795   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
796   { EL_SP_PORT_UP,                                                   MV_DOWN },
797   { EL_SP_PORT_DOWN,                                         MV_UP           },
798   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
799   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
800   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
801   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
802   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
803   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
804   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
805   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
806   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
807   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
808   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
809   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
810   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
811   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
812   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
813
814   { EL_UNDEFINED,                       MV_NONE                              }
815 };
816
817 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
818
819 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
820 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
821 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
822                                  IS_JUST_CHANGING(x, y))
823
824 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
825
826 /* static variables for playfield scan mode (scanning forward or backward) */
827 static int playfield_scan_start_x = 0;
828 static int playfield_scan_start_y = 0;
829 static int playfield_scan_delta_x = 1;
830 static int playfield_scan_delta_y = 1;
831
832 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
833                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
834                                      (y) += playfield_scan_delta_y)     \
835                                 for ((x) = playfield_scan_start_x;      \
836                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
837                                      (x) += playfield_scan_delta_x)     \
838
839 #ifdef DEBUG
840 void DEBUG_SetMaximumDynamite()
841 {
842   int i;
843
844   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
845     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
846       local_player->inventory_element[local_player->inventory_size++] =
847         EL_DYNAMITE;
848 }
849 #endif
850
851 static void InitPlayfieldScanModeVars()
852 {
853   if (game.use_reverse_scan_direction)
854   {
855     playfield_scan_start_x = lev_fieldx - 1;
856     playfield_scan_start_y = lev_fieldy - 1;
857
858     playfield_scan_delta_x = -1;
859     playfield_scan_delta_y = -1;
860   }
861   else
862   {
863     playfield_scan_start_x = 0;
864     playfield_scan_start_y = 0;
865
866     playfield_scan_delta_x = 1;
867     playfield_scan_delta_y = 1;
868   }
869 }
870
871 static void InitPlayfieldScanMode(int mode)
872 {
873   game.use_reverse_scan_direction =
874     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
875
876   InitPlayfieldScanModeVars();
877 }
878
879 static int get_move_delay_from_stepsize(int move_stepsize)
880 {
881   move_stepsize =
882     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
883
884   /* make sure that stepsize value is always a power of 2 */
885   move_stepsize = (1 << log_2(move_stepsize));
886
887   return TILEX / move_stepsize;
888 }
889
890 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
891                                boolean init_game)
892 {
893   int player_nr = player->index_nr;
894   int move_delay = get_move_delay_from_stepsize(move_stepsize);
895   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
896
897   /* do no immediately change move delay -- the player might just be moving */
898   player->move_delay_value_next = move_delay;
899
900   /* information if player can move must be set separately */
901   player->cannot_move = cannot_move;
902
903   if (init_game)
904   {
905     player->move_delay       = game.initial_move_delay[player_nr];
906     player->move_delay_value = game.initial_move_delay_value[player_nr];
907
908     player->move_delay_value_next = -1;
909
910     player->move_delay_reset_counter = 0;
911   }
912 }
913
914 void GetPlayerConfig()
915 {
916   if (!audio.sound_available)
917     setup.sound_simple = FALSE;
918
919   if (!audio.loops_available)
920     setup.sound_loops = FALSE;
921
922   if (!audio.music_available)
923     setup.sound_music = FALSE;
924
925   if (!video.fullscreen_available)
926     setup.fullscreen = FALSE;
927
928   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
929
930   SetAudioMode(setup.sound);
931   InitJoysticks();
932 }
933
934 static int getBeltNrFromBeltElement(int element)
935 {
936   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
937           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
938           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
939 }
940
941 static int getBeltNrFromBeltActiveElement(int element)
942 {
943   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
944           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
945           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
946 }
947
948 static int getBeltNrFromBeltSwitchElement(int element)
949 {
950   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
951           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
952           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
953 }
954
955 static int getBeltDirNrFromBeltSwitchElement(int element)
956 {
957   static int belt_base_element[4] =
958   {
959     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
960     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
961     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
962     EL_CONVEYOR_BELT_4_SWITCH_LEFT
963   };
964
965   int belt_nr = getBeltNrFromBeltSwitchElement(element);
966   int belt_dir_nr = element - belt_base_element[belt_nr];
967
968   return (belt_dir_nr % 3);
969 }
970
971 static int getBeltDirFromBeltSwitchElement(int element)
972 {
973   static int belt_move_dir[3] =
974   {
975     MV_LEFT,
976     MV_NONE,
977     MV_RIGHT
978   };
979
980   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
981
982   return belt_move_dir[belt_dir_nr];
983 }
984
985 static int get_element_from_group_element(int element)
986 {
987   if (IS_GROUP_ELEMENT(element))
988   {
989     struct ElementGroupInfo *group = element_info[element].group;
990     int last_anim_random_frame = gfx.anim_random_frame;
991     int element_pos;
992
993     if (group->choice_mode == ANIM_RANDOM)
994       gfx.anim_random_frame = RND(group->num_elements_resolved);
995
996     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
997                                     group->choice_mode, 0,
998                                     group->choice_pos);
999
1000     if (group->choice_mode == ANIM_RANDOM)
1001       gfx.anim_random_frame = last_anim_random_frame;
1002
1003     group->choice_pos++;
1004
1005     element = group->element_resolved[element_pos];
1006   }
1007
1008   return element;
1009 }
1010
1011 static void InitPlayerField(int x, int y, int element, boolean init_game)
1012 {
1013   if (element == EL_SP_MURPHY)
1014   {
1015     if (init_game)
1016     {
1017       if (stored_player[0].present)
1018       {
1019         Feld[x][y] = EL_SP_MURPHY_CLONE;
1020
1021         return;
1022       }
1023       else
1024       {
1025         stored_player[0].use_murphy = TRUE;
1026
1027         if (!level.use_artwork_element[0])
1028           stored_player[0].artwork_element = EL_SP_MURPHY;
1029       }
1030
1031       Feld[x][y] = EL_PLAYER_1;
1032     }
1033   }
1034
1035   if (init_game)
1036   {
1037     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1038     int jx = player->jx, jy = player->jy;
1039
1040     player->present = TRUE;
1041
1042     player->block_last_field = (element == EL_SP_MURPHY ?
1043                                 level.sp_block_last_field :
1044                                 level.block_last_field);
1045
1046     /* ---------- initialize player's last field block delay --------------- */
1047
1048     /* always start with reliable default value (no adjustment needed) */
1049     player->block_delay_adjustment = 0;
1050
1051     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1052     if (player->block_last_field && element == EL_SP_MURPHY)
1053       player->block_delay_adjustment = 1;
1054
1055     /* special case 2: in game engines before 3.1.1, blocking was different */
1056     if (game.use_block_last_field_bug)
1057       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1058
1059     if (!options.network || player->connected)
1060     {
1061       player->active = TRUE;
1062
1063       /* remove potentially duplicate players */
1064       if (StorePlayer[jx][jy] == Feld[x][y])
1065         StorePlayer[jx][jy] = 0;
1066
1067       StorePlayer[x][y] = Feld[x][y];
1068
1069       if (options.debug)
1070       {
1071         printf("Player %d activated.\n", player->element_nr);
1072         printf("[Local player is %d and currently %s.]\n",
1073                local_player->element_nr,
1074                local_player->active ? "active" : "not active");
1075       }
1076     }
1077
1078     Feld[x][y] = EL_EMPTY;
1079
1080     player->jx = player->last_jx = x;
1081     player->jy = player->last_jy = y;
1082   }
1083 }
1084
1085 static void InitField(int x, int y, boolean init_game)
1086 {
1087   int element = Feld[x][y];
1088
1089   switch (element)
1090   {
1091     case EL_SP_MURPHY:
1092     case EL_PLAYER_1:
1093     case EL_PLAYER_2:
1094     case EL_PLAYER_3:
1095     case EL_PLAYER_4:
1096       InitPlayerField(x, y, element, init_game);
1097       break;
1098
1099     case EL_SOKOBAN_FIELD_PLAYER:
1100       element = Feld[x][y] = EL_PLAYER_1;
1101       InitField(x, y, init_game);
1102
1103       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1104       InitField(x, y, init_game);
1105       break;
1106
1107     case EL_SOKOBAN_FIELD_EMPTY:
1108       local_player->sokobanfields_still_needed++;
1109       break;
1110
1111     case EL_STONEBLOCK:
1112       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1113         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1114       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1115         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1116       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1117         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1118       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1119         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1120       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1121         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1122       break;
1123
1124     case EL_BUG:
1125     case EL_BUG_RIGHT:
1126     case EL_BUG_UP:
1127     case EL_BUG_LEFT:
1128     case EL_BUG_DOWN:
1129     case EL_SPACESHIP:
1130     case EL_SPACESHIP_RIGHT:
1131     case EL_SPACESHIP_UP:
1132     case EL_SPACESHIP_LEFT:
1133     case EL_SPACESHIP_DOWN:
1134     case EL_BD_BUTTERFLY:
1135     case EL_BD_BUTTERFLY_RIGHT:
1136     case EL_BD_BUTTERFLY_UP:
1137     case EL_BD_BUTTERFLY_LEFT:
1138     case EL_BD_BUTTERFLY_DOWN:
1139     case EL_BD_FIREFLY:
1140     case EL_BD_FIREFLY_RIGHT:
1141     case EL_BD_FIREFLY_UP:
1142     case EL_BD_FIREFLY_LEFT:
1143     case EL_BD_FIREFLY_DOWN:
1144     case EL_PACMAN_RIGHT:
1145     case EL_PACMAN_UP:
1146     case EL_PACMAN_LEFT:
1147     case EL_PACMAN_DOWN:
1148     case EL_YAMYAM:
1149     case EL_YAMYAM_LEFT:
1150     case EL_YAMYAM_RIGHT:
1151     case EL_YAMYAM_UP:
1152     case EL_YAMYAM_DOWN:
1153     case EL_DARK_YAMYAM:
1154     case EL_ROBOT:
1155     case EL_PACMAN:
1156     case EL_SP_SNIKSNAK:
1157     case EL_SP_ELECTRON:
1158     case EL_MOLE:
1159     case EL_MOLE_LEFT:
1160     case EL_MOLE_RIGHT:
1161     case EL_MOLE_UP:
1162     case EL_MOLE_DOWN:
1163       InitMovDir(x, y);
1164       break;
1165
1166     case EL_AMOEBA_FULL:
1167     case EL_BD_AMOEBA:
1168       InitAmoebaNr(x, y);
1169       break;
1170
1171     case EL_AMOEBA_DROP:
1172       if (y == lev_fieldy - 1)
1173       {
1174         Feld[x][y] = EL_AMOEBA_GROWING;
1175         Store[x][y] = EL_AMOEBA_WET;
1176       }
1177       break;
1178
1179     case EL_DYNAMITE_ACTIVE:
1180     case EL_SP_DISK_RED_ACTIVE:
1181     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1182     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1183     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1184     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1185       MovDelay[x][y] = 96;
1186       break;
1187
1188     case EL_EM_DYNAMITE_ACTIVE:
1189       MovDelay[x][y] = 32;
1190       break;
1191
1192     case EL_LAMP:
1193       local_player->lights_still_needed++;
1194       break;
1195
1196     case EL_PENGUIN:
1197       local_player->friends_still_needed++;
1198       break;
1199
1200     case EL_PIG:
1201     case EL_DRAGON:
1202       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1203       break;
1204
1205     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1206     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1207     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1208     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1209     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1210     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1211     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1212     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1213     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1214     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1215     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1216     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1217       if (init_game)
1218       {
1219         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1220         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1221         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1222
1223         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1224         {
1225           game.belt_dir[belt_nr] = belt_dir;
1226           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1227         }
1228         else    /* more than one switch -- set it like the first switch */
1229         {
1230           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1231         }
1232       }
1233       break;
1234
1235 #if !USE_BOTH_SWITCHGATE_SWITCHES
1236     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1237       if (init_game)
1238         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1239       break;
1240
1241     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1242       if (init_game)
1243         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1244       break;
1245 #endif
1246
1247     case EL_LIGHT_SWITCH_ACTIVE:
1248       if (init_game)
1249         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1250       break;
1251
1252     case EL_INVISIBLE_STEELWALL:
1253     case EL_INVISIBLE_WALL:
1254     case EL_INVISIBLE_SAND:
1255       if (game.light_time_left > 0 ||
1256           game.lenses_time_left > 0)
1257         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1258       break;
1259
1260     case EL_EMC_MAGIC_BALL:
1261       if (game.ball_state)
1262         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1263       break;
1264
1265     case EL_EMC_MAGIC_BALL_SWITCH:
1266       if (game.ball_state)
1267         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1268       break;
1269
1270     default:
1271       if (IS_CUSTOM_ELEMENT(element))
1272       {
1273         if (CAN_MOVE(element))
1274           InitMovDir(x, y);
1275
1276 #if USE_NEW_CUSTOM_VALUE
1277         if (!element_info[element].use_last_ce_value || init_game)
1278           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1279 #endif
1280       }
1281       else if (IS_GROUP_ELEMENT(element))
1282       {
1283         Feld[x][y] = get_element_from_group_element(element);
1284
1285         InitField(x, y, init_game);
1286       }
1287
1288       break;
1289   }
1290
1291   if (!init_game)
1292     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1293 }
1294
1295 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1296 {
1297   InitField(x, y, init_game);
1298
1299   /* not needed to call InitMovDir() -- already done by InitField()! */
1300   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1301       CAN_MOVE(Feld[x][y]))
1302     InitMovDir(x, y);
1303 }
1304
1305 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1306 {
1307   int old_element = Feld[x][y];
1308
1309   InitField(x, y, init_game);
1310
1311   /* not needed to call InitMovDir() -- already done by InitField()! */
1312   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1313       CAN_MOVE(old_element) &&
1314       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1315     InitMovDir(x, y);
1316
1317   /* this case is in fact a combination of not less than three bugs:
1318      first, it calls InitMovDir() for elements that can move, although this is
1319      already done by InitField(); then, it checks the element that was at this
1320      field _before_ the call to InitField() (which can change it); lastly, it
1321      was not called for "mole with direction" elements, which were treated as
1322      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1323   */
1324 }
1325
1326 #if 1
1327
1328 inline void DrawGameValue_Emeralds(int value)
1329 {
1330   int font_nr = FONT_TEXT_2;
1331   int font_width = getFontWidth(font_nr);
1332   int xpos = (3 * 14 - 3 * font_width) / 2;
1333
1334   if (PANEL_DEACTIVATED(game.panel.gems))
1335     return;
1336
1337   game.panel.gems.width = game.panel.gems.chars * font_width;
1338   xpos = 0;
1339
1340   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1341 }
1342
1343 inline void DrawGameValue_Dynamite(int value)
1344 {
1345   int font_nr = FONT_TEXT_2;
1346   int font_width = getFontWidth(font_nr);
1347   int xpos = (3 * 14 - 3 * font_width) / 2;
1348
1349   if (PANEL_DEACTIVATED(game.panel.inventory))
1350     return;
1351
1352   game.panel.inventory.width = game.panel.inventory.chars * font_width;
1353   xpos = 0;
1354
1355   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1356 }
1357
1358 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1359 {
1360   int base_key_graphic = EL_KEY_1;
1361   int i;
1362
1363   if (PANEL_DEACTIVATED(game.panel.keys))
1364     return;
1365
1366   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1367     base_key_graphic = EL_EM_KEY_1;
1368
1369   /* currently only 4 of 8 possible keys are displayed */
1370   for (i = 0; i < STD_NUM_KEYS; i++)
1371   {
1372     int x = XX_KEYS + i * MINI_TILEX;
1373     int y = YY_KEYS;
1374
1375     if (key[i])
1376       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1377     else
1378       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1379                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1380   }
1381 }
1382
1383 inline void DrawGameValue_Score(int value)
1384 {
1385   int font_nr = FONT_TEXT_2;
1386   int font_width = getFontWidth(font_nr);
1387   int xpos = (5 * 14 - 5 * font_width) / 2;
1388
1389   if (PANEL_DEACTIVATED(game.panel.score))
1390     return;
1391
1392   game.panel.score.width = game.panel.score.chars * font_width;
1393   xpos = 0;
1394
1395   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1396 }
1397
1398 inline void DrawGameValue_Time(int value)
1399 {
1400   static int last_value = -1;
1401   int num_digits1 = 3;
1402   int num_digits2 = 4;
1403   int num_digits = game.panel.time.chars;
1404   int font1_nr = FONT_TEXT_2;
1405   int font2_nr = FONT_TEXT_1;
1406   int font_nr = font1_nr;
1407   int font1_width = getFontWidth(font1_nr);
1408   int font2_width = getFontWidth(font2_nr);
1409   int xpos3 = (3 * 14 - 3 * font1_width) / 2;
1410   int xpos4 = (4 * 10 - 4 * font2_width) / 2;
1411
1412   if (PANEL_DEACTIVATED(game.panel.time))
1413     return;
1414
1415   if (num_digits == -1)                 /* use dynamic number of digits */
1416   {
1417     num_digits = (value < 1000 ? num_digits1 : num_digits2);
1418     font_nr    = (value < 1000 ? font1_nr : font2_nr);
1419   }
1420
1421   xpos3 = 0;
1422   xpos4 = 0;
1423
1424   /* clear background if value just changed its size (dynamic digits only) */
1425   if (game.panel.time.chars == -1 && (last_value < 1000) != (value < 1000))
1426   {
1427     int width1 = num_digits1 * getFontWidth(font1_nr);
1428     int width2 = num_digits2 * getFontWidth(font2_nr);
1429     int max_width = MAX(width1, width2);
1430     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1431
1432     game.panel.time.width = max_width;
1433
1434     ClearRectangleOnBackground(drawto, DX_TIME, DY_TIME, max_width, max_height);
1435   }
1436
1437   game.panel.time.width = num_digits * getFontWidth(font_nr);
1438
1439   DrawText(DX_TIME, DY_TIME, int2str(value, num_digits), font_nr);
1440
1441   last_value = value;
1442 }
1443
1444 inline void DrawGameValue_Level(int value)
1445 {
1446   int num_digits1 = 2;
1447   int num_digits2 = 3;
1448   int num_digits = game.panel.level.chars;
1449   int font1_nr = FONT_TEXT_2;
1450   int font2_nr = FONT_TEXT_1;
1451   int font_nr = font1_nr;
1452
1453   if (PANEL_DEACTIVATED(game.panel.level))
1454     return;
1455
1456   if (num_digits == -1)                 /* use dynamic number of digits */
1457   {
1458     num_digits = (level_nr < 100 ? num_digits1 : num_digits2);
1459     font_nr    = (level_nr < 100 ? font1_nr : font2_nr);
1460   }
1461
1462   game.panel.level.width = num_digits * getFontWidth(font_nr);
1463
1464   DrawText(DX_LEVEL, DY_LEVEL, int2str(value, num_digits), font_nr);
1465 }
1466
1467 #else
1468
1469 inline void DrawGameValue_Emeralds(int value)
1470 {
1471   int font_nr = FONT_TEXT_2;
1472   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1473
1474   if (PANEL_DEACTIVATED(game.panel.gems))
1475     return;
1476
1477   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1478 }
1479
1480 inline void DrawGameValue_Dynamite(int value)
1481 {
1482   int font_nr = FONT_TEXT_2;
1483   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1484
1485   if (PANEL_DEACTIVATED(game.panel.inventory))
1486     return;
1487
1488   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1489 }
1490
1491 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1492 {
1493   int base_key_graphic = EL_KEY_1;
1494   int i;
1495
1496   if (PANEL_DEACTIVATED(game.panel.keys))
1497     return;
1498
1499   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1500     base_key_graphic = EL_EM_KEY_1;
1501
1502   /* currently only 4 of 8 possible keys are displayed */
1503   for (i = 0; i < STD_NUM_KEYS; i++)
1504   {
1505     int x = XX_KEYS + i * MINI_TILEX;
1506     int y = YY_KEYS;
1507
1508     if (key[i])
1509       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1510     else
1511       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1512                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1513   }
1514 }
1515
1516 inline void DrawGameValue_Score(int value)
1517 {
1518   int font_nr = FONT_TEXT_2;
1519   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1520
1521   if (PANEL_DEACTIVATED(game.panel.score))
1522     return;
1523
1524   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1525 }
1526
1527 inline void DrawGameValue_Time(int value)
1528 {
1529   int font1_nr = FONT_TEXT_2;
1530 #if 1
1531   int font2_nr = FONT_TEXT_1;
1532 #else
1533   int font2_nr = FONT_LEVEL_NUMBER;
1534 #endif
1535   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1536   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1537
1538   if (PANEL_DEACTIVATED(game.panel.time))
1539     return;
1540
1541   /* clear background if value just changed its size */
1542   if (value == 999 || value == 1000)
1543     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1544
1545   if (value < 1000)
1546     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1547   else
1548     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1549 }
1550
1551 inline void DrawGameValue_Level(int value)
1552 {
1553   int font1_nr = FONT_TEXT_2;
1554 #if 1
1555   int font2_nr = FONT_TEXT_1;
1556 #else
1557   int font2_nr = FONT_LEVEL_NUMBER;
1558 #endif
1559
1560   if (PANEL_DEACTIVATED(game.panel.level))
1561     return;
1562
1563   if (level_nr < 100)
1564     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1565   else
1566     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1567 }
1568
1569 #endif
1570
1571 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1572                        int key_bits)
1573 {
1574   int key[MAX_NUM_KEYS];
1575   int i;
1576
1577   /* prevent EM engine from updating time/score values parallel to GameWon() */
1578   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1579       local_player->LevelSolved)
1580     return;
1581
1582   for (i = 0; i < MAX_NUM_KEYS; i++)
1583     key[i] = key_bits & (1 << i);
1584
1585   DrawGameValue_Level(level_nr);
1586
1587   DrawGameValue_Emeralds(emeralds);
1588   DrawGameValue_Dynamite(dynamite);
1589   DrawGameValue_Score(score);
1590   DrawGameValue_Time(time);
1591
1592   DrawGameValue_Keys(key);
1593 }
1594
1595 void DrawGameDoorValues()
1596 {
1597   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1598   int dynamite_value = 0;
1599   int score_value = (local_player->LevelSolved ? local_player->score_final :
1600                      local_player->score);
1601   int gems_value = local_player->gems_still_needed;
1602   int key_bits = 0;
1603   int i, j;
1604
1605   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1606   {
1607     DrawGameDoorValues_EM();
1608
1609     return;
1610   }
1611
1612   if (game.centered_player_nr == -1)
1613   {
1614     for (i = 0; i < MAX_PLAYERS; i++)
1615     {
1616       for (j = 0; j < MAX_NUM_KEYS; j++)
1617         if (stored_player[i].key[j])
1618           key_bits |= (1 << j);
1619
1620       dynamite_value += stored_player[i].inventory_size;
1621     }
1622   }
1623   else
1624   {
1625     int player_nr = game.centered_player_nr;
1626
1627     for (i = 0; i < MAX_NUM_KEYS; i++)
1628       if (stored_player[player_nr].key[i])
1629         key_bits |= (1 << i);
1630
1631     dynamite_value = stored_player[player_nr].inventory_size;
1632   }
1633
1634   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1635                     key_bits);
1636 }
1637
1638
1639 /*
1640   =============================================================================
1641   InitGameEngine()
1642   -----------------------------------------------------------------------------
1643   initialize game engine due to level / tape version number
1644   =============================================================================
1645 */
1646
1647 static void InitGameEngine()
1648 {
1649   int i, j, k, l, x, y;
1650
1651   /* set game engine from tape file when re-playing, else from level file */
1652   game.engine_version = (tape.playing ? tape.engine_version :
1653                          level.game_version);
1654
1655   /* ---------------------------------------------------------------------- */
1656   /* set flags for bugs and changes according to active game engine version */
1657   /* ---------------------------------------------------------------------- */
1658
1659   /*
1660     Summary of bugfix/change:
1661     Fixed handling for custom elements that change when pushed by the player.
1662
1663     Fixed/changed in version:
1664     3.1.0
1665
1666     Description:
1667     Before 3.1.0, custom elements that "change when pushing" changed directly
1668     after the player started pushing them (until then handled in "DigField()").
1669     Since 3.1.0, these custom elements are not changed until the "pushing"
1670     move of the element is finished (now handled in "ContinueMoving()").
1671
1672     Affected levels/tapes:
1673     The first condition is generally needed for all levels/tapes before version
1674     3.1.0, which might use the old behaviour before it was changed; known tapes
1675     that are affected are some tapes from the level set "Walpurgis Gardens" by
1676     Jamie Cullen.
1677     The second condition is an exception from the above case and is needed for
1678     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1679     above (including some development versions of 3.1.0), but before it was
1680     known that this change would break tapes like the above and was fixed in
1681     3.1.1, so that the changed behaviour was active although the engine version
1682     while recording maybe was before 3.1.0. There is at least one tape that is
1683     affected by this exception, which is the tape for the one-level set "Bug
1684     Machine" by Juergen Bonhagen.
1685   */
1686
1687   game.use_change_when_pushing_bug =
1688     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1689      !(tape.playing &&
1690        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1691        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1692
1693   /*
1694     Summary of bugfix/change:
1695     Fixed handling for blocking the field the player leaves when moving.
1696
1697     Fixed/changed in version:
1698     3.1.1
1699
1700     Description:
1701     Before 3.1.1, when "block last field when moving" was enabled, the field
1702     the player is leaving when moving was blocked for the time of the move,
1703     and was directly unblocked afterwards. This resulted in the last field
1704     being blocked for exactly one less than the number of frames of one player
1705     move. Additionally, even when blocking was disabled, the last field was
1706     blocked for exactly one frame.
1707     Since 3.1.1, due to changes in player movement handling, the last field
1708     is not blocked at all when blocking is disabled. When blocking is enabled,
1709     the last field is blocked for exactly the number of frames of one player
1710     move. Additionally, if the player is Murphy, the hero of Supaplex, the
1711     last field is blocked for exactly one more than the number of frames of
1712     one player move.
1713
1714     Affected levels/tapes:
1715     (!!! yet to be determined -- probably many !!!)
1716   */
1717
1718   game.use_block_last_field_bug =
1719     (game.engine_version < VERSION_IDENT(3,1,1,0));
1720
1721   /*
1722     Summary of bugfix/change:
1723     Changed behaviour of CE changes with multiple changes per single frame.
1724
1725     Fixed/changed in version:
1726     3.2.0-6
1727
1728     Description:
1729     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1730     This resulted in race conditions where CEs seem to behave strange in some
1731     situations (where triggered CE changes were just skipped because there was
1732     already a CE change on that tile in the playfield in that engine frame).
1733     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1734     (The number of changes per frame must be limited in any case, because else
1735     it is easily possible to define CE changes that would result in an infinite
1736     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1737     should be set large enough so that it would only be reached in cases where
1738     the corresponding CE change conditions run into a loop. Therefore, it seems
1739     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1740     maximal number of change pages for custom elements.)
1741
1742     Affected levels/tapes:
1743     Probably many.
1744   */
1745
1746 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1747   game.max_num_changes_per_frame = 1;
1748 #else
1749   game.max_num_changes_per_frame =
1750     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1751 #endif
1752
1753   /* ---------------------------------------------------------------------- */
1754
1755   /* default scan direction: scan playfield from top/left to bottom/right */
1756   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1757
1758   /* dynamically adjust element properties according to game engine version */
1759   InitElementPropertiesEngine(game.engine_version);
1760
1761 #if 0
1762   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1763   printf("          tape version == %06d [%s] [file: %06d]\n",
1764          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1765          tape.file_version);
1766   printf("       => game.engine_version == %06d\n", game.engine_version);
1767 #endif
1768
1769   /* ---------- initialize player's initial move delay --------------------- */
1770
1771   /* dynamically adjust player properties according to level information */
1772   for (i = 0; i < MAX_PLAYERS; i++)
1773     game.initial_move_delay_value[i] =
1774       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1775
1776   /* dynamically adjust player properties according to game engine version */
1777   for (i = 0; i < MAX_PLAYERS; i++)
1778     game.initial_move_delay[i] =
1779       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1780        game.initial_move_delay_value[i] : 0);
1781
1782   /* ---------- initialize player's initial push delay --------------------- */
1783
1784   /* dynamically adjust player properties according to game engine version */
1785   game.initial_push_delay_value =
1786     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1787
1788   /* ---------- initialize changing elements ------------------------------- */
1789
1790   /* initialize changing elements information */
1791   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1792   {
1793     struct ElementInfo *ei = &element_info[i];
1794
1795     /* this pointer might have been changed in the level editor */
1796     ei->change = &ei->change_page[0];
1797
1798     if (!IS_CUSTOM_ELEMENT(i))
1799     {
1800       ei->change->target_element = EL_EMPTY_SPACE;
1801       ei->change->delay_fixed = 0;
1802       ei->change->delay_random = 0;
1803       ei->change->delay_frames = 1;
1804     }
1805
1806     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1807     {
1808       ei->has_change_event[j] = FALSE;
1809
1810       ei->event_page_nr[j] = 0;
1811       ei->event_page[j] = &ei->change_page[0];
1812     }
1813   }
1814
1815   /* add changing elements from pre-defined list */
1816   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1817   {
1818     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1819     struct ElementInfo *ei = &element_info[ch_delay->element];
1820
1821     ei->change->target_element       = ch_delay->target_element;
1822     ei->change->delay_fixed          = ch_delay->change_delay;
1823
1824     ei->change->pre_change_function  = ch_delay->pre_change_function;
1825     ei->change->change_function      = ch_delay->change_function;
1826     ei->change->post_change_function = ch_delay->post_change_function;
1827
1828     ei->change->can_change = TRUE;
1829     ei->change->can_change_or_has_action = TRUE;
1830
1831     ei->has_change_event[CE_DELAY] = TRUE;
1832
1833     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1834     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1835   }
1836
1837   /* ---------- initialize internal run-time variables ------------- */
1838
1839   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1840   {
1841     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1842
1843     for (j = 0; j < ei->num_change_pages; j++)
1844     {
1845       ei->change_page[j].can_change_or_has_action =
1846         (ei->change_page[j].can_change |
1847          ei->change_page[j].has_action);
1848     }
1849   }
1850
1851   /* add change events from custom element configuration */
1852   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1853   {
1854     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1855
1856     for (j = 0; j < ei->num_change_pages; j++)
1857     {
1858       if (!ei->change_page[j].can_change_or_has_action)
1859         continue;
1860
1861       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1862       {
1863         /* only add event page for the first page found with this event */
1864         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1865         {
1866           ei->has_change_event[k] = TRUE;
1867
1868           ei->event_page_nr[k] = j;
1869           ei->event_page[k] = &ei->change_page[j];
1870         }
1871       }
1872     }
1873   }
1874
1875   /* ---------- initialize run-time trigger player and element ------------- */
1876
1877   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1878   {
1879     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1880
1881     for (j = 0; j < ei->num_change_pages; j++)
1882     {
1883       ei->change_page[j].actual_trigger_element = EL_EMPTY;
1884       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1885       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1886       ei->change_page[j].actual_trigger_ce_value = 0;
1887       ei->change_page[j].actual_trigger_ce_score = 0;
1888     }
1889   }
1890
1891   /* ---------- initialize trigger events ---------------------------------- */
1892
1893   /* initialize trigger events information */
1894   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1895     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1896       trigger_events[i][j] = FALSE;
1897
1898   /* add trigger events from element change event properties */
1899   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1900   {
1901     struct ElementInfo *ei = &element_info[i];
1902
1903     for (j = 0; j < ei->num_change_pages; j++)
1904     {
1905       if (!ei->change_page[j].can_change_or_has_action)
1906         continue;
1907
1908       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1909       {
1910         int trigger_element = ei->change_page[j].trigger_element;
1911
1912         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1913         {
1914           if (ei->change_page[j].has_event[k])
1915           {
1916             if (IS_GROUP_ELEMENT(trigger_element))
1917             {
1918               struct ElementGroupInfo *group =
1919                 element_info[trigger_element].group;
1920
1921               for (l = 0; l < group->num_elements_resolved; l++)
1922                 trigger_events[group->element_resolved[l]][k] = TRUE;
1923             }
1924             else if (trigger_element == EL_ANY_ELEMENT)
1925               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1926                 trigger_events[l][k] = TRUE;
1927             else
1928               trigger_events[trigger_element][k] = TRUE;
1929           }
1930         }
1931       }
1932     }
1933   }
1934
1935   /* ---------- initialize push delay -------------------------------------- */
1936
1937   /* initialize push delay values to default */
1938   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1939   {
1940     if (!IS_CUSTOM_ELEMENT(i))
1941     {
1942       /* set default push delay values (corrected since version 3.0.7-1) */
1943       if (game.engine_version < VERSION_IDENT(3,0,7,1))
1944       {
1945         element_info[i].push_delay_fixed = 2;
1946         element_info[i].push_delay_random = 8;
1947       }
1948       else
1949       {
1950         element_info[i].push_delay_fixed = 8;
1951         element_info[i].push_delay_random = 8;
1952       }
1953     }
1954   }
1955
1956   /* set push delay value for certain elements from pre-defined list */
1957   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1958   {
1959     int e = push_delay_list[i].element;
1960
1961     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1962     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1963   }
1964
1965   /* set push delay value for Supaplex elements for newer engine versions */
1966   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1967   {
1968     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1969     {
1970       if (IS_SP_ELEMENT(i))
1971       {
1972         /* set SP push delay to just enough to push under a falling zonk */
1973         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1974
1975         element_info[i].push_delay_fixed  = delay;
1976         element_info[i].push_delay_random = 0;
1977       }
1978     }
1979   }
1980
1981   /* ---------- initialize move stepsize ----------------------------------- */
1982
1983   /* initialize move stepsize values to default */
1984   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1985     if (!IS_CUSTOM_ELEMENT(i))
1986       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1987
1988   /* set move stepsize value for certain elements from pre-defined list */
1989   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1990   {
1991     int e = move_stepsize_list[i].element;
1992
1993     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1994   }
1995
1996   /* ---------- initialize collect score ----------------------------------- */
1997
1998   /* initialize collect score values for custom elements from initial value */
1999   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2000     if (IS_CUSTOM_ELEMENT(i))
2001       element_info[i].collect_score = element_info[i].collect_score_initial;
2002
2003   /* ---------- initialize collect count ----------------------------------- */
2004
2005   /* initialize collect count values for non-custom elements */
2006   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2007     if (!IS_CUSTOM_ELEMENT(i))
2008       element_info[i].collect_count_initial = 0;
2009
2010   /* add collect count values for all elements from pre-defined list */
2011   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2012     element_info[collect_count_list[i].element].collect_count_initial =
2013       collect_count_list[i].count;
2014
2015   /* ---------- initialize access direction -------------------------------- */
2016
2017   /* initialize access direction values to default (access from every side) */
2018   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2019     if (!IS_CUSTOM_ELEMENT(i))
2020       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2021
2022   /* set access direction value for certain elements from pre-defined list */
2023   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2024     element_info[access_direction_list[i].element].access_direction =
2025       access_direction_list[i].direction;
2026
2027   /* ---------- initialize explosion content ------------------------------- */
2028   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2029   {
2030     if (IS_CUSTOM_ELEMENT(i))
2031       continue;
2032
2033     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2034     {
2035       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2036
2037       element_info[i].content.e[x][y] =
2038         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2039          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2040          i == EL_PLAYER_3 ? EL_EMERALD :
2041          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2042          i == EL_MOLE ? EL_EMERALD_RED :
2043          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2044          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2045          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2046          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2047          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2048          i == EL_WALL_EMERALD ? EL_EMERALD :
2049          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2050          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2051          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2052          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2053          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2054          i == EL_WALL_PEARL ? EL_PEARL :
2055          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2056          EL_EMPTY);
2057     }
2058   }
2059
2060   /* ---------- initialize recursion detection ------------------------------ */
2061   recursion_loop_depth = 0;
2062   recursion_loop_detected = FALSE;
2063   recursion_loop_element = EL_UNDEFINED;
2064 }
2065
2066 int get_num_special_action(int element, int action_first, int action_last)
2067 {
2068   int num_special_action = 0;
2069   int i, j;
2070
2071   for (i = action_first; i <= action_last; i++)
2072   {
2073     boolean found = FALSE;
2074
2075     for (j = 0; j < NUM_DIRECTIONS; j++)
2076       if (el_act_dir2img(element, i, j) !=
2077           el_act_dir2img(element, ACTION_DEFAULT, j))
2078         found = TRUE;
2079
2080     if (found)
2081       num_special_action++;
2082     else
2083       break;
2084   }
2085
2086   return num_special_action;
2087 }
2088
2089
2090 /*
2091   =============================================================================
2092   InitGame()
2093   -----------------------------------------------------------------------------
2094   initialize and start new game
2095   =============================================================================
2096 */
2097
2098 void InitGame()
2099 {
2100   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2101   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2102   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2103   boolean do_fading = (game_status == GAME_MODE_MAIN);
2104   int i, j, x, y;
2105
2106   game_status = GAME_MODE_PLAYING;
2107
2108   InitGameEngine();
2109
2110   /* don't play tapes over network */
2111   network_playing = (options.network && !tape.playing);
2112
2113   for (i = 0; i < MAX_PLAYERS; i++)
2114   {
2115     struct PlayerInfo *player = &stored_player[i];
2116
2117     player->index_nr = i;
2118     player->index_bit = (1 << i);
2119     player->element_nr = EL_PLAYER_1 + i;
2120
2121     player->present = FALSE;
2122     player->active = FALSE;
2123     player->killed = FALSE;
2124
2125     player->action = 0;
2126     player->effective_action = 0;
2127     player->programmed_action = 0;
2128
2129     player->score = 0;
2130     player->score_final = 0;
2131
2132     player->gems_still_needed = level.gems_needed;
2133     player->sokobanfields_still_needed = 0;
2134     player->lights_still_needed = 0;
2135     player->friends_still_needed = 0;
2136
2137     for (j = 0; j < MAX_NUM_KEYS; j++)
2138       player->key[j] = FALSE;
2139
2140     player->num_white_keys = 0;
2141
2142     player->dynabomb_count = 0;
2143     player->dynabomb_size = 1;
2144     player->dynabombs_left = 0;
2145     player->dynabomb_xl = FALSE;
2146
2147     player->MovDir = MV_NONE;
2148     player->MovPos = 0;
2149     player->GfxPos = 0;
2150     player->GfxDir = MV_NONE;
2151     player->GfxAction = ACTION_DEFAULT;
2152     player->Frame = 0;
2153     player->StepFrame = 0;
2154
2155     player->use_murphy = FALSE;
2156     player->artwork_element =
2157       (level.use_artwork_element[i] ? level.artwork_element[i] :
2158        player->element_nr);
2159
2160     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2161     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2162
2163     player->gravity = level.initial_player_gravity[i];
2164
2165     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2166
2167     player->actual_frame_counter = 0;
2168
2169     player->step_counter = 0;
2170
2171     player->last_move_dir = MV_NONE;
2172
2173     player->is_active = FALSE;
2174
2175     player->is_waiting = FALSE;
2176     player->is_moving = FALSE;
2177     player->is_auto_moving = FALSE;
2178     player->is_digging = FALSE;
2179     player->is_snapping = FALSE;
2180     player->is_collecting = FALSE;
2181     player->is_pushing = FALSE;
2182     player->is_switching = FALSE;
2183     player->is_dropping = FALSE;
2184     player->is_dropping_pressed = FALSE;
2185
2186     player->is_bored = FALSE;
2187     player->is_sleeping = FALSE;
2188
2189     player->frame_counter_bored = -1;
2190     player->frame_counter_sleeping = -1;
2191
2192     player->anim_delay_counter = 0;
2193     player->post_delay_counter = 0;
2194
2195     player->dir_waiting = MV_NONE;
2196     player->action_waiting = ACTION_DEFAULT;
2197     player->last_action_waiting = ACTION_DEFAULT;
2198     player->special_action_bored = ACTION_DEFAULT;
2199     player->special_action_sleeping = ACTION_DEFAULT;
2200
2201     player->switch_x = -1;
2202     player->switch_y = -1;
2203
2204     player->drop_x = -1;
2205     player->drop_y = -1;
2206
2207     player->show_envelope = 0;
2208
2209     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2210
2211     player->push_delay       = -1;      /* initialized when pushing starts */
2212     player->push_delay_value = game.initial_push_delay_value;
2213
2214     player->drop_delay = 0;
2215     player->drop_pressed_delay = 0;
2216
2217     player->last_jx = -1;
2218     player->last_jy = -1;
2219     player->jx = -1;
2220     player->jy = -1;
2221
2222     player->shield_normal_time_left = 0;
2223     player->shield_deadly_time_left = 0;
2224
2225     player->inventory_infinite_element = EL_UNDEFINED;
2226     player->inventory_size = 0;
2227
2228     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2229     SnapField(player, 0, 0);
2230
2231     player->LevelSolved = FALSE;
2232     player->GameOver = FALSE;
2233
2234     player->LevelSolved_GameEnd = FALSE;
2235     player->LevelSolved_SaveTape = FALSE;
2236     player->LevelSolved_SaveScore = FALSE;
2237   }
2238
2239   network_player_action_received = FALSE;
2240
2241 #if defined(NETWORK_AVALIABLE)
2242   /* initial null action */
2243   if (network_playing)
2244     SendToServer_MovePlayer(MV_NONE);
2245 #endif
2246
2247   ZX = ZY = -1;
2248   ExitX = ExitY = -1;
2249
2250   FrameCounter = 0;
2251   TimeFrames = 0;
2252   TimePlayed = 0;
2253   TimeLeft = level.time;
2254   TapeTime = 0;
2255
2256   ScreenMovDir = MV_NONE;
2257   ScreenMovPos = 0;
2258   ScreenGfxPos = 0;
2259
2260   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2261
2262   AllPlayersGone = FALSE;
2263
2264   game.yamyam_content_nr = 0;
2265   game.magic_wall_active = FALSE;
2266   game.magic_wall_time_left = 0;
2267   game.light_time_left = 0;
2268   game.timegate_time_left = 0;
2269   game.switchgate_pos = 0;
2270   game.wind_direction = level.wind_direction_initial;
2271
2272 #if !USE_PLAYER_GRAVITY
2273   game.gravity = FALSE;
2274   game.explosions_delayed = TRUE;
2275 #endif
2276
2277   game.lenses_time_left = 0;
2278   game.magnify_time_left = 0;
2279
2280   game.ball_state = level.ball_state_initial;
2281   game.ball_content_nr = 0;
2282
2283   game.envelope_active = FALSE;
2284
2285   /* set focus to local player for network games, else to all players */
2286   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2287   game.centered_player_nr_next = game.centered_player_nr;
2288   game.set_centered_player = FALSE;
2289
2290   if (network_playing && tape.recording)
2291   {
2292     /* store client dependent player focus when recording network games */
2293     tape.centered_player_nr_next = game.centered_player_nr_next;
2294     tape.set_centered_player = TRUE;
2295   }
2296
2297   for (i = 0; i < NUM_BELTS; i++)
2298   {
2299     game.belt_dir[i] = MV_NONE;
2300     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2301   }
2302
2303   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2304     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2305
2306   SCAN_PLAYFIELD(x, y)
2307   {
2308     Feld[x][y] = level.field[x][y];
2309     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2310     ChangeDelay[x][y] = 0;
2311     ChangePage[x][y] = -1;
2312 #if USE_NEW_CUSTOM_VALUE
2313     CustomValue[x][y] = 0;              /* initialized in InitField() */
2314 #endif
2315     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2316     AmoebaNr[x][y] = 0;
2317     WasJustMoving[x][y] = 0;
2318     WasJustFalling[x][y] = 0;
2319     CheckCollision[x][y] = 0;
2320     CheckImpact[x][y] = 0;
2321     Stop[x][y] = FALSE;
2322     Pushed[x][y] = FALSE;
2323
2324     ChangeCount[x][y] = 0;
2325     ChangeEvent[x][y] = -1;
2326
2327     ExplodePhase[x][y] = 0;
2328     ExplodeDelay[x][y] = 0;
2329     ExplodeField[x][y] = EX_TYPE_NONE;
2330
2331     RunnerVisit[x][y] = 0;
2332     PlayerVisit[x][y] = 0;
2333
2334     GfxFrame[x][y] = 0;
2335     GfxRandom[x][y] = INIT_GFX_RANDOM();
2336     GfxElement[x][y] = EL_UNDEFINED;
2337     GfxAction[x][y] = ACTION_DEFAULT;
2338     GfxDir[x][y] = MV_NONE;
2339   }
2340
2341   SCAN_PLAYFIELD(x, y)
2342   {
2343     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2344       emulate_bd = FALSE;
2345     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2346       emulate_sb = FALSE;
2347     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2348       emulate_sp = FALSE;
2349
2350     InitField(x, y, TRUE);
2351   }
2352
2353   InitBeltMovement();
2354
2355   for (i = 0; i < MAX_PLAYERS; i++)
2356   {
2357     struct PlayerInfo *player = &stored_player[i];
2358
2359     /* set number of special actions for bored and sleeping animation */
2360     player->num_special_action_bored =
2361       get_num_special_action(player->artwork_element,
2362                              ACTION_BORING_1, ACTION_BORING_LAST);
2363     player->num_special_action_sleeping =
2364       get_num_special_action(player->artwork_element,
2365                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2366   }
2367
2368   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2369                     emulate_sb ? EMU_SOKOBAN :
2370                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2371
2372 #if USE_NEW_ALL_SLIPPERY
2373   /* initialize type of slippery elements */
2374   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2375   {
2376     if (!IS_CUSTOM_ELEMENT(i))
2377     {
2378       /* default: elements slip down either to the left or right randomly */
2379       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2380
2381       /* SP style elements prefer to slip down on the left side */
2382       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2383         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2384
2385       /* BD style elements prefer to slip down on the left side */
2386       if (game.emulation == EMU_BOULDERDASH)
2387         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2388     }
2389   }
2390 #endif
2391
2392   /* initialize explosion and ignition delay */
2393   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2394   {
2395     if (!IS_CUSTOM_ELEMENT(i))
2396     {
2397       int num_phase = 8;
2398       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2399                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2400                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2401       int last_phase = (num_phase + 1) * delay;
2402       int half_phase = (num_phase / 2) * delay;
2403
2404       element_info[i].explosion_delay = last_phase - 1;
2405       element_info[i].ignition_delay = half_phase;
2406
2407       if (i == EL_BLACK_ORB)
2408         element_info[i].ignition_delay = 1;
2409     }
2410
2411 #if 0
2412     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2413       element_info[i].explosion_delay = 1;
2414
2415     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2416       element_info[i].ignition_delay = 1;
2417 #endif
2418   }
2419
2420   /* correct non-moving belts to start moving left */
2421   for (i = 0; i < NUM_BELTS; i++)
2422     if (game.belt_dir[i] == MV_NONE)
2423       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2424
2425   /* check if any connected player was not found in playfield */
2426   for (i = 0; i < MAX_PLAYERS; i++)
2427   {
2428     struct PlayerInfo *player = &stored_player[i];
2429
2430     if (player->connected && !player->present)
2431     {
2432       for (j = 0; j < MAX_PLAYERS; j++)
2433       {
2434         struct PlayerInfo *some_player = &stored_player[j];
2435         int jx = some_player->jx, jy = some_player->jy;
2436
2437         /* assign first free player found that is present in the playfield */
2438         if (some_player->present && !some_player->connected)
2439         {
2440           player->present = TRUE;
2441           player->active = TRUE;
2442
2443           some_player->present = FALSE;
2444           some_player->active = FALSE;
2445
2446           player->artwork_element = some_player->artwork_element;
2447
2448           player->block_last_field       = some_player->block_last_field;
2449           player->block_delay_adjustment = some_player->block_delay_adjustment;
2450
2451           StorePlayer[jx][jy] = player->element_nr;
2452           player->jx = player->last_jx = jx;
2453           player->jy = player->last_jy = jy;
2454
2455           break;
2456         }
2457       }
2458     }
2459   }
2460
2461   if (tape.playing)
2462   {
2463     /* when playing a tape, eliminate all players who do not participate */
2464
2465     for (i = 0; i < MAX_PLAYERS; i++)
2466     {
2467       if (stored_player[i].active && !tape.player_participates[i])
2468       {
2469         struct PlayerInfo *player = &stored_player[i];
2470         int jx = player->jx, jy = player->jy;
2471
2472         player->active = FALSE;
2473         StorePlayer[jx][jy] = 0;
2474         Feld[jx][jy] = EL_EMPTY;
2475       }
2476     }
2477   }
2478   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2479   {
2480     /* when in single player mode, eliminate all but the first active player */
2481
2482     for (i = 0; i < MAX_PLAYERS; i++)
2483     {
2484       if (stored_player[i].active)
2485       {
2486         for (j = i + 1; j < MAX_PLAYERS; j++)
2487         {
2488           if (stored_player[j].active)
2489           {
2490             struct PlayerInfo *player = &stored_player[j];
2491             int jx = player->jx, jy = player->jy;
2492
2493             player->active = FALSE;
2494             player->present = FALSE;
2495
2496             StorePlayer[jx][jy] = 0;
2497             Feld[jx][jy] = EL_EMPTY;
2498           }
2499         }
2500       }
2501     }
2502   }
2503
2504   /* when recording the game, store which players take part in the game */
2505   if (tape.recording)
2506   {
2507     for (i = 0; i < MAX_PLAYERS; i++)
2508       if (stored_player[i].active)
2509         tape.player_participates[i] = TRUE;
2510   }
2511
2512   if (options.debug)
2513   {
2514     for (i = 0; i < MAX_PLAYERS; i++)
2515     {
2516       struct PlayerInfo *player = &stored_player[i];
2517
2518       printf("Player %d: present == %d, connected == %d, active == %d.\n",
2519              i+1,
2520              player->present,
2521              player->connected,
2522              player->active);
2523       if (local_player == player)
2524         printf("Player  %d is local player.\n", i+1);
2525     }
2526   }
2527
2528   if (BorderElement == EL_EMPTY)
2529   {
2530     SBX_Left = 0;
2531     SBX_Right = lev_fieldx - SCR_FIELDX;
2532     SBY_Upper = 0;
2533     SBY_Lower = lev_fieldy - SCR_FIELDY;
2534   }
2535   else
2536   {
2537     SBX_Left = -1;
2538     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2539     SBY_Upper = -1;
2540     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2541   }
2542
2543   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2544     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2545
2546   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2547     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2548
2549   /* if local player not found, look for custom element that might create
2550      the player (make some assumptions about the right custom element) */
2551   if (!local_player->present)
2552   {
2553     int start_x = 0, start_y = 0;
2554     int found_rating = 0;
2555     int found_element = EL_UNDEFINED;
2556     int player_nr = local_player->index_nr;
2557
2558     SCAN_PLAYFIELD(x, y)
2559     {
2560       int element = Feld[x][y];
2561       int content;
2562       int xx, yy;
2563       boolean is_player;
2564
2565       if (level.use_start_element[player_nr] &&
2566           level.start_element[player_nr] == element &&
2567           found_rating < 4)
2568       {
2569         start_x = x;
2570         start_y = y;
2571
2572         found_rating = 4;
2573         found_element = element;
2574       }
2575
2576       if (!IS_CUSTOM_ELEMENT(element))
2577         continue;
2578
2579       if (CAN_CHANGE(element))
2580       {
2581         for (i = 0; i < element_info[element].num_change_pages; i++)
2582         {
2583           /* check for player created from custom element as single target */
2584           content = element_info[element].change_page[i].target_element;
2585           is_player = ELEM_IS_PLAYER(content);
2586
2587           if (is_player && (found_rating < 3 || element < found_element))
2588           {
2589             start_x = x;
2590             start_y = y;
2591
2592             found_rating = 3;
2593             found_element = element;
2594           }
2595         }
2596       }
2597
2598       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2599       {
2600         /* check for player created from custom element as explosion content */
2601         content = element_info[element].content.e[xx][yy];
2602         is_player = ELEM_IS_PLAYER(content);
2603
2604         if (is_player && (found_rating < 2 || element < found_element))
2605         {
2606           start_x = x + xx - 1;
2607           start_y = y + yy - 1;
2608
2609           found_rating = 2;
2610           found_element = element;
2611         }
2612
2613         if (!CAN_CHANGE(element))
2614           continue;
2615
2616         for (i = 0; i < element_info[element].num_change_pages; i++)
2617         {
2618           /* check for player created from custom element as extended target */
2619           content =
2620             element_info[element].change_page[i].target_content.e[xx][yy];
2621
2622           is_player = ELEM_IS_PLAYER(content);
2623
2624           if (is_player && (found_rating < 1 || element < found_element))
2625           {
2626             start_x = x + xx - 1;
2627             start_y = y + yy - 1;
2628
2629             found_rating = 1;
2630             found_element = element;
2631           }
2632         }
2633       }
2634     }
2635
2636     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
2637                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2638                 start_x - MIDPOSX);
2639
2640     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2641                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2642                 start_y - MIDPOSY);
2643   }
2644   else
2645   {
2646     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2647                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2648                 local_player->jx - MIDPOSX);
2649
2650     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2651                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2652                 local_player->jy - MIDPOSY);
2653   }
2654
2655   StopAnimation();
2656
2657   if (!game.restart_level)
2658     CloseDoor(DOOR_CLOSE_1);
2659
2660   if (do_fading)
2661     FadeOut(REDRAW_FIELD);
2662
2663   /* !!! FIX THIS (START) !!! */
2664   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2665   {
2666     InitGameEngine_EM();
2667
2668     /* blit playfield from scroll buffer to normal back buffer for fading in */
2669     BlitScreenToBitmap_EM(backbuffer);
2670   }
2671   else
2672   {
2673     DrawLevel();
2674     DrawAllPlayers();
2675
2676     /* after drawing the level, correct some elements */
2677     if (game.timegate_time_left == 0)
2678       CloseAllOpenTimegates();
2679
2680     /* blit playfield from scroll buffer to normal back buffer for fading in */
2681     if (setup.soft_scrolling)
2682       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2683
2684     redraw_mask |= REDRAW_FROM_BACKBUFFER;
2685   }
2686   /* !!! FIX THIS (END) !!! */
2687
2688   if (do_fading)
2689     FadeIn(REDRAW_FIELD);
2690
2691   BackToFront();
2692
2693   if (!game.restart_level)
2694   {
2695     /* copy default game door content to main double buffer */
2696     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2697                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2698   }
2699
2700   SetPanelBackground();
2701   SetDrawBackgroundMask(REDRAW_DOOR_1);
2702
2703   DrawGameDoorValues();
2704
2705   if (!game.restart_level)
2706   {
2707     UnmapGameButtons();
2708     UnmapTapeButtons();
2709     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2710     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2711     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2712     MapGameButtons();
2713     MapTapeButtons();
2714
2715     /* copy actual game door content to door double buffer for OpenDoor() */
2716     BlitBitmap(drawto, bitmap_db_door,
2717                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2718
2719     OpenDoor(DOOR_OPEN_ALL);
2720
2721     PlaySound(SND_GAME_STARTING);
2722
2723     if (setup.sound_music)
2724       PlayLevelMusic();
2725
2726     KeyboardAutoRepeatOffUnlessAutoplay();
2727
2728     if (options.debug)
2729     {
2730       for (i = 0; i < MAX_PLAYERS; i++)
2731         printf("Player %d %sactive.\n",
2732                i + 1, (stored_player[i].active ? "" : "not "));
2733     }
2734   }
2735
2736 #if 1
2737   UnmapAllGadgets();
2738
2739   MapGameButtons();
2740   MapTapeButtons();
2741 #endif
2742
2743   game.restart_level = FALSE;
2744 }
2745
2746 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2747 {
2748   /* this is used for non-R'n'D game engines to update certain engine values */
2749
2750   /* needed to determine if sounds are played within the visible screen area */
2751   scroll_x = actual_scroll_x;
2752   scroll_y = actual_scroll_y;
2753 }
2754
2755 void InitMovDir(int x, int y)
2756 {
2757   int i, element = Feld[x][y];
2758   static int xy[4][2] =
2759   {
2760     {  0, +1 },
2761     { +1,  0 },
2762     {  0, -1 },
2763     { -1,  0 }
2764   };
2765   static int direction[3][4] =
2766   {
2767     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
2768     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
2769     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
2770   };
2771
2772   switch (element)
2773   {
2774     case EL_BUG_RIGHT:
2775     case EL_BUG_UP:
2776     case EL_BUG_LEFT:
2777     case EL_BUG_DOWN:
2778       Feld[x][y] = EL_BUG;
2779       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2780       break;
2781
2782     case EL_SPACESHIP_RIGHT:
2783     case EL_SPACESHIP_UP:
2784     case EL_SPACESHIP_LEFT:
2785     case EL_SPACESHIP_DOWN:
2786       Feld[x][y] = EL_SPACESHIP;
2787       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2788       break;
2789
2790     case EL_BD_BUTTERFLY_RIGHT:
2791     case EL_BD_BUTTERFLY_UP:
2792     case EL_BD_BUTTERFLY_LEFT:
2793     case EL_BD_BUTTERFLY_DOWN:
2794       Feld[x][y] = EL_BD_BUTTERFLY;
2795       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2796       break;
2797
2798     case EL_BD_FIREFLY_RIGHT:
2799     case EL_BD_FIREFLY_UP:
2800     case EL_BD_FIREFLY_LEFT:
2801     case EL_BD_FIREFLY_DOWN:
2802       Feld[x][y] = EL_BD_FIREFLY;
2803       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2804       break;
2805
2806     case EL_PACMAN_RIGHT:
2807     case EL_PACMAN_UP:
2808     case EL_PACMAN_LEFT:
2809     case EL_PACMAN_DOWN:
2810       Feld[x][y] = EL_PACMAN;
2811       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2812       break;
2813
2814     case EL_YAMYAM_LEFT:
2815     case EL_YAMYAM_RIGHT:
2816     case EL_YAMYAM_UP:
2817     case EL_YAMYAM_DOWN:
2818       Feld[x][y] = EL_YAMYAM;
2819       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2820       break;
2821
2822     case EL_SP_SNIKSNAK:
2823       MovDir[x][y] = MV_UP;
2824       break;
2825
2826     case EL_SP_ELECTRON:
2827       MovDir[x][y] = MV_LEFT;
2828       break;
2829
2830     case EL_MOLE_LEFT:
2831     case EL_MOLE_RIGHT:
2832     case EL_MOLE_UP:
2833     case EL_MOLE_DOWN:
2834       Feld[x][y] = EL_MOLE;
2835       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2836       break;
2837
2838     default:
2839       if (IS_CUSTOM_ELEMENT(element))
2840       {
2841         struct ElementInfo *ei = &element_info[element];
2842         int move_direction_initial = ei->move_direction_initial;
2843         int move_pattern = ei->move_pattern;
2844
2845         if (move_direction_initial == MV_START_PREVIOUS)
2846         {
2847           if (MovDir[x][y] != MV_NONE)
2848             return;
2849
2850           move_direction_initial = MV_START_AUTOMATIC;
2851         }
2852
2853         if (move_direction_initial == MV_START_RANDOM)
2854           MovDir[x][y] = 1 << RND(4);
2855         else if (move_direction_initial & MV_ANY_DIRECTION)
2856           MovDir[x][y] = move_direction_initial;
2857         else if (move_pattern == MV_ALL_DIRECTIONS ||
2858                  move_pattern == MV_TURNING_LEFT ||
2859                  move_pattern == MV_TURNING_RIGHT ||
2860                  move_pattern == MV_TURNING_LEFT_RIGHT ||
2861                  move_pattern == MV_TURNING_RIGHT_LEFT ||
2862                  move_pattern == MV_TURNING_RANDOM)
2863           MovDir[x][y] = 1 << RND(4);
2864         else if (move_pattern == MV_HORIZONTAL)
2865           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2866         else if (move_pattern == MV_VERTICAL)
2867           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2868         else if (move_pattern & MV_ANY_DIRECTION)
2869           MovDir[x][y] = element_info[element].move_pattern;
2870         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2871                  move_pattern == MV_ALONG_RIGHT_SIDE)
2872         {
2873           /* use random direction as default start direction */
2874           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2875             MovDir[x][y] = 1 << RND(4);
2876
2877           for (i = 0; i < NUM_DIRECTIONS; i++)
2878           {
2879             int x1 = x + xy[i][0];
2880             int y1 = y + xy[i][1];
2881
2882             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2883             {
2884               if (move_pattern == MV_ALONG_RIGHT_SIDE)
2885                 MovDir[x][y] = direction[0][i];
2886               else
2887                 MovDir[x][y] = direction[1][i];
2888
2889               break;
2890             }
2891           }
2892         }                
2893       }
2894       else
2895       {
2896         MovDir[x][y] = 1 << RND(4);
2897
2898         if (element != EL_BUG &&
2899             element != EL_SPACESHIP &&
2900             element != EL_BD_BUTTERFLY &&
2901             element != EL_BD_FIREFLY)
2902           break;
2903
2904         for (i = 0; i < NUM_DIRECTIONS; i++)
2905         {
2906           int x1 = x + xy[i][0];
2907           int y1 = y + xy[i][1];
2908
2909           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2910           {
2911             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2912             {
2913               MovDir[x][y] = direction[0][i];
2914               break;
2915             }
2916             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2917                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2918             {
2919               MovDir[x][y] = direction[1][i];
2920               break;
2921             }
2922           }
2923         }
2924       }
2925       break;
2926   }
2927
2928   GfxDir[x][y] = MovDir[x][y];
2929 }
2930
2931 void InitAmoebaNr(int x, int y)
2932 {
2933   int i;
2934   int group_nr = AmoebeNachbarNr(x, y);
2935
2936   if (group_nr == 0)
2937   {
2938     for (i = 1; i < MAX_NUM_AMOEBA; i++)
2939     {
2940       if (AmoebaCnt[i] == 0)
2941       {
2942         group_nr = i;
2943         break;
2944       }
2945     }
2946   }
2947
2948   AmoebaNr[x][y] = group_nr;
2949   AmoebaCnt[group_nr]++;
2950   AmoebaCnt2[group_nr]++;
2951 }
2952
2953 static void PlayerWins(struct PlayerInfo *player)
2954 {
2955   player->LevelSolved = TRUE;
2956   player->GameOver = TRUE;
2957
2958   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2959                          level.native_em_level->lev->score : player->score);
2960 }
2961
2962 void GameWon()
2963 {
2964   static int time, time_final;
2965   static int score, score_final;
2966   static int game_over_delay = 0;
2967   int game_over_delay_value = 50;
2968
2969   if (!local_player->LevelSolved_GameEnd)
2970   {
2971     int i;
2972
2973     /* do not start end game actions before the player stops moving (to exit) */
2974     if (local_player->MovPos)
2975       return;
2976
2977     local_player->LevelSolved_GameEnd = TRUE;
2978     local_player->LevelSolved_SaveTape = tape.recording;
2979     local_player->LevelSolved_SaveScore = !tape.playing;
2980
2981     if (tape.auto_play)         /* tape might already be stopped here */
2982       tape.auto_play_level_solved = TRUE;
2983
2984 #if 1
2985     TapeStop();
2986 #endif
2987
2988     game_over_delay = game_over_delay_value;
2989
2990     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
2991     score = score_final = local_player->score_final;
2992
2993     if (TimeLeft > 0)
2994     {
2995       time_final = 0;
2996       score_final += TimeLeft * level.score[SC_TIME_BONUS];
2997     }
2998     else if (level.time == 0 && TimePlayed < 999)
2999     {
3000       time_final = 999;
3001       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3002     }
3003
3004     local_player->score_final = score_final;
3005
3006     if (level_editor_test_game)
3007     {
3008       time = time_final;
3009       score = score_final;
3010
3011       DrawGameValue_Time(time);
3012       DrawGameValue_Score(score);
3013     }
3014
3015     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3016     {
3017       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3018       {
3019         /* close exit door after last player */
3020         if ((AllPlayersGone &&
3021              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3022               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3023               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3024             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3025             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3026         {
3027           int element = Feld[ExitX][ExitY];
3028
3029 #if 0
3030           if (element == EL_EM_EXIT_OPEN ||
3031               element == EL_EM_STEEL_EXIT_OPEN)
3032           {
3033             Bang(ExitX, ExitY);
3034           }
3035           else
3036 #endif
3037           {
3038             Feld[ExitX][ExitY] =
3039               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3040                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3041                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3042                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3043                EL_EM_STEEL_EXIT_CLOSING);
3044
3045             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3046           }
3047         }
3048
3049         /* player disappears */
3050         DrawLevelField(ExitX, ExitY);
3051       }
3052
3053       for (i = 0; i < MAX_PLAYERS; i++)
3054       {
3055         struct PlayerInfo *player = &stored_player[i];
3056
3057         if (player->present)
3058         {
3059           RemovePlayer(player);
3060
3061           /* player disappears */
3062           DrawLevelField(player->jx, player->jy);
3063         }
3064       }
3065     }
3066
3067     PlaySound(SND_GAME_WINNING);
3068   }
3069
3070   if (game_over_delay > 0)
3071   {
3072     game_over_delay--;
3073
3074     return;
3075   }
3076
3077   if (time != time_final)
3078   {
3079     int time_to_go = ABS(time_final - time);
3080     int time_count_dir = (time < time_final ? +1 : -1);
3081     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3082
3083     time  += time_count_steps * time_count_dir;
3084     score += time_count_steps * level.score[SC_TIME_BONUS];
3085
3086     DrawGameValue_Time(time);
3087     DrawGameValue_Score(score);
3088
3089     if (time == time_final)
3090       StopSound(SND_GAME_LEVELTIME_BONUS);
3091     else if (setup.sound_loops)
3092       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3093     else
3094       PlaySound(SND_GAME_LEVELTIME_BONUS);
3095   }
3096 }
3097
3098 void GameEnd()
3099 {
3100   int hi_pos;
3101   boolean raise_level = FALSE;
3102
3103   CloseDoor(DOOR_CLOSE_1);
3104
3105   if (local_player->LevelSolved_SaveTape)
3106   {
3107 #if 0
3108     TapeStop();
3109 #endif
3110
3111 #if 1
3112     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3113 #else
3114     SaveTape(tape.level_nr);            /* ask to save tape */
3115 #endif
3116   }
3117
3118   if (level_editor_test_game)
3119   {
3120     game_status = GAME_MODE_MAIN;
3121
3122     DrawMainMenu();
3123
3124     return;
3125   }
3126
3127   if (!local_player->LevelSolved_SaveScore)
3128   {
3129     FadeOut(REDRAW_FIELD);
3130
3131     game_status = GAME_MODE_MAIN;
3132
3133     DrawAndFadeInMainMenu(REDRAW_FIELD);
3134
3135     return;
3136   }
3137
3138   if (level_nr == leveldir_current->handicap_level)
3139   {
3140     leveldir_current->handicap_level++;
3141     SaveLevelSetup_SeriesInfo();
3142   }
3143
3144   if (level_nr < leveldir_current->last_level)
3145     raise_level = TRUE;                 /* advance to next level */
3146
3147   if ((hi_pos = NewHiScore()) >= 0) 
3148   {
3149     game_status = GAME_MODE_SCORES;
3150
3151     DrawHallOfFame(hi_pos);
3152
3153     if (raise_level)
3154     {
3155       level_nr++;
3156       TapeErase();
3157     }
3158   }
3159   else
3160   {
3161     FadeOut(REDRAW_FIELD);
3162
3163     game_status = GAME_MODE_MAIN;
3164
3165     if (raise_level)
3166     {
3167       level_nr++;
3168       TapeErase();
3169     }
3170
3171     DrawAndFadeInMainMenu(REDRAW_FIELD);
3172   }
3173 }
3174
3175 int NewHiScore()
3176 {
3177   int k, l;
3178   int position = -1;
3179
3180   LoadScore(level_nr);
3181
3182   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3183       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3184     return -1;
3185
3186   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3187   {
3188     if (local_player->score_final > highscore[k].Score)
3189     {
3190       /* player has made it to the hall of fame */
3191
3192       if (k < MAX_SCORE_ENTRIES - 1)
3193       {
3194         int m = MAX_SCORE_ENTRIES - 1;
3195
3196 #ifdef ONE_PER_NAME
3197         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3198           if (strEqual(setup.player_name, highscore[l].Name))
3199             m = l;
3200         if (m == k)     /* player's new highscore overwrites his old one */
3201           goto put_into_list;
3202 #endif
3203
3204         for (l = m; l > k; l--)
3205         {
3206           strcpy(highscore[l].Name, highscore[l - 1].Name);
3207           highscore[l].Score = highscore[l - 1].Score;
3208         }
3209       }
3210
3211 #ifdef ONE_PER_NAME
3212       put_into_list:
3213 #endif
3214       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3215       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3216       highscore[k].Score = local_player->score_final; 
3217       position = k;
3218       break;
3219     }
3220
3221 #ifdef ONE_PER_NAME
3222     else if (!strncmp(setup.player_name, highscore[k].Name,
3223                       MAX_PLAYER_NAME_LEN))
3224       break;    /* player already there with a higher score */
3225 #endif
3226
3227   }
3228
3229   if (position >= 0) 
3230     SaveScore(level_nr);
3231
3232   return position;
3233 }
3234
3235 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3236 {
3237   int element = Feld[x][y];
3238   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3239   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3240   int horiz_move = (dx != 0);
3241   int sign = (horiz_move ? dx : dy);
3242   int step = sign * element_info[element].move_stepsize;
3243
3244   /* special values for move stepsize for spring and things on conveyor belt */
3245   if (horiz_move)
3246   {
3247     if (CAN_FALL(element) &&
3248         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3249       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3250     else if (element == EL_SPRING)
3251       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3252   }
3253
3254   return step;
3255 }
3256
3257 inline static int getElementMoveStepsize(int x, int y)
3258 {
3259   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3260 }
3261
3262 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3263 {
3264   if (player->GfxAction != action || player->GfxDir != dir)
3265   {
3266 #if 0
3267     printf("Player frame reset! (%d => %d, %d => %d)\n",
3268            player->GfxAction, action, player->GfxDir, dir);
3269 #endif
3270
3271     player->GfxAction = action;
3272     player->GfxDir = dir;
3273     player->Frame = 0;
3274     player->StepFrame = 0;
3275   }
3276 }
3277
3278 #if USE_GFX_RESET_GFX_ANIMATION
3279 static void ResetGfxFrame(int x, int y, boolean redraw)
3280 {
3281   int element = Feld[x][y];
3282   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3283   int last_gfx_frame = GfxFrame[x][y];
3284
3285   if (graphic_info[graphic].anim_global_sync)
3286     GfxFrame[x][y] = FrameCounter;
3287   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3288     GfxFrame[x][y] = CustomValue[x][y];
3289   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3290     GfxFrame[x][y] = element_info[element].collect_score;
3291   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3292     GfxFrame[x][y] = ChangeDelay[x][y];
3293
3294   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3295     DrawLevelGraphicAnimation(x, y, graphic);
3296 }
3297 #endif
3298
3299 static void ResetGfxAnimation(int x, int y)
3300 {
3301   GfxAction[x][y] = ACTION_DEFAULT;
3302   GfxDir[x][y] = MovDir[x][y];
3303   GfxFrame[x][y] = 0;
3304
3305 #if USE_GFX_RESET_GFX_ANIMATION
3306   ResetGfxFrame(x, y, FALSE);
3307 #endif
3308 }
3309
3310 static void ResetRandomAnimationValue(int x, int y)
3311 {
3312   GfxRandom[x][y] = INIT_GFX_RANDOM();
3313 }
3314
3315 void InitMovingField(int x, int y, int direction)
3316 {
3317   int element = Feld[x][y];
3318   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3319   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3320   int newx = x + dx;
3321   int newy = y + dy;
3322   boolean is_moving_before, is_moving_after;
3323 #if 0
3324   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3325 #endif
3326
3327   /* check if element was/is moving or being moved before/after mode change */
3328 #if 1
3329   is_moving_before = WasJustMoving[x][y];
3330 #else
3331   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3332 #endif
3333   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
3334
3335   /* reset animation only for moving elements which change direction of moving
3336      or which just started or stopped moving
3337      (else CEs with property "can move" / "not moving" are reset each frame) */
3338 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3339 #if 1
3340   if (is_moving_before != is_moving_after ||
3341       direction != MovDir[x][y])
3342     ResetGfxAnimation(x, y);
3343 #else
3344   if ((is_moving_before || is_moving_after) && !continues_moving)
3345     ResetGfxAnimation(x, y);
3346 #endif
3347 #else
3348   if (!continues_moving)
3349     ResetGfxAnimation(x, y);
3350 #endif
3351
3352   MovDir[x][y] = direction;
3353   GfxDir[x][y] = direction;
3354
3355 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3356   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3357                      direction == MV_DOWN && CAN_FALL(element) ?
3358                      ACTION_FALLING : ACTION_MOVING);
3359 #else
3360   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3361                      ACTION_FALLING : ACTION_MOVING);
3362 #endif
3363
3364   /* this is needed for CEs with property "can move" / "not moving" */
3365
3366   if (is_moving_after)
3367   {
3368     if (Feld[newx][newy] == EL_EMPTY)
3369       Feld[newx][newy] = EL_BLOCKED;
3370
3371     MovDir[newx][newy] = MovDir[x][y];
3372
3373 #if USE_NEW_CUSTOM_VALUE
3374     CustomValue[newx][newy] = CustomValue[x][y];
3375 #endif
3376
3377     GfxFrame[newx][newy] = GfxFrame[x][y];
3378     GfxRandom[newx][newy] = GfxRandom[x][y];
3379     GfxAction[newx][newy] = GfxAction[x][y];
3380     GfxDir[newx][newy] = GfxDir[x][y];
3381   }
3382 }
3383
3384 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3385 {
3386   int direction = MovDir[x][y];
3387   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3388   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3389
3390   *goes_to_x = newx;
3391   *goes_to_y = newy;
3392 }
3393
3394 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3395 {
3396   int oldx = x, oldy = y;
3397   int direction = MovDir[x][y];
3398
3399   if (direction == MV_LEFT)
3400     oldx++;
3401   else if (direction == MV_RIGHT)
3402     oldx--;
3403   else if (direction == MV_UP)
3404     oldy++;
3405   else if (direction == MV_DOWN)
3406     oldy--;
3407
3408   *comes_from_x = oldx;
3409   *comes_from_y = oldy;
3410 }
3411
3412 int MovingOrBlocked2Element(int x, int y)
3413 {
3414   int element = Feld[x][y];
3415
3416   if (element == EL_BLOCKED)
3417   {
3418     int oldx, oldy;
3419
3420     Blocked2Moving(x, y, &oldx, &oldy);
3421     return Feld[oldx][oldy];
3422   }
3423   else
3424     return element;
3425 }
3426
3427 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3428 {
3429   /* like MovingOrBlocked2Element(), but if element is moving
3430      and (x,y) is the field the moving element is just leaving,
3431      return EL_BLOCKED instead of the element value */
3432   int element = Feld[x][y];
3433
3434   if (IS_MOVING(x, y))
3435   {
3436     if (element == EL_BLOCKED)
3437     {
3438       int oldx, oldy;
3439
3440       Blocked2Moving(x, y, &oldx, &oldy);
3441       return Feld[oldx][oldy];
3442     }
3443     else
3444       return EL_BLOCKED;
3445   }
3446   else
3447     return element;
3448 }
3449
3450 static void RemoveField(int x, int y)
3451 {
3452   Feld[x][y] = EL_EMPTY;
3453
3454   MovPos[x][y] = 0;
3455   MovDir[x][y] = 0;
3456   MovDelay[x][y] = 0;
3457
3458 #if USE_NEW_CUSTOM_VALUE
3459   CustomValue[x][y] = 0;
3460 #endif
3461
3462   AmoebaNr[x][y] = 0;
3463   ChangeDelay[x][y] = 0;
3464   ChangePage[x][y] = -1;
3465   Pushed[x][y] = FALSE;
3466
3467 #if 0
3468   ExplodeField[x][y] = EX_TYPE_NONE;
3469 #endif
3470
3471   GfxElement[x][y] = EL_UNDEFINED;
3472   GfxAction[x][y] = ACTION_DEFAULT;
3473   GfxDir[x][y] = MV_NONE;
3474 }
3475
3476 void RemoveMovingField(int x, int y)
3477 {
3478   int oldx = x, oldy = y, newx = x, newy = y;
3479   int element = Feld[x][y];
3480   int next_element = EL_UNDEFINED;
3481
3482   if (element != EL_BLOCKED && !IS_MOVING(x, y))
3483     return;
3484
3485   if (IS_MOVING(x, y))
3486   {
3487     Moving2Blocked(x, y, &newx, &newy);
3488
3489     if (Feld[newx][newy] != EL_BLOCKED)
3490     {
3491       /* element is moving, but target field is not free (blocked), but
3492          already occupied by something different (example: acid pool);
3493          in this case, only remove the moving field, but not the target */
3494
3495       RemoveField(oldx, oldy);
3496
3497       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3498
3499       DrawLevelField(oldx, oldy);
3500
3501       return;
3502     }
3503   }
3504   else if (element == EL_BLOCKED)
3505   {
3506     Blocked2Moving(x, y, &oldx, &oldy);
3507     if (!IS_MOVING(oldx, oldy))
3508       return;
3509   }
3510
3511   if (element == EL_BLOCKED &&
3512       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3513        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3514        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3515        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3516        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3517        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3518     next_element = get_next_element(Feld[oldx][oldy]);
3519
3520   RemoveField(oldx, oldy);
3521   RemoveField(newx, newy);
3522
3523   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3524
3525   if (next_element != EL_UNDEFINED)
3526     Feld[oldx][oldy] = next_element;
3527
3528   DrawLevelField(oldx, oldy);
3529   DrawLevelField(newx, newy);
3530 }
3531
3532 void DrawDynamite(int x, int y)
3533 {
3534   int sx = SCREENX(x), sy = SCREENY(y);
3535   int graphic = el2img(Feld[x][y]);
3536   int frame;
3537
3538   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3539     return;
3540
3541   if (IS_WALKABLE_INSIDE(Back[x][y]))
3542     return;
3543
3544   if (Back[x][y])
3545     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3546   else if (Store[x][y])
3547     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3548
3549   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3550
3551   if (Back[x][y] || Store[x][y])
3552     DrawGraphicThruMask(sx, sy, graphic, frame);
3553   else
3554     DrawGraphic(sx, sy, graphic, frame);
3555 }
3556
3557 void CheckDynamite(int x, int y)
3558 {
3559   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
3560   {
3561     MovDelay[x][y]--;
3562
3563     if (MovDelay[x][y] != 0)
3564     {
3565       DrawDynamite(x, y);
3566       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3567
3568       return;
3569     }
3570   }
3571
3572   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3573
3574   Bang(x, y);
3575 }
3576
3577 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3578 {
3579   boolean num_checked_players = 0;
3580   int i;
3581
3582   for (i = 0; i < MAX_PLAYERS; i++)
3583   {
3584     if (stored_player[i].active)
3585     {
3586       int sx = stored_player[i].jx;
3587       int sy = stored_player[i].jy;
3588
3589       if (num_checked_players == 0)
3590       {
3591         *sx1 = *sx2 = sx;
3592         *sy1 = *sy2 = sy;
3593       }
3594       else
3595       {
3596         *sx1 = MIN(*sx1, sx);
3597         *sy1 = MIN(*sy1, sy);
3598         *sx2 = MAX(*sx2, sx);
3599         *sy2 = MAX(*sy2, sy);
3600       }
3601
3602       num_checked_players++;
3603     }
3604   }
3605 }
3606
3607 static boolean checkIfAllPlayersFitToScreen_RND()
3608 {
3609   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3610
3611   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3612
3613   return (sx2 - sx1 < SCR_FIELDX &&
3614           sy2 - sy1 < SCR_FIELDY);
3615 }
3616
3617 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3618 {
3619   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3620
3621   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3622
3623   *sx = (sx1 + sx2) / 2;
3624   *sy = (sy1 + sy2) / 2;
3625 }
3626
3627 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3628                         boolean center_screen, boolean quick_relocation)
3629 {
3630   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3631   boolean no_delay = (tape.warp_forward);
3632   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3633   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3634
3635   if (quick_relocation)
3636   {
3637     int offset = (setup.scroll_delay ? 3 : 0);
3638
3639     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3640     {
3641       if (center_screen)
3642       {
3643         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3644                     x > SBX_Right + MIDPOSX ? SBX_Right :
3645                     x - MIDPOSX);
3646
3647         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3648                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
3649                     y - MIDPOSY);
3650       }
3651       else
3652       {
3653         /* quick relocation (without scrolling), but do not center screen */
3654
3655         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
3656                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
3657                                old_x - MIDPOSX);
3658
3659         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3660                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3661                                old_y - MIDPOSY);
3662
3663         int offset_x = x + (scroll_x - center_scroll_x);
3664         int offset_y = y + (scroll_y - center_scroll_y);
3665
3666         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
3667                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3668                     offset_x - MIDPOSX);
3669
3670         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3671                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3672                     offset_y - MIDPOSY);
3673       }
3674     }
3675     else
3676     {
3677       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
3678           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3679         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3680
3681       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
3682           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3683         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3684
3685       /* don't scroll over playfield boundaries */
3686       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3687         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3688
3689       /* don't scroll over playfield boundaries */
3690       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3691         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3692     }
3693
3694     RedrawPlayfield(TRUE, 0,0,0,0);
3695   }
3696   else
3697   {
3698     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3699                      x > SBX_Right + MIDPOSX ? SBX_Right :
3700                      x - MIDPOSX);
3701
3702     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3703                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
3704                      y - MIDPOSY);
3705
3706     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3707
3708     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3709     {
3710       int dx = 0, dy = 0;
3711       int fx = FX, fy = FY;
3712
3713       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3714       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3715
3716       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3717         break;
3718
3719       scroll_x -= dx;
3720       scroll_y -= dy;
3721
3722       fx += dx * TILEX / 2;
3723       fy += dy * TILEY / 2;
3724
3725       ScrollLevel(dx, dy);
3726       DrawAllPlayers();
3727
3728       /* scroll in two steps of half tile size to make things smoother */
3729       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3730       FlushDisplay();
3731       Delay(wait_delay_value);
3732
3733       /* scroll second step to align at full tile size */
3734       BackToFront();
3735       Delay(wait_delay_value);
3736     }
3737
3738     DrawAllPlayers();
3739     BackToFront();
3740     Delay(wait_delay_value);
3741   }
3742 }
3743
3744 void RelocatePlayer(int jx, int jy, int el_player_raw)
3745 {
3746   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3747   int player_nr = GET_PLAYER_NR(el_player);
3748   struct PlayerInfo *player = &stored_player[player_nr];
3749   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3750   boolean no_delay = (tape.warp_forward);
3751   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3752   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3753   int old_jx = player->jx;
3754   int old_jy = player->jy;
3755   int old_element = Feld[old_jx][old_jy];
3756   int element = Feld[jx][jy];
3757   boolean player_relocated = (old_jx != jx || old_jy != jy);
3758
3759   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3760   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
3761   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3762   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
3763   int leave_side_horiz = move_dir_horiz;
3764   int leave_side_vert  = move_dir_vert;
3765   int enter_side = enter_side_horiz | enter_side_vert;
3766   int leave_side = leave_side_horiz | leave_side_vert;
3767
3768   if (player->GameOver)         /* do not reanimate dead player */
3769     return;
3770
3771   if (!player_relocated)        /* no need to relocate the player */
3772     return;
3773
3774   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
3775   {
3776     RemoveField(jx, jy);        /* temporarily remove newly placed player */
3777     DrawLevelField(jx, jy);
3778   }
3779
3780   if (player->present)
3781   {
3782     while (player->MovPos)
3783     {
3784       ScrollPlayer(player, SCROLL_GO_ON);
3785       ScrollScreen(NULL, SCROLL_GO_ON);
3786
3787       AdvanceFrameAndPlayerCounters(player->index_nr);
3788
3789       DrawPlayer(player);
3790
3791       BackToFront();
3792       Delay(wait_delay_value);
3793     }
3794
3795     DrawPlayer(player);         /* needed here only to cleanup last field */
3796     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
3797
3798     player->is_moving = FALSE;
3799   }
3800
3801   if (IS_CUSTOM_ELEMENT(old_element))
3802     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3803                                CE_LEFT_BY_PLAYER,
3804                                player->index_bit, leave_side);
3805
3806   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3807                                       CE_PLAYER_LEAVES_X,
3808                                       player->index_bit, leave_side);
3809
3810   Feld[jx][jy] = el_player;
3811   InitPlayerField(jx, jy, el_player, TRUE);
3812
3813   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3814   {
3815     Feld[jx][jy] = element;
3816     InitField(jx, jy, FALSE);
3817   }
3818
3819   /* only visually relocate centered player */
3820   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3821                      FALSE, level.instant_relocation);
3822
3823   TestIfPlayerTouchesBadThing(jx, jy);
3824   TestIfPlayerTouchesCustomElement(jx, jy);
3825
3826   if (IS_CUSTOM_ELEMENT(element))
3827     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3828                                player->index_bit, enter_side);
3829
3830   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3831                                       player->index_bit, enter_side);
3832 }
3833
3834 void Explode(int ex, int ey, int phase, int mode)
3835 {
3836   int x, y;
3837   int last_phase;
3838   int border_element;
3839
3840   /* !!! eliminate this variable !!! */
3841   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3842
3843   if (game.explosions_delayed)
3844   {
3845     ExplodeField[ex][ey] = mode;
3846     return;
3847   }
3848
3849   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
3850   {
3851     int center_element = Feld[ex][ey];
3852     int artwork_element, explosion_element;     /* set these values later */
3853
3854 #if 0
3855     /* --- This is only really needed (and now handled) in "Impact()". --- */
3856     /* do not explode moving elements that left the explode field in time */
3857     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3858         center_element == EL_EMPTY &&
3859         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3860       return;
3861 #endif
3862
3863 #if 0
3864     /* !!! at this place, the center element may be EL_BLOCKED !!! */
3865     if (mode == EX_TYPE_NORMAL ||
3866         mode == EX_TYPE_CENTER ||
3867         mode == EX_TYPE_CROSS)
3868       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3869 #endif
3870
3871     /* remove things displayed in background while burning dynamite */
3872     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3873       Back[ex][ey] = 0;
3874
3875     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3876     {
3877       /* put moving element to center field (and let it explode there) */
3878       center_element = MovingOrBlocked2Element(ex, ey);
3879       RemoveMovingField(ex, ey);
3880       Feld[ex][ey] = center_element;
3881     }
3882
3883     /* now "center_element" is finally determined -- set related values now */
3884     artwork_element = center_element;           /* for custom player artwork */
3885     explosion_element = center_element;         /* for custom player artwork */
3886
3887     if (IS_PLAYER(ex, ey))
3888     {
3889       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3890
3891       artwork_element = stored_player[player_nr].artwork_element;
3892
3893       if (level.use_explosion_element[player_nr])
3894       {
3895         explosion_element = level.explosion_element[player_nr];
3896         artwork_element = explosion_element;
3897       }
3898     }
3899
3900 #if 1
3901     if (mode == EX_TYPE_NORMAL ||
3902         mode == EX_TYPE_CENTER ||
3903         mode == EX_TYPE_CROSS)
3904       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3905 #endif
3906
3907     last_phase = element_info[explosion_element].explosion_delay + 1;
3908
3909     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3910     {
3911       int xx = x - ex + 1;
3912       int yy = y - ey + 1;
3913       int element;
3914
3915       if (!IN_LEV_FIELD(x, y) ||
3916           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3917           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
3918         continue;
3919
3920       element = Feld[x][y];
3921
3922       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3923       {
3924         element = MovingOrBlocked2Element(x, y);
3925
3926         if (!IS_EXPLOSION_PROOF(element))
3927           RemoveMovingField(x, y);
3928       }
3929
3930       /* indestructible elements can only explode in center (but not flames) */
3931       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3932                                            mode == EX_TYPE_BORDER)) ||
3933           element == EL_FLAMES)
3934         continue;
3935
3936       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3937          behaviour, for example when touching a yamyam that explodes to rocks
3938          with active deadly shield, a rock is created under the player !!! */
3939       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3940 #if 0
3941       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3942           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3943            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3944 #else
3945       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3946 #endif
3947       {
3948         if (IS_ACTIVE_BOMB(element))
3949         {
3950           /* re-activate things under the bomb like gate or penguin */
3951           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3952           Back[x][y] = 0;
3953         }
3954
3955         continue;
3956       }
3957
3958       /* save walkable background elements while explosion on same tile */
3959       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3960           (x != ex || y != ey || mode == EX_TYPE_BORDER))
3961         Back[x][y] = element;
3962
3963       /* ignite explodable elements reached by other explosion */
3964       if (element == EL_EXPLOSION)
3965         element = Store2[x][y];
3966
3967       if (AmoebaNr[x][y] &&
3968           (element == EL_AMOEBA_FULL ||
3969            element == EL_BD_AMOEBA ||
3970            element == EL_AMOEBA_GROWING))
3971       {
3972         AmoebaCnt[AmoebaNr[x][y]]--;
3973         AmoebaCnt2[AmoebaNr[x][y]]--;
3974       }
3975
3976       RemoveField(x, y);
3977
3978       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3979       {
3980         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3981
3982         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3983
3984         if (PLAYERINFO(ex, ey)->use_murphy)
3985           Store[x][y] = EL_EMPTY;
3986       }
3987
3988       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3989          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3990       else if (ELEM_IS_PLAYER(center_element))
3991         Store[x][y] = EL_EMPTY;
3992       else if (center_element == EL_YAMYAM)
3993         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3994       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3995         Store[x][y] = element_info[center_element].content.e[xx][yy];
3996 #if 1
3997       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3998          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3999          otherwise) -- FIX THIS !!! */
4000       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4001         Store[x][y] = element_info[element].content.e[1][1];
4002 #else
4003       else if (!CAN_EXPLODE(element))
4004         Store[x][y] = element_info[element].content.e[1][1];
4005 #endif
4006       else
4007         Store[x][y] = EL_EMPTY;
4008
4009       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4010           center_element == EL_AMOEBA_TO_DIAMOND)
4011         Store2[x][y] = element;
4012
4013       Feld[x][y] = EL_EXPLOSION;
4014       GfxElement[x][y] = artwork_element;
4015
4016       ExplodePhase[x][y] = 1;
4017       ExplodeDelay[x][y] = last_phase;
4018
4019       Stop[x][y] = TRUE;
4020     }
4021
4022     if (center_element == EL_YAMYAM)
4023       game.yamyam_content_nr =
4024         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4025
4026     return;
4027   }
4028
4029   if (Stop[ex][ey])
4030     return;
4031
4032   x = ex;
4033   y = ey;
4034
4035   if (phase == 1)
4036     GfxFrame[x][y] = 0;         /* restart explosion animation */
4037
4038   last_phase = ExplodeDelay[x][y];
4039
4040   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4041
4042 #ifdef DEBUG
4043
4044   /* activate this even in non-DEBUG version until cause for crash in
4045      getGraphicAnimationFrame() (see below) is found and eliminated */
4046
4047 #endif
4048 #if 1
4049
4050 #if 1
4051   /* this can happen if the player leaves an explosion just in time */
4052   if (GfxElement[x][y] == EL_UNDEFINED)
4053     GfxElement[x][y] = EL_EMPTY;
4054 #else
4055   if (GfxElement[x][y] == EL_UNDEFINED)
4056   {
4057     printf("\n\n");
4058     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4059     printf("Explode(): This should never happen!\n");
4060     printf("\n\n");
4061
4062     GfxElement[x][y] = EL_EMPTY;
4063   }
4064 #endif
4065
4066 #endif
4067
4068   border_element = Store2[x][y];
4069   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4070     border_element = StorePlayer[x][y];
4071
4072   if (phase == element_info[border_element].ignition_delay ||
4073       phase == last_phase)
4074   {
4075     boolean border_explosion = FALSE;
4076
4077     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4078         !PLAYER_EXPLOSION_PROTECTED(x, y))
4079     {
4080       KillPlayerUnlessExplosionProtected(x, y);
4081       border_explosion = TRUE;
4082     }
4083     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4084     {
4085       Feld[x][y] = Store2[x][y];
4086       Store2[x][y] = 0;
4087       Bang(x, y);
4088       border_explosion = TRUE;
4089     }
4090     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4091     {
4092       AmoebeUmwandeln(x, y);
4093       Store2[x][y] = 0;
4094       border_explosion = TRUE;
4095     }
4096
4097     /* if an element just explodes due to another explosion (chain-reaction),
4098        do not immediately end the new explosion when it was the last frame of
4099        the explosion (as it would be done in the following "if"-statement!) */
4100     if (border_explosion && phase == last_phase)
4101       return;
4102   }
4103
4104   if (phase == last_phase)
4105   {
4106     int element;
4107
4108     element = Feld[x][y] = Store[x][y];
4109     Store[x][y] = Store2[x][y] = 0;
4110     GfxElement[x][y] = EL_UNDEFINED;
4111
4112     /* player can escape from explosions and might therefore be still alive */
4113     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4114         element <= EL_PLAYER_IS_EXPLODING_4)
4115     {
4116       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4117       int explosion_element = EL_PLAYER_1 + player_nr;
4118       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4119       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4120
4121       if (level.use_explosion_element[player_nr])
4122         explosion_element = level.explosion_element[player_nr];
4123
4124       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4125                     element_info[explosion_element].content.e[xx][yy]);
4126     }
4127
4128     /* restore probably existing indestructible background element */
4129     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4130       element = Feld[x][y] = Back[x][y];
4131     Back[x][y] = 0;
4132
4133     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4134     GfxDir[x][y] = MV_NONE;
4135     ChangeDelay[x][y] = 0;
4136     ChangePage[x][y] = -1;
4137
4138 #if USE_NEW_CUSTOM_VALUE
4139     CustomValue[x][y] = 0;
4140 #endif
4141
4142     InitField_WithBug2(x, y, FALSE);
4143
4144     DrawLevelField(x, y);
4145
4146     TestIfElementTouchesCustomElement(x, y);
4147
4148     if (GFX_CRUMBLED(element))
4149       DrawLevelFieldCrumbledSandNeighbours(x, y);
4150
4151     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4152       StorePlayer[x][y] = 0;
4153
4154     if (ELEM_IS_PLAYER(element))
4155       RelocatePlayer(x, y, element);
4156   }
4157   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4158   {
4159     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4160     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4161
4162     if (phase == delay)
4163       DrawLevelFieldCrumbledSand(x, y);
4164
4165     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4166     {
4167       DrawLevelElement(x, y, Back[x][y]);
4168       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4169     }
4170     else if (IS_WALKABLE_UNDER(Back[x][y]))
4171     {
4172       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4173       DrawLevelElementThruMask(x, y, Back[x][y]);
4174     }
4175     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4176       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4177   }
4178 }
4179
4180 void DynaExplode(int ex, int ey)
4181 {
4182   int i, j;
4183   int dynabomb_element = Feld[ex][ey];
4184   int dynabomb_size = 1;
4185   boolean dynabomb_xl = FALSE;
4186   struct PlayerInfo *player;
4187   static int xy[4][2] =
4188   {
4189     { 0, -1 },
4190     { -1, 0 },
4191     { +1, 0 },
4192     { 0, +1 }
4193   };
4194
4195   if (IS_ACTIVE_BOMB(dynabomb_element))
4196   {
4197     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4198     dynabomb_size = player->dynabomb_size;
4199     dynabomb_xl = player->dynabomb_xl;
4200     player->dynabombs_left++;
4201   }
4202
4203   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4204
4205   for (i = 0; i < NUM_DIRECTIONS; i++)
4206   {
4207     for (j = 1; j <= dynabomb_size; j++)
4208     {
4209       int x = ex + j * xy[i][0];
4210       int y = ey + j * xy[i][1];
4211       int element;
4212
4213       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4214         break;
4215
4216       element = Feld[x][y];
4217
4218       /* do not restart explosions of fields with active bombs */
4219       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4220         continue;
4221
4222       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4223
4224       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4225           !IS_DIGGABLE(element) && !dynabomb_xl)
4226         break;
4227     }
4228   }
4229 }
4230
4231 void Bang(int x, int y)
4232 {
4233   int element = MovingOrBlocked2Element(x, y);
4234   int explosion_type = EX_TYPE_NORMAL;
4235
4236   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4237   {
4238     struct PlayerInfo *player = PLAYERINFO(x, y);
4239
4240     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4241                             player->element_nr);
4242
4243     if (level.use_explosion_element[player->index_nr])
4244     {
4245       int explosion_element = level.explosion_element[player->index_nr];
4246
4247       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4248         explosion_type = EX_TYPE_CROSS;
4249       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4250         explosion_type = EX_TYPE_CENTER;
4251     }
4252   }
4253
4254   switch (element)
4255   {
4256     case EL_BUG:
4257     case EL_SPACESHIP:
4258     case EL_BD_BUTTERFLY:
4259     case EL_BD_FIREFLY:
4260     case EL_YAMYAM:
4261     case EL_DARK_YAMYAM:
4262     case EL_ROBOT:
4263     case EL_PACMAN:
4264     case EL_MOLE:
4265       RaiseScoreElement(element);
4266       break;
4267
4268     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4269     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4270     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4271     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4272     case EL_DYNABOMB_INCREASE_NUMBER:
4273     case EL_DYNABOMB_INCREASE_SIZE:
4274     case EL_DYNABOMB_INCREASE_POWER:
4275       explosion_type = EX_TYPE_DYNA;
4276       break;
4277
4278     case EL_DC_LANDMINE:
4279 #if 0
4280     case EL_EM_EXIT_OPEN:
4281     case EL_EM_STEEL_EXIT_OPEN:
4282 #endif
4283       explosion_type = EX_TYPE_CENTER;
4284       break;
4285
4286     case EL_PENGUIN:
4287     case EL_LAMP:
4288     case EL_LAMP_ACTIVE:
4289     case EL_AMOEBA_TO_DIAMOND:
4290       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4291         explosion_type = EX_TYPE_CENTER;
4292       break;
4293
4294     default:
4295       if (element_info[element].explosion_type == EXPLODES_CROSS)
4296         explosion_type = EX_TYPE_CROSS;
4297       else if (element_info[element].explosion_type == EXPLODES_1X1)
4298         explosion_type = EX_TYPE_CENTER;
4299       break;
4300   }
4301
4302   if (explosion_type == EX_TYPE_DYNA)
4303     DynaExplode(x, y);
4304   else
4305     Explode(x, y, EX_PHASE_START, explosion_type);
4306
4307   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4308 }
4309
4310 void SplashAcid(int x, int y)
4311 {
4312   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4313       (!IN_LEV_FIELD(x - 1, y - 2) ||
4314        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4315     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4316
4317   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4318       (!IN_LEV_FIELD(x + 1, y - 2) ||
4319        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4320     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4321
4322   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4323 }
4324
4325 static void InitBeltMovement()
4326 {
4327   static int belt_base_element[4] =
4328   {
4329     EL_CONVEYOR_BELT_1_LEFT,
4330     EL_CONVEYOR_BELT_2_LEFT,
4331     EL_CONVEYOR_BELT_3_LEFT,
4332     EL_CONVEYOR_BELT_4_LEFT
4333   };
4334   static int belt_base_active_element[4] =
4335   {
4336     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4337     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4338     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4339     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4340   };
4341
4342   int x, y, i, j;
4343
4344   /* set frame order for belt animation graphic according to belt direction */
4345   for (i = 0; i < NUM_BELTS; i++)
4346   {
4347     int belt_nr = i;
4348
4349     for (j = 0; j < NUM_BELT_PARTS; j++)
4350     {
4351       int element = belt_base_active_element[belt_nr] + j;
4352       int graphic = el2img(element);
4353
4354       if (game.belt_dir[i] == MV_LEFT)
4355         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4356       else
4357         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4358     }
4359   }
4360
4361   SCAN_PLAYFIELD(x, y)
4362   {
4363     int element = Feld[x][y];
4364
4365     for (i = 0; i < NUM_BELTS; i++)
4366     {
4367       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4368       {
4369         int e_belt_nr = getBeltNrFromBeltElement(element);
4370         int belt_nr = i;
4371
4372         if (e_belt_nr == belt_nr)
4373         {
4374           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4375
4376           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4377         }
4378       }
4379     }
4380   }
4381 }
4382
4383 static void ToggleBeltSwitch(int x, int y)
4384 {
4385   static int belt_base_element[4] =
4386   {
4387     EL_CONVEYOR_BELT_1_LEFT,
4388     EL_CONVEYOR_BELT_2_LEFT,
4389     EL_CONVEYOR_BELT_3_LEFT,
4390     EL_CONVEYOR_BELT_4_LEFT
4391   };
4392   static int belt_base_active_element[4] =
4393   {
4394     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4395     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4396     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4397     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4398   };
4399   static int belt_base_switch_element[4] =
4400   {
4401     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4402     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4403     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4404     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4405   };
4406   static int belt_move_dir[4] =
4407   {
4408     MV_LEFT,
4409     MV_NONE,
4410     MV_RIGHT,
4411     MV_NONE,
4412   };
4413
4414   int element = Feld[x][y];
4415   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4416   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4417   int belt_dir = belt_move_dir[belt_dir_nr];
4418   int xx, yy, i;
4419
4420   if (!IS_BELT_SWITCH(element))
4421     return;
4422
4423   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4424   game.belt_dir[belt_nr] = belt_dir;
4425
4426   if (belt_dir_nr == 3)
4427     belt_dir_nr = 1;
4428
4429   /* set frame order for belt animation graphic according to belt direction */
4430   for (i = 0; i < NUM_BELT_PARTS; i++)
4431   {
4432     int element = belt_base_active_element[belt_nr] + i;
4433     int graphic = el2img(element);
4434
4435     if (belt_dir == MV_LEFT)
4436       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4437     else
4438       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4439   }
4440
4441   SCAN_PLAYFIELD(xx, yy)
4442   {
4443     int element = Feld[xx][yy];
4444
4445     if (IS_BELT_SWITCH(element))
4446     {
4447       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4448
4449       if (e_belt_nr == belt_nr)
4450       {
4451         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4452         DrawLevelField(xx, yy);
4453       }
4454     }
4455     else if (IS_BELT(element) && belt_dir != MV_NONE)
4456     {
4457       int e_belt_nr = getBeltNrFromBeltElement(element);
4458
4459       if (e_belt_nr == belt_nr)
4460       {
4461         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4462
4463         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4464         DrawLevelField(xx, yy);
4465       }
4466     }
4467     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4468     {
4469       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4470
4471       if (e_belt_nr == belt_nr)
4472       {
4473         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4474
4475         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4476         DrawLevelField(xx, yy);
4477       }
4478     }
4479   }
4480 }
4481
4482 static void ToggleSwitchgateSwitch(int x, int y)
4483 {
4484   int xx, yy;
4485
4486   game.switchgate_pos = !game.switchgate_pos;
4487
4488   SCAN_PLAYFIELD(xx, yy)
4489   {
4490     int element = Feld[xx][yy];
4491
4492 #if !USE_BOTH_SWITCHGATE_SWITCHES
4493     if (element == EL_SWITCHGATE_SWITCH_UP ||
4494         element == EL_SWITCHGATE_SWITCH_DOWN)
4495     {
4496       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4497       DrawLevelField(xx, yy);
4498     }
4499     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4500              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4501     {
4502       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4503       DrawLevelField(xx, yy);
4504     }
4505 #else
4506     if (element == EL_SWITCHGATE_SWITCH_UP)
4507     {
4508       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4509       DrawLevelField(xx, yy);
4510     }
4511     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4512     {
4513       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4514       DrawLevelField(xx, yy);
4515     }
4516     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4517     {
4518       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4519       DrawLevelField(xx, yy);
4520     }
4521     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4522     {
4523       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4524       DrawLevelField(xx, yy);
4525     }
4526 #endif
4527     else if (element == EL_SWITCHGATE_OPEN ||
4528              element == EL_SWITCHGATE_OPENING)
4529     {
4530       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4531
4532       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4533     }
4534     else if (element == EL_SWITCHGATE_CLOSED ||
4535              element == EL_SWITCHGATE_CLOSING)
4536     {
4537       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4538
4539       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4540     }
4541   }
4542 }
4543
4544 static int getInvisibleActiveFromInvisibleElement(int element)
4545 {
4546   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4547           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
4548           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
4549           element);
4550 }
4551
4552 static int getInvisibleFromInvisibleActiveElement(int element)
4553 {
4554   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4555           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
4556           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
4557           element);
4558 }
4559
4560 static void RedrawAllLightSwitchesAndInvisibleElements()
4561 {
4562   int x, y;
4563
4564   SCAN_PLAYFIELD(x, y)
4565   {
4566     int element = Feld[x][y];
4567
4568     if (element == EL_LIGHT_SWITCH &&
4569         game.light_time_left > 0)
4570     {
4571       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4572       DrawLevelField(x, y);
4573     }
4574     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4575              game.light_time_left == 0)
4576     {
4577       Feld[x][y] = EL_LIGHT_SWITCH;
4578       DrawLevelField(x, y);
4579     }
4580     else if (element == EL_EMC_DRIPPER &&
4581              game.light_time_left > 0)
4582     {
4583       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4584       DrawLevelField(x, y);
4585     }
4586     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4587              game.light_time_left == 0)
4588     {
4589       Feld[x][y] = EL_EMC_DRIPPER;
4590       DrawLevelField(x, y);
4591     }
4592     else if (element == EL_INVISIBLE_STEELWALL ||
4593              element == EL_INVISIBLE_WALL ||
4594              element == EL_INVISIBLE_SAND)
4595     {
4596       if (game.light_time_left > 0)
4597         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4598
4599       DrawLevelField(x, y);
4600
4601       /* uncrumble neighbour fields, if needed */
4602       if (element == EL_INVISIBLE_SAND)
4603         DrawLevelFieldCrumbledSandNeighbours(x, y);
4604     }
4605     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4606              element == EL_INVISIBLE_WALL_ACTIVE ||
4607              element == EL_INVISIBLE_SAND_ACTIVE)
4608     {
4609       if (game.light_time_left == 0)
4610         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4611
4612       DrawLevelField(x, y);
4613
4614       /* re-crumble neighbour fields, if needed */
4615       if (element == EL_INVISIBLE_SAND)
4616         DrawLevelFieldCrumbledSandNeighbours(x, y);
4617     }
4618   }
4619 }
4620
4621 static void RedrawAllInvisibleElementsForLenses()
4622 {
4623   int x, y;
4624
4625   SCAN_PLAYFIELD(x, y)
4626   {
4627     int element = Feld[x][y];
4628
4629     if (element == EL_EMC_DRIPPER &&
4630         game.lenses_time_left > 0)
4631     {
4632       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4633       DrawLevelField(x, y);
4634     }
4635     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4636              game.lenses_time_left == 0)
4637     {
4638       Feld[x][y] = EL_EMC_DRIPPER;
4639       DrawLevelField(x, y);
4640     }
4641     else if (element == EL_INVISIBLE_STEELWALL ||
4642              element == EL_INVISIBLE_WALL ||
4643              element == EL_INVISIBLE_SAND)
4644     {
4645       if (game.lenses_time_left > 0)
4646         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4647
4648       DrawLevelField(x, y);
4649
4650       /* uncrumble neighbour fields, if needed */
4651       if (element == EL_INVISIBLE_SAND)
4652         DrawLevelFieldCrumbledSandNeighbours(x, y);
4653     }
4654     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4655              element == EL_INVISIBLE_WALL_ACTIVE ||
4656              element == EL_INVISIBLE_SAND_ACTIVE)
4657     {
4658       if (game.lenses_time_left == 0)
4659         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4660
4661       DrawLevelField(x, y);
4662
4663       /* re-crumble neighbour fields, if needed */
4664       if (element == EL_INVISIBLE_SAND)
4665         DrawLevelFieldCrumbledSandNeighbours(x, y);
4666     }
4667   }
4668 }
4669
4670 static void RedrawAllInvisibleElementsForMagnifier()
4671 {
4672   int x, y;
4673
4674   SCAN_PLAYFIELD(x, y)
4675   {
4676     int element = Feld[x][y];
4677
4678     if (element == EL_EMC_FAKE_GRASS &&
4679         game.magnify_time_left > 0)
4680     {
4681       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4682       DrawLevelField(x, y);
4683     }
4684     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4685              game.magnify_time_left == 0)
4686     {
4687       Feld[x][y] = EL_EMC_FAKE_GRASS;
4688       DrawLevelField(x, y);
4689     }
4690     else if (IS_GATE_GRAY(element) &&
4691              game.magnify_time_left > 0)
4692     {
4693       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4694                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4695                     IS_EM_GATE_GRAY(element) ?
4696                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4697                     IS_EMC_GATE_GRAY(element) ?
4698                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4699                     element);
4700       DrawLevelField(x, y);
4701     }
4702     else if (IS_GATE_GRAY_ACTIVE(element) &&
4703              game.magnify_time_left == 0)
4704     {
4705       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4706                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4707                     IS_EM_GATE_GRAY_ACTIVE(element) ?
4708                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4709                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
4710                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4711                     element);
4712       DrawLevelField(x, y);
4713     }
4714   }
4715 }
4716
4717 static void ToggleLightSwitch(int x, int y)
4718 {
4719   int element = Feld[x][y];
4720
4721   game.light_time_left =
4722     (element == EL_LIGHT_SWITCH ?
4723      level.time_light * FRAMES_PER_SECOND : 0);
4724
4725   RedrawAllLightSwitchesAndInvisibleElements();
4726 }
4727
4728 static void ActivateTimegateSwitch(int x, int y)
4729 {
4730   int xx, yy;
4731
4732   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4733
4734   SCAN_PLAYFIELD(xx, yy)
4735   {
4736     int element = Feld[xx][yy];
4737
4738     if (element == EL_TIMEGATE_CLOSED ||
4739         element == EL_TIMEGATE_CLOSING)
4740     {
4741       Feld[xx][yy] = EL_TIMEGATE_OPENING;
4742       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4743     }
4744
4745     /*
4746     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4747     {
4748       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4749       DrawLevelField(xx, yy);
4750     }
4751     */
4752
4753   }
4754
4755 #if 1
4756   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4757                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4758 #else
4759   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4760 #endif
4761 }
4762
4763 void Impact(int x, int y)
4764 {
4765   boolean last_line = (y == lev_fieldy - 1);
4766   boolean object_hit = FALSE;
4767   boolean impact = (last_line || object_hit);
4768   int element = Feld[x][y];
4769   int smashed = EL_STEELWALL;
4770
4771   if (!last_line)       /* check if element below was hit */
4772   {
4773     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4774       return;
4775
4776     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4777                                          MovDir[x][y + 1] != MV_DOWN ||
4778                                          MovPos[x][y + 1] <= TILEY / 2));
4779
4780     /* do not smash moving elements that left the smashed field in time */
4781     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4782         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4783       object_hit = FALSE;
4784
4785 #if USE_QUICKSAND_IMPACT_BUGFIX
4786     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4787     {
4788       RemoveMovingField(x, y + 1);
4789       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4790       Feld[x][y + 2] = EL_ROCK;
4791       DrawLevelField(x, y + 2);
4792
4793       object_hit = TRUE;
4794     }
4795
4796     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
4797     {
4798       RemoveMovingField(x, y + 1);
4799       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
4800       Feld[x][y + 2] = EL_ROCK;
4801       DrawLevelField(x, y + 2);
4802
4803       object_hit = TRUE;
4804     }
4805 #endif
4806
4807     if (object_hit)
4808       smashed = MovingOrBlocked2Element(x, y + 1);
4809
4810     impact = (last_line || object_hit);
4811   }
4812
4813   if (!last_line && smashed == EL_ACID) /* element falls into acid */
4814   {
4815     SplashAcid(x, y + 1);
4816     return;
4817   }
4818
4819   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4820   /* only reset graphic animation if graphic really changes after impact */
4821   if (impact &&
4822       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4823   {
4824     ResetGfxAnimation(x, y);
4825     DrawLevelField(x, y);
4826   }
4827
4828   if (impact && CAN_EXPLODE_IMPACT(element))
4829   {
4830     Bang(x, y);
4831     return;
4832   }
4833   else if (impact && element == EL_PEARL &&
4834            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
4835   {
4836     ResetGfxAnimation(x, y);
4837
4838     Feld[x][y] = EL_PEARL_BREAKING;
4839     PlayLevelSound(x, y, SND_PEARL_BREAKING);
4840     return;
4841   }
4842   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4843   {
4844     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4845
4846     return;
4847   }
4848
4849   if (impact && element == EL_AMOEBA_DROP)
4850   {
4851     if (object_hit && IS_PLAYER(x, y + 1))
4852       KillPlayerUnlessEnemyProtected(x, y + 1);
4853     else if (object_hit && smashed == EL_PENGUIN)
4854       Bang(x, y + 1);
4855     else
4856     {
4857       Feld[x][y] = EL_AMOEBA_GROWING;
4858       Store[x][y] = EL_AMOEBA_WET;
4859
4860       ResetRandomAnimationValue(x, y);
4861     }
4862     return;
4863   }
4864
4865   if (object_hit)               /* check which object was hit */
4866   {
4867     if ((CAN_PASS_MAGIC_WALL(element) && 
4868          (smashed == EL_MAGIC_WALL ||
4869           smashed == EL_BD_MAGIC_WALL)) ||
4870         (CAN_PASS_DC_MAGIC_WALL(element) &&
4871          smashed == EL_DC_MAGIC_WALL))
4872     {
4873       int xx, yy;
4874       int activated_magic_wall =
4875         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4876          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
4877          EL_DC_MAGIC_WALL_ACTIVE);
4878
4879       /* activate magic wall / mill */
4880       SCAN_PLAYFIELD(xx, yy)
4881       {
4882         if (Feld[xx][yy] == smashed)
4883           Feld[xx][yy] = activated_magic_wall;
4884       }
4885
4886       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4887       game.magic_wall_active = TRUE;
4888
4889       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4890                             SND_MAGIC_WALL_ACTIVATING :
4891                             smashed == EL_BD_MAGIC_WALL ?
4892                             SND_BD_MAGIC_WALL_ACTIVATING :
4893                             SND_DC_MAGIC_WALL_ACTIVATING));
4894     }
4895
4896     if (IS_PLAYER(x, y + 1))
4897     {
4898       if (CAN_SMASH_PLAYER(element))
4899       {
4900         KillPlayerUnlessEnemyProtected(x, y + 1);
4901         return;
4902       }
4903     }
4904     else if (smashed == EL_PENGUIN)
4905     {
4906       if (CAN_SMASH_PLAYER(element))
4907       {
4908         Bang(x, y + 1);
4909         return;
4910       }
4911     }
4912     else if (element == EL_BD_DIAMOND)
4913     {
4914       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4915       {
4916         Bang(x, y + 1);
4917         return;
4918       }
4919     }
4920     else if (((element == EL_SP_INFOTRON ||
4921                element == EL_SP_ZONK) &&
4922               (smashed == EL_SP_SNIKSNAK ||
4923                smashed == EL_SP_ELECTRON ||
4924                smashed == EL_SP_DISK_ORANGE)) ||
4925              (element == EL_SP_INFOTRON &&
4926               smashed == EL_SP_DISK_YELLOW))
4927     {
4928       Bang(x, y + 1);
4929       return;
4930     }
4931     else if (CAN_SMASH_EVERYTHING(element))
4932     {
4933       if (IS_CLASSIC_ENEMY(smashed) ||
4934           CAN_EXPLODE_SMASHED(smashed))
4935       {
4936         Bang(x, y + 1);
4937         return;
4938       }
4939       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4940       {
4941         if (smashed == EL_LAMP ||
4942             smashed == EL_LAMP_ACTIVE)
4943         {
4944           Bang(x, y + 1);
4945           return;
4946         }
4947         else if (smashed == EL_NUT)
4948         {
4949           Feld[x][y + 1] = EL_NUT_BREAKING;
4950           PlayLevelSound(x, y, SND_NUT_BREAKING);
4951           RaiseScoreElement(EL_NUT);
4952           return;
4953         }
4954         else if (smashed == EL_PEARL)
4955         {
4956           ResetGfxAnimation(x, y);
4957
4958           Feld[x][y + 1] = EL_PEARL_BREAKING;
4959           PlayLevelSound(x, y, SND_PEARL_BREAKING);
4960           return;
4961         }
4962         else if (smashed == EL_DIAMOND)
4963         {
4964           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4965           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4966           return;
4967         }
4968         else if (IS_BELT_SWITCH(smashed))
4969         {
4970           ToggleBeltSwitch(x, y + 1);
4971         }
4972         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4973                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
4974                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
4975                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
4976         {
4977           ToggleSwitchgateSwitch(x, y + 1);
4978         }
4979         else if (smashed == EL_LIGHT_SWITCH ||
4980                  smashed == EL_LIGHT_SWITCH_ACTIVE)
4981         {
4982           ToggleLightSwitch(x, y + 1);
4983         }
4984         else
4985         {
4986 #if 0
4987           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4988 #endif
4989
4990           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4991
4992           CheckElementChangeBySide(x, y + 1, smashed, element,
4993                                    CE_SWITCHED, CH_SIDE_TOP);
4994           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4995                                             CH_SIDE_TOP);
4996         }
4997       }
4998       else
4999       {
5000         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5001       }
5002     }
5003   }
5004
5005   /* play sound of magic wall / mill */
5006   if (!last_line &&
5007       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5008        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5009        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5010   {
5011     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5012       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5013     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5014       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5015     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5016       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5017
5018     return;
5019   }
5020
5021   /* play sound of object that hits the ground */
5022   if (last_line || object_hit)
5023     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5024 }
5025
5026 inline static void TurnRoundExt(int x, int y)
5027 {
5028   static struct
5029   {
5030     int dx, dy;
5031   } move_xy[] =
5032   {
5033     {  0,  0 },
5034     { -1,  0 },
5035     { +1,  0 },
5036     {  0,  0 },
5037     {  0, -1 },
5038     {  0,  0 }, { 0, 0 }, { 0, 0 },
5039     {  0, +1 }
5040   };
5041   static struct
5042   {
5043     int left, right, back;
5044   } turn[] =
5045   {
5046     { 0,        0,              0        },
5047     { MV_DOWN,  MV_UP,          MV_RIGHT },
5048     { MV_UP,    MV_DOWN,        MV_LEFT  },
5049     { 0,        0,              0        },
5050     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5051     { 0,        0,              0        },
5052     { 0,        0,              0        },
5053     { 0,        0,              0        },
5054     { MV_RIGHT, MV_LEFT,        MV_UP    }
5055   };
5056
5057   int element = Feld[x][y];
5058   int move_pattern = element_info[element].move_pattern;
5059
5060   int old_move_dir = MovDir[x][y];
5061   int left_dir  = turn[old_move_dir].left;
5062   int right_dir = turn[old_move_dir].right;
5063   int back_dir  = turn[old_move_dir].back;
5064
5065   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5066   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5067   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5068   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5069
5070   int left_x  = x + left_dx,  left_y  = y + left_dy;
5071   int right_x = x + right_dx, right_y = y + right_dy;
5072   int move_x  = x + move_dx,  move_y  = y + move_dy;
5073
5074   int xx, yy;
5075
5076   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5077   {
5078     TestIfBadThingTouchesOtherBadThing(x, y);
5079
5080     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5081       MovDir[x][y] = right_dir;
5082     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5083       MovDir[x][y] = left_dir;
5084
5085     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5086       MovDelay[x][y] = 9;
5087     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5088       MovDelay[x][y] = 1;
5089   }
5090   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5091   {
5092     TestIfBadThingTouchesOtherBadThing(x, y);
5093
5094     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5095       MovDir[x][y] = left_dir;
5096     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5097       MovDir[x][y] = right_dir;
5098
5099     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5100       MovDelay[x][y] = 9;
5101     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5102       MovDelay[x][y] = 1;
5103   }
5104   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5105   {
5106     TestIfBadThingTouchesOtherBadThing(x, y);
5107
5108     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5109       MovDir[x][y] = left_dir;
5110     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5111       MovDir[x][y] = right_dir;
5112
5113     if (MovDir[x][y] != old_move_dir)
5114       MovDelay[x][y] = 9;
5115   }
5116   else if (element == EL_YAMYAM)
5117   {
5118     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5119     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5120
5121     if (can_turn_left && can_turn_right)
5122       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5123     else if (can_turn_left)
5124       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5125     else if (can_turn_right)
5126       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5127     else
5128       MovDir[x][y] = back_dir;
5129
5130     MovDelay[x][y] = 16 + 16 * RND(3);
5131   }
5132   else if (element == EL_DARK_YAMYAM)
5133   {
5134     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5135                                                          left_x, left_y);
5136     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5137                                                          right_x, right_y);
5138
5139     if (can_turn_left && can_turn_right)
5140       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5141     else if (can_turn_left)
5142       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5143     else if (can_turn_right)
5144       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5145     else
5146       MovDir[x][y] = back_dir;
5147
5148     MovDelay[x][y] = 16 + 16 * RND(3);
5149   }
5150   else if (element == EL_PACMAN)
5151   {
5152     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5153     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5154
5155     if (can_turn_left && can_turn_right)
5156       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5157     else if (can_turn_left)
5158       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5159     else if (can_turn_right)
5160       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5161     else
5162       MovDir[x][y] = back_dir;
5163
5164     MovDelay[x][y] = 6 + RND(40);
5165   }
5166   else if (element == EL_PIG)
5167   {
5168     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5169     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5170     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5171     boolean should_turn_left, should_turn_right, should_move_on;
5172     int rnd_value = 24;
5173     int rnd = RND(rnd_value);
5174
5175     should_turn_left = (can_turn_left &&
5176                         (!can_move_on ||
5177                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5178                                                    y + back_dy + left_dy)));
5179     should_turn_right = (can_turn_right &&
5180                          (!can_move_on ||
5181                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5182                                                     y + back_dy + right_dy)));
5183     should_move_on = (can_move_on &&
5184                       (!can_turn_left ||
5185                        !can_turn_right ||
5186                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5187                                                  y + move_dy + left_dy) ||
5188                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5189                                                  y + move_dy + right_dy)));
5190
5191     if (should_turn_left || should_turn_right || should_move_on)
5192     {
5193       if (should_turn_left && should_turn_right && should_move_on)
5194         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5195                         rnd < 2 * rnd_value / 3 ? right_dir :
5196                         old_move_dir);
5197       else if (should_turn_left && should_turn_right)
5198         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5199       else if (should_turn_left && should_move_on)
5200         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5201       else if (should_turn_right && should_move_on)
5202         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5203       else if (should_turn_left)
5204         MovDir[x][y] = left_dir;
5205       else if (should_turn_right)
5206         MovDir[x][y] = right_dir;
5207       else if (should_move_on)
5208         MovDir[x][y] = old_move_dir;
5209     }
5210     else if (can_move_on && rnd > rnd_value / 8)
5211       MovDir[x][y] = old_move_dir;
5212     else if (can_turn_left && can_turn_right)
5213       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5214     else if (can_turn_left && rnd > rnd_value / 8)
5215       MovDir[x][y] = left_dir;
5216     else if (can_turn_right && rnd > rnd_value/8)
5217       MovDir[x][y] = right_dir;
5218     else
5219       MovDir[x][y] = back_dir;
5220
5221     xx = x + move_xy[MovDir[x][y]].dx;
5222     yy = y + move_xy[MovDir[x][y]].dy;
5223
5224     if (!IN_LEV_FIELD(xx, yy) ||
5225         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5226       MovDir[x][y] = old_move_dir;
5227
5228     MovDelay[x][y] = 0;
5229   }
5230   else if (element == EL_DRAGON)
5231   {
5232     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5233     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5234     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5235     int rnd_value = 24;
5236     int rnd = RND(rnd_value);
5237
5238     if (can_move_on && rnd > rnd_value / 8)
5239       MovDir[x][y] = old_move_dir;
5240     else if (can_turn_left && can_turn_right)
5241       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5242     else if (can_turn_left && rnd > rnd_value / 8)
5243       MovDir[x][y] = left_dir;
5244     else if (can_turn_right && rnd > rnd_value / 8)
5245       MovDir[x][y] = right_dir;
5246     else
5247       MovDir[x][y] = back_dir;
5248
5249     xx = x + move_xy[MovDir[x][y]].dx;
5250     yy = y + move_xy[MovDir[x][y]].dy;
5251
5252     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5253       MovDir[x][y] = old_move_dir;
5254
5255     MovDelay[x][y] = 0;
5256   }
5257   else if (element == EL_MOLE)
5258   {
5259     boolean can_move_on =
5260       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5261                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5262                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5263     if (!can_move_on)
5264     {
5265       boolean can_turn_left =
5266         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5267                               IS_AMOEBOID(Feld[left_x][left_y])));
5268
5269       boolean can_turn_right =
5270         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5271                               IS_AMOEBOID(Feld[right_x][right_y])));
5272
5273       if (can_turn_left && can_turn_right)
5274         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5275       else if (can_turn_left)
5276         MovDir[x][y] = left_dir;
5277       else
5278         MovDir[x][y] = right_dir;
5279     }
5280
5281     if (MovDir[x][y] != old_move_dir)
5282       MovDelay[x][y] = 9;
5283   }
5284   else if (element == EL_BALLOON)
5285   {
5286     MovDir[x][y] = game.wind_direction;
5287     MovDelay[x][y] = 0;
5288   }
5289   else if (element == EL_SPRING)
5290   {
5291 #if USE_NEW_SPRING_BUMPER
5292     if (MovDir[x][y] & MV_HORIZONTAL)
5293     {
5294       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5295           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5296       {
5297         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5298         ResetGfxAnimation(move_x, move_y);
5299         DrawLevelField(move_x, move_y);
5300
5301         MovDir[x][y] = back_dir;
5302       }
5303       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5304                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5305         MovDir[x][y] = MV_NONE;
5306     }
5307 #else
5308     if (MovDir[x][y] & MV_HORIZONTAL &&
5309         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5310          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5311       MovDir[x][y] = MV_NONE;
5312 #endif
5313
5314     MovDelay[x][y] = 0;
5315   }
5316   else if (element == EL_ROBOT ||
5317            element == EL_SATELLITE ||
5318            element == EL_PENGUIN ||
5319            element == EL_EMC_ANDROID)
5320   {
5321     int attr_x = -1, attr_y = -1;
5322
5323     if (AllPlayersGone)
5324     {
5325       attr_x = ExitX;
5326       attr_y = ExitY;
5327     }
5328     else
5329     {
5330       int i;
5331
5332       for (i = 0; i < MAX_PLAYERS; i++)
5333       {
5334         struct PlayerInfo *player = &stored_player[i];
5335         int jx = player->jx, jy = player->jy;
5336
5337         if (!player->active)
5338           continue;
5339
5340         if (attr_x == -1 ||
5341             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5342         {
5343           attr_x = jx;
5344           attr_y = jy;
5345         }
5346       }
5347     }
5348
5349     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5350         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5351          game.engine_version < VERSION_IDENT(3,1,0,0)))
5352     {
5353       attr_x = ZX;
5354       attr_y = ZY;
5355     }
5356
5357     if (element == EL_PENGUIN)
5358     {
5359       int i;
5360       static int xy[4][2] =
5361       {
5362         { 0, -1 },
5363         { -1, 0 },
5364         { +1, 0 },
5365         { 0, +1 }
5366       };
5367
5368       for (i = 0; i < NUM_DIRECTIONS; i++)
5369       {
5370         int ex = x + xy[i][0];
5371         int ey = y + xy[i][1];
5372
5373         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5374                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5375                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5376                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5377         {
5378           attr_x = ex;
5379           attr_y = ey;
5380           break;
5381         }
5382       }
5383     }
5384
5385     MovDir[x][y] = MV_NONE;
5386     if (attr_x < x)
5387       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5388     else if (attr_x > x)
5389       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5390     if (attr_y < y)
5391       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5392     else if (attr_y > y)
5393       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5394
5395     if (element == EL_ROBOT)
5396     {
5397       int newx, newy;
5398
5399       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5400         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5401       Moving2Blocked(x, y, &newx, &newy);
5402
5403       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5404         MovDelay[x][y] = 8 + 8 * !RND(3);
5405       else
5406         MovDelay[x][y] = 16;
5407     }
5408     else if (element == EL_PENGUIN)
5409     {
5410       int newx, newy;
5411
5412       MovDelay[x][y] = 1;
5413
5414       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5415       {
5416         boolean first_horiz = RND(2);
5417         int new_move_dir = MovDir[x][y];
5418
5419         MovDir[x][y] =
5420           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5421         Moving2Blocked(x, y, &newx, &newy);
5422
5423         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5424           return;
5425
5426         MovDir[x][y] =
5427           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5428         Moving2Blocked(x, y, &newx, &newy);
5429
5430         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5431           return;
5432
5433         MovDir[x][y] = old_move_dir;
5434         return;
5435       }
5436     }
5437     else if (element == EL_SATELLITE)
5438     {
5439       int newx, newy;
5440
5441       MovDelay[x][y] = 1;
5442
5443       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5444       {
5445         boolean first_horiz = RND(2);
5446         int new_move_dir = MovDir[x][y];
5447
5448         MovDir[x][y] =
5449           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5450         Moving2Blocked(x, y, &newx, &newy);
5451
5452         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5453           return;
5454
5455         MovDir[x][y] =
5456           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5457         Moving2Blocked(x, y, &newx, &newy);
5458
5459         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5460           return;
5461
5462         MovDir[x][y] = old_move_dir;
5463         return;
5464       }
5465     }
5466     else if (element == EL_EMC_ANDROID)
5467     {
5468       static int check_pos[16] =
5469       {
5470         -1,             /*  0 => (invalid)          */
5471         7,              /*  1 => MV_LEFT            */
5472         3,              /*  2 => MV_RIGHT           */
5473         -1,             /*  3 => (invalid)          */
5474         1,              /*  4 =>            MV_UP   */
5475         0,              /*  5 => MV_LEFT  | MV_UP   */
5476         2,              /*  6 => MV_RIGHT | MV_UP   */
5477         -1,             /*  7 => (invalid)          */
5478         5,              /*  8 =>            MV_DOWN */
5479         6,              /*  9 => MV_LEFT  | MV_DOWN */
5480         4,              /* 10 => MV_RIGHT | MV_DOWN */
5481         -1,             /* 11 => (invalid)          */
5482         -1,             /* 12 => (invalid)          */
5483         -1,             /* 13 => (invalid)          */
5484         -1,             /* 14 => (invalid)          */
5485         -1,             /* 15 => (invalid)          */
5486       };
5487       static struct
5488       {
5489         int dx, dy;
5490         int dir;
5491       } check_xy[8] =
5492       {
5493         { -1, -1,       MV_LEFT  | MV_UP   },
5494         {  0, -1,                  MV_UP   },
5495         { +1, -1,       MV_RIGHT | MV_UP   },
5496         { +1,  0,       MV_RIGHT           },
5497         { +1, +1,       MV_RIGHT | MV_DOWN },
5498         {  0, +1,                  MV_DOWN },
5499         { -1, +1,       MV_LEFT  | MV_DOWN },
5500         { -1,  0,       MV_LEFT            },
5501       };
5502       int start_pos, check_order;
5503       boolean can_clone = FALSE;
5504       int i;
5505
5506       /* check if there is any free field around current position */
5507       for (i = 0; i < 8; i++)
5508       {
5509         int newx = x + check_xy[i].dx;
5510         int newy = y + check_xy[i].dy;
5511
5512         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5513         {
5514           can_clone = TRUE;
5515
5516           break;
5517         }
5518       }
5519
5520       if (can_clone)            /* randomly find an element to clone */
5521       {
5522         can_clone = FALSE;
5523
5524         start_pos = check_pos[RND(8)];
5525         check_order = (RND(2) ? -1 : +1);
5526
5527         for (i = 0; i < 8; i++)
5528         {
5529           int pos_raw = start_pos + i * check_order;
5530           int pos = (pos_raw + 8) % 8;
5531           int newx = x + check_xy[pos].dx;
5532           int newy = y + check_xy[pos].dy;
5533
5534           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5535           {
5536             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5537             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5538
5539             Store[x][y] = Feld[newx][newy];
5540
5541             can_clone = TRUE;
5542
5543             break;
5544           }
5545         }
5546       }
5547
5548       if (can_clone)            /* randomly find a direction to move */
5549       {
5550         can_clone = FALSE;
5551
5552         start_pos = check_pos[RND(8)];
5553         check_order = (RND(2) ? -1 : +1);
5554
5555         for (i = 0; i < 8; i++)
5556         {
5557           int pos_raw = start_pos + i * check_order;
5558           int pos = (pos_raw + 8) % 8;
5559           int newx = x + check_xy[pos].dx;
5560           int newy = y + check_xy[pos].dy;
5561           int new_move_dir = check_xy[pos].dir;
5562
5563           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5564           {
5565             MovDir[x][y] = new_move_dir;
5566             MovDelay[x][y] = level.android_clone_time * 8 + 1;
5567
5568             can_clone = TRUE;
5569
5570             break;
5571           }
5572         }
5573       }
5574
5575       if (can_clone)            /* cloning and moving successful */
5576         return;
5577
5578       /* cannot clone -- try to move towards player */
5579
5580       start_pos = check_pos[MovDir[x][y] & 0x0f];
5581       check_order = (RND(2) ? -1 : +1);
5582
5583       for (i = 0; i < 3; i++)
5584       {
5585         /* first check start_pos, then previous/next or (next/previous) pos */
5586         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5587         int pos = (pos_raw + 8) % 8;
5588         int newx = x + check_xy[pos].dx;
5589         int newy = y + check_xy[pos].dy;
5590         int new_move_dir = check_xy[pos].dir;
5591
5592         if (IS_PLAYER(newx, newy))
5593           break;
5594
5595         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5596         {
5597           MovDir[x][y] = new_move_dir;
5598           MovDelay[x][y] = level.android_move_time * 8 + 1;
5599
5600           break;
5601         }
5602       }
5603     }
5604   }
5605   else if (move_pattern == MV_TURNING_LEFT ||
5606            move_pattern == MV_TURNING_RIGHT ||
5607            move_pattern == MV_TURNING_LEFT_RIGHT ||
5608            move_pattern == MV_TURNING_RIGHT_LEFT ||
5609            move_pattern == MV_TURNING_RANDOM ||
5610            move_pattern == MV_ALL_DIRECTIONS)
5611   {
5612     boolean can_turn_left =
5613       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5614     boolean can_turn_right =
5615       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5616
5617     if (element_info[element].move_stepsize == 0)       /* "not moving" */
5618       return;
5619
5620     if (move_pattern == MV_TURNING_LEFT)
5621       MovDir[x][y] = left_dir;
5622     else if (move_pattern == MV_TURNING_RIGHT)
5623       MovDir[x][y] = right_dir;
5624     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5625       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5626     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5627       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5628     else if (move_pattern == MV_TURNING_RANDOM)
5629       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5630                       can_turn_right && !can_turn_left ? right_dir :
5631                       RND(2) ? left_dir : right_dir);
5632     else if (can_turn_left && can_turn_right)
5633       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5634     else if (can_turn_left)
5635       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5636     else if (can_turn_right)
5637       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5638     else
5639       MovDir[x][y] = back_dir;
5640
5641     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5642   }
5643   else if (move_pattern == MV_HORIZONTAL ||
5644            move_pattern == MV_VERTICAL)
5645   {
5646     if (move_pattern & old_move_dir)
5647       MovDir[x][y] = back_dir;
5648     else if (move_pattern == MV_HORIZONTAL)
5649       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5650     else if (move_pattern == MV_VERTICAL)
5651       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5652
5653     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5654   }
5655   else if (move_pattern & MV_ANY_DIRECTION)
5656   {
5657     MovDir[x][y] = move_pattern;
5658     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5659   }
5660   else if (move_pattern & MV_WIND_DIRECTION)
5661   {
5662     MovDir[x][y] = game.wind_direction;
5663     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5664   }
5665   else if (move_pattern == MV_ALONG_LEFT_SIDE)
5666   {
5667     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5668       MovDir[x][y] = left_dir;
5669     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5670       MovDir[x][y] = right_dir;
5671
5672     if (MovDir[x][y] != old_move_dir)
5673       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5674   }
5675   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5676   {
5677     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5678       MovDir[x][y] = right_dir;
5679     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5680       MovDir[x][y] = left_dir;
5681
5682     if (MovDir[x][y] != old_move_dir)
5683       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5684   }
5685   else if (move_pattern == MV_TOWARDS_PLAYER ||
5686            move_pattern == MV_AWAY_FROM_PLAYER)
5687   {
5688     int attr_x = -1, attr_y = -1;
5689     int newx, newy;
5690     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5691
5692     if (AllPlayersGone)
5693     {
5694       attr_x = ExitX;
5695       attr_y = ExitY;
5696     }
5697     else
5698     {
5699       int i;
5700
5701       for (i = 0; i < MAX_PLAYERS; i++)
5702       {
5703         struct PlayerInfo *player = &stored_player[i];
5704         int jx = player->jx, jy = player->jy;
5705
5706         if (!player->active)
5707           continue;
5708
5709         if (attr_x == -1 ||
5710             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5711         {
5712           attr_x = jx;
5713           attr_y = jy;
5714         }
5715       }
5716     }
5717
5718     MovDir[x][y] = MV_NONE;
5719     if (attr_x < x)
5720       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5721     else if (attr_x > x)
5722       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5723     if (attr_y < y)
5724       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5725     else if (attr_y > y)
5726       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5727
5728     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5729
5730     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5731     {
5732       boolean first_horiz = RND(2);
5733       int new_move_dir = MovDir[x][y];
5734
5735       if (element_info[element].move_stepsize == 0)     /* "not moving" */
5736       {
5737         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5738         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5739
5740         return;
5741       }
5742
5743       MovDir[x][y] =
5744         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5745       Moving2Blocked(x, y, &newx, &newy);
5746
5747       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5748         return;
5749
5750       MovDir[x][y] =
5751         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5752       Moving2Blocked(x, y, &newx, &newy);
5753
5754       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5755         return;
5756
5757       MovDir[x][y] = old_move_dir;
5758     }
5759   }
5760   else if (move_pattern == MV_WHEN_PUSHED ||
5761            move_pattern == MV_WHEN_DROPPED)
5762   {
5763     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5764       MovDir[x][y] = MV_NONE;
5765
5766     MovDelay[x][y] = 0;
5767   }
5768   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5769   {
5770     static int test_xy[7][2] =
5771     {
5772       { 0, -1 },
5773       { -1, 0 },
5774       { +1, 0 },
5775       { 0, +1 },
5776       { 0, -1 },
5777       { -1, 0 },
5778       { +1, 0 },
5779     };
5780     static int test_dir[7] =
5781     {
5782       MV_UP,
5783       MV_LEFT,
5784       MV_RIGHT,
5785       MV_DOWN,
5786       MV_UP,
5787       MV_LEFT,
5788       MV_RIGHT,
5789     };
5790     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5791     int move_preference = -1000000;     /* start with very low preference */
5792     int new_move_dir = MV_NONE;
5793     int start_test = RND(4);
5794     int i;
5795
5796     for (i = 0; i < NUM_DIRECTIONS; i++)
5797     {
5798       int move_dir = test_dir[start_test + i];
5799       int move_dir_preference;
5800
5801       xx = x + test_xy[start_test + i][0];
5802       yy = y + test_xy[start_test + i][1];
5803
5804       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5805           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5806       {
5807         new_move_dir = move_dir;
5808
5809         break;
5810       }
5811
5812       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5813         continue;
5814
5815       move_dir_preference = -1 * RunnerVisit[xx][yy];
5816       if (hunter_mode && PlayerVisit[xx][yy] > 0)
5817         move_dir_preference = PlayerVisit[xx][yy];
5818
5819       if (move_dir_preference > move_preference)
5820       {
5821         /* prefer field that has not been visited for the longest time */
5822         move_preference = move_dir_preference;
5823         new_move_dir = move_dir;
5824       }
5825       else if (move_dir_preference == move_preference &&
5826                move_dir == old_move_dir)
5827       {
5828         /* prefer last direction when all directions are preferred equally */
5829         move_preference = move_dir_preference;
5830         new_move_dir = move_dir;
5831       }
5832     }
5833
5834     MovDir[x][y] = new_move_dir;
5835     if (old_move_dir != new_move_dir)
5836       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5837   }
5838 }
5839
5840 static void TurnRound(int x, int y)
5841 {
5842   int direction = MovDir[x][y];
5843
5844   TurnRoundExt(x, y);
5845
5846   GfxDir[x][y] = MovDir[x][y];
5847
5848   if (direction != MovDir[x][y])
5849     GfxFrame[x][y] = 0;
5850
5851   if (MovDelay[x][y])
5852     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5853
5854   ResetGfxFrame(x, y, FALSE);
5855 }
5856
5857 static boolean JustBeingPushed(int x, int y)
5858 {
5859   int i;
5860
5861   for (i = 0; i < MAX_PLAYERS; i++)
5862   {
5863     struct PlayerInfo *player = &stored_player[i];
5864
5865     if (player->active && player->is_pushing && player->MovPos)
5866     {
5867       int next_jx = player->jx + (player->jx - player->last_jx);
5868       int next_jy = player->jy + (player->jy - player->last_jy);
5869
5870       if (x == next_jx && y == next_jy)
5871         return TRUE;
5872     }
5873   }
5874
5875   return FALSE;
5876 }
5877
5878 void StartMoving(int x, int y)
5879 {
5880   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
5881   int element = Feld[x][y];
5882
5883   if (Stop[x][y])
5884     return;
5885
5886   if (MovDelay[x][y] == 0)
5887     GfxAction[x][y] = ACTION_DEFAULT;
5888
5889   if (CAN_FALL(element) && y < lev_fieldy - 1)
5890   {
5891     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5892         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5893       if (JustBeingPushed(x, y))
5894         return;
5895
5896     if (element == EL_QUICKSAND_FULL)
5897     {
5898       if (IS_FREE(x, y + 1))
5899       {
5900         InitMovingField(x, y, MV_DOWN);
5901         started_moving = TRUE;
5902
5903         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5904 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5905         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5906           Store[x][y] = EL_ROCK;
5907 #else
5908         Store[x][y] = EL_ROCK;
5909 #endif
5910
5911         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5912       }
5913       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5914       {
5915         if (!MovDelay[x][y])
5916           MovDelay[x][y] = TILEY + 1;
5917
5918         if (MovDelay[x][y])
5919         {
5920           MovDelay[x][y]--;
5921           if (MovDelay[x][y])
5922             return;
5923         }
5924
5925         Feld[x][y] = EL_QUICKSAND_EMPTY;
5926         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5927         Store[x][y + 1] = Store[x][y];
5928         Store[x][y] = 0;
5929
5930         PlayLevelSoundAction(x, y, ACTION_FILLING);
5931       }
5932     }
5933     else if (element == EL_QUICKSAND_FAST_FULL)
5934     {
5935       if (IS_FREE(x, y + 1))
5936       {
5937         InitMovingField(x, y, MV_DOWN);
5938         started_moving = TRUE;
5939
5940         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
5941 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5942         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5943           Store[x][y] = EL_ROCK;
5944 #else
5945         Store[x][y] = EL_ROCK;
5946 #endif
5947
5948         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5949       }
5950       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5951       {
5952         if (!MovDelay[x][y])
5953           MovDelay[x][y] = TILEY + 1;
5954
5955         if (MovDelay[x][y])
5956         {
5957           MovDelay[x][y]--;
5958           if (MovDelay[x][y])
5959             return;
5960         }
5961
5962         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
5963         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
5964         Store[x][y + 1] = Store[x][y];
5965         Store[x][y] = 0;
5966
5967         PlayLevelSoundAction(x, y, ACTION_FILLING);
5968       }
5969     }
5970     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5971              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5972     {
5973       InitMovingField(x, y, MV_DOWN);
5974       started_moving = TRUE;
5975
5976       Feld[x][y] = EL_QUICKSAND_FILLING;
5977       Store[x][y] = element;
5978
5979       PlayLevelSoundAction(x, y, ACTION_FILLING);
5980     }
5981     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5982              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5983     {
5984       InitMovingField(x, y, MV_DOWN);
5985       started_moving = TRUE;
5986
5987       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
5988       Store[x][y] = element;
5989
5990       PlayLevelSoundAction(x, y, ACTION_FILLING);
5991     }
5992     else if (element == EL_MAGIC_WALL_FULL)
5993     {
5994       if (IS_FREE(x, y + 1))
5995       {
5996         InitMovingField(x, y, MV_DOWN);
5997         started_moving = TRUE;
5998
5999         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6000         Store[x][y] = EL_CHANGED(Store[x][y]);
6001       }
6002       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6003       {
6004         if (!MovDelay[x][y])
6005           MovDelay[x][y] = TILEY/4 + 1;
6006
6007         if (MovDelay[x][y])
6008         {
6009           MovDelay[x][y]--;
6010           if (MovDelay[x][y])
6011             return;
6012         }
6013
6014         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6015         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6016         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6017         Store[x][y] = 0;
6018       }
6019     }
6020     else if (element == EL_BD_MAGIC_WALL_FULL)
6021     {
6022       if (IS_FREE(x, y + 1))
6023       {
6024         InitMovingField(x, y, MV_DOWN);
6025         started_moving = TRUE;
6026
6027         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6028         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6029       }
6030       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6031       {
6032         if (!MovDelay[x][y])
6033           MovDelay[x][y] = TILEY/4 + 1;
6034
6035         if (MovDelay[x][y])
6036         {
6037           MovDelay[x][y]--;
6038           if (MovDelay[x][y])
6039             return;
6040         }
6041
6042         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6043         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6044         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6045         Store[x][y] = 0;
6046       }
6047     }
6048     else if (element == EL_DC_MAGIC_WALL_FULL)
6049     {
6050       if (IS_FREE(x, y + 1))
6051       {
6052         InitMovingField(x, y, MV_DOWN);
6053         started_moving = TRUE;
6054
6055         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6056         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6057       }
6058       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6059       {
6060         if (!MovDelay[x][y])
6061           MovDelay[x][y] = TILEY/4 + 1;
6062
6063         if (MovDelay[x][y])
6064         {
6065           MovDelay[x][y]--;
6066           if (MovDelay[x][y])
6067             return;
6068         }
6069
6070         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6071         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6072         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6073         Store[x][y] = 0;
6074       }
6075     }
6076     else if ((CAN_PASS_MAGIC_WALL(element) &&
6077               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6078                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6079              (CAN_PASS_DC_MAGIC_WALL(element) &&
6080               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6081
6082     {
6083       InitMovingField(x, y, MV_DOWN);
6084       started_moving = TRUE;
6085
6086       Feld[x][y] =
6087         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6088          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6089          EL_DC_MAGIC_WALL_FILLING);
6090       Store[x][y] = element;
6091     }
6092     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6093     {
6094       SplashAcid(x, y + 1);
6095
6096       InitMovingField(x, y, MV_DOWN);
6097       started_moving = TRUE;
6098
6099       Store[x][y] = EL_ACID;
6100     }
6101     else if (
6102 #if USE_FIX_IMPACT_COLLISION
6103              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6104               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6105 #else
6106              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6107               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6108 #endif
6109              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6110               CAN_FALL(element) && WasJustFalling[x][y] &&
6111               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6112
6113              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6114               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6115               (Feld[x][y + 1] == EL_BLOCKED)))
6116     {
6117       /* this is needed for a special case not covered by calling "Impact()"
6118          from "ContinueMoving()": if an element moves to a tile directly below
6119          another element which was just falling on that tile (which was empty
6120          in the previous frame), the falling element above would just stop
6121          instead of smashing the element below (in previous version, the above
6122          element was just checked for "moving" instead of "falling", resulting
6123          in incorrect smashes caused by horizontal movement of the above
6124          element; also, the case of the player being the element to smash was
6125          simply not covered here... :-/ ) */
6126
6127       CheckCollision[x][y] = 0;
6128       CheckImpact[x][y] = 0;
6129
6130       Impact(x, y);
6131     }
6132     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6133     {
6134       if (MovDir[x][y] == MV_NONE)
6135       {
6136         InitMovingField(x, y, MV_DOWN);
6137         started_moving = TRUE;
6138       }
6139     }
6140     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6141     {
6142       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6143         MovDir[x][y] = MV_DOWN;
6144
6145       InitMovingField(x, y, MV_DOWN);
6146       started_moving = TRUE;
6147     }
6148     else if (element == EL_AMOEBA_DROP)
6149     {
6150       Feld[x][y] = EL_AMOEBA_GROWING;
6151       Store[x][y] = EL_AMOEBA_WET;
6152     }
6153     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6154               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6155              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6156              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6157     {
6158       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6159                                 (IS_FREE(x - 1, y + 1) ||
6160                                  Feld[x - 1][y + 1] == EL_ACID));
6161       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6162                                 (IS_FREE(x + 1, y + 1) ||
6163                                  Feld[x + 1][y + 1] == EL_ACID));
6164       boolean can_fall_any  = (can_fall_left || can_fall_right);
6165       boolean can_fall_both = (can_fall_left && can_fall_right);
6166       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6167
6168 #if USE_NEW_ALL_SLIPPERY
6169       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6170       {
6171         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6172           can_fall_right = FALSE;
6173         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6174           can_fall_left = FALSE;
6175         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6176           can_fall_right = FALSE;
6177         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6178           can_fall_left = FALSE;
6179
6180         can_fall_any  = (can_fall_left || can_fall_right);
6181         can_fall_both = FALSE;
6182       }
6183 #else
6184       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6185       {
6186         if (slippery_type == SLIPPERY_ONLY_LEFT)
6187           can_fall_right = FALSE;
6188         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6189           can_fall_left = FALSE;
6190         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6191           can_fall_right = FALSE;
6192         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6193           can_fall_left = FALSE;
6194
6195         can_fall_any  = (can_fall_left || can_fall_right);
6196         can_fall_both = (can_fall_left && can_fall_right);
6197       }
6198 #endif
6199
6200 #if USE_NEW_ALL_SLIPPERY
6201 #else
6202 #if USE_NEW_SP_SLIPPERY
6203       /* !!! better use the same properties as for custom elements here !!! */
6204       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6205                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6206       {
6207         can_fall_right = FALSE;         /* slip down on left side */
6208         can_fall_both = FALSE;
6209       }
6210 #endif
6211 #endif
6212
6213 #if USE_NEW_ALL_SLIPPERY
6214       if (can_fall_both)
6215       {
6216         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6217           can_fall_right = FALSE;       /* slip down on left side */
6218         else
6219           can_fall_left = !(can_fall_right = RND(2));
6220
6221         can_fall_both = FALSE;
6222       }
6223 #else
6224       if (can_fall_both)
6225       {
6226         if (game.emulation == EMU_BOULDERDASH ||
6227             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6228           can_fall_right = FALSE;       /* slip down on left side */
6229         else
6230           can_fall_left = !(can_fall_right = RND(2));
6231
6232         can_fall_both = FALSE;
6233       }
6234 #endif
6235
6236       if (can_fall_any)
6237       {
6238         /* if not determined otherwise, prefer left side for slipping down */
6239         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6240         started_moving = TRUE;
6241       }
6242     }
6243 #if 0
6244     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6245 #else
6246     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6247 #endif
6248     {
6249       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6250       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6251       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6252       int belt_dir = game.belt_dir[belt_nr];
6253
6254       if ((belt_dir == MV_LEFT  && left_is_free) ||
6255           (belt_dir == MV_RIGHT && right_is_free))
6256       {
6257         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6258
6259         InitMovingField(x, y, belt_dir);
6260         started_moving = TRUE;
6261
6262         Pushed[x][y] = TRUE;
6263         Pushed[nextx][y] = TRUE;
6264
6265         GfxAction[x][y] = ACTION_DEFAULT;
6266       }
6267       else
6268       {
6269         MovDir[x][y] = 0;       /* if element was moving, stop it */
6270       }
6271     }
6272   }
6273
6274   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6275 #if 0
6276   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6277 #else
6278   if (CAN_MOVE(element) && !started_moving)
6279 #endif
6280   {
6281     int move_pattern = element_info[element].move_pattern;
6282     int newx, newy;
6283
6284 #if 0
6285 #if DEBUG
6286     if (MovDir[x][y] == MV_NONE)
6287     {
6288       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6289              x, y, element, element_info[element].token_name);
6290       printf("StartMoving(): This should never happen!\n");
6291     }
6292 #endif
6293 #endif
6294
6295     Moving2Blocked(x, y, &newx, &newy);
6296
6297     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6298       return;
6299
6300     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6301         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6302     {
6303       WasJustMoving[x][y] = 0;
6304       CheckCollision[x][y] = 0;
6305
6306       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6307
6308       if (Feld[x][y] != element)        /* element has changed */
6309         return;
6310     }
6311
6312     if (!MovDelay[x][y])        /* start new movement phase */
6313     {
6314       /* all objects that can change their move direction after each step
6315          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6316
6317       if (element != EL_YAMYAM &&
6318           element != EL_DARK_YAMYAM &&
6319           element != EL_PACMAN &&
6320           !(move_pattern & MV_ANY_DIRECTION) &&
6321           move_pattern != MV_TURNING_LEFT &&
6322           move_pattern != MV_TURNING_RIGHT &&
6323           move_pattern != MV_TURNING_LEFT_RIGHT &&
6324           move_pattern != MV_TURNING_RIGHT_LEFT &&
6325           move_pattern != MV_TURNING_RANDOM)
6326       {
6327         TurnRound(x, y);
6328
6329         if (MovDelay[x][y] && (element == EL_BUG ||
6330                                element == EL_SPACESHIP ||
6331                                element == EL_SP_SNIKSNAK ||
6332                                element == EL_SP_ELECTRON ||
6333                                element == EL_MOLE))
6334           DrawLevelField(x, y);
6335       }
6336     }
6337
6338     if (MovDelay[x][y])         /* wait some time before next movement */
6339     {
6340       MovDelay[x][y]--;
6341
6342       if (element == EL_ROBOT ||
6343           element == EL_YAMYAM ||
6344           element == EL_DARK_YAMYAM)
6345       {
6346         DrawLevelElementAnimationIfNeeded(x, y, element);
6347         PlayLevelSoundAction(x, y, ACTION_WAITING);
6348       }
6349       else if (element == EL_SP_ELECTRON)
6350         DrawLevelElementAnimationIfNeeded(x, y, element);
6351       else if (element == EL_DRAGON)
6352       {
6353         int i;
6354         int dir = MovDir[x][y];
6355         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6356         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6357         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6358                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6359                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6360                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6361         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6362
6363         GfxAction[x][y] = ACTION_ATTACKING;
6364
6365         if (IS_PLAYER(x, y))
6366           DrawPlayerField(x, y);
6367         else
6368           DrawLevelField(x, y);
6369
6370         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6371
6372         for (i = 1; i <= 3; i++)
6373         {
6374           int xx = x + i * dx;
6375           int yy = y + i * dy;
6376           int sx = SCREENX(xx);
6377           int sy = SCREENY(yy);
6378           int flame_graphic = graphic + (i - 1);
6379
6380           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6381             break;
6382
6383           if (MovDelay[x][y])
6384           {
6385             int flamed = MovingOrBlocked2Element(xx, yy);
6386
6387             /* !!! */
6388 #if 0
6389             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6390               Bang(xx, yy);
6391             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6392               RemoveMovingField(xx, yy);
6393             else
6394               RemoveField(xx, yy);
6395 #else
6396             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6397               Bang(xx, yy);
6398             else
6399               RemoveMovingField(xx, yy);
6400 #endif
6401
6402             ChangeDelay[xx][yy] = 0;
6403
6404             Feld[xx][yy] = EL_FLAMES;
6405
6406             if (IN_SCR_FIELD(sx, sy))
6407             {
6408               DrawLevelFieldCrumbledSand(xx, yy);
6409               DrawGraphic(sx, sy, flame_graphic, frame);
6410             }
6411           }
6412           else
6413           {
6414             if (Feld[xx][yy] == EL_FLAMES)
6415               Feld[xx][yy] = EL_EMPTY;
6416             DrawLevelField(xx, yy);
6417           }
6418         }
6419       }
6420
6421       if (MovDelay[x][y])       /* element still has to wait some time */
6422       {
6423         PlayLevelSoundAction(x, y, ACTION_WAITING);
6424
6425         return;
6426       }
6427     }
6428
6429     /* now make next step */
6430
6431     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6432
6433     if (DONT_COLLIDE_WITH(element) &&
6434         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6435         !PLAYER_ENEMY_PROTECTED(newx, newy))
6436     {
6437       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6438
6439       return;
6440     }
6441
6442     else if (CAN_MOVE_INTO_ACID(element) &&
6443              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6444              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6445              (MovDir[x][y] == MV_DOWN ||
6446               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6447     {
6448       SplashAcid(newx, newy);
6449       Store[x][y] = EL_ACID;
6450     }
6451     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6452     {
6453       if (Feld[newx][newy] == EL_EXIT_OPEN ||
6454           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6455           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6456           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6457       {
6458         RemoveField(x, y);
6459         DrawLevelField(x, y);
6460
6461         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6462         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6463           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6464
6465         local_player->friends_still_needed--;
6466         if (!local_player->friends_still_needed &&
6467             !local_player->GameOver && AllPlayersGone)
6468           PlayerWins(local_player);
6469
6470         return;
6471       }
6472       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6473       {
6474         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6475           DrawLevelField(newx, newy);
6476         else
6477           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6478       }
6479       else if (!IS_FREE(newx, newy))
6480       {
6481         GfxAction[x][y] = ACTION_WAITING;
6482
6483         if (IS_PLAYER(x, y))
6484           DrawPlayerField(x, y);
6485         else
6486           DrawLevelField(x, y);
6487
6488         return;
6489       }
6490     }
6491     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6492     {
6493       if (IS_FOOD_PIG(Feld[newx][newy]))
6494       {
6495         if (IS_MOVING(newx, newy))
6496           RemoveMovingField(newx, newy);
6497         else
6498         {
6499           Feld[newx][newy] = EL_EMPTY;
6500           DrawLevelField(newx, newy);
6501         }
6502
6503         PlayLevelSound(x, y, SND_PIG_DIGGING);
6504       }
6505       else if (!IS_FREE(newx, newy))
6506       {
6507         if (IS_PLAYER(x, y))
6508           DrawPlayerField(x, y);
6509         else
6510           DrawLevelField(x, y);
6511
6512         return;
6513       }
6514     }
6515     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6516     {
6517       if (Store[x][y] != EL_EMPTY)
6518       {
6519         boolean can_clone = FALSE;
6520         int xx, yy;
6521
6522         /* check if element to clone is still there */
6523         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6524         {
6525           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6526           {
6527             can_clone = TRUE;
6528
6529             break;
6530           }
6531         }
6532
6533         /* cannot clone or target field not free anymore -- do not clone */
6534         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6535           Store[x][y] = EL_EMPTY;
6536       }
6537
6538       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6539       {
6540         if (IS_MV_DIAGONAL(MovDir[x][y]))
6541         {
6542           int diagonal_move_dir = MovDir[x][y];
6543           int stored = Store[x][y];
6544           int change_delay = 8;
6545           int graphic;
6546
6547           /* android is moving diagonally */
6548
6549           CreateField(x, y, EL_DIAGONAL_SHRINKING);
6550
6551           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6552           GfxElement[x][y] = EL_EMC_ANDROID;
6553           GfxAction[x][y] = ACTION_SHRINKING;
6554           GfxDir[x][y] = diagonal_move_dir;
6555           ChangeDelay[x][y] = change_delay;
6556
6557           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6558                                    GfxDir[x][y]);
6559
6560           DrawLevelGraphicAnimation(x, y, graphic);
6561           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6562
6563           if (Feld[newx][newy] == EL_ACID)
6564           {
6565             SplashAcid(newx, newy);
6566
6567             return;
6568           }
6569
6570           CreateField(newx, newy, EL_DIAGONAL_GROWING);
6571
6572           Store[newx][newy] = EL_EMC_ANDROID;
6573           GfxElement[newx][newy] = EL_EMC_ANDROID;
6574           GfxAction[newx][newy] = ACTION_GROWING;
6575           GfxDir[newx][newy] = diagonal_move_dir;
6576           ChangeDelay[newx][newy] = change_delay;
6577
6578           graphic = el_act_dir2img(GfxElement[newx][newy],
6579                                    GfxAction[newx][newy], GfxDir[newx][newy]);
6580
6581           DrawLevelGraphicAnimation(newx, newy, graphic);
6582           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6583
6584           return;
6585         }
6586         else
6587         {
6588           Feld[newx][newy] = EL_EMPTY;
6589           DrawLevelField(newx, newy);
6590
6591           PlayLevelSoundAction(x, y, ACTION_DIGGING);
6592         }
6593       }
6594       else if (!IS_FREE(newx, newy))
6595       {
6596 #if 0
6597         if (IS_PLAYER(x, y))
6598           DrawPlayerField(x, y);
6599         else
6600           DrawLevelField(x, y);
6601 #endif
6602
6603         return;
6604       }
6605     }
6606     else if (IS_CUSTOM_ELEMENT(element) &&
6607              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6608     {
6609       int new_element = Feld[newx][newy];
6610
6611       if (!IS_FREE(newx, newy))
6612       {
6613         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6614                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6615                       ACTION_BREAKING);
6616
6617         /* no element can dig solid indestructible elements */
6618         if (IS_INDESTRUCTIBLE(new_element) &&
6619             !IS_DIGGABLE(new_element) &&
6620             !IS_COLLECTIBLE(new_element))
6621           return;
6622
6623         if (AmoebaNr[newx][newy] &&
6624             (new_element == EL_AMOEBA_FULL ||
6625              new_element == EL_BD_AMOEBA ||
6626              new_element == EL_AMOEBA_GROWING))
6627         {
6628           AmoebaCnt[AmoebaNr[newx][newy]]--;
6629           AmoebaCnt2[AmoebaNr[newx][newy]]--;
6630         }
6631
6632         if (IS_MOVING(newx, newy))
6633           RemoveMovingField(newx, newy);
6634         else
6635         {
6636           RemoveField(newx, newy);
6637           DrawLevelField(newx, newy);
6638         }
6639
6640         /* if digged element was about to explode, prevent the explosion */
6641         ExplodeField[newx][newy] = EX_TYPE_NONE;
6642
6643         PlayLevelSoundAction(x, y, action);
6644       }
6645
6646       Store[newx][newy] = EL_EMPTY;
6647 #if 1
6648       /* this makes it possible to leave the removed element again */
6649       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6650         Store[newx][newy] = new_element;
6651 #else
6652       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6653       {
6654         int move_leave_element = element_info[element].move_leave_element;
6655
6656         /* this makes it possible to leave the removed element again */
6657         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6658                              new_element : move_leave_element);
6659       }
6660 #endif
6661
6662       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6663       {
6664         RunnerVisit[x][y] = FrameCounter;
6665         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6666       }
6667     }
6668     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6669     {
6670       if (!IS_FREE(newx, newy))
6671       {
6672         if (IS_PLAYER(x, y))
6673           DrawPlayerField(x, y);
6674         else
6675           DrawLevelField(x, y);
6676
6677         return;
6678       }
6679       else
6680       {
6681         boolean wanna_flame = !RND(10);
6682         int dx = newx - x, dy = newy - y;
6683         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6684         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6685         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6686                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6687         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6688                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6689
6690         if ((wanna_flame ||
6691              IS_CLASSIC_ENEMY(element1) ||
6692              IS_CLASSIC_ENEMY(element2)) &&
6693             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6694             element1 != EL_FLAMES && element2 != EL_FLAMES)
6695         {
6696           ResetGfxAnimation(x, y);
6697           GfxAction[x][y] = ACTION_ATTACKING;
6698
6699           if (IS_PLAYER(x, y))
6700             DrawPlayerField(x, y);
6701           else
6702             DrawLevelField(x, y);
6703
6704           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6705
6706           MovDelay[x][y] = 50;
6707
6708           /* !!! */
6709 #if 0
6710           RemoveField(newx, newy);
6711 #endif
6712           Feld[newx][newy] = EL_FLAMES;
6713           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6714           {
6715 #if 0
6716             RemoveField(newx1, newy1);
6717 #endif
6718             Feld[newx1][newy1] = EL_FLAMES;
6719           }
6720           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6721           {
6722 #if 0
6723             RemoveField(newx2, newy2);
6724 #endif
6725             Feld[newx2][newy2] = EL_FLAMES;
6726           }
6727
6728           return;
6729         }
6730       }
6731     }
6732     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6733              Feld[newx][newy] == EL_DIAMOND)
6734     {
6735       if (IS_MOVING(newx, newy))
6736         RemoveMovingField(newx, newy);
6737       else
6738       {
6739         Feld[newx][newy] = EL_EMPTY;
6740         DrawLevelField(newx, newy);
6741       }
6742
6743       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6744     }
6745     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6746              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6747     {
6748       if (AmoebaNr[newx][newy])
6749       {
6750         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6751         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6752             Feld[newx][newy] == EL_BD_AMOEBA)
6753           AmoebaCnt[AmoebaNr[newx][newy]]--;
6754       }
6755
6756 #if 0
6757       /* !!! test !!! */
6758       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6759       {
6760         RemoveMovingField(newx, newy);
6761       }
6762 #else
6763       if (IS_MOVING(newx, newy))
6764       {
6765         RemoveMovingField(newx, newy);
6766       }
6767 #endif
6768       else
6769       {
6770         Feld[newx][newy] = EL_EMPTY;
6771         DrawLevelField(newx, newy);
6772       }
6773
6774       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6775     }
6776     else if ((element == EL_PACMAN || element == EL_MOLE)
6777              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6778     {
6779       if (AmoebaNr[newx][newy])
6780       {
6781         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6782         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6783             Feld[newx][newy] == EL_BD_AMOEBA)
6784           AmoebaCnt[AmoebaNr[newx][newy]]--;
6785       }
6786
6787       if (element == EL_MOLE)
6788       {
6789         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6790         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6791
6792         ResetGfxAnimation(x, y);
6793         GfxAction[x][y] = ACTION_DIGGING;
6794         DrawLevelField(x, y);
6795
6796         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6797
6798         return;                         /* wait for shrinking amoeba */
6799       }
6800       else      /* element == EL_PACMAN */
6801       {
6802         Feld[newx][newy] = EL_EMPTY;
6803         DrawLevelField(newx, newy);
6804         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6805       }
6806     }
6807     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6808              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6809               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6810     {
6811       /* wait for shrinking amoeba to completely disappear */
6812       return;
6813     }
6814     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6815     {
6816       /* object was running against a wall */
6817
6818       TurnRound(x, y);
6819
6820 #if 0
6821       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6822       if (move_pattern & MV_ANY_DIRECTION &&
6823           move_pattern == MovDir[x][y])
6824       {
6825         int blocking_element =
6826           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6827
6828         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6829                                  MovDir[x][y]);
6830
6831         element = Feld[x][y];   /* element might have changed */
6832       }
6833 #endif
6834
6835       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
6836         DrawLevelElementAnimation(x, y, element);
6837
6838       if (DONT_TOUCH(element))
6839         TestIfBadThingTouchesPlayer(x, y);
6840
6841       return;
6842     }
6843
6844     InitMovingField(x, y, MovDir[x][y]);
6845
6846     PlayLevelSoundAction(x, y, ACTION_MOVING);
6847   }
6848
6849   if (MovDir[x][y])
6850     ContinueMoving(x, y);
6851 }
6852
6853 void ContinueMoving(int x, int y)
6854 {
6855   int element = Feld[x][y];
6856   struct ElementInfo *ei = &element_info[element];
6857   int direction = MovDir[x][y];
6858   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6859   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
6860   int newx = x + dx, newy = y + dy;
6861   int stored = Store[x][y];
6862   int stored_new = Store[newx][newy];
6863   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
6864   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6865   boolean last_line = (newy == lev_fieldy - 1);
6866
6867   MovPos[x][y] += getElementMoveStepsize(x, y);
6868
6869   if (pushed_by_player) /* special case: moving object pushed by player */
6870     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6871
6872   if (ABS(MovPos[x][y]) < TILEX)
6873   {
6874     DrawLevelField(x, y);
6875
6876     return;     /* element is still moving */
6877   }
6878
6879   /* element reached destination field */
6880
6881   Feld[x][y] = EL_EMPTY;
6882   Feld[newx][newy] = element;
6883   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6884
6885   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6886   {
6887     element = Feld[newx][newy] = EL_ACID;
6888   }
6889   else if (element == EL_MOLE)
6890   {
6891     Feld[x][y] = EL_SAND;
6892
6893     DrawLevelFieldCrumbledSandNeighbours(x, y);
6894   }
6895   else if (element == EL_QUICKSAND_FILLING)
6896   {
6897     element = Feld[newx][newy] = get_next_element(element);
6898     Store[newx][newy] = Store[x][y];
6899   }
6900   else if (element == EL_QUICKSAND_EMPTYING)
6901   {
6902     Feld[x][y] = get_next_element(element);
6903     element = Feld[newx][newy] = Store[x][y];
6904   }
6905   else if (element == EL_QUICKSAND_FAST_FILLING)
6906   {
6907     element = Feld[newx][newy] = get_next_element(element);
6908     Store[newx][newy] = Store[x][y];
6909   }
6910   else if (element == EL_QUICKSAND_FAST_EMPTYING)
6911   {
6912     Feld[x][y] = get_next_element(element);
6913     element = Feld[newx][newy] = Store[x][y];
6914   }
6915   else if (element == EL_MAGIC_WALL_FILLING)
6916   {
6917     element = Feld[newx][newy] = get_next_element(element);
6918     if (!game.magic_wall_active)
6919       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6920     Store[newx][newy] = Store[x][y];
6921   }
6922   else if (element == EL_MAGIC_WALL_EMPTYING)
6923   {
6924     Feld[x][y] = get_next_element(element);
6925     if (!game.magic_wall_active)
6926       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6927     element = Feld[newx][newy] = Store[x][y];
6928
6929 #if USE_NEW_CUSTOM_VALUE
6930     InitField(newx, newy, FALSE);
6931 #endif
6932   }
6933   else if (element == EL_BD_MAGIC_WALL_FILLING)
6934   {
6935     element = Feld[newx][newy] = get_next_element(element);
6936     if (!game.magic_wall_active)
6937       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6938     Store[newx][newy] = Store[x][y];
6939   }
6940   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6941   {
6942     Feld[x][y] = get_next_element(element);
6943     if (!game.magic_wall_active)
6944       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6945     element = Feld[newx][newy] = Store[x][y];
6946
6947 #if USE_NEW_CUSTOM_VALUE
6948     InitField(newx, newy, FALSE);
6949 #endif
6950   }
6951   else if (element == EL_DC_MAGIC_WALL_FILLING)
6952   {
6953     element = Feld[newx][newy] = get_next_element(element);
6954     if (!game.magic_wall_active)
6955       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
6956     Store[newx][newy] = Store[x][y];
6957   }
6958   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
6959   {
6960     Feld[x][y] = get_next_element(element);
6961     if (!game.magic_wall_active)
6962       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
6963     element = Feld[newx][newy] = Store[x][y];
6964
6965 #if USE_NEW_CUSTOM_VALUE
6966     InitField(newx, newy, FALSE);
6967 #endif
6968   }
6969   else if (element == EL_AMOEBA_DROPPING)
6970   {
6971     Feld[x][y] = get_next_element(element);
6972     element = Feld[newx][newy] = Store[x][y];
6973   }
6974   else if (element == EL_SOKOBAN_OBJECT)
6975   {
6976     if (Back[x][y])
6977       Feld[x][y] = Back[x][y];
6978
6979     if (Back[newx][newy])
6980       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6981
6982     Back[x][y] = Back[newx][newy] = 0;
6983   }
6984
6985   Store[x][y] = EL_EMPTY;
6986   MovPos[x][y] = 0;
6987   MovDir[x][y] = 0;
6988   MovDelay[x][y] = 0;
6989
6990   MovDelay[newx][newy] = 0;
6991
6992   if (CAN_CHANGE_OR_HAS_ACTION(element))
6993   {
6994     /* copy element change control values to new field */
6995     ChangeDelay[newx][newy] = ChangeDelay[x][y];
6996     ChangePage[newx][newy]  = ChangePage[x][y];
6997     ChangeCount[newx][newy] = ChangeCount[x][y];
6998     ChangeEvent[newx][newy] = ChangeEvent[x][y];
6999   }
7000
7001 #if USE_NEW_CUSTOM_VALUE
7002     CustomValue[newx][newy] = CustomValue[x][y];
7003 #endif
7004
7005   ChangeDelay[x][y] = 0;
7006   ChangePage[x][y] = -1;
7007   ChangeCount[x][y] = 0;
7008   ChangeEvent[x][y] = -1;
7009
7010 #if USE_NEW_CUSTOM_VALUE
7011   CustomValue[x][y] = 0;
7012 #endif
7013
7014   /* copy animation control values to new field */
7015   GfxFrame[newx][newy]  = GfxFrame[x][y];
7016   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7017   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7018   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7019
7020   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7021
7022   /* some elements can leave other elements behind after moving */
7023 #if 1
7024   if (ei->move_leave_element != EL_EMPTY &&
7025       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7026       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7027 #else
7028   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7029       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7030       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7031 #endif
7032   {
7033     int move_leave_element = ei->move_leave_element;
7034
7035 #if 1
7036 #if 1
7037     /* this makes it possible to leave the removed element again */
7038     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7039       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7040 #else
7041     /* this makes it possible to leave the removed element again */
7042     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7043       move_leave_element = stored;
7044 #endif
7045 #else
7046     /* this makes it possible to leave the removed element again */
7047     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7048         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7049       move_leave_element = stored;
7050 #endif
7051
7052     Feld[x][y] = move_leave_element;
7053
7054     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7055       MovDir[x][y] = direction;
7056
7057     InitField(x, y, FALSE);
7058
7059     if (GFX_CRUMBLED(Feld[x][y]))
7060       DrawLevelFieldCrumbledSandNeighbours(x, y);
7061
7062     if (ELEM_IS_PLAYER(move_leave_element))
7063       RelocatePlayer(x, y, move_leave_element);
7064   }
7065
7066   /* do this after checking for left-behind element */
7067   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7068
7069   if (!CAN_MOVE(element) ||
7070       (CAN_FALL(element) && direction == MV_DOWN &&
7071        (element == EL_SPRING ||
7072         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7073         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7074     GfxDir[x][y] = MovDir[newx][newy] = 0;
7075
7076   DrawLevelField(x, y);
7077   DrawLevelField(newx, newy);
7078
7079   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7080
7081   /* prevent pushed element from moving on in pushed direction */
7082   if (pushed_by_player && CAN_MOVE(element) &&
7083       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7084       !(element_info[element].move_pattern & direction))
7085     TurnRound(newx, newy);
7086
7087   /* prevent elements on conveyor belt from moving on in last direction */
7088   if (pushed_by_conveyor && CAN_FALL(element) &&
7089       direction & MV_HORIZONTAL)
7090     MovDir[newx][newy] = 0;
7091
7092   if (!pushed_by_player)
7093   {
7094     int nextx = newx + dx, nexty = newy + dy;
7095     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7096
7097     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7098
7099     if (CAN_FALL(element) && direction == MV_DOWN)
7100       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7101
7102     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7103       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7104
7105 #if USE_FIX_IMPACT_COLLISION
7106     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7107       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7108 #endif
7109   }
7110
7111   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7112   {
7113     TestIfBadThingTouchesPlayer(newx, newy);
7114     TestIfBadThingTouchesFriend(newx, newy);
7115
7116     if (!IS_CUSTOM_ELEMENT(element))
7117       TestIfBadThingTouchesOtherBadThing(newx, newy);
7118   }
7119   else if (element == EL_PENGUIN)
7120     TestIfFriendTouchesBadThing(newx, newy);
7121
7122   /* give the player one last chance (one more frame) to move away */
7123   if (CAN_FALL(element) && direction == MV_DOWN &&
7124       (last_line || (!IS_FREE(x, newy + 1) &&
7125                      (!IS_PLAYER(x, newy + 1) ||
7126                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7127     Impact(x, newy);
7128
7129   if (pushed_by_player && !game.use_change_when_pushing_bug)
7130   {
7131     int push_side = MV_DIR_OPPOSITE(direction);
7132     struct PlayerInfo *player = PLAYERINFO(x, y);
7133
7134     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7135                                player->index_bit, push_side);
7136     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7137                                         player->index_bit, push_side);
7138   }
7139
7140   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7141     MovDelay[newx][newy] = 1;
7142
7143   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7144
7145   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7146
7147 #if 0
7148   if (ChangePage[newx][newy] != -1)             /* delayed change */
7149   {
7150     int page = ChangePage[newx][newy];
7151     struct ElementChangeInfo *change = &ei->change_page[page];
7152
7153     ChangePage[newx][newy] = -1;
7154
7155     if (change->can_change)
7156     {
7157       if (ChangeElement(newx, newy, element, page))
7158       {
7159         if (change->post_change_function)
7160           change->post_change_function(newx, newy);
7161       }
7162     }
7163
7164     if (change->has_action)
7165       ExecuteCustomElementAction(newx, newy, element, page);
7166   }
7167 #endif
7168
7169   TestIfElementHitsCustomElement(newx, newy, direction);
7170   TestIfPlayerTouchesCustomElement(newx, newy);
7171   TestIfElementTouchesCustomElement(newx, newy);
7172
7173   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7174       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7175     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7176                              MV_DIR_OPPOSITE(direction));
7177 }
7178
7179 int AmoebeNachbarNr(int ax, int ay)
7180 {
7181   int i;
7182   int element = Feld[ax][ay];
7183   int group_nr = 0;
7184   static int xy[4][2] =
7185   {
7186     { 0, -1 },
7187     { -1, 0 },
7188     { +1, 0 },
7189     { 0, +1 }
7190   };
7191
7192   for (i = 0; i < NUM_DIRECTIONS; i++)
7193   {
7194     int x = ax + xy[i][0];
7195     int y = ay + xy[i][1];
7196
7197     if (!IN_LEV_FIELD(x, y))
7198       continue;
7199
7200     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7201       group_nr = AmoebaNr[x][y];
7202   }
7203
7204   return group_nr;
7205 }
7206
7207 void AmoebenVereinigen(int ax, int ay)
7208 {
7209   int i, x, y, xx, yy;
7210   int new_group_nr = AmoebaNr[ax][ay];
7211   static int xy[4][2] =
7212   {
7213     { 0, -1 },
7214     { -1, 0 },
7215     { +1, 0 },
7216     { 0, +1 }
7217   };
7218
7219   if (new_group_nr == 0)
7220     return;
7221
7222   for (i = 0; i < NUM_DIRECTIONS; i++)
7223   {
7224     x = ax + xy[i][0];
7225     y = ay + xy[i][1];
7226
7227     if (!IN_LEV_FIELD(x, y))
7228       continue;
7229
7230     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7231          Feld[x][y] == EL_BD_AMOEBA ||
7232          Feld[x][y] == EL_AMOEBA_DEAD) &&
7233         AmoebaNr[x][y] != new_group_nr)
7234     {
7235       int old_group_nr = AmoebaNr[x][y];
7236
7237       if (old_group_nr == 0)
7238         return;
7239
7240       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7241       AmoebaCnt[old_group_nr] = 0;
7242       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7243       AmoebaCnt2[old_group_nr] = 0;
7244
7245       SCAN_PLAYFIELD(xx, yy)
7246       {
7247         if (AmoebaNr[xx][yy] == old_group_nr)
7248           AmoebaNr[xx][yy] = new_group_nr;
7249       }
7250     }
7251   }
7252 }
7253
7254 void AmoebeUmwandeln(int ax, int ay)
7255 {
7256   int i, x, y;
7257
7258   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7259   {
7260     int group_nr = AmoebaNr[ax][ay];
7261
7262 #ifdef DEBUG
7263     if (group_nr == 0)
7264     {
7265       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7266       printf("AmoebeUmwandeln(): This should never happen!\n");
7267       return;
7268     }
7269 #endif
7270
7271     SCAN_PLAYFIELD(x, y)
7272     {
7273       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7274       {
7275         AmoebaNr[x][y] = 0;
7276         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7277       }
7278     }
7279
7280     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7281                             SND_AMOEBA_TURNING_TO_GEM :
7282                             SND_AMOEBA_TURNING_TO_ROCK));
7283     Bang(ax, ay);
7284   }
7285   else
7286   {
7287     static int xy[4][2] =
7288     {
7289       { 0, -1 },
7290       { -1, 0 },
7291       { +1, 0 },
7292       { 0, +1 }
7293     };
7294
7295     for (i = 0; i < NUM_DIRECTIONS; i++)
7296     {
7297       x = ax + xy[i][0];
7298       y = ay + xy[i][1];
7299
7300       if (!IN_LEV_FIELD(x, y))
7301         continue;
7302
7303       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7304       {
7305         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7306                               SND_AMOEBA_TURNING_TO_GEM :
7307                               SND_AMOEBA_TURNING_TO_ROCK));
7308         Bang(x, y);
7309       }
7310     }
7311   }
7312 }
7313
7314 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7315 {
7316   int x, y;
7317   int group_nr = AmoebaNr[ax][ay];
7318   boolean done = FALSE;
7319
7320 #ifdef DEBUG
7321   if (group_nr == 0)
7322   {
7323     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7324     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7325     return;
7326   }
7327 #endif
7328
7329   SCAN_PLAYFIELD(x, y)
7330   {
7331     if (AmoebaNr[x][y] == group_nr &&
7332         (Feld[x][y] == EL_AMOEBA_DEAD ||
7333          Feld[x][y] == EL_BD_AMOEBA ||
7334          Feld[x][y] == EL_AMOEBA_GROWING))
7335     {
7336       AmoebaNr[x][y] = 0;
7337       Feld[x][y] = new_element;
7338       InitField(x, y, FALSE);
7339       DrawLevelField(x, y);
7340       done = TRUE;
7341     }
7342   }
7343
7344   if (done)
7345     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7346                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7347                             SND_BD_AMOEBA_TURNING_TO_GEM));
7348 }
7349
7350 void AmoebeWaechst(int x, int y)
7351 {
7352   static unsigned long sound_delay = 0;
7353   static unsigned long sound_delay_value = 0;
7354
7355   if (!MovDelay[x][y])          /* start new growing cycle */
7356   {
7357     MovDelay[x][y] = 7;
7358
7359     if (DelayReached(&sound_delay, sound_delay_value))
7360     {
7361       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7362       sound_delay_value = 30;
7363     }
7364   }
7365
7366   if (MovDelay[x][y])           /* wait some time before growing bigger */
7367   {
7368     MovDelay[x][y]--;
7369     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7370     {
7371       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7372                                            6 - MovDelay[x][y]);
7373
7374       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7375     }
7376
7377     if (!MovDelay[x][y])
7378     {
7379       Feld[x][y] = Store[x][y];
7380       Store[x][y] = 0;
7381       DrawLevelField(x, y);
7382     }
7383   }
7384 }
7385
7386 void AmoebaDisappearing(int x, int y)
7387 {
7388   static unsigned long sound_delay = 0;
7389   static unsigned long sound_delay_value = 0;
7390
7391   if (!MovDelay[x][y])          /* start new shrinking cycle */
7392   {
7393     MovDelay[x][y] = 7;
7394
7395     if (DelayReached(&sound_delay, sound_delay_value))
7396       sound_delay_value = 30;
7397   }
7398
7399   if (MovDelay[x][y])           /* wait some time before shrinking */
7400   {
7401     MovDelay[x][y]--;
7402     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7403     {
7404       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7405                                            6 - MovDelay[x][y]);
7406
7407       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7408     }
7409
7410     if (!MovDelay[x][y])
7411     {
7412       Feld[x][y] = EL_EMPTY;
7413       DrawLevelField(x, y);
7414
7415       /* don't let mole enter this field in this cycle;
7416          (give priority to objects falling to this field from above) */
7417       Stop[x][y] = TRUE;
7418     }
7419   }
7420 }
7421
7422 void AmoebeAbleger(int ax, int ay)
7423 {
7424   int i;
7425   int element = Feld[ax][ay];
7426   int graphic = el2img(element);
7427   int newax = ax, neway = ay;
7428   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7429   static int xy[4][2] =
7430   {
7431     { 0, -1 },
7432     { -1, 0 },
7433     { +1, 0 },
7434     { 0, +1 }
7435   };
7436
7437   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7438   {
7439     Feld[ax][ay] = EL_AMOEBA_DEAD;
7440     DrawLevelField(ax, ay);
7441     return;
7442   }
7443
7444   if (IS_ANIMATED(graphic))
7445     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7446
7447   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7448     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7449
7450   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7451   {
7452     MovDelay[ax][ay]--;
7453     if (MovDelay[ax][ay])
7454       return;
7455   }
7456
7457   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7458   {
7459     int start = RND(4);
7460     int x = ax + xy[start][0];
7461     int y = ay + xy[start][1];
7462
7463     if (!IN_LEV_FIELD(x, y))
7464       return;
7465
7466     if (IS_FREE(x, y) ||
7467         CAN_GROW_INTO(Feld[x][y]) ||
7468         Feld[x][y] == EL_QUICKSAND_EMPTY ||
7469         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7470     {
7471       newax = x;
7472       neway = y;
7473     }
7474
7475     if (newax == ax && neway == ay)
7476       return;
7477   }
7478   else                          /* normal or "filled" (BD style) amoeba */
7479   {
7480     int start = RND(4);
7481     boolean waiting_for_player = FALSE;
7482
7483     for (i = 0; i < NUM_DIRECTIONS; i++)
7484     {
7485       int j = (start + i) % 4;
7486       int x = ax + xy[j][0];
7487       int y = ay + xy[j][1];
7488
7489       if (!IN_LEV_FIELD(x, y))
7490         continue;
7491
7492       if (IS_FREE(x, y) ||
7493           CAN_GROW_INTO(Feld[x][y]) ||
7494           Feld[x][y] == EL_QUICKSAND_EMPTY ||
7495           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7496       {
7497         newax = x;
7498         neway = y;
7499         break;
7500       }
7501       else if (IS_PLAYER(x, y))
7502         waiting_for_player = TRUE;
7503     }
7504
7505     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7506     {
7507       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7508       {
7509         Feld[ax][ay] = EL_AMOEBA_DEAD;
7510         DrawLevelField(ax, ay);
7511         AmoebaCnt[AmoebaNr[ax][ay]]--;
7512
7513         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7514         {
7515           if (element == EL_AMOEBA_FULL)
7516             AmoebeUmwandeln(ax, ay);
7517           else if (element == EL_BD_AMOEBA)
7518             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7519         }
7520       }
7521       return;
7522     }
7523     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7524     {
7525       /* amoeba gets larger by growing in some direction */
7526
7527       int new_group_nr = AmoebaNr[ax][ay];
7528
7529 #ifdef DEBUG
7530   if (new_group_nr == 0)
7531   {
7532     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7533     printf("AmoebeAbleger(): This should never happen!\n");
7534     return;
7535   }
7536 #endif
7537
7538       AmoebaNr[newax][neway] = new_group_nr;
7539       AmoebaCnt[new_group_nr]++;
7540       AmoebaCnt2[new_group_nr]++;
7541
7542       /* if amoeba touches other amoeba(s) after growing, unify them */
7543       AmoebenVereinigen(newax, neway);
7544
7545       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7546       {
7547         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7548         return;
7549       }
7550     }
7551   }
7552
7553   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7554       (neway == lev_fieldy - 1 && newax != ax))
7555   {
7556     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7557     Store[newax][neway] = element;
7558   }
7559   else if (neway == ay || element == EL_EMC_DRIPPER)
7560   {
7561     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7562
7563     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7564   }
7565   else
7566   {
7567     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7568     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7569     Store[ax][ay] = EL_AMOEBA_DROP;
7570     ContinueMoving(ax, ay);
7571     return;
7572   }
7573
7574   DrawLevelField(newax, neway);
7575 }
7576
7577 void Life(int ax, int ay)
7578 {
7579   int x1, y1, x2, y2;
7580   int life_time = 40;
7581   int element = Feld[ax][ay];
7582   int graphic = el2img(element);
7583   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7584                          level.biomaze);
7585   boolean changed = FALSE;
7586
7587   if (IS_ANIMATED(graphic))
7588     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7589
7590   if (Stop[ax][ay])
7591     return;
7592
7593   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7594     MovDelay[ax][ay] = life_time;
7595
7596   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7597   {
7598     MovDelay[ax][ay]--;
7599     if (MovDelay[ax][ay])
7600       return;
7601   }
7602
7603   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7604   {
7605     int xx = ax+x1, yy = ay+y1;
7606     int nachbarn = 0;
7607
7608     if (!IN_LEV_FIELD(xx, yy))
7609       continue;
7610
7611     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7612     {
7613       int x = xx+x2, y = yy+y2;
7614
7615       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7616         continue;
7617
7618       if (((Feld[x][y] == element ||
7619             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7620            !Stop[x][y]) ||
7621           (IS_FREE(x, y) && Stop[x][y]))
7622         nachbarn++;
7623     }
7624
7625     if (xx == ax && yy == ay)           /* field in the middle */
7626     {
7627       if (nachbarn < life_parameter[0] ||
7628           nachbarn > life_parameter[1])
7629       {
7630         Feld[xx][yy] = EL_EMPTY;
7631         if (!Stop[xx][yy])
7632           DrawLevelField(xx, yy);
7633         Stop[xx][yy] = TRUE;
7634         changed = TRUE;
7635       }
7636     }
7637     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7638     {                                   /* free border field */
7639       if (nachbarn >= life_parameter[2] &&
7640           nachbarn <= life_parameter[3])
7641       {
7642         Feld[xx][yy] = element;
7643         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7644         if (!Stop[xx][yy])
7645           DrawLevelField(xx, yy);
7646         Stop[xx][yy] = TRUE;
7647         changed = TRUE;
7648       }
7649     }
7650   }
7651
7652   if (changed)
7653     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7654                    SND_GAME_OF_LIFE_GROWING);
7655 }
7656
7657 static void InitRobotWheel(int x, int y)
7658 {
7659   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7660 }
7661
7662 static void RunRobotWheel(int x, int y)
7663 {
7664   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7665 }
7666
7667 static void StopRobotWheel(int x, int y)
7668 {
7669   if (ZX == x && ZY == y)
7670     ZX = ZY = -1;
7671 }
7672
7673 static void InitTimegateWheel(int x, int y)
7674 {
7675   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7676 }
7677
7678 static void RunTimegateWheel(int x, int y)
7679 {
7680   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7681 }
7682
7683 static void InitMagicBallDelay(int x, int y)
7684 {
7685 #if 1
7686   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7687 #else
7688   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7689 #endif
7690 }
7691
7692 static void ActivateMagicBall(int bx, int by)
7693 {
7694   int x, y;
7695
7696   if (level.ball_random)
7697   {
7698     int pos_border = RND(8);    /* select one of the eight border elements */
7699     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7700     int xx = pos_content % 3;
7701     int yy = pos_content / 3;
7702
7703     x = bx - 1 + xx;
7704     y = by - 1 + yy;
7705
7706     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7707       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7708   }
7709   else
7710   {
7711     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7712     {
7713       int xx = x - bx + 1;
7714       int yy = y - by + 1;
7715
7716       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7717         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7718     }
7719   }
7720
7721   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7722 }
7723
7724 void CheckExit(int x, int y)
7725 {
7726   if (local_player->gems_still_needed > 0 ||
7727       local_player->sokobanfields_still_needed > 0 ||
7728       local_player->lights_still_needed > 0)
7729   {
7730     int element = Feld[x][y];
7731     int graphic = el2img(element);
7732
7733     if (IS_ANIMATED(graphic))
7734       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7735
7736     return;
7737   }
7738
7739   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7740     return;
7741
7742   Feld[x][y] = EL_EXIT_OPENING;
7743
7744   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7745 }
7746
7747 void CheckExitEM(int x, int y)
7748 {
7749   if (local_player->gems_still_needed > 0 ||
7750       local_player->sokobanfields_still_needed > 0 ||
7751       local_player->lights_still_needed > 0)
7752   {
7753     int element = Feld[x][y];
7754     int graphic = el2img(element);
7755
7756     if (IS_ANIMATED(graphic))
7757       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7758
7759     return;
7760   }
7761
7762   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7763     return;
7764
7765   Feld[x][y] = EL_EM_EXIT_OPENING;
7766
7767   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7768 }
7769
7770 void CheckExitSteel(int x, int y)
7771 {
7772   if (local_player->gems_still_needed > 0 ||
7773       local_player->sokobanfields_still_needed > 0 ||
7774       local_player->lights_still_needed > 0)
7775   {
7776     int element = Feld[x][y];
7777     int graphic = el2img(element);
7778
7779     if (IS_ANIMATED(graphic))
7780       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7781
7782     return;
7783   }
7784
7785   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7786     return;
7787
7788   Feld[x][y] = EL_STEEL_EXIT_OPENING;
7789
7790   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7791 }
7792
7793 void CheckExitSteelEM(int x, int y)
7794 {
7795   if (local_player->gems_still_needed > 0 ||
7796       local_player->sokobanfields_still_needed > 0 ||
7797       local_player->lights_still_needed > 0)
7798   {
7799     int element = Feld[x][y];
7800     int graphic = el2img(element);
7801
7802     if (IS_ANIMATED(graphic))
7803       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7804
7805     return;
7806   }
7807
7808   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7809     return;
7810
7811   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7812
7813   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7814 }
7815
7816 void CheckExitSP(int x, int y)
7817 {
7818   if (local_player->gems_still_needed > 0)
7819   {
7820     int element = Feld[x][y];
7821     int graphic = el2img(element);
7822
7823     if (IS_ANIMATED(graphic))
7824       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7825
7826     return;
7827   }
7828
7829   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7830     return;
7831
7832   Feld[x][y] = EL_SP_EXIT_OPENING;
7833
7834   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7835 }
7836
7837 static void CloseAllOpenTimegates()
7838 {
7839   int x, y;
7840
7841   SCAN_PLAYFIELD(x, y)
7842   {
7843     int element = Feld[x][y];
7844
7845     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7846     {
7847       Feld[x][y] = EL_TIMEGATE_CLOSING;
7848
7849       PlayLevelSoundAction(x, y, ACTION_CLOSING);
7850     }
7851   }
7852 }
7853
7854 void DrawTwinkleOnField(int x, int y)
7855 {
7856   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7857     return;
7858
7859   if (Feld[x][y] == EL_BD_DIAMOND)
7860     return;
7861
7862   if (MovDelay[x][y] == 0)      /* next animation frame */
7863     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7864
7865   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7866   {
7867     MovDelay[x][y]--;
7868
7869     if (setup.direct_draw && MovDelay[x][y])
7870       SetDrawtoField(DRAW_BUFFERED);
7871
7872     DrawLevelElementAnimation(x, y, Feld[x][y]);
7873
7874     if (MovDelay[x][y] != 0)
7875     {
7876       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7877                                            10 - MovDelay[x][y]);
7878
7879       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7880
7881       if (setup.direct_draw)
7882       {
7883         int dest_x, dest_y;
7884
7885         dest_x = FX + SCREENX(x) * TILEX;
7886         dest_y = FY + SCREENY(y) * TILEY;
7887
7888         BlitBitmap(drawto_field, window,
7889                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7890         SetDrawtoField(DRAW_DIRECT);
7891       }
7892     }
7893   }
7894 }
7895
7896 void MauerWaechst(int x, int y)
7897 {
7898   int delay = 6;
7899
7900   if (!MovDelay[x][y])          /* next animation frame */
7901     MovDelay[x][y] = 3 * delay;
7902
7903   if (MovDelay[x][y])           /* wait some time before next frame */
7904   {
7905     MovDelay[x][y]--;
7906
7907     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7908     {
7909       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7910       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7911
7912       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7913     }
7914
7915     if (!MovDelay[x][y])
7916     {
7917       if (MovDir[x][y] == MV_LEFT)
7918       {
7919         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7920           DrawLevelField(x - 1, y);
7921       }
7922       else if (MovDir[x][y] == MV_RIGHT)
7923       {
7924         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7925           DrawLevelField(x + 1, y);
7926       }
7927       else if (MovDir[x][y] == MV_UP)
7928       {
7929         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7930           DrawLevelField(x, y - 1);
7931       }
7932       else
7933       {
7934         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7935           DrawLevelField(x, y + 1);
7936       }
7937
7938       Feld[x][y] = Store[x][y];
7939       Store[x][y] = 0;
7940       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7941       DrawLevelField(x, y);
7942     }
7943   }
7944 }
7945
7946 void MauerAbleger(int ax, int ay)
7947 {
7948   int element = Feld[ax][ay];
7949   int graphic = el2img(element);
7950   boolean oben_frei = FALSE, unten_frei = FALSE;
7951   boolean links_frei = FALSE, rechts_frei = FALSE;
7952   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7953   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7954   boolean new_wall = FALSE;
7955
7956   if (IS_ANIMATED(graphic))
7957     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7958
7959   if (!MovDelay[ax][ay])        /* start building new wall */
7960     MovDelay[ax][ay] = 6;
7961
7962   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7963   {
7964     MovDelay[ax][ay]--;
7965     if (MovDelay[ax][ay])
7966       return;
7967   }
7968
7969   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7970     oben_frei = TRUE;
7971   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7972     unten_frei = TRUE;
7973   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7974     links_frei = TRUE;
7975   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7976     rechts_frei = TRUE;
7977
7978   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7979       element == EL_EXPANDABLE_WALL_ANY)
7980   {
7981     if (oben_frei)
7982     {
7983       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7984       Store[ax][ay-1] = element;
7985       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7986       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7987         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7988                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7989       new_wall = TRUE;
7990     }
7991     if (unten_frei)
7992     {
7993       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7994       Store[ax][ay+1] = element;
7995       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7996       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7997         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7998                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7999       new_wall = TRUE;
8000     }
8001   }
8002
8003   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8004       element == EL_EXPANDABLE_WALL_ANY ||
8005       element == EL_EXPANDABLE_WALL ||
8006       element == EL_BD_EXPANDABLE_WALL)
8007   {
8008     if (links_frei)
8009     {
8010       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8011       Store[ax-1][ay] = element;
8012       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8013       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8014         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8015                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8016       new_wall = TRUE;
8017     }
8018
8019     if (rechts_frei)
8020     {
8021       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8022       Store[ax+1][ay] = element;
8023       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8024       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8025         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8026                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8027       new_wall = TRUE;
8028     }
8029   }
8030
8031   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8032     DrawLevelField(ax, ay);
8033
8034   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8035     oben_massiv = TRUE;
8036   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8037     unten_massiv = TRUE;
8038   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8039     links_massiv = TRUE;
8040   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8041     rechts_massiv = TRUE;
8042
8043   if (((oben_massiv && unten_massiv) ||
8044        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8045        element == EL_EXPANDABLE_WALL) &&
8046       ((links_massiv && rechts_massiv) ||
8047        element == EL_EXPANDABLE_WALL_VERTICAL))
8048     Feld[ax][ay] = EL_WALL;
8049
8050   if (new_wall)
8051     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8052 }
8053
8054 void MauerAblegerStahl(int ax, int ay)
8055 {
8056   int element = Feld[ax][ay];
8057   int graphic = el2img(element);
8058   boolean oben_frei = FALSE, unten_frei = FALSE;
8059   boolean links_frei = FALSE, rechts_frei = FALSE;
8060   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8061   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8062   boolean new_wall = FALSE;
8063
8064   if (IS_ANIMATED(graphic))
8065     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8066
8067   if (!MovDelay[ax][ay])        /* start building new wall */
8068     MovDelay[ax][ay] = 6;
8069
8070   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8071   {
8072     MovDelay[ax][ay]--;
8073     if (MovDelay[ax][ay])
8074       return;
8075   }
8076
8077   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8078     oben_frei = TRUE;
8079   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8080     unten_frei = TRUE;
8081   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8082     links_frei = TRUE;
8083   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8084     rechts_frei = TRUE;
8085
8086   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8087       element == EL_EXPANDABLE_STEELWALL_ANY)
8088   {
8089     if (oben_frei)
8090     {
8091       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8092       Store[ax][ay-1] = element;
8093       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8094       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8095         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8096                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8097       new_wall = TRUE;
8098     }
8099     if (unten_frei)
8100     {
8101       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8102       Store[ax][ay+1] = element;
8103       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8104       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8105         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8106                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8107       new_wall = TRUE;
8108     }
8109   }
8110
8111   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8112       element == EL_EXPANDABLE_STEELWALL_ANY)
8113   {
8114     if (links_frei)
8115     {
8116       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8117       Store[ax-1][ay] = element;
8118       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8119       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8120         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8121                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8122       new_wall = TRUE;
8123     }
8124
8125     if (rechts_frei)
8126     {
8127       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8128       Store[ax+1][ay] = element;
8129       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8130       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8131         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8132                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8133       new_wall = TRUE;
8134     }
8135   }
8136
8137   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8138     oben_massiv = TRUE;
8139   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8140     unten_massiv = TRUE;
8141   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8142     links_massiv = TRUE;
8143   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8144     rechts_massiv = TRUE;
8145
8146   if (((oben_massiv && unten_massiv) ||
8147        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8148       ((links_massiv && rechts_massiv) ||
8149        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8150     Feld[ax][ay] = EL_WALL;
8151
8152   if (new_wall)
8153     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8154 }
8155
8156 void CheckForDragon(int x, int y)
8157 {
8158   int i, j;
8159   boolean dragon_found = FALSE;
8160   static int xy[4][2] =
8161   {
8162     { 0, -1 },
8163     { -1, 0 },
8164     { +1, 0 },
8165     { 0, +1 }
8166   };
8167
8168   for (i = 0; i < NUM_DIRECTIONS; i++)
8169   {
8170     for (j = 0; j < 4; j++)
8171     {
8172       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8173
8174       if (IN_LEV_FIELD(xx, yy) &&
8175           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8176       {
8177         if (Feld[xx][yy] == EL_DRAGON)
8178           dragon_found = TRUE;
8179       }
8180       else
8181         break;
8182     }
8183   }
8184
8185   if (!dragon_found)
8186   {
8187     for (i = 0; i < NUM_DIRECTIONS; i++)
8188     {
8189       for (j = 0; j < 3; j++)
8190       {
8191         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8192   
8193         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8194         {
8195           Feld[xx][yy] = EL_EMPTY;
8196           DrawLevelField(xx, yy);
8197         }
8198         else
8199           break;
8200       }
8201     }
8202   }
8203 }
8204
8205 static void InitBuggyBase(int x, int y)
8206 {
8207   int element = Feld[x][y];
8208   int activating_delay = FRAMES_PER_SECOND / 4;
8209
8210   ChangeDelay[x][y] =
8211     (element == EL_SP_BUGGY_BASE ?
8212      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8213      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8214      activating_delay :
8215      element == EL_SP_BUGGY_BASE_ACTIVE ?
8216      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8217 }
8218
8219 static void WarnBuggyBase(int x, int y)
8220 {
8221   int i;
8222   static int xy[4][2] =
8223   {
8224     { 0, -1 },
8225     { -1, 0 },
8226     { +1, 0 },
8227     { 0, +1 }
8228   };
8229
8230   for (i = 0; i < NUM_DIRECTIONS; i++)
8231   {
8232     int xx = x + xy[i][0];
8233     int yy = y + xy[i][1];
8234
8235     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8236     {
8237       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8238
8239       break;
8240     }
8241   }
8242 }
8243
8244 static void InitTrap(int x, int y)
8245 {
8246   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8247 }
8248
8249 static void ActivateTrap(int x, int y)
8250 {
8251   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8252 }
8253
8254 static void ChangeActiveTrap(int x, int y)
8255 {
8256   int graphic = IMG_TRAP_ACTIVE;
8257
8258   /* if new animation frame was drawn, correct crumbled sand border */
8259   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8260     DrawLevelFieldCrumbledSand(x, y);
8261 }
8262
8263 static int getSpecialActionElement(int element, int number, int base_element)
8264 {
8265   return (element != EL_EMPTY ? element :
8266           number != -1 ? base_element + number - 1 :
8267           EL_EMPTY);
8268 }
8269
8270 static int getModifiedActionNumber(int value_old, int operator, int operand,
8271                                    int value_min, int value_max)
8272 {
8273   int value_new = (operator == CA_MODE_SET      ? operand :
8274                    operator == CA_MODE_ADD      ? value_old + operand :
8275                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8276                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8277                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8278                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8279                    value_old);
8280
8281   return (value_new < value_min ? value_min :
8282           value_new > value_max ? value_max :
8283           value_new);
8284 }
8285
8286 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8287 {
8288   struct ElementInfo *ei = &element_info[element];
8289   struct ElementChangeInfo *change = &ei->change_page[page];
8290   int target_element = change->target_element;
8291   int action_type = change->action_type;
8292   int action_mode = change->action_mode;
8293   int action_arg = change->action_arg;
8294   int i;
8295
8296   if (!change->has_action)
8297     return;
8298
8299   /* ---------- determine action paramater values -------------------------- */
8300
8301   int level_time_value =
8302     (level.time > 0 ? TimeLeft :
8303      TimePlayed);
8304
8305   int action_arg_element =
8306     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8307      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8308      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8309      EL_EMPTY);
8310
8311   int action_arg_direction =
8312     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8313      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8314      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8315      change->actual_trigger_side :
8316      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8317      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8318      MV_NONE);
8319
8320   int action_arg_number_min =
8321     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8322      CA_ARG_MIN);
8323
8324   int action_arg_number_max =
8325     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8326      action_type == CA_SET_LEVEL_GEMS ? 999 :
8327      action_type == CA_SET_LEVEL_TIME ? 9999 :
8328      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8329      action_type == CA_SET_CE_VALUE ? 9999 :
8330      action_type == CA_SET_CE_SCORE ? 9999 :
8331      CA_ARG_MAX);
8332
8333   int action_arg_number_reset =
8334     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8335      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8336      action_type == CA_SET_LEVEL_TIME ? level.time :
8337      action_type == CA_SET_LEVEL_SCORE ? 0 :
8338 #if USE_NEW_CUSTOM_VALUE
8339      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8340 #else
8341      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8342 #endif
8343      action_type == CA_SET_CE_SCORE ? 0 :
8344      0);
8345
8346   int action_arg_number =
8347     (action_arg <= CA_ARG_MAX ? action_arg :
8348      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8349      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8350      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8351      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8352      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8353      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8354 #if USE_NEW_CUSTOM_VALUE
8355      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8356 #else
8357      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8358 #endif
8359      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8360      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8361      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8362      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8363      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8364      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8365      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8366      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8367      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8368      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8369      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8370      -1);
8371
8372   int action_arg_number_old =
8373     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8374      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8375      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8376      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8377      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8378      0);
8379
8380   int action_arg_number_new =
8381     getModifiedActionNumber(action_arg_number_old,
8382                             action_mode, action_arg_number,
8383                             action_arg_number_min, action_arg_number_max);
8384
8385   int trigger_player_bits =
8386     (change->actual_trigger_player >= EL_PLAYER_1 &&
8387      change->actual_trigger_player <= EL_PLAYER_4 ?
8388      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8389      PLAYER_BITS_ANY);
8390
8391   int action_arg_player_bits =
8392     (action_arg >= CA_ARG_PLAYER_1 &&
8393      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8394      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8395      PLAYER_BITS_ANY);
8396
8397   /* ---------- execute action  -------------------------------------------- */
8398
8399   switch (action_type)
8400   {
8401     case CA_NO_ACTION:
8402     {
8403       return;
8404     }
8405
8406     /* ---------- level actions  ------------------------------------------- */
8407
8408     case CA_RESTART_LEVEL:
8409     {
8410       game.restart_level = TRUE;
8411
8412       break;
8413     }
8414
8415     case CA_SHOW_ENVELOPE:
8416     {
8417       int element = getSpecialActionElement(action_arg_element,
8418                                             action_arg_number, EL_ENVELOPE_1);
8419
8420       if (IS_ENVELOPE(element))
8421         local_player->show_envelope = element;
8422
8423       break;
8424     }
8425
8426     case CA_SET_LEVEL_TIME:
8427     {
8428       if (level.time > 0)       /* only modify limited time value */
8429       {
8430         TimeLeft = action_arg_number_new;
8431
8432         DrawGameValue_Time(TimeLeft);
8433
8434         if (!TimeLeft && setup.time_limit)
8435           for (i = 0; i < MAX_PLAYERS; i++)
8436             KillPlayer(&stored_player[i]);
8437       }
8438
8439       break;
8440     }
8441
8442     case CA_SET_LEVEL_SCORE:
8443     {
8444       local_player->score = action_arg_number_new;
8445
8446       DrawGameValue_Score(local_player->score);
8447
8448       break;
8449     }
8450
8451     case CA_SET_LEVEL_GEMS:
8452     {
8453       local_player->gems_still_needed = action_arg_number_new;
8454
8455       DrawGameValue_Emeralds(local_player->gems_still_needed);
8456
8457       break;
8458     }
8459
8460 #if !USE_PLAYER_GRAVITY
8461     case CA_SET_LEVEL_GRAVITY:
8462     {
8463       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
8464                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
8465                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8466                       game.gravity);
8467       break;
8468     }
8469 #endif
8470
8471     case CA_SET_LEVEL_WIND:
8472     {
8473       game.wind_direction = action_arg_direction;
8474
8475       break;
8476     }
8477
8478     /* ---------- player actions  ------------------------------------------ */
8479
8480     case CA_MOVE_PLAYER:
8481     {
8482       /* automatically move to the next field in specified direction */
8483       for (i = 0; i < MAX_PLAYERS; i++)
8484         if (trigger_player_bits & (1 << i))
8485           stored_player[i].programmed_action = action_arg_direction;
8486
8487       break;
8488     }
8489
8490     case CA_EXIT_PLAYER:
8491     {
8492       for (i = 0; i < MAX_PLAYERS; i++)
8493         if (action_arg_player_bits & (1 << i))
8494           PlayerWins(&stored_player[i]);
8495
8496       break;
8497     }
8498
8499     case CA_KILL_PLAYER:
8500     {
8501       for (i = 0; i < MAX_PLAYERS; i++)
8502         if (action_arg_player_bits & (1 << i))
8503           KillPlayer(&stored_player[i]);
8504
8505       break;
8506     }
8507
8508     case CA_SET_PLAYER_KEYS:
8509     {
8510       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8511       int element = getSpecialActionElement(action_arg_element,
8512                                             action_arg_number, EL_KEY_1);
8513
8514       if (IS_KEY(element))
8515       {
8516         for (i = 0; i < MAX_PLAYERS; i++)
8517         {
8518           if (trigger_player_bits & (1 << i))
8519           {
8520             stored_player[i].key[KEY_NR(element)] = key_state;
8521
8522             DrawGameDoorValues();
8523           }
8524         }
8525       }
8526
8527       break;
8528     }
8529
8530     case CA_SET_PLAYER_SPEED:
8531     {
8532       for (i = 0; i < MAX_PLAYERS; i++)
8533       {
8534         if (trigger_player_bits & (1 << i))
8535         {
8536           int move_stepsize = TILEX / stored_player[i].move_delay_value;
8537
8538           if (action_arg == CA_ARG_SPEED_FASTER &&
8539               stored_player[i].cannot_move)
8540           {
8541             action_arg_number = STEPSIZE_VERY_SLOW;
8542           }
8543           else if (action_arg == CA_ARG_SPEED_SLOWER ||
8544                    action_arg == CA_ARG_SPEED_FASTER)
8545           {
8546             action_arg_number = 2;
8547             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8548                            CA_MODE_MULTIPLY);
8549           }
8550           else if (action_arg == CA_ARG_NUMBER_RESET)
8551           {
8552             action_arg_number = level.initial_player_stepsize[i];
8553           }
8554
8555           move_stepsize =
8556             getModifiedActionNumber(move_stepsize,
8557                                     action_mode,
8558                                     action_arg_number,
8559                                     action_arg_number_min,
8560                                     action_arg_number_max);
8561
8562           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8563         }
8564       }
8565
8566       break;
8567     }
8568
8569     case CA_SET_PLAYER_SHIELD:
8570     {
8571       for (i = 0; i < MAX_PLAYERS; i++)
8572       {
8573         if (trigger_player_bits & (1 << i))
8574         {
8575           if (action_arg == CA_ARG_SHIELD_OFF)
8576           {
8577             stored_player[i].shield_normal_time_left = 0;
8578             stored_player[i].shield_deadly_time_left = 0;
8579           }
8580           else if (action_arg == CA_ARG_SHIELD_NORMAL)
8581           {
8582             stored_player[i].shield_normal_time_left = 999999;
8583           }
8584           else if (action_arg == CA_ARG_SHIELD_DEADLY)
8585           {
8586             stored_player[i].shield_normal_time_left = 999999;
8587             stored_player[i].shield_deadly_time_left = 999999;
8588           }
8589         }
8590       }
8591
8592       break;
8593     }
8594
8595 #if USE_PLAYER_GRAVITY
8596     case CA_SET_PLAYER_GRAVITY:
8597     {
8598       for (i = 0; i < MAX_PLAYERS; i++)
8599       {
8600         if (trigger_player_bits & (1 << i))
8601         {
8602           stored_player[i].gravity =
8603             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
8604              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
8605              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8606              stored_player[i].gravity);
8607         }
8608       }
8609
8610       break;
8611     }
8612 #endif
8613
8614     case CA_SET_PLAYER_ARTWORK:
8615     {
8616       for (i = 0; i < MAX_PLAYERS; i++)
8617       {
8618         if (trigger_player_bits & (1 << i))
8619         {
8620           int artwork_element = action_arg_element;
8621
8622           if (action_arg == CA_ARG_ELEMENT_RESET)
8623             artwork_element =
8624               (level.use_artwork_element[i] ? level.artwork_element[i] :
8625                stored_player[i].element_nr);
8626
8627 #if USE_GFX_RESET_PLAYER_ARTWORK
8628           if (stored_player[i].artwork_element != artwork_element)
8629             stored_player[i].Frame = 0;
8630 #endif
8631
8632           stored_player[i].artwork_element = artwork_element;
8633
8634           SetPlayerWaiting(&stored_player[i], FALSE);
8635
8636           /* set number of special actions for bored and sleeping animation */
8637           stored_player[i].num_special_action_bored =
8638             get_num_special_action(artwork_element,
8639                                    ACTION_BORING_1, ACTION_BORING_LAST);
8640           stored_player[i].num_special_action_sleeping =
8641             get_num_special_action(artwork_element,
8642                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8643         }
8644       }
8645
8646       break;
8647     }
8648
8649     /* ---------- CE actions  ---------------------------------------------- */
8650
8651     case CA_SET_CE_VALUE:
8652     {
8653 #if USE_NEW_CUSTOM_VALUE
8654       int last_ce_value = CustomValue[x][y];
8655
8656       CustomValue[x][y] = action_arg_number_new;
8657
8658       if (CustomValue[x][y] != last_ce_value)
8659       {
8660         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8661         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8662
8663         if (CustomValue[x][y] == 0)
8664         {
8665           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8666           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8667         }
8668       }
8669 #endif
8670
8671       break;
8672     }
8673
8674     case CA_SET_CE_SCORE:
8675     {
8676 #if USE_NEW_CUSTOM_VALUE
8677       int last_ce_score = ei->collect_score;
8678
8679       ei->collect_score = action_arg_number_new;
8680
8681       if (ei->collect_score != last_ce_score)
8682       {
8683         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8684         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8685
8686         if (ei->collect_score == 0)
8687         {
8688           int xx, yy;
8689
8690           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8691           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8692
8693           /*
8694             This is a very special case that seems to be a mixture between
8695             CheckElementChange() and CheckTriggeredElementChange(): while
8696             the first one only affects single elements that are triggered
8697             directly, the second one affects multiple elements in the playfield
8698             that are triggered indirectly by another element. This is a third
8699             case: Changing the CE score always affects multiple identical CEs,
8700             so every affected CE must be checked, not only the single CE for
8701             which the CE score was changed in the first place (as every instance
8702             of that CE shares the same CE score, and therefore also can change)!
8703           */
8704           SCAN_PLAYFIELD(xx, yy)
8705           {
8706             if (Feld[xx][yy] == element)
8707               CheckElementChange(xx, yy, element, EL_UNDEFINED,
8708                                  CE_SCORE_GETS_ZERO);
8709           }
8710         }
8711       }
8712 #endif
8713
8714       break;
8715     }
8716
8717     /* ---------- engine actions  ------------------------------------------ */
8718
8719     case CA_SET_ENGINE_SCAN_MODE:
8720     {
8721       InitPlayfieldScanMode(action_arg);
8722
8723       break;
8724     }
8725
8726     default:
8727       break;
8728   }
8729 }
8730
8731 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8732 {
8733   int old_element = Feld[x][y];
8734   int new_element = get_element_from_group_element(element);
8735   int previous_move_direction = MovDir[x][y];
8736 #if USE_NEW_CUSTOM_VALUE
8737   int last_ce_value = CustomValue[x][y];
8738 #endif
8739   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8740   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8741   boolean add_player_onto_element = (new_element_is_player &&
8742 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8743                                      /* this breaks SnakeBite when a snake is
8744                                         halfway through a door that closes */
8745                                      /* NOW FIXED AT LEVEL INIT IN files.c */
8746                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
8747 #endif
8748                                      IS_WALKABLE(old_element));
8749
8750 #if 0
8751   /* check if element under the player changes from accessible to unaccessible
8752      (needed for special case of dropping element which then changes) */
8753   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8754       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8755   {
8756     Bang(x, y);
8757
8758     return;
8759   }
8760 #endif
8761
8762   if (!add_player_onto_element)
8763   {
8764     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8765       RemoveMovingField(x, y);
8766     else
8767       RemoveField(x, y);
8768
8769     Feld[x][y] = new_element;
8770
8771 #if !USE_GFX_RESET_GFX_ANIMATION
8772     ResetGfxAnimation(x, y);
8773     ResetRandomAnimationValue(x, y);
8774 #endif
8775
8776     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8777       MovDir[x][y] = previous_move_direction;
8778
8779 #if USE_NEW_CUSTOM_VALUE
8780     if (element_info[new_element].use_last_ce_value)
8781       CustomValue[x][y] = last_ce_value;
8782 #endif
8783
8784     InitField_WithBug1(x, y, FALSE);
8785
8786     new_element = Feld[x][y];   /* element may have changed */
8787
8788 #if USE_GFX_RESET_GFX_ANIMATION
8789     ResetGfxAnimation(x, y);
8790     ResetRandomAnimationValue(x, y);
8791 #endif
8792
8793     DrawLevelField(x, y);
8794
8795     if (GFX_CRUMBLED(new_element))
8796       DrawLevelFieldCrumbledSandNeighbours(x, y);
8797   }
8798
8799 #if 1
8800   /* check if element under the player changes from accessible to unaccessible
8801      (needed for special case of dropping element which then changes) */
8802   /* (must be checked after creating new element for walkable group elements) */
8803 #if USE_FIX_KILLED_BY_NON_WALKABLE
8804   if (IS_PLAYER(x, y) && !player_explosion_protected &&
8805       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8806   {
8807     Bang(x, y);
8808
8809     return;
8810   }
8811 #else
8812   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8813       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8814   {
8815     Bang(x, y);
8816
8817     return;
8818   }
8819 #endif
8820 #endif
8821
8822   /* "ChangeCount" not set yet to allow "entered by player" change one time */
8823   if (new_element_is_player)
8824     RelocatePlayer(x, y, new_element);
8825
8826   if (is_change)
8827     ChangeCount[x][y]++;        /* count number of changes in the same frame */
8828
8829   TestIfBadThingTouchesPlayer(x, y);
8830   TestIfPlayerTouchesCustomElement(x, y);
8831   TestIfElementTouchesCustomElement(x, y);
8832 }
8833
8834 static void CreateField(int x, int y, int element)
8835 {
8836   CreateFieldExt(x, y, element, FALSE);
8837 }
8838
8839 static void CreateElementFromChange(int x, int y, int element)
8840 {
8841   element = GET_VALID_RUNTIME_ELEMENT(element);
8842
8843 #if USE_STOP_CHANGED_ELEMENTS
8844   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8845   {
8846     int old_element = Feld[x][y];
8847
8848     /* prevent changed element from moving in same engine frame
8849        unless both old and new element can either fall or move */
8850     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8851         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8852       Stop[x][y] = TRUE;
8853   }
8854 #endif
8855
8856   CreateFieldExt(x, y, element, TRUE);
8857 }
8858
8859 static boolean ChangeElement(int x, int y, int element, int page)
8860 {
8861   struct ElementInfo *ei = &element_info[element];
8862   struct ElementChangeInfo *change = &ei->change_page[page];
8863   int ce_value = CustomValue[x][y];
8864   int ce_score = ei->collect_score;
8865   int target_element;
8866   int old_element = Feld[x][y];
8867
8868   /* always use default change event to prevent running into a loop */
8869   if (ChangeEvent[x][y] == -1)
8870     ChangeEvent[x][y] = CE_DELAY;
8871
8872   if (ChangeEvent[x][y] == CE_DELAY)
8873   {
8874     /* reset actual trigger element, trigger player and action element */
8875     change->actual_trigger_element = EL_EMPTY;
8876     change->actual_trigger_player = EL_PLAYER_1;
8877     change->actual_trigger_side = CH_SIDE_NONE;
8878     change->actual_trigger_ce_value = 0;
8879     change->actual_trigger_ce_score = 0;
8880   }
8881
8882   /* do not change elements more than a specified maximum number of changes */
8883   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8884     return FALSE;
8885
8886   ChangeCount[x][y]++;          /* count number of changes in the same frame */
8887
8888   if (change->explode)
8889   {
8890     Bang(x, y);
8891
8892     return TRUE;
8893   }
8894
8895   if (change->use_target_content)
8896   {
8897     boolean complete_replace = TRUE;
8898     boolean can_replace[3][3];
8899     int xx, yy;
8900
8901     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8902     {
8903       boolean is_empty;
8904       boolean is_walkable;
8905       boolean is_diggable;
8906       boolean is_collectible;
8907       boolean is_removable;
8908       boolean is_destructible;
8909       int ex = x + xx - 1;
8910       int ey = y + yy - 1;
8911       int content_element = change->target_content.e[xx][yy];
8912       int e;
8913
8914       can_replace[xx][yy] = TRUE;
8915
8916       if (ex == x && ey == y)   /* do not check changing element itself */
8917         continue;
8918
8919       if (content_element == EL_EMPTY_SPACE)
8920       {
8921         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8922
8923         continue;
8924       }
8925
8926       if (!IN_LEV_FIELD(ex, ey))
8927       {
8928         can_replace[xx][yy] = FALSE;
8929         complete_replace = FALSE;
8930
8931         continue;
8932       }
8933
8934       e = Feld[ex][ey];
8935
8936       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8937         e = MovingOrBlocked2Element(ex, ey);
8938
8939       is_empty = (IS_FREE(ex, ey) ||
8940                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8941
8942       is_walkable     = (is_empty || IS_WALKABLE(e));
8943       is_diggable     = (is_empty || IS_DIGGABLE(e));
8944       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8945       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8946       is_removable    = (is_diggable || is_collectible);
8947
8948       can_replace[xx][yy] =
8949         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
8950           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
8951           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
8952           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
8953           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
8954           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8955          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8956
8957       if (!can_replace[xx][yy])
8958         complete_replace = FALSE;
8959     }
8960
8961     if (!change->only_if_complete || complete_replace)
8962     {
8963       boolean something_has_changed = FALSE;
8964
8965       if (change->only_if_complete && change->use_random_replace &&
8966           RND(100) < change->random_percentage)
8967         return FALSE;
8968
8969       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8970       {
8971         int ex = x + xx - 1;
8972         int ey = y + yy - 1;
8973         int content_element;
8974
8975         if (can_replace[xx][yy] && (!change->use_random_replace ||
8976                                     RND(100) < change->random_percentage))
8977         {
8978           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8979             RemoveMovingField(ex, ey);
8980
8981           ChangeEvent[ex][ey] = ChangeEvent[x][y];
8982
8983           content_element = change->target_content.e[xx][yy];
8984           target_element = GET_TARGET_ELEMENT(element, content_element, change,
8985                                               ce_value, ce_score);
8986
8987           CreateElementFromChange(ex, ey, target_element);
8988
8989           something_has_changed = TRUE;
8990
8991           /* for symmetry reasons, freeze newly created border elements */
8992           if (ex != x || ey != y)
8993             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
8994         }
8995       }
8996
8997       if (something_has_changed)
8998       {
8999         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9000         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9001       }
9002     }
9003   }
9004   else
9005   {
9006     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9007                                         ce_value, ce_score);
9008
9009     if (element == EL_DIAGONAL_GROWING ||
9010         element == EL_DIAGONAL_SHRINKING)
9011     {
9012       target_element = Store[x][y];
9013
9014       Store[x][y] = EL_EMPTY;
9015     }
9016
9017     CreateElementFromChange(x, y, target_element);
9018
9019     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9020     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9021   }
9022
9023   /* this uses direct change before indirect change */
9024   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9025
9026   return TRUE;
9027 }
9028
9029 #if USE_NEW_DELAYED_ACTION
9030
9031 static void HandleElementChange(int x, int y, int page)
9032 {
9033   int element = MovingOrBlocked2Element(x, y);
9034   struct ElementInfo *ei = &element_info[element];
9035   struct ElementChangeInfo *change = &ei->change_page[page];
9036
9037 #ifdef DEBUG
9038   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9039       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9040   {
9041     printf("\n\n");
9042     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9043            x, y, element, element_info[element].token_name);
9044     printf("HandleElementChange(): This should never happen!\n");
9045     printf("\n\n");
9046   }
9047 #endif
9048
9049   /* this can happen with classic bombs on walkable, changing elements */
9050   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9051   {
9052 #if 0
9053     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9054       ChangeDelay[x][y] = 0;
9055 #endif
9056
9057     return;
9058   }
9059
9060   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9061   {
9062     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9063
9064     if (change->can_change)
9065     {
9066       ResetGfxAnimation(x, y);
9067       ResetRandomAnimationValue(x, y);
9068
9069       if (change->pre_change_function)
9070         change->pre_change_function(x, y);
9071     }
9072   }
9073
9074   ChangeDelay[x][y]--;
9075
9076   if (ChangeDelay[x][y] != 0)           /* continue element change */
9077   {
9078     if (change->can_change)
9079     {
9080       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9081
9082       if (IS_ANIMATED(graphic))
9083         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9084
9085       if (change->change_function)
9086         change->change_function(x, y);
9087     }
9088   }
9089   else                                  /* finish element change */
9090   {
9091     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9092     {
9093       page = ChangePage[x][y];
9094       ChangePage[x][y] = -1;
9095
9096       change = &ei->change_page[page];
9097     }
9098
9099     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9100     {
9101       ChangeDelay[x][y] = 1;            /* try change after next move step */
9102       ChangePage[x][y] = page;          /* remember page to use for change */
9103
9104       return;
9105     }
9106
9107     if (change->can_change)
9108     {
9109       if (ChangeElement(x, y, element, page))
9110       {
9111         if (change->post_change_function)
9112           change->post_change_function(x, y);
9113       }
9114     }
9115
9116     if (change->has_action)
9117       ExecuteCustomElementAction(x, y, element, page);
9118   }
9119 }
9120
9121 #else
9122
9123 static void HandleElementChange(int x, int y, int page)
9124 {
9125   int element = MovingOrBlocked2Element(x, y);
9126   struct ElementInfo *ei = &element_info[element];
9127   struct ElementChangeInfo *change = &ei->change_page[page];
9128
9129 #ifdef DEBUG
9130   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9131   {
9132     printf("\n\n");
9133     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9134            x, y, element, element_info[element].token_name);
9135     printf("HandleElementChange(): This should never happen!\n");
9136     printf("\n\n");
9137   }
9138 #endif
9139
9140   /* this can happen with classic bombs on walkable, changing elements */
9141   if (!CAN_CHANGE(element))
9142   {
9143 #if 0
9144     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9145       ChangeDelay[x][y] = 0;
9146 #endif
9147
9148     return;
9149   }
9150
9151   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9152   {
9153     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9154
9155     ResetGfxAnimation(x, y);
9156     ResetRandomAnimationValue(x, y);
9157
9158     if (change->pre_change_function)
9159       change->pre_change_function(x, y);
9160   }
9161
9162   ChangeDelay[x][y]--;
9163
9164   if (ChangeDelay[x][y] != 0)           /* continue element change */
9165   {
9166     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9167
9168     if (IS_ANIMATED(graphic))
9169       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9170
9171     if (change->change_function)
9172       change->change_function(x, y);
9173   }
9174   else                                  /* finish element change */
9175   {
9176     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9177     {
9178       page = ChangePage[x][y];
9179       ChangePage[x][y] = -1;
9180
9181       change = &ei->change_page[page];
9182     }
9183
9184     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9185     {
9186       ChangeDelay[x][y] = 1;            /* try change after next move step */
9187       ChangePage[x][y] = page;          /* remember page to use for change */
9188
9189       return;
9190     }
9191
9192     if (ChangeElement(x, y, element, page))
9193     {
9194       if (change->post_change_function)
9195         change->post_change_function(x, y);
9196     }
9197   }
9198 }
9199
9200 #endif
9201
9202 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9203                                               int trigger_element,
9204                                               int trigger_event,
9205                                               int trigger_player,
9206                                               int trigger_side,
9207                                               int trigger_page)
9208 {
9209   boolean change_done_any = FALSE;
9210   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9211   int i;
9212
9213   if (!(trigger_events[trigger_element][trigger_event]))
9214     return FALSE;
9215
9216 #if 0
9217   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9218          trigger_event, recursion_loop_depth, recursion_loop_detected,
9219          recursion_loop_element, EL_NAME(recursion_loop_element));
9220 #endif
9221
9222   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9223
9224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9225   {
9226     int element = EL_CUSTOM_START + i;
9227     boolean change_done = FALSE;
9228     int p;
9229
9230     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9231         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9232       continue;
9233
9234     for (p = 0; p < element_info[element].num_change_pages; p++)
9235     {
9236       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9237
9238       if (change->can_change_or_has_action &&
9239           change->has_event[trigger_event] &&
9240           change->trigger_side & trigger_side &&
9241           change->trigger_player & trigger_player &&
9242           change->trigger_page & trigger_page_bits &&
9243           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9244       {
9245         change->actual_trigger_element = trigger_element;
9246         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9247         change->actual_trigger_side = trigger_side;
9248         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9249         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9250
9251         if ((change->can_change && !change_done) || change->has_action)
9252         {
9253           int x, y;
9254
9255           SCAN_PLAYFIELD(x, y)
9256           {
9257             if (Feld[x][y] == element)
9258             {
9259               if (change->can_change && !change_done)
9260               {
9261                 ChangeDelay[x][y] = 1;
9262                 ChangeEvent[x][y] = trigger_event;
9263
9264                 HandleElementChange(x, y, p);
9265               }
9266 #if USE_NEW_DELAYED_ACTION
9267               else if (change->has_action)
9268               {
9269                 ExecuteCustomElementAction(x, y, element, p);
9270                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9271               }
9272 #else
9273               if (change->has_action)
9274               {
9275                 ExecuteCustomElementAction(x, y, element, p);
9276                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9277               }
9278 #endif
9279             }
9280           }
9281
9282           if (change->can_change)
9283           {
9284             change_done = TRUE;
9285             change_done_any = TRUE;
9286           }
9287         }
9288       }
9289     }
9290   }
9291
9292   RECURSION_LOOP_DETECTION_END();
9293
9294   return change_done_any;
9295 }
9296
9297 static boolean CheckElementChangeExt(int x, int y,
9298                                      int element,
9299                                      int trigger_element,
9300                                      int trigger_event,
9301                                      int trigger_player,
9302                                      int trigger_side)
9303 {
9304   boolean change_done = FALSE;
9305   int p;
9306
9307   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9308       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9309     return FALSE;
9310
9311   if (Feld[x][y] == EL_BLOCKED)
9312   {
9313     Blocked2Moving(x, y, &x, &y);
9314     element = Feld[x][y];
9315   }
9316
9317 #if 0
9318   /* check if element has already changed */
9319   if (Feld[x][y] != element)
9320     return FALSE;
9321 #else
9322   /* check if element has already changed or is about to change after moving */
9323   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9324        Feld[x][y] != element) ||
9325
9326       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9327        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9328         ChangePage[x][y] != -1)))
9329     return FALSE;
9330 #endif
9331
9332 #if 0
9333   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9334          trigger_event, recursion_loop_depth, recursion_loop_detected,
9335          recursion_loop_element, EL_NAME(recursion_loop_element));
9336 #endif
9337
9338   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9339
9340   for (p = 0; p < element_info[element].num_change_pages; p++)
9341   {
9342     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9343
9344     /* check trigger element for all events where the element that is checked
9345        for changing interacts with a directly adjacent element -- this is
9346        different to element changes that affect other elements to change on the
9347        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9348     boolean check_trigger_element =
9349       (trigger_event == CE_TOUCHING_X ||
9350        trigger_event == CE_HITTING_X ||
9351        trigger_event == CE_HIT_BY_X ||
9352 #if 1
9353        /* this one was forgotten until 3.2.3 */
9354        trigger_event == CE_DIGGING_X);
9355 #endif
9356
9357     if (change->can_change_or_has_action &&
9358         change->has_event[trigger_event] &&
9359         change->trigger_side & trigger_side &&
9360         change->trigger_player & trigger_player &&
9361         (!check_trigger_element ||
9362          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9363     {
9364       change->actual_trigger_element = trigger_element;
9365       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9366       change->actual_trigger_side = trigger_side;
9367       change->actual_trigger_ce_value = CustomValue[x][y];
9368       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9369
9370       /* special case: trigger element not at (x,y) position for some events */
9371       if (check_trigger_element)
9372       {
9373         static struct
9374         {
9375           int dx, dy;
9376         } move_xy[] =
9377           {
9378             {  0,  0 },
9379             { -1,  0 },
9380             { +1,  0 },
9381             {  0,  0 },
9382             {  0, -1 },
9383             {  0,  0 }, { 0, 0 }, { 0, 0 },
9384             {  0, +1 }
9385           };
9386
9387         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9388         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9389
9390         change->actual_trigger_ce_value = CustomValue[xx][yy];
9391         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9392       }
9393
9394       if (change->can_change && !change_done)
9395       {
9396         ChangeDelay[x][y] = 1;
9397         ChangeEvent[x][y] = trigger_event;
9398
9399         HandleElementChange(x, y, p);
9400
9401         change_done = TRUE;
9402       }
9403 #if USE_NEW_DELAYED_ACTION
9404       else if (change->has_action)
9405       {
9406         ExecuteCustomElementAction(x, y, element, p);
9407         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9408       }
9409 #else
9410       if (change->has_action)
9411       {
9412         ExecuteCustomElementAction(x, y, element, p);
9413         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9414       }
9415 #endif
9416     }
9417   }
9418
9419   RECURSION_LOOP_DETECTION_END();
9420
9421   return change_done;
9422 }
9423
9424 static void PlayPlayerSound(struct PlayerInfo *player)
9425 {
9426   int jx = player->jx, jy = player->jy;
9427   int sound_element = player->artwork_element;
9428   int last_action = player->last_action_waiting;
9429   int action = player->action_waiting;
9430
9431   if (player->is_waiting)
9432   {
9433     if (action != last_action)
9434       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9435     else
9436       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9437   }
9438   else
9439   {
9440     if (action != last_action)
9441       StopSound(element_info[sound_element].sound[last_action]);
9442
9443     if (last_action == ACTION_SLEEPING)
9444       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9445   }
9446 }
9447
9448 static void PlayAllPlayersSound()
9449 {
9450   int i;
9451
9452   for (i = 0; i < MAX_PLAYERS; i++)
9453     if (stored_player[i].active)
9454       PlayPlayerSound(&stored_player[i]);
9455 }
9456
9457 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9458 {
9459   boolean last_waiting = player->is_waiting;
9460   int move_dir = player->MovDir;
9461
9462   player->dir_waiting = move_dir;
9463   player->last_action_waiting = player->action_waiting;
9464
9465   if (is_waiting)
9466   {
9467     if (!last_waiting)          /* not waiting -> waiting */
9468     {
9469       player->is_waiting = TRUE;
9470
9471       player->frame_counter_bored =
9472         FrameCounter +
9473         game.player_boring_delay_fixed +
9474         GetSimpleRandom(game.player_boring_delay_random);
9475       player->frame_counter_sleeping =
9476         FrameCounter +
9477         game.player_sleeping_delay_fixed +
9478         GetSimpleRandom(game.player_sleeping_delay_random);
9479
9480       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9481     }
9482
9483     if (game.player_sleeping_delay_fixed +
9484         game.player_sleeping_delay_random > 0 &&
9485         player->anim_delay_counter == 0 &&
9486         player->post_delay_counter == 0 &&
9487         FrameCounter >= player->frame_counter_sleeping)
9488       player->is_sleeping = TRUE;
9489     else if (game.player_boring_delay_fixed +
9490              game.player_boring_delay_random > 0 &&
9491              FrameCounter >= player->frame_counter_bored)
9492       player->is_bored = TRUE;
9493
9494     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9495                               player->is_bored ? ACTION_BORING :
9496                               ACTION_WAITING);
9497
9498     if (player->is_sleeping && player->use_murphy)
9499     {
9500       /* special case for sleeping Murphy when leaning against non-free tile */
9501
9502       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9503           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9504            !IS_MOVING(player->jx - 1, player->jy)))
9505         move_dir = MV_LEFT;
9506       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9507                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9508                 !IS_MOVING(player->jx + 1, player->jy)))
9509         move_dir = MV_RIGHT;
9510       else
9511         player->is_sleeping = FALSE;
9512
9513       player->dir_waiting = move_dir;
9514     }
9515
9516     if (player->is_sleeping)
9517     {
9518       if (player->num_special_action_sleeping > 0)
9519       {
9520         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9521         {
9522           int last_special_action = player->special_action_sleeping;
9523           int num_special_action = player->num_special_action_sleeping;
9524           int special_action =
9525             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9526              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9527              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9528              last_special_action + 1 : ACTION_SLEEPING);
9529           int special_graphic =
9530             el_act_dir2img(player->artwork_element, special_action, move_dir);
9531
9532           player->anim_delay_counter =
9533             graphic_info[special_graphic].anim_delay_fixed +
9534             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9535           player->post_delay_counter =
9536             graphic_info[special_graphic].post_delay_fixed +
9537             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9538
9539           player->special_action_sleeping = special_action;
9540         }
9541
9542         if (player->anim_delay_counter > 0)
9543         {
9544           player->action_waiting = player->special_action_sleeping;
9545           player->anim_delay_counter--;
9546         }
9547         else if (player->post_delay_counter > 0)
9548         {
9549           player->post_delay_counter--;
9550         }
9551       }
9552     }
9553     else if (player->is_bored)
9554     {
9555       if (player->num_special_action_bored > 0)
9556       {
9557         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9558         {
9559           int special_action =
9560             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9561           int special_graphic =
9562             el_act_dir2img(player->artwork_element, special_action, move_dir);
9563
9564           player->anim_delay_counter =
9565             graphic_info[special_graphic].anim_delay_fixed +
9566             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9567           player->post_delay_counter =
9568             graphic_info[special_graphic].post_delay_fixed +
9569             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9570
9571           player->special_action_bored = special_action;
9572         }
9573
9574         if (player->anim_delay_counter > 0)
9575         {
9576           player->action_waiting = player->special_action_bored;
9577           player->anim_delay_counter--;
9578         }
9579         else if (player->post_delay_counter > 0)
9580         {
9581           player->post_delay_counter--;
9582         }
9583       }
9584     }
9585   }
9586   else if (last_waiting)        /* waiting -> not waiting */
9587   {
9588     player->is_waiting = FALSE;
9589     player->is_bored = FALSE;
9590     player->is_sleeping = FALSE;
9591
9592     player->frame_counter_bored = -1;
9593     player->frame_counter_sleeping = -1;
9594
9595     player->anim_delay_counter = 0;
9596     player->post_delay_counter = 0;
9597
9598     player->dir_waiting = player->MovDir;
9599     player->action_waiting = ACTION_DEFAULT;
9600
9601     player->special_action_bored = ACTION_DEFAULT;
9602     player->special_action_sleeping = ACTION_DEFAULT;
9603   }
9604 }
9605
9606 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9607 {
9608   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9609   int left      = player_action & JOY_LEFT;
9610   int right     = player_action & JOY_RIGHT;
9611   int up        = player_action & JOY_UP;
9612   int down      = player_action & JOY_DOWN;
9613   int button1   = player_action & JOY_BUTTON_1;
9614   int button2   = player_action & JOY_BUTTON_2;
9615   int dx        = (left ? -1 : right ? 1 : 0);
9616   int dy        = (up   ? -1 : down  ? 1 : 0);
9617
9618   if (!player->active || tape.pausing)
9619     return 0;
9620
9621   if (player_action)
9622   {
9623     if (button1)
9624       snapped = SnapField(player, dx, dy);
9625     else
9626     {
9627       if (button2)
9628         dropped = DropElement(player);
9629
9630       moved = MovePlayer(player, dx, dy);
9631     }
9632
9633     if (tape.single_step && tape.recording && !tape.pausing)
9634     {
9635       if (button1 || (dropped && !moved))
9636       {
9637         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9638         SnapField(player, 0, 0);                /* stop snapping */
9639       }
9640     }
9641
9642     SetPlayerWaiting(player, FALSE);
9643
9644     return player_action;
9645   }
9646   else
9647   {
9648     /* no actions for this player (no input at player's configured device) */
9649
9650     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9651     SnapField(player, 0, 0);
9652     CheckGravityMovementWhenNotMoving(player);
9653
9654     if (player->MovPos == 0)
9655       SetPlayerWaiting(player, TRUE);
9656
9657     if (player->MovPos == 0)    /* needed for tape.playing */
9658       player->is_moving = FALSE;
9659
9660     player->is_dropping = FALSE;
9661     player->is_dropping_pressed = FALSE;
9662     player->drop_pressed_delay = 0;
9663
9664     return 0;
9665   }
9666 }
9667
9668 static void CheckLevelTime()
9669 {
9670   int i;
9671
9672   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9673   {
9674     if (level.native_em_level->lev->home == 0)  /* all players at home */
9675     {
9676       PlayerWins(local_player);
9677
9678       AllPlayersGone = TRUE;
9679
9680       level.native_em_level->lev->home = -1;
9681     }
9682
9683     if (level.native_em_level->ply[0]->alive == 0 &&
9684         level.native_em_level->ply[1]->alive == 0 &&
9685         level.native_em_level->ply[2]->alive == 0 &&
9686         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9687       AllPlayersGone = TRUE;
9688   }
9689
9690   if (TimeFrames >= FRAMES_PER_SECOND)
9691   {
9692     TimeFrames = 0;
9693     TapeTime++;
9694
9695     for (i = 0; i < MAX_PLAYERS; i++)
9696     {
9697       struct PlayerInfo *player = &stored_player[i];
9698
9699       if (SHIELD_ON(player))
9700       {
9701         player->shield_normal_time_left--;
9702
9703         if (player->shield_deadly_time_left > 0)
9704           player->shield_deadly_time_left--;
9705       }
9706     }
9707
9708     if (!local_player->LevelSolved && !level.use_step_counter)
9709     {
9710       TimePlayed++;
9711
9712       if (TimeLeft > 0)
9713       {
9714         TimeLeft--;
9715
9716         if (TimeLeft <= 10 && setup.time_limit)
9717           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9718
9719         DrawGameValue_Time(TimeLeft);
9720
9721         if (!TimeLeft && setup.time_limit)
9722         {
9723           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9724             level.native_em_level->lev->killed_out_of_time = TRUE;
9725           else
9726             for (i = 0; i < MAX_PLAYERS; i++)
9727               KillPlayer(&stored_player[i]);
9728         }
9729       }
9730       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9731         DrawGameValue_Time(TimePlayed);
9732
9733       level.native_em_level->lev->time =
9734         (level.time == 0 ? TimePlayed : TimeLeft);
9735     }
9736
9737     if (tape.recording || tape.playing)
9738       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9739   }
9740 }
9741
9742 void AdvanceFrameAndPlayerCounters(int player_nr)
9743 {
9744   int i;
9745
9746   /* advance frame counters (global frame counter and time frame counter) */
9747   FrameCounter++;
9748   TimeFrames++;
9749
9750   /* advance player counters (counters for move delay, move animation etc.) */
9751   for (i = 0; i < MAX_PLAYERS; i++)
9752   {
9753     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9754     int move_delay_value = stored_player[i].move_delay_value;
9755     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9756
9757     if (!advance_player_counters)       /* not all players may be affected */
9758       continue;
9759
9760 #if USE_NEW_PLAYER_ANIM
9761     if (move_frames == 0)       /* less than one move per game frame */
9762     {
9763       int stepsize = TILEX / move_delay_value;
9764       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9765       int count = (stored_player[i].is_moving ?
9766                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9767
9768       if (count % delay == 0)
9769         move_frames = 1;
9770     }
9771 #endif
9772
9773     stored_player[i].Frame += move_frames;
9774
9775     if (stored_player[i].MovPos != 0)
9776       stored_player[i].StepFrame += move_frames;
9777
9778     if (stored_player[i].move_delay > 0)
9779       stored_player[i].move_delay--;
9780
9781     /* due to bugs in previous versions, counter must count up, not down */
9782     if (stored_player[i].push_delay != -1)
9783       stored_player[i].push_delay++;
9784
9785     if (stored_player[i].drop_delay > 0)
9786       stored_player[i].drop_delay--;
9787
9788     if (stored_player[i].is_dropping_pressed)
9789       stored_player[i].drop_pressed_delay++;
9790   }
9791 }
9792
9793 void StartGameActions(boolean init_network_game, boolean record_tape,
9794                       long random_seed)
9795 {
9796   unsigned long new_random_seed = InitRND(random_seed);
9797
9798   if (record_tape)
9799     TapeStartRecording(new_random_seed);
9800
9801 #if defined(NETWORK_AVALIABLE)
9802   if (init_network_game)
9803   {
9804     SendToServer_StartPlaying();
9805
9806     return;
9807   }
9808 #endif
9809
9810   InitGame();
9811 }
9812
9813 void GameActions()
9814 {
9815   static unsigned long game_frame_delay = 0;
9816   unsigned long game_frame_delay_value;
9817   byte *recorded_player_action;
9818   byte summarized_player_action = 0;
9819   byte tape_action[MAX_PLAYERS];
9820   int i;
9821
9822   /* detect endless loops, caused by custom element programming */
9823   if (recursion_loop_detected && recursion_loop_depth == 0)
9824   {
9825     char *message = getStringCat3("Internal Error ! Element ",
9826                                   EL_NAME(recursion_loop_element),
9827                                   " caused endless loop ! Quit the game ?");
9828
9829     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9830           EL_NAME(recursion_loop_element));
9831
9832     RequestQuitGameExt(FALSE, level_editor_test_game, message);
9833
9834     recursion_loop_detected = FALSE;    /* if game should be continued */
9835
9836     free(message);
9837
9838     return;
9839   }
9840
9841   if (game.restart_level)
9842     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9843
9844   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9845   {
9846     if (level.native_em_level->lev->home == 0)  /* all players at home */
9847     {
9848       PlayerWins(local_player);
9849
9850       AllPlayersGone = TRUE;
9851
9852       level.native_em_level->lev->home = -1;
9853     }
9854
9855     if (level.native_em_level->ply[0]->alive == 0 &&
9856         level.native_em_level->ply[1]->alive == 0 &&
9857         level.native_em_level->ply[2]->alive == 0 &&
9858         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9859       AllPlayersGone = TRUE;
9860   }
9861
9862   if (local_player->LevelSolved)
9863     GameWon();
9864
9865   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9866     TapeStop();
9867
9868   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9869     return;
9870
9871   game_frame_delay_value =
9872     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9873
9874   if (tape.playing && tape.warp_forward && !tape.pausing)
9875     game_frame_delay_value = 0;
9876
9877   /* ---------- main game synchronization point ---------- */
9878
9879   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9880
9881   if (network_playing && !network_player_action_received)
9882   {
9883     /* try to get network player actions in time */
9884
9885 #if defined(NETWORK_AVALIABLE)
9886     /* last chance to get network player actions without main loop delay */
9887     HandleNetworking();
9888 #endif
9889
9890     /* game was quit by network peer */
9891     if (game_status != GAME_MODE_PLAYING)
9892       return;
9893
9894     if (!network_player_action_received)
9895       return;           /* failed to get network player actions in time */
9896
9897     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9898   }
9899
9900   if (tape.pausing)
9901     return;
9902
9903   /* at this point we know that we really continue executing the game */
9904
9905   network_player_action_received = FALSE;
9906
9907   /* when playing tape, read previously recorded player input from tape data */
9908   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9909
9910 #if 1
9911   /* TapePlayAction() may return NULL when toggling to "pause before death" */
9912   if (tape.pausing)
9913     return;
9914 #endif
9915
9916   if (tape.set_centered_player)
9917   {
9918     game.centered_player_nr_next = tape.centered_player_nr_next;
9919     game.set_centered_player = TRUE;
9920   }
9921
9922   for (i = 0; i < MAX_PLAYERS; i++)
9923   {
9924     summarized_player_action |= stored_player[i].action;
9925
9926     if (!network_playing)
9927       stored_player[i].effective_action = stored_player[i].action;
9928   }
9929
9930 #if defined(NETWORK_AVALIABLE)
9931   if (network_playing)
9932     SendToServer_MovePlayer(summarized_player_action);
9933 #endif
9934
9935   if (!options.network && !setup.team_mode)
9936     local_player->effective_action = summarized_player_action;
9937
9938   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9939   {
9940     for (i = 0; i < MAX_PLAYERS; i++)
9941       stored_player[i].effective_action =
9942         (i == game.centered_player_nr ? summarized_player_action : 0);
9943   }
9944
9945   if (recorded_player_action != NULL)
9946     for (i = 0; i < MAX_PLAYERS; i++)
9947       stored_player[i].effective_action = recorded_player_action[i];
9948
9949   for (i = 0; i < MAX_PLAYERS; i++)
9950   {
9951     tape_action[i] = stored_player[i].effective_action;
9952
9953     /* (this can only happen in the R'n'D game engine) */
9954     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9955       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9956   }
9957
9958   /* only record actions from input devices, but not programmed actions */
9959   if (tape.recording)
9960     TapeRecordAction(tape_action);
9961
9962   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9963   {
9964     GameActions_EM_Main();
9965   }
9966   else
9967   {
9968     GameActions_RND();
9969   }
9970 }
9971
9972 void GameActions_EM_Main()
9973 {
9974   byte effective_action[MAX_PLAYERS];
9975   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9976   int i;
9977
9978   for (i = 0; i < MAX_PLAYERS; i++)
9979     effective_action[i] = stored_player[i].effective_action;
9980
9981   GameActions_EM(effective_action, warp_mode);
9982
9983   CheckLevelTime();
9984
9985   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9986 }
9987
9988 void GameActions_RND()
9989 {
9990   int magic_wall_x = 0, magic_wall_y = 0;
9991   int i, x, y, element, graphic;
9992
9993   InitPlayfieldScanModeVars();
9994
9995 #if USE_ONE_MORE_CHANGE_PER_FRAME
9996   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9997   {
9998     SCAN_PLAYFIELD(x, y)
9999     {
10000       ChangeCount[x][y] = 0;
10001       ChangeEvent[x][y] = -1;
10002     }
10003   }
10004 #endif
10005
10006   if (game.set_centered_player)
10007   {
10008     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10009
10010     /* switching to "all players" only possible if all players fit to screen */
10011     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10012     {
10013       game.centered_player_nr_next = game.centered_player_nr;
10014       game.set_centered_player = FALSE;
10015     }
10016
10017     /* do not switch focus to non-existing (or non-active) player */
10018     if (game.centered_player_nr_next >= 0 &&
10019         !stored_player[game.centered_player_nr_next].active)
10020     {
10021       game.centered_player_nr_next = game.centered_player_nr;
10022       game.set_centered_player = FALSE;
10023     }
10024   }
10025
10026   if (game.set_centered_player &&
10027       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10028   {
10029     int sx, sy;
10030
10031     if (game.centered_player_nr_next == -1)
10032     {
10033       setScreenCenteredToAllPlayers(&sx, &sy);
10034     }
10035     else
10036     {
10037       sx = stored_player[game.centered_player_nr_next].jx;
10038       sy = stored_player[game.centered_player_nr_next].jy;
10039     }
10040
10041     game.centered_player_nr = game.centered_player_nr_next;
10042     game.set_centered_player = FALSE;
10043
10044     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10045     DrawGameDoorValues();
10046   }
10047
10048   for (i = 0; i < MAX_PLAYERS; i++)
10049   {
10050     int actual_player_action = stored_player[i].effective_action;
10051
10052 #if 1
10053     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10054        - rnd_equinox_tetrachloride 048
10055        - rnd_equinox_tetrachloride_ii 096
10056        - rnd_emanuel_schmieg 002
10057        - doctor_sloan_ww 001, 020
10058     */
10059     if (stored_player[i].MovPos == 0)
10060       CheckGravityMovement(&stored_player[i]);
10061 #endif
10062
10063     /* overwrite programmed action with tape action */
10064     if (stored_player[i].programmed_action)
10065       actual_player_action = stored_player[i].programmed_action;
10066
10067     PlayerActions(&stored_player[i], actual_player_action);
10068
10069     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10070   }
10071
10072   ScrollScreen(NULL, SCROLL_GO_ON);
10073
10074   /* for backwards compatibility, the following code emulates a fixed bug that
10075      occured when pushing elements (causing elements that just made their last
10076      pushing step to already (if possible) make their first falling step in the
10077      same game frame, which is bad); this code is also needed to use the famous
10078      "spring push bug" which is used in older levels and might be wanted to be
10079      used also in newer levels, but in this case the buggy pushing code is only
10080      affecting the "spring" element and no other elements */
10081
10082   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10083   {
10084     for (i = 0; i < MAX_PLAYERS; i++)
10085     {
10086       struct PlayerInfo *player = &stored_player[i];
10087       int x = player->jx;
10088       int y = player->jy;
10089
10090       if (player->active && player->is_pushing && player->is_moving &&
10091           IS_MOVING(x, y) &&
10092           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10093            Feld[x][y] == EL_SPRING))
10094       {
10095         ContinueMoving(x, y);
10096
10097         /* continue moving after pushing (this is actually a bug) */
10098         if (!IS_MOVING(x, y))
10099         {
10100           Stop[x][y] = FALSE;
10101         }
10102       }
10103     }
10104   }
10105
10106   SCAN_PLAYFIELD(x, y)
10107   {
10108     ChangeCount[x][y] = 0;
10109     ChangeEvent[x][y] = -1;
10110
10111     /* this must be handled before main playfield loop */
10112     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10113     {
10114       MovDelay[x][y]--;
10115       if (MovDelay[x][y] <= 0)
10116         RemoveField(x, y);
10117     }
10118
10119 #if USE_NEW_SNAP_DELAY
10120     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10121     {
10122       MovDelay[x][y]--;
10123       if (MovDelay[x][y] <= 0)
10124       {
10125         RemoveField(x, y);
10126         DrawLevelField(x, y);
10127
10128         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10129       }
10130     }
10131 #endif
10132
10133 #if DEBUG
10134     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10135     {
10136       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10137       printf("GameActions(): This should never happen!\n");
10138
10139       ChangePage[x][y] = -1;
10140     }
10141 #endif
10142
10143     Stop[x][y] = FALSE;
10144     if (WasJustMoving[x][y] > 0)
10145       WasJustMoving[x][y]--;
10146     if (WasJustFalling[x][y] > 0)
10147       WasJustFalling[x][y]--;
10148     if (CheckCollision[x][y] > 0)
10149       CheckCollision[x][y]--;
10150     if (CheckImpact[x][y] > 0)
10151       CheckImpact[x][y]--;
10152
10153     GfxFrame[x][y]++;
10154
10155     /* reset finished pushing action (not done in ContinueMoving() to allow
10156        continuous pushing animation for elements with zero push delay) */
10157     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10158     {
10159       ResetGfxAnimation(x, y);
10160       DrawLevelField(x, y);
10161     }
10162
10163 #if DEBUG
10164     if (IS_BLOCKED(x, y))
10165     {
10166       int oldx, oldy;
10167
10168       Blocked2Moving(x, y, &oldx, &oldy);
10169       if (!IS_MOVING(oldx, oldy))
10170       {
10171         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10172         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10173         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10174         printf("GameActions(): This should never happen!\n");
10175       }
10176     }
10177 #endif
10178   }
10179
10180   SCAN_PLAYFIELD(x, y)
10181   {
10182     element = Feld[x][y];
10183     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10184
10185     ResetGfxFrame(x, y, TRUE);
10186
10187     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10188         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10189       ResetRandomAnimationValue(x, y);
10190
10191     SetRandomAnimationValue(x, y);
10192
10193     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10194
10195     if (IS_INACTIVE(element))
10196     {
10197       if (IS_ANIMATED(graphic))
10198         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10199
10200       continue;
10201     }
10202
10203     /* this may take place after moving, so 'element' may have changed */
10204     if (IS_CHANGING(x, y) &&
10205         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10206     {
10207       int page = element_info[element].event_page_nr[CE_DELAY];
10208
10209 #if 1
10210       HandleElementChange(x, y, page);
10211 #else
10212       if (CAN_CHANGE(element))
10213         HandleElementChange(x, y, page);
10214
10215       if (HAS_ACTION(element))
10216         ExecuteCustomElementAction(x, y, element, page);
10217 #endif
10218
10219       element = Feld[x][y];
10220       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10221     }
10222
10223     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10224     {
10225       StartMoving(x, y);
10226
10227       element = Feld[x][y];
10228       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10229
10230       if (IS_ANIMATED(graphic) &&
10231           !IS_MOVING(x, y) &&
10232           !Stop[x][y])
10233         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10234
10235       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10236         DrawTwinkleOnField(x, y);
10237     }
10238     else if ((element == EL_ACID ||
10239               element == EL_EXIT_OPEN ||
10240               element == EL_EM_EXIT_OPEN ||
10241               element == EL_SP_EXIT_OPEN ||
10242               element == EL_STEEL_EXIT_OPEN ||
10243               element == EL_EM_STEEL_EXIT_OPEN ||
10244               element == EL_SP_TERMINAL ||
10245               element == EL_SP_TERMINAL_ACTIVE ||
10246               element == EL_EXTRA_TIME ||
10247               element == EL_SHIELD_NORMAL ||
10248               element == EL_SHIELD_DEADLY) &&
10249              IS_ANIMATED(graphic))
10250       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10251     else if (IS_MOVING(x, y))
10252       ContinueMoving(x, y);
10253     else if (IS_ACTIVE_BOMB(element))
10254       CheckDynamite(x, y);
10255     else if (element == EL_AMOEBA_GROWING)
10256       AmoebeWaechst(x, y);
10257     else if (element == EL_AMOEBA_SHRINKING)
10258       AmoebaDisappearing(x, y);
10259
10260 #if !USE_NEW_AMOEBA_CODE
10261     else if (IS_AMOEBALIVE(element))
10262       AmoebeAbleger(x, y);
10263 #endif
10264
10265     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10266       Life(x, y);
10267     else if (element == EL_EXIT_CLOSED)
10268       CheckExit(x, y);
10269     else if (element == EL_EM_EXIT_CLOSED)
10270       CheckExitEM(x, y);
10271     else if (element == EL_STEEL_EXIT_CLOSED)
10272       CheckExitSteel(x, y);
10273     else if (element == EL_EM_STEEL_EXIT_CLOSED)
10274       CheckExitSteelEM(x, y);
10275     else if (element == EL_SP_EXIT_CLOSED)
10276       CheckExitSP(x, y);
10277     else if (element == EL_EXPANDABLE_WALL_GROWING ||
10278              element == EL_EXPANDABLE_STEELWALL_GROWING)
10279       MauerWaechst(x, y);
10280     else if (element == EL_EXPANDABLE_WALL ||
10281              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10282              element == EL_EXPANDABLE_WALL_VERTICAL ||
10283              element == EL_EXPANDABLE_WALL_ANY ||
10284              element == EL_BD_EXPANDABLE_WALL)
10285       MauerAbleger(x, y);
10286     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10287              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10288              element == EL_EXPANDABLE_STEELWALL_ANY)
10289       MauerAblegerStahl(x, y);
10290     else if (element == EL_FLAMES)
10291       CheckForDragon(x, y);
10292     else if (element == EL_EXPLOSION)
10293       ; /* drawing of correct explosion animation is handled separately */
10294     else if (element == EL_ELEMENT_SNAPPING ||
10295              element == EL_DIAGONAL_SHRINKING ||
10296              element == EL_DIAGONAL_GROWING)
10297     {
10298       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10299
10300       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10301     }
10302     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10303       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10304
10305     if (IS_BELT_ACTIVE(element))
10306       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10307
10308     if (game.magic_wall_active)
10309     {
10310       int jx = local_player->jx, jy = local_player->jy;
10311
10312       /* play the element sound at the position nearest to the player */
10313       if ((element == EL_MAGIC_WALL_FULL ||
10314            element == EL_MAGIC_WALL_ACTIVE ||
10315            element == EL_MAGIC_WALL_EMPTYING ||
10316            element == EL_BD_MAGIC_WALL_FULL ||
10317            element == EL_BD_MAGIC_WALL_ACTIVE ||
10318            element == EL_BD_MAGIC_WALL_EMPTYING ||
10319            element == EL_DC_MAGIC_WALL_FULL ||
10320            element == EL_DC_MAGIC_WALL_ACTIVE ||
10321            element == EL_DC_MAGIC_WALL_EMPTYING) &&
10322           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10323       {
10324         magic_wall_x = x;
10325         magic_wall_y = y;
10326       }
10327     }
10328   }
10329
10330 #if USE_NEW_AMOEBA_CODE
10331   /* new experimental amoeba growth stuff */
10332   if (!(FrameCounter % 8))
10333   {
10334     static unsigned long random = 1684108901;
10335
10336     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10337     {
10338       x = RND(lev_fieldx);
10339       y = RND(lev_fieldy);
10340       element = Feld[x][y];
10341
10342       if (!IS_PLAYER(x,y) &&
10343           (element == EL_EMPTY ||
10344            CAN_GROW_INTO(element) ||
10345            element == EL_QUICKSAND_EMPTY ||
10346            element == EL_QUICKSAND_FAST_EMPTY ||
10347            element == EL_ACID_SPLASH_LEFT ||
10348            element == EL_ACID_SPLASH_RIGHT))
10349       {
10350         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10351             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10352             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10353             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10354           Feld[x][y] = EL_AMOEBA_DROP;
10355       }
10356
10357       random = random * 129 + 1;
10358     }
10359   }
10360 #endif
10361
10362 #if 0
10363   if (game.explosions_delayed)
10364 #endif
10365   {
10366     game.explosions_delayed = FALSE;
10367
10368     SCAN_PLAYFIELD(x, y)
10369     {
10370       element = Feld[x][y];
10371
10372       if (ExplodeField[x][y])
10373         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10374       else if (element == EL_EXPLOSION)
10375         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10376
10377       ExplodeField[x][y] = EX_TYPE_NONE;
10378     }
10379
10380     game.explosions_delayed = TRUE;
10381   }
10382
10383   if (game.magic_wall_active)
10384   {
10385     if (!(game.magic_wall_time_left % 4))
10386     {
10387       int element = Feld[magic_wall_x][magic_wall_y];
10388
10389       if (element == EL_BD_MAGIC_WALL_FULL ||
10390           element == EL_BD_MAGIC_WALL_ACTIVE ||
10391           element == EL_BD_MAGIC_WALL_EMPTYING)
10392         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10393       else if (element == EL_DC_MAGIC_WALL_FULL ||
10394                element == EL_DC_MAGIC_WALL_ACTIVE ||
10395                element == EL_DC_MAGIC_WALL_EMPTYING)
10396         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10397       else
10398         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10399     }
10400
10401     if (game.magic_wall_time_left > 0)
10402     {
10403       game.magic_wall_time_left--;
10404       if (!game.magic_wall_time_left)
10405       {
10406         SCAN_PLAYFIELD(x, y)
10407         {
10408           element = Feld[x][y];
10409
10410           if (element == EL_MAGIC_WALL_ACTIVE ||
10411               element == EL_MAGIC_WALL_FULL)
10412           {
10413             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10414             DrawLevelField(x, y);
10415           }
10416           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10417                    element == EL_BD_MAGIC_WALL_FULL)
10418           {
10419             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10420             DrawLevelField(x, y);
10421           }
10422           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10423                    element == EL_DC_MAGIC_WALL_FULL)
10424           {
10425             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10426             DrawLevelField(x, y);
10427           }
10428         }
10429
10430         game.magic_wall_active = FALSE;
10431       }
10432     }
10433   }
10434
10435   if (game.light_time_left > 0)
10436   {
10437     game.light_time_left--;
10438
10439     if (game.light_time_left == 0)
10440       RedrawAllLightSwitchesAndInvisibleElements();
10441   }
10442
10443   if (game.timegate_time_left > 0)
10444   {
10445     game.timegate_time_left--;
10446
10447     if (game.timegate_time_left == 0)
10448       CloseAllOpenTimegates();
10449   }
10450
10451   if (game.lenses_time_left > 0)
10452   {
10453     game.lenses_time_left--;
10454
10455     if (game.lenses_time_left == 0)
10456       RedrawAllInvisibleElementsForLenses();
10457   }
10458
10459   if (game.magnify_time_left > 0)
10460   {
10461     game.magnify_time_left--;
10462
10463     if (game.magnify_time_left == 0)
10464       RedrawAllInvisibleElementsForMagnifier();
10465   }
10466
10467   for (i = 0; i < MAX_PLAYERS; i++)
10468   {
10469     struct PlayerInfo *player = &stored_player[i];
10470
10471     if (SHIELD_ON(player))
10472     {
10473       if (player->shield_deadly_time_left)
10474         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10475       else if (player->shield_normal_time_left)
10476         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10477     }
10478   }
10479
10480   CheckLevelTime();
10481
10482   DrawAllPlayers();
10483   PlayAllPlayersSound();
10484
10485   if (options.debug)                    /* calculate frames per second */
10486   {
10487     static unsigned long fps_counter = 0;
10488     static int fps_frames = 0;
10489     unsigned long fps_delay_ms = Counter() - fps_counter;
10490
10491     fps_frames++;
10492
10493     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10494     {
10495       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10496
10497       fps_frames = 0;
10498       fps_counter = Counter();
10499     }
10500
10501     redraw_mask |= REDRAW_FPS;
10502   }
10503
10504   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10505
10506   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10507   {
10508     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10509
10510     local_player->show_envelope = 0;
10511   }
10512
10513   /* use random number generator in every frame to make it less predictable */
10514   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10515     RND(1);
10516 }
10517
10518 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10519 {
10520   int min_x = x, min_y = y, max_x = x, max_y = y;
10521   int i;
10522
10523   for (i = 0; i < MAX_PLAYERS; i++)
10524   {
10525     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10526
10527     if (!stored_player[i].active || &stored_player[i] == player)
10528       continue;
10529
10530     min_x = MIN(min_x, jx);
10531     min_y = MIN(min_y, jy);
10532     max_x = MAX(max_x, jx);
10533     max_y = MAX(max_y, jy);
10534   }
10535
10536   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10537 }
10538
10539 static boolean AllPlayersInVisibleScreen()
10540 {
10541   int i;
10542
10543   for (i = 0; i < MAX_PLAYERS; i++)
10544   {
10545     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10546
10547     if (!stored_player[i].active)
10548       continue;
10549
10550     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10551       return FALSE;
10552   }
10553
10554   return TRUE;
10555 }
10556
10557 void ScrollLevel(int dx, int dy)
10558 {
10559   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10560   int x, y;
10561
10562   BlitBitmap(drawto_field, drawto_field,
10563              FX + TILEX * (dx == -1) - softscroll_offset,
10564              FY + TILEY * (dy == -1) - softscroll_offset,
10565              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10566              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10567              FX + TILEX * (dx == 1) - softscroll_offset,
10568              FY + TILEY * (dy == 1) - softscroll_offset);
10569
10570   if (dx)
10571   {
10572     x = (dx == 1 ? BX1 : BX2);
10573     for (y = BY1; y <= BY2; y++)
10574       DrawScreenField(x, y);
10575   }
10576
10577   if (dy)
10578   {
10579     y = (dy == 1 ? BY1 : BY2);
10580     for (x = BX1; x <= BX2; x++)
10581       DrawScreenField(x, y);
10582   }
10583
10584   redraw_mask |= REDRAW_FIELD;
10585 }
10586
10587 static boolean canFallDown(struct PlayerInfo *player)
10588 {
10589   int jx = player->jx, jy = player->jy;
10590
10591   return (IN_LEV_FIELD(jx, jy + 1) &&
10592           (IS_FREE(jx, jy + 1) ||
10593            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10594           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10595           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10596 }
10597
10598 static boolean canPassField(int x, int y, int move_dir)
10599 {
10600   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10601   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10602   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10603   int nextx = x + dx;
10604   int nexty = y + dy;
10605   int element = Feld[x][y];
10606
10607   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10608           !CAN_MOVE(element) &&
10609           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10610           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10611           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10612 }
10613
10614 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10615 {
10616   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10617   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10618   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10619   int newx = x + dx;
10620   int newy = y + dy;
10621
10622   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10623           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10624           (IS_DIGGABLE(Feld[newx][newy]) ||
10625            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10626            canPassField(newx, newy, move_dir)));
10627 }
10628
10629 static void CheckGravityMovement(struct PlayerInfo *player)
10630 {
10631 #if USE_PLAYER_GRAVITY
10632   if (player->gravity && !player->programmed_action)
10633 #else
10634   if (game.gravity && !player->programmed_action)
10635 #endif
10636   {
10637     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10638     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10639     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10640     int jx = player->jx, jy = player->jy;
10641     boolean player_is_moving_to_valid_field =
10642       (!player_is_snapping &&
10643        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10644         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10645     boolean player_can_fall_down = canFallDown(player);
10646
10647     if (player_can_fall_down &&
10648         !player_is_moving_to_valid_field)
10649       player->programmed_action = MV_DOWN;
10650   }
10651 }
10652
10653 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10654 {
10655   return CheckGravityMovement(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 jx = player->jx, jy = player->jy;
10664     boolean field_under_player_is_free =
10665       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10666     boolean player_is_standing_on_valid_field =
10667       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10668        (IS_WALKABLE(Feld[jx][jy]) &&
10669         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10670
10671     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10672       player->programmed_action = MV_DOWN;
10673   }
10674 }
10675
10676 /*
10677   MovePlayerOneStep()
10678   -----------------------------------------------------------------------------
10679   dx, dy:               direction (non-diagonal) to try to move the player to
10680   real_dx, real_dy:     direction as read from input device (can be diagonal)
10681 */
10682
10683 boolean MovePlayerOneStep(struct PlayerInfo *player,
10684                           int dx, int dy, int real_dx, int real_dy)
10685 {
10686   int jx = player->jx, jy = player->jy;
10687   int new_jx = jx + dx, new_jy = jy + dy;
10688 #if !USE_FIXED_DONT_RUN_INTO
10689   int element;
10690 #endif
10691   int can_move;
10692   boolean player_can_move = !player->cannot_move;
10693
10694   if (!player->active || (!dx && !dy))
10695     return MP_NO_ACTION;
10696
10697   player->MovDir = (dx < 0 ? MV_LEFT :
10698                     dx > 0 ? MV_RIGHT :
10699                     dy < 0 ? MV_UP :
10700                     dy > 0 ? MV_DOWN :  MV_NONE);
10701
10702   if (!IN_LEV_FIELD(new_jx, new_jy))
10703     return MP_NO_ACTION;
10704
10705   if (!player_can_move)
10706   {
10707     if (player->MovPos == 0)
10708     {
10709       player->is_moving = FALSE;
10710       player->is_digging = FALSE;
10711       player->is_collecting = FALSE;
10712       player->is_snapping = FALSE;
10713       player->is_pushing = FALSE;
10714     }
10715   }
10716
10717 #if 1
10718   if (!options.network && game.centered_player_nr == -1 &&
10719       !AllPlayersInSight(player, new_jx, new_jy))
10720     return MP_NO_ACTION;
10721 #else
10722   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10723     return MP_NO_ACTION;
10724 #endif
10725
10726 #if !USE_FIXED_DONT_RUN_INTO
10727   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10728
10729   /* (moved to DigField()) */
10730   if (player_can_move && DONT_RUN_INTO(element))
10731   {
10732     if (element == EL_ACID && dx == 0 && dy == 1)
10733     {
10734       SplashAcid(new_jx, new_jy);
10735       Feld[jx][jy] = EL_PLAYER_1;
10736       InitMovingField(jx, jy, MV_DOWN);
10737       Store[jx][jy] = EL_ACID;
10738       ContinueMoving(jx, jy);
10739       BuryPlayer(player);
10740     }
10741     else
10742       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10743
10744     return MP_MOVING;
10745   }
10746 #endif
10747
10748   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10749   if (can_move != MP_MOVING)
10750     return can_move;
10751
10752   /* check if DigField() has caused relocation of the player */
10753   if (player->jx != jx || player->jy != jy)
10754     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10755
10756   StorePlayer[jx][jy] = 0;
10757   player->last_jx = jx;
10758   player->last_jy = jy;
10759   player->jx = new_jx;
10760   player->jy = new_jy;
10761   StorePlayer[new_jx][new_jy] = player->element_nr;
10762
10763   if (player->move_delay_value_next != -1)
10764   {
10765     player->move_delay_value = player->move_delay_value_next;
10766     player->move_delay_value_next = -1;
10767   }
10768
10769   player->MovPos =
10770     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10771
10772   player->step_counter++;
10773
10774   PlayerVisit[jx][jy] = FrameCounter;
10775
10776 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10777   player->is_moving = TRUE;
10778 #endif
10779
10780 #if 1
10781   /* should better be called in MovePlayer(), but this breaks some tapes */
10782   ScrollPlayer(player, SCROLL_INIT);
10783 #endif
10784
10785   return MP_MOVING;
10786 }
10787
10788 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10789 {
10790   int jx = player->jx, jy = player->jy;
10791   int old_jx = jx, old_jy = jy;
10792   int moved = MP_NO_ACTION;
10793
10794   if (!player->active)
10795     return FALSE;
10796
10797   if (!dx && !dy)
10798   {
10799     if (player->MovPos == 0)
10800     {
10801       player->is_moving = FALSE;
10802       player->is_digging = FALSE;
10803       player->is_collecting = FALSE;
10804       player->is_snapping = FALSE;
10805       player->is_pushing = FALSE;
10806     }
10807
10808     return FALSE;
10809   }
10810
10811   if (player->move_delay > 0)
10812     return FALSE;
10813
10814   player->move_delay = -1;              /* set to "uninitialized" value */
10815
10816   /* store if player is automatically moved to next field */
10817   player->is_auto_moving = (player->programmed_action != MV_NONE);
10818
10819   /* remove the last programmed player action */
10820   player->programmed_action = 0;
10821
10822   if (player->MovPos)
10823   {
10824     /* should only happen if pre-1.2 tape recordings are played */
10825     /* this is only for backward compatibility */
10826
10827     int original_move_delay_value = player->move_delay_value;
10828
10829 #if DEBUG
10830     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10831            tape.counter);
10832 #endif
10833
10834     /* scroll remaining steps with finest movement resolution */
10835     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10836
10837     while (player->MovPos)
10838     {
10839       ScrollPlayer(player, SCROLL_GO_ON);
10840       ScrollScreen(NULL, SCROLL_GO_ON);
10841
10842       AdvanceFrameAndPlayerCounters(player->index_nr);
10843
10844       DrawAllPlayers();
10845       BackToFront();
10846     }
10847
10848     player->move_delay_value = original_move_delay_value;
10849   }
10850
10851   player->is_active = FALSE;
10852
10853   if (player->last_move_dir & MV_HORIZONTAL)
10854   {
10855     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10856       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10857   }
10858   else
10859   {
10860     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10861       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10862   }
10863
10864 #if USE_FIXED_BORDER_RUNNING_GFX
10865   if (!moved && !player->is_active)
10866   {
10867     player->is_moving = FALSE;
10868     player->is_digging = FALSE;
10869     player->is_collecting = FALSE;
10870     player->is_snapping = FALSE;
10871     player->is_pushing = FALSE;
10872   }
10873 #endif
10874
10875   jx = player->jx;
10876   jy = player->jy;
10877
10878 #if 1
10879   if (moved & MP_MOVING && !ScreenMovPos &&
10880       (player->index_nr == game.centered_player_nr ||
10881        game.centered_player_nr == -1))
10882 #else
10883   if (moved & MP_MOVING && !ScreenMovPos &&
10884       (player == local_player || !options.network))
10885 #endif
10886   {
10887     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10888     int offset = (setup.scroll_delay ? 3 : 0);
10889
10890     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10891     {
10892       /* actual player has left the screen -- scroll in that direction */
10893       if (jx != old_jx)         /* player has moved horizontally */
10894         scroll_x += (jx - old_jx);
10895       else                      /* player has moved vertically */
10896         scroll_y += (jy - old_jy);
10897     }
10898     else
10899     {
10900       if (jx != old_jx)         /* player has moved horizontally */
10901       {
10902         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10903             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10904           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10905
10906         /* don't scroll over playfield boundaries */
10907         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10908           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10909
10910         /* don't scroll more than one field at a time */
10911         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10912
10913         /* don't scroll against the player's moving direction */
10914         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10915             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10916           scroll_x = old_scroll_x;
10917       }
10918       else                      /* player has moved vertically */
10919       {
10920         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10921             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10922           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10923
10924         /* don't scroll over playfield boundaries */
10925         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10926           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10927
10928         /* don't scroll more than one field at a time */
10929         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10930
10931         /* don't scroll against the player's moving direction */
10932         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10933             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10934           scroll_y = old_scroll_y;
10935       }
10936     }
10937
10938     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10939     {
10940 #if 1
10941       if (!options.network && game.centered_player_nr == -1 &&
10942           !AllPlayersInVisibleScreen())
10943       {
10944         scroll_x = old_scroll_x;
10945         scroll_y = old_scroll_y;
10946       }
10947       else
10948 #else
10949       if (!options.network && !AllPlayersInVisibleScreen())
10950       {
10951         scroll_x = old_scroll_x;
10952         scroll_y = old_scroll_y;
10953       }
10954       else
10955 #endif
10956       {
10957         ScrollScreen(player, SCROLL_INIT);
10958         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10959       }
10960     }
10961   }
10962
10963   player->StepFrame = 0;
10964
10965   if (moved & MP_MOVING)
10966   {
10967     if (old_jx != jx && old_jy == jy)
10968       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10969     else if (old_jx == jx && old_jy != jy)
10970       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10971
10972     DrawLevelField(jx, jy);     /* for "crumbled sand" */
10973
10974     player->last_move_dir = player->MovDir;
10975     player->is_moving = TRUE;
10976     player->is_snapping = FALSE;
10977     player->is_switching = FALSE;
10978     player->is_dropping = FALSE;
10979     player->is_dropping_pressed = FALSE;
10980     player->drop_pressed_delay = 0;
10981
10982 #if 0
10983     /* should better be called here than above, but this breaks some tapes */
10984     ScrollPlayer(player, SCROLL_INIT);
10985 #endif
10986   }
10987   else
10988   {
10989     CheckGravityMovementWhenNotMoving(player);
10990
10991     player->is_moving = FALSE;
10992
10993     /* at this point, the player is allowed to move, but cannot move right now
10994        (e.g. because of something blocking the way) -- ensure that the player
10995        is also allowed to move in the next frame (in old versions before 3.1.1,
10996        the player was forced to wait again for eight frames before next try) */
10997
10998     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10999       player->move_delay = 0;   /* allow direct movement in the next frame */
11000   }
11001
11002   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11003     player->move_delay = player->move_delay_value;
11004
11005   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11006   {
11007     TestIfPlayerTouchesBadThing(jx, jy);
11008     TestIfPlayerTouchesCustomElement(jx, jy);
11009   }
11010
11011   if (!player->active)
11012     RemovePlayer(player);
11013
11014   return moved;
11015 }
11016
11017 void ScrollPlayer(struct PlayerInfo *player, int mode)
11018 {
11019   int jx = player->jx, jy = player->jy;
11020   int last_jx = player->last_jx, last_jy = player->last_jy;
11021   int move_stepsize = TILEX / player->move_delay_value;
11022
11023 #if USE_NEW_PLAYER_SPEED
11024   if (!player->active)
11025     return;
11026
11027   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11028     return;
11029 #else
11030   if (!player->active || player->MovPos == 0)
11031     return;
11032 #endif
11033
11034   if (mode == SCROLL_INIT)
11035   {
11036     player->actual_frame_counter = FrameCounter;
11037     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11038
11039     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11040         Feld[last_jx][last_jy] == EL_EMPTY)
11041     {
11042       int last_field_block_delay = 0;   /* start with no blocking at all */
11043       int block_delay_adjustment = player->block_delay_adjustment;
11044
11045       /* if player blocks last field, add delay for exactly one move */
11046       if (player->block_last_field)
11047       {
11048         last_field_block_delay += player->move_delay_value;
11049
11050         /* when blocking enabled, prevent moving up despite gravity */
11051 #if USE_PLAYER_GRAVITY
11052         if (player->gravity && player->MovDir == MV_UP)
11053           block_delay_adjustment = -1;
11054 #else
11055         if (game.gravity && player->MovDir == MV_UP)
11056           block_delay_adjustment = -1;
11057 #endif
11058       }
11059
11060       /* add block delay adjustment (also possible when not blocking) */
11061       last_field_block_delay += block_delay_adjustment;
11062
11063       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11064       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11065     }
11066
11067 #if USE_NEW_PLAYER_SPEED
11068     if (player->MovPos != 0)    /* player has not yet reached destination */
11069       return;
11070 #else
11071     return;
11072 #endif
11073   }
11074   else if (!FrameReached(&player->actual_frame_counter, 1))
11075     return;
11076
11077 #if USE_NEW_PLAYER_SPEED
11078   if (player->MovPos != 0)
11079   {
11080     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11081     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11082
11083     /* before DrawPlayer() to draw correct player graphic for this case */
11084     if (player->MovPos == 0)
11085       CheckGravityMovement(player);
11086   }
11087 #else
11088   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11089   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11090
11091   /* before DrawPlayer() to draw correct player graphic for this case */
11092   if (player->MovPos == 0)
11093     CheckGravityMovement(player);
11094 #endif
11095
11096   if (player->MovPos == 0)      /* player reached destination field */
11097   {
11098     if (player->move_delay_reset_counter > 0)
11099     {
11100       player->move_delay_reset_counter--;
11101
11102       if (player->move_delay_reset_counter == 0)
11103       {
11104         /* continue with normal speed after quickly moving through gate */
11105         HALVE_PLAYER_SPEED(player);
11106
11107         /* be able to make the next move without delay */
11108         player->move_delay = 0;
11109       }
11110     }
11111
11112     player->last_jx = jx;
11113     player->last_jy = jy;
11114
11115     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11116         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11117         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11118         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11119         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11120         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11121     {
11122       DrawPlayer(player);       /* needed here only to cleanup last field */
11123       RemovePlayer(player);
11124
11125       if (local_player->friends_still_needed == 0 ||
11126           IS_SP_ELEMENT(Feld[jx][jy]))
11127         PlayerWins(player);
11128     }
11129
11130     /* this breaks one level: "machine", level 000 */
11131     {
11132       int move_direction = player->MovDir;
11133       int enter_side = MV_DIR_OPPOSITE(move_direction);
11134       int leave_side = move_direction;
11135       int old_jx = last_jx;
11136       int old_jy = last_jy;
11137       int old_element = Feld[old_jx][old_jy];
11138       int new_element = Feld[jx][jy];
11139
11140       if (IS_CUSTOM_ELEMENT(old_element))
11141         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11142                                    CE_LEFT_BY_PLAYER,
11143                                    player->index_bit, leave_side);
11144
11145       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11146                                           CE_PLAYER_LEAVES_X,
11147                                           player->index_bit, leave_side);
11148
11149       if (IS_CUSTOM_ELEMENT(new_element))
11150         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11151                                    player->index_bit, enter_side);
11152
11153       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11154                                           CE_PLAYER_ENTERS_X,
11155                                           player->index_bit, enter_side);
11156
11157       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11158                                         CE_MOVE_OF_X, move_direction);
11159     }
11160
11161     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11162     {
11163       TestIfPlayerTouchesBadThing(jx, jy);
11164       TestIfPlayerTouchesCustomElement(jx, jy);
11165
11166       /* needed because pushed element has not yet reached its destination,
11167          so it would trigger a change event at its previous field location */
11168       if (!player->is_pushing)
11169         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
11170
11171       if (!player->active)
11172         RemovePlayer(player);
11173     }
11174
11175     if (!local_player->LevelSolved && level.use_step_counter)
11176     {
11177       int i;
11178
11179       TimePlayed++;
11180
11181       if (TimeLeft > 0)
11182       {
11183         TimeLeft--;
11184
11185         if (TimeLeft <= 10 && setup.time_limit)
11186           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11187
11188         DrawGameValue_Time(TimeLeft);
11189
11190         if (!TimeLeft && setup.time_limit)
11191           for (i = 0; i < MAX_PLAYERS; i++)
11192             KillPlayer(&stored_player[i]);
11193       }
11194       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11195         DrawGameValue_Time(TimePlayed);
11196     }
11197
11198     if (tape.single_step && tape.recording && !tape.pausing &&
11199         !player->programmed_action)
11200       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11201   }
11202 }
11203
11204 void ScrollScreen(struct PlayerInfo *player, int mode)
11205 {
11206   static unsigned long screen_frame_counter = 0;
11207
11208   if (mode == SCROLL_INIT)
11209   {
11210     /* set scrolling step size according to actual player's moving speed */
11211     ScrollStepSize = TILEX / player->move_delay_value;
11212
11213     screen_frame_counter = FrameCounter;
11214     ScreenMovDir = player->MovDir;
11215     ScreenMovPos = player->MovPos;
11216     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11217     return;
11218   }
11219   else if (!FrameReached(&screen_frame_counter, 1))
11220     return;
11221
11222   if (ScreenMovPos)
11223   {
11224     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11225     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11226     redraw_mask |= REDRAW_FIELD;
11227   }
11228   else
11229     ScreenMovDir = MV_NONE;
11230 }
11231
11232 void TestIfPlayerTouchesCustomElement(int x, int y)
11233 {
11234   static int xy[4][2] =
11235   {
11236     { 0, -1 },
11237     { -1, 0 },
11238     { +1, 0 },
11239     { 0, +1 }
11240   };
11241   static int trigger_sides[4][2] =
11242   {
11243     /* center side       border side */
11244     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11245     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11246     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11247     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11248   };
11249   static int touch_dir[4] =
11250   {
11251     MV_LEFT | MV_RIGHT,
11252     MV_UP   | MV_DOWN,
11253     MV_UP   | MV_DOWN,
11254     MV_LEFT | MV_RIGHT
11255   };
11256   int center_element = Feld[x][y];      /* should always be non-moving! */
11257   int i;
11258
11259   for (i = 0; i < NUM_DIRECTIONS; i++)
11260   {
11261     int xx = x + xy[i][0];
11262     int yy = y + xy[i][1];
11263     int center_side = trigger_sides[i][0];
11264     int border_side = trigger_sides[i][1];
11265     int border_element;
11266
11267     if (!IN_LEV_FIELD(xx, yy))
11268       continue;
11269
11270     if (IS_PLAYER(x, y))
11271     {
11272       struct PlayerInfo *player = PLAYERINFO(x, y);
11273
11274       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11275         border_element = Feld[xx][yy];          /* may be moving! */
11276       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11277         border_element = Feld[xx][yy];
11278       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11279         border_element = MovingOrBlocked2Element(xx, yy);
11280       else
11281         continue;               /* center and border element do not touch */
11282
11283       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11284                                  player->index_bit, border_side);
11285       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11286                                           CE_PLAYER_TOUCHES_X,
11287                                           player->index_bit, border_side);
11288     }
11289     else if (IS_PLAYER(xx, yy))
11290     {
11291       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11292
11293       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11294       {
11295         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11296           continue;             /* center and border element do not touch */
11297       }
11298
11299       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11300                                  player->index_bit, center_side);
11301       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11302                                           CE_PLAYER_TOUCHES_X,
11303                                           player->index_bit, center_side);
11304       break;
11305     }
11306   }
11307 }
11308
11309 #if USE_ELEMENT_TOUCHING_BUGFIX
11310
11311 void TestIfElementTouchesCustomElement(int x, int y)
11312 {
11313   static int xy[4][2] =
11314   {
11315     { 0, -1 },
11316     { -1, 0 },
11317     { +1, 0 },
11318     { 0, +1 }
11319   };
11320   static int trigger_sides[4][2] =
11321   {
11322     /* center side      border side */
11323     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11324     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11325     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11326     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11327   };
11328   static int touch_dir[4] =
11329   {
11330     MV_LEFT | MV_RIGHT,
11331     MV_UP   | MV_DOWN,
11332     MV_UP   | MV_DOWN,
11333     MV_LEFT | MV_RIGHT
11334   };
11335   boolean change_center_element = FALSE;
11336   int center_element = Feld[x][y];      /* should always be non-moving! */
11337   int border_element_old[NUM_DIRECTIONS];
11338   int i;
11339
11340   for (i = 0; i < NUM_DIRECTIONS; i++)
11341   {
11342     int xx = x + xy[i][0];
11343     int yy = y + xy[i][1];
11344     int border_element;
11345
11346     border_element_old[i] = -1;
11347
11348     if (!IN_LEV_FIELD(xx, yy))
11349       continue;
11350
11351     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11352       border_element = Feld[xx][yy];    /* may be moving! */
11353     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11354       border_element = Feld[xx][yy];
11355     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11356       border_element = MovingOrBlocked2Element(xx, yy);
11357     else
11358       continue;                 /* center and border element do not touch */
11359
11360     border_element_old[i] = border_element;
11361   }
11362
11363   for (i = 0; i < NUM_DIRECTIONS; i++)
11364   {
11365     int xx = x + xy[i][0];
11366     int yy = y + xy[i][1];
11367     int center_side = trigger_sides[i][0];
11368     int border_element = border_element_old[i];
11369
11370     if (border_element == -1)
11371       continue;
11372
11373     /* check for change of border element */
11374     CheckElementChangeBySide(xx, yy, border_element, center_element,
11375                              CE_TOUCHING_X, center_side);
11376   }
11377
11378   for (i = 0; i < NUM_DIRECTIONS; i++)
11379   {
11380     int border_side = trigger_sides[i][1];
11381     int border_element = border_element_old[i];
11382
11383     if (border_element == -1)
11384       continue;
11385
11386     /* check for change of center element (but change it only once) */
11387     if (!change_center_element)
11388       change_center_element =
11389         CheckElementChangeBySide(x, y, center_element, border_element,
11390                                  CE_TOUCHING_X, border_side);
11391   }
11392 }
11393
11394 #else
11395
11396 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11397 {
11398   static int xy[4][2] =
11399   {
11400     { 0, -1 },
11401     { -1, 0 },
11402     { +1, 0 },
11403     { 0, +1 }
11404   };
11405   static int trigger_sides[4][2] =
11406   {
11407     /* center side      border side */
11408     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11409     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11410     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11411     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11412   };
11413   static int touch_dir[4] =
11414   {
11415     MV_LEFT | MV_RIGHT,
11416     MV_UP   | MV_DOWN,
11417     MV_UP   | MV_DOWN,
11418     MV_LEFT | MV_RIGHT
11419   };
11420   boolean change_center_element = FALSE;
11421   int center_element = Feld[x][y];      /* should always be non-moving! */
11422   int i;
11423
11424   for (i = 0; i < NUM_DIRECTIONS; i++)
11425   {
11426     int xx = x + xy[i][0];
11427     int yy = y + xy[i][1];
11428     int center_side = trigger_sides[i][0];
11429     int border_side = trigger_sides[i][1];
11430     int border_element;
11431
11432     if (!IN_LEV_FIELD(xx, yy))
11433       continue;
11434
11435     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11436       border_element = Feld[xx][yy];    /* may be moving! */
11437     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11438       border_element = Feld[xx][yy];
11439     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11440       border_element = MovingOrBlocked2Element(xx, yy);
11441     else
11442       continue;                 /* center and border element do not touch */
11443
11444     /* check for change of center element (but change it only once) */
11445     if (!change_center_element)
11446       change_center_element =
11447         CheckElementChangeBySide(x, y, center_element, border_element,
11448                                  CE_TOUCHING_X, border_side);
11449
11450     /* check for change of border element */
11451     CheckElementChangeBySide(xx, yy, border_element, center_element,
11452                              CE_TOUCHING_X, center_side);
11453   }
11454 }
11455
11456 #endif
11457
11458 void TestIfElementHitsCustomElement(int x, int y, int direction)
11459 {
11460   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11461   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11462   int hitx = x + dx, hity = y + dy;
11463   int hitting_element = Feld[x][y];
11464   int touched_element;
11465
11466   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11467     return;
11468
11469   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11470                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11471
11472   if (IN_LEV_FIELD(hitx, hity))
11473   {
11474     int opposite_direction = MV_DIR_OPPOSITE(direction);
11475     int hitting_side = direction;
11476     int touched_side = opposite_direction;
11477     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11478                           MovDir[hitx][hity] != direction ||
11479                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11480
11481     object_hit = TRUE;
11482
11483     if (object_hit)
11484     {
11485       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11486                                CE_HITTING_X, touched_side);
11487
11488       CheckElementChangeBySide(hitx, hity, touched_element,
11489                                hitting_element, CE_HIT_BY_X, hitting_side);
11490
11491       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11492                                CE_HIT_BY_SOMETHING, opposite_direction);
11493     }
11494   }
11495
11496   /* "hitting something" is also true when hitting the playfield border */
11497   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11498                            CE_HITTING_SOMETHING, direction);
11499 }
11500
11501 #if 0
11502 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11503 {
11504   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11505   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11506   int hitx = x + dx, hity = y + dy;
11507   int hitting_element = Feld[x][y];
11508   int touched_element;
11509 #if 0
11510   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11511                         !IS_FREE(hitx, hity) &&
11512                         (!IS_MOVING(hitx, hity) ||
11513                          MovDir[hitx][hity] != direction ||
11514                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11515 #endif
11516
11517   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11518     return;
11519
11520 #if 0
11521   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11522     return;
11523 #endif
11524
11525   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11526                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11527
11528   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11529                            EP_CAN_SMASH_EVERYTHING, direction);
11530
11531   if (IN_LEV_FIELD(hitx, hity))
11532   {
11533     int opposite_direction = MV_DIR_OPPOSITE(direction);
11534     int hitting_side = direction;
11535     int touched_side = opposite_direction;
11536 #if 0
11537     int touched_element = MovingOrBlocked2Element(hitx, hity);
11538 #endif
11539 #if 1
11540     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11541                           MovDir[hitx][hity] != direction ||
11542                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11543
11544     object_hit = TRUE;
11545 #endif
11546
11547     if (object_hit)
11548     {
11549       int i;
11550
11551       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11552                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11553
11554       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11555                                CE_OTHER_IS_SMASHING, touched_side);
11556
11557       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11558                                CE_OTHER_GETS_SMASHED, hitting_side);
11559     }
11560   }
11561 }
11562 #endif
11563
11564 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11565 {
11566   int i, kill_x = -1, kill_y = -1;
11567
11568   int bad_element = -1;
11569   static int test_xy[4][2] =
11570   {
11571     { 0, -1 },
11572     { -1, 0 },
11573     { +1, 0 },
11574     { 0, +1 }
11575   };
11576   static int test_dir[4] =
11577   {
11578     MV_UP,
11579     MV_LEFT,
11580     MV_RIGHT,
11581     MV_DOWN
11582   };
11583
11584   for (i = 0; i < NUM_DIRECTIONS; i++)
11585   {
11586     int test_x, test_y, test_move_dir, test_element;
11587
11588     test_x = good_x + test_xy[i][0];
11589     test_y = good_y + test_xy[i][1];
11590
11591     if (!IN_LEV_FIELD(test_x, test_y))
11592       continue;
11593
11594     test_move_dir =
11595       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11596
11597     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11598
11599     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11600        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11601     */
11602     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11603         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11604     {
11605       kill_x = test_x;
11606       kill_y = test_y;
11607       bad_element = test_element;
11608
11609       break;
11610     }
11611   }
11612
11613   if (kill_x != -1 || kill_y != -1)
11614   {
11615     if (IS_PLAYER(good_x, good_y))
11616     {
11617       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11618
11619       if (player->shield_deadly_time_left > 0 &&
11620           !IS_INDESTRUCTIBLE(bad_element))
11621         Bang(kill_x, kill_y);
11622       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11623         KillPlayer(player);
11624     }
11625     else
11626       Bang(good_x, good_y);
11627   }
11628 }
11629
11630 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11631 {
11632   int i, kill_x = -1, kill_y = -1;
11633   int bad_element = Feld[bad_x][bad_y];
11634   static int test_xy[4][2] =
11635   {
11636     { 0, -1 },
11637     { -1, 0 },
11638     { +1, 0 },
11639     { 0, +1 }
11640   };
11641   static int touch_dir[4] =
11642   {
11643     MV_LEFT | MV_RIGHT,
11644     MV_UP   | MV_DOWN,
11645     MV_UP   | MV_DOWN,
11646     MV_LEFT | MV_RIGHT
11647   };
11648   static int test_dir[4] =
11649   {
11650     MV_UP,
11651     MV_LEFT,
11652     MV_RIGHT,
11653     MV_DOWN
11654   };
11655
11656   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11657     return;
11658
11659   for (i = 0; i < NUM_DIRECTIONS; i++)
11660   {
11661     int test_x, test_y, test_move_dir, test_element;
11662
11663     test_x = bad_x + test_xy[i][0];
11664     test_y = bad_y + test_xy[i][1];
11665     if (!IN_LEV_FIELD(test_x, test_y))
11666       continue;
11667
11668     test_move_dir =
11669       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11670
11671     test_element = Feld[test_x][test_y];
11672
11673     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11674        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11675     */
11676     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11677         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11678     {
11679       /* good thing is player or penguin that does not move away */
11680       if (IS_PLAYER(test_x, test_y))
11681       {
11682         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11683
11684         if (bad_element == EL_ROBOT && player->is_moving)
11685           continue;     /* robot does not kill player if he is moving */
11686
11687         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11688         {
11689           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11690             continue;           /* center and border element do not touch */
11691         }
11692
11693         kill_x = test_x;
11694         kill_y = test_y;
11695         break;
11696       }
11697       else if (test_element == EL_PENGUIN)
11698       {
11699         kill_x = test_x;
11700         kill_y = test_y;
11701         break;
11702       }
11703     }
11704   }
11705
11706   if (kill_x != -1 || kill_y != -1)
11707   {
11708     if (IS_PLAYER(kill_x, kill_y))
11709     {
11710       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11711
11712       if (player->shield_deadly_time_left > 0 &&
11713           !IS_INDESTRUCTIBLE(bad_element))
11714         Bang(bad_x, bad_y);
11715       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11716         KillPlayer(player);
11717     }
11718     else
11719       Bang(kill_x, kill_y);
11720   }
11721 }
11722
11723 void TestIfPlayerTouchesBadThing(int x, int y)
11724 {
11725   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11726 }
11727
11728 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11729 {
11730   TestIfGoodThingHitsBadThing(x, y, move_dir);
11731 }
11732
11733 void TestIfBadThingTouchesPlayer(int x, int y)
11734 {
11735   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11736 }
11737
11738 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11739 {
11740   TestIfBadThingHitsGoodThing(x, y, move_dir);
11741 }
11742
11743 void TestIfFriendTouchesBadThing(int x, int y)
11744 {
11745   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11746 }
11747
11748 void TestIfBadThingTouchesFriend(int x, int y)
11749 {
11750   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11751 }
11752
11753 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11754 {
11755   int i, kill_x = bad_x, kill_y = bad_y;
11756   static int xy[4][2] =
11757   {
11758     { 0, -1 },
11759     { -1, 0 },
11760     { +1, 0 },
11761     { 0, +1 }
11762   };
11763
11764   for (i = 0; i < NUM_DIRECTIONS; i++)
11765   {
11766     int x, y, element;
11767
11768     x = bad_x + xy[i][0];
11769     y = bad_y + xy[i][1];
11770     if (!IN_LEV_FIELD(x, y))
11771       continue;
11772
11773     element = Feld[x][y];
11774     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11775         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11776     {
11777       kill_x = x;
11778       kill_y = y;
11779       break;
11780     }
11781   }
11782
11783   if (kill_x != bad_x || kill_y != bad_y)
11784     Bang(bad_x, bad_y);
11785 }
11786
11787 void KillPlayer(struct PlayerInfo *player)
11788 {
11789   int jx = player->jx, jy = player->jy;
11790
11791   if (!player->active)
11792     return;
11793
11794   /* the following code was introduced to prevent an infinite loop when calling
11795      -> Bang()
11796      -> CheckTriggeredElementChangeExt()
11797      -> ExecuteCustomElementAction()
11798      -> KillPlayer()
11799      -> (infinitely repeating the above sequence of function calls)
11800      which occurs when killing the player while having a CE with the setting
11801      "kill player X when explosion of <player X>"; the solution using a new
11802      field "player->killed" was chosen for backwards compatibility, although
11803      clever use of the fields "player->active" etc. would probably also work */
11804 #if 1
11805   if (player->killed)
11806     return;
11807 #endif
11808
11809   player->killed = TRUE;
11810
11811   /* remove accessible field at the player's position */
11812   Feld[jx][jy] = EL_EMPTY;
11813
11814   /* deactivate shield (else Bang()/Explode() would not work right) */
11815   player->shield_normal_time_left = 0;
11816   player->shield_deadly_time_left = 0;
11817
11818   Bang(jx, jy);
11819   BuryPlayer(player);
11820 }
11821
11822 static void KillPlayerUnlessEnemyProtected(int x, int y)
11823 {
11824   if (!PLAYER_ENEMY_PROTECTED(x, y))
11825     KillPlayer(PLAYERINFO(x, y));
11826 }
11827
11828 static void KillPlayerUnlessExplosionProtected(int x, int y)
11829 {
11830   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11831     KillPlayer(PLAYERINFO(x, y));
11832 }
11833
11834 void BuryPlayer(struct PlayerInfo *player)
11835 {
11836   int jx = player->jx, jy = player->jy;
11837
11838   if (!player->active)
11839     return;
11840
11841   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11842   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11843
11844   player->GameOver = TRUE;
11845   RemovePlayer(player);
11846 }
11847
11848 void RemovePlayer(struct PlayerInfo *player)
11849 {
11850   int jx = player->jx, jy = player->jy;
11851   int i, found = FALSE;
11852
11853   player->present = FALSE;
11854   player->active = FALSE;
11855
11856   if (!ExplodeField[jx][jy])
11857     StorePlayer[jx][jy] = 0;
11858
11859   if (player->is_moving)
11860     DrawLevelField(player->last_jx, player->last_jy);
11861
11862   for (i = 0; i < MAX_PLAYERS; i++)
11863     if (stored_player[i].active)
11864       found = TRUE;
11865
11866   if (!found)
11867     AllPlayersGone = TRUE;
11868
11869   ExitX = ZX = jx;
11870   ExitY = ZY = jy;
11871 }
11872
11873 #if USE_NEW_SNAP_DELAY
11874 static void setFieldForSnapping(int x, int y, int element, int direction)
11875 {
11876   struct ElementInfo *ei = &element_info[element];
11877   int direction_bit = MV_DIR_TO_BIT(direction);
11878   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11879   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11880                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11881
11882   Feld[x][y] = EL_ELEMENT_SNAPPING;
11883   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11884
11885   ResetGfxAnimation(x, y);
11886
11887   GfxElement[x][y] = element;
11888   GfxAction[x][y] = action;
11889   GfxDir[x][y] = direction;
11890   GfxFrame[x][y] = -1;
11891 }
11892 #endif
11893
11894 /*
11895   =============================================================================
11896   checkDiagonalPushing()
11897   -----------------------------------------------------------------------------
11898   check if diagonal input device direction results in pushing of object
11899   (by checking if the alternative direction is walkable, diggable, ...)
11900   =============================================================================
11901 */
11902
11903 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11904                                     int x, int y, int real_dx, int real_dy)
11905 {
11906   int jx, jy, dx, dy, xx, yy;
11907
11908   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11909     return TRUE;
11910
11911   /* diagonal direction: check alternative direction */
11912   jx = player->jx;
11913   jy = player->jy;
11914   dx = x - jx;
11915   dy = y - jy;
11916   xx = jx + (dx == 0 ? real_dx : 0);
11917   yy = jy + (dy == 0 ? real_dy : 0);
11918
11919   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11920 }
11921
11922 /*
11923   =============================================================================
11924   DigField()
11925   -----------------------------------------------------------------------------
11926   x, y:                 field next to player (non-diagonal) to try to dig to
11927   real_dx, real_dy:     direction as read from input device (can be diagonal)
11928   =============================================================================
11929 */
11930
11931 int DigField(struct PlayerInfo *player,
11932              int oldx, int oldy, int x, int y,
11933              int real_dx, int real_dy, int mode)
11934 {
11935   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11936   boolean player_was_pushing = player->is_pushing;
11937   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11938   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11939   int jx = oldx, jy = oldy;
11940   int dx = x - jx, dy = y - jy;
11941   int nextx = x + dx, nexty = y + dy;
11942   int move_direction = (dx == -1 ? MV_LEFT  :
11943                         dx == +1 ? MV_RIGHT :
11944                         dy == -1 ? MV_UP    :
11945                         dy == +1 ? MV_DOWN  : MV_NONE);
11946   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11947   int dig_side = MV_DIR_OPPOSITE(move_direction);
11948   int old_element = Feld[jx][jy];
11949 #if USE_FIXED_DONT_RUN_INTO
11950   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11951 #else
11952   int element;
11953 #endif
11954   int collect_count;
11955
11956   if (is_player)                /* function can also be called by EL_PENGUIN */
11957   {
11958     if (player->MovPos == 0)
11959     {
11960       player->is_digging = FALSE;
11961       player->is_collecting = FALSE;
11962     }
11963
11964     if (player->MovPos == 0)    /* last pushing move finished */
11965       player->is_pushing = FALSE;
11966
11967     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11968     {
11969       player->is_switching = FALSE;
11970       player->push_delay = -1;
11971
11972       return MP_NO_ACTION;
11973     }
11974   }
11975
11976 #if !USE_FIXED_DONT_RUN_INTO
11977   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11978     return MP_NO_ACTION;
11979 #endif
11980
11981   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11982     old_element = Back[jx][jy];
11983
11984   /* in case of element dropped at player position, check background */
11985   else if (Back[jx][jy] != EL_EMPTY &&
11986            game.engine_version >= VERSION_IDENT(2,2,0,0))
11987     old_element = Back[jx][jy];
11988
11989   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11990     return MP_NO_ACTION;        /* field has no opening in this direction */
11991
11992   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11993     return MP_NO_ACTION;        /* field has no opening in this direction */
11994
11995 #if USE_FIXED_DONT_RUN_INTO
11996   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11997   {
11998     SplashAcid(x, y);
11999
12000     Feld[jx][jy] = player->artwork_element;
12001     InitMovingField(jx, jy, MV_DOWN);
12002     Store[jx][jy] = EL_ACID;
12003     ContinueMoving(jx, jy);
12004     BuryPlayer(player);
12005
12006     return MP_DONT_RUN_INTO;
12007   }
12008 #endif
12009
12010 #if USE_FIXED_DONT_RUN_INTO
12011   if (player_can_move && DONT_RUN_INTO(element))
12012   {
12013     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12014
12015     return MP_DONT_RUN_INTO;
12016   }
12017 #endif
12018
12019 #if USE_FIXED_DONT_RUN_INTO
12020   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12021     return MP_NO_ACTION;
12022 #endif
12023
12024 #if !USE_FIXED_DONT_RUN_INTO
12025   element = Feld[x][y];
12026 #endif
12027
12028   collect_count = element_info[element].collect_count_initial;
12029
12030   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12031     return MP_NO_ACTION;
12032
12033   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12034     player_can_move = player_can_move_or_snap;
12035
12036   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12037       game.engine_version >= VERSION_IDENT(2,2,0,0))
12038   {
12039     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12040                                player->index_bit, dig_side);
12041     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12042                                         player->index_bit, dig_side);
12043
12044     if (element == EL_DC_LANDMINE)
12045       Bang(x, y);
12046
12047     if (Feld[x][y] != element)          /* field changed by snapping */
12048       return MP_ACTION;
12049
12050     return MP_NO_ACTION;
12051   }
12052
12053 #if USE_PLAYER_GRAVITY
12054   if (player->gravity && is_player && !player->is_auto_moving &&
12055       canFallDown(player) && move_direction != MV_DOWN &&
12056       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12057     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12058 #else
12059   if (game.gravity && is_player && !player->is_auto_moving &&
12060       canFallDown(player) && move_direction != MV_DOWN &&
12061       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12062     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12063 #endif
12064
12065   if (player_can_move &&
12066       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12067   {
12068     int sound_element = SND_ELEMENT(element);
12069     int sound_action = ACTION_WALKING;
12070
12071     if (IS_RND_GATE(element))
12072     {
12073       if (!player->key[RND_GATE_NR(element)])
12074         return MP_NO_ACTION;
12075     }
12076     else if (IS_RND_GATE_GRAY(element))
12077     {
12078       if (!player->key[RND_GATE_GRAY_NR(element)])
12079         return MP_NO_ACTION;
12080     }
12081     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12082     {
12083       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12084         return MP_NO_ACTION;
12085     }
12086     else if (element == EL_EXIT_OPEN ||
12087              element == EL_EM_EXIT_OPEN ||
12088              element == EL_STEEL_EXIT_OPEN ||
12089              element == EL_EM_STEEL_EXIT_OPEN ||
12090              element == EL_SP_EXIT_OPEN ||
12091              element == EL_SP_EXIT_OPENING)
12092     {
12093       sound_action = ACTION_PASSING;    /* player is passing exit */
12094     }
12095     else if (element == EL_EMPTY)
12096     {
12097       sound_action = ACTION_MOVING;             /* nothing to walk on */
12098     }
12099
12100     /* play sound from background or player, whatever is available */
12101     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12102       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12103     else
12104       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12105   }
12106   else if (player_can_move &&
12107            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12108   {
12109     if (!ACCESS_FROM(element, opposite_direction))
12110       return MP_NO_ACTION;      /* field not accessible from this direction */
12111
12112     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12113       return MP_NO_ACTION;
12114
12115     if (IS_EM_GATE(element))
12116     {
12117       if (!player->key[EM_GATE_NR(element)])
12118         return MP_NO_ACTION;
12119     }
12120     else if (IS_EM_GATE_GRAY(element))
12121     {
12122       if (!player->key[EM_GATE_GRAY_NR(element)])
12123         return MP_NO_ACTION;
12124     }
12125     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12126     {
12127       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12128         return MP_NO_ACTION;
12129     }
12130     else if (IS_EMC_GATE(element))
12131     {
12132       if (!player->key[EMC_GATE_NR(element)])
12133         return MP_NO_ACTION;
12134     }
12135     else if (IS_EMC_GATE_GRAY(element))
12136     {
12137       if (!player->key[EMC_GATE_GRAY_NR(element)])
12138         return MP_NO_ACTION;
12139     }
12140     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12141     {
12142       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12143         return MP_NO_ACTION;
12144     }
12145     else if (element == EL_DC_GATE_WHITE ||
12146              element == EL_DC_GATE_WHITE_GRAY ||
12147              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12148     {
12149       if (player->num_white_keys == 0)
12150         return MP_NO_ACTION;
12151
12152       player->num_white_keys--;
12153     }
12154     else if (IS_SP_PORT(element))
12155     {
12156       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12157           element == EL_SP_GRAVITY_PORT_RIGHT ||
12158           element == EL_SP_GRAVITY_PORT_UP ||
12159           element == EL_SP_GRAVITY_PORT_DOWN)
12160 #if USE_PLAYER_GRAVITY
12161         player->gravity = !player->gravity;
12162 #else
12163         game.gravity = !game.gravity;
12164 #endif
12165       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12166                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12167                element == EL_SP_GRAVITY_ON_PORT_UP ||
12168                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12169 #if USE_PLAYER_GRAVITY
12170         player->gravity = TRUE;
12171 #else
12172         game.gravity = TRUE;
12173 #endif
12174       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12175                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12176                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12177                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12178 #if USE_PLAYER_GRAVITY
12179         player->gravity = FALSE;
12180 #else
12181         game.gravity = FALSE;
12182 #endif
12183     }
12184
12185     /* automatically move to the next field with double speed */
12186     player->programmed_action = move_direction;
12187
12188     if (player->move_delay_reset_counter == 0)
12189     {
12190       player->move_delay_reset_counter = 2;     /* two double speed steps */
12191
12192       DOUBLE_PLAYER_SPEED(player);
12193     }
12194
12195     PlayLevelSoundAction(x, y, ACTION_PASSING);
12196   }
12197   else if (player_can_move_or_snap && IS_DIGGABLE(element))
12198   {
12199     RemoveField(x, y);
12200
12201     if (mode != DF_SNAP)
12202     {
12203       GfxElement[x][y] = GFX_ELEMENT(element);
12204       player->is_digging = TRUE;
12205     }
12206
12207     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12208
12209     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12210                                         player->index_bit, dig_side);
12211
12212     if (mode == DF_SNAP)
12213     {
12214 #if USE_NEW_SNAP_DELAY
12215       if (level.block_snap_field)
12216         setFieldForSnapping(x, y, element, move_direction);
12217       else
12218         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12219 #else
12220       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12221 #endif
12222
12223       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12224                                           player->index_bit, dig_side);
12225     }
12226   }
12227   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12228   {
12229     RemoveField(x, y);
12230
12231     if (is_player && mode != DF_SNAP)
12232     {
12233       GfxElement[x][y] = element;
12234       player->is_collecting = TRUE;
12235     }
12236
12237     if (element == EL_SPEED_PILL)
12238     {
12239       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12240     }
12241     else if (element == EL_EXTRA_TIME && level.time > 0)
12242     {
12243       TimeLeft += level.extra_time;
12244       DrawGameValue_Time(TimeLeft);
12245     }
12246     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12247     {
12248       player->shield_normal_time_left += level.shield_normal_time;
12249       if (element == EL_SHIELD_DEADLY)
12250         player->shield_deadly_time_left += level.shield_deadly_time;
12251     }
12252     else if (element == EL_DYNAMITE ||
12253              element == EL_EM_DYNAMITE ||
12254              element == EL_SP_DISK_RED)
12255     {
12256       if (player->inventory_size < MAX_INVENTORY_SIZE)
12257         player->inventory_element[player->inventory_size++] = element;
12258
12259       DrawGameDoorValues();
12260     }
12261     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12262     {
12263       player->dynabomb_count++;
12264       player->dynabombs_left++;
12265     }
12266     else if (element == EL_DYNABOMB_INCREASE_SIZE)
12267     {
12268       player->dynabomb_size++;
12269     }
12270     else if (element == EL_DYNABOMB_INCREASE_POWER)
12271     {
12272       player->dynabomb_xl = TRUE;
12273     }
12274     else if (IS_KEY(element))
12275     {
12276       player->key[KEY_NR(element)] = TRUE;
12277
12278       DrawGameDoorValues();
12279     }
12280     else if (element == EL_DC_KEY_WHITE)
12281     {
12282       player->num_white_keys++;
12283
12284       /* display white keys? */
12285       /* DrawGameDoorValues(); */
12286     }
12287     else if (IS_ENVELOPE(element))
12288     {
12289       player->show_envelope = element;
12290     }
12291     else if (element == EL_EMC_LENSES)
12292     {
12293       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12294
12295       RedrawAllInvisibleElementsForLenses();
12296     }
12297     else if (element == EL_EMC_MAGNIFIER)
12298     {
12299       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12300
12301       RedrawAllInvisibleElementsForMagnifier();
12302     }
12303     else if (IS_DROPPABLE(element) ||
12304              IS_THROWABLE(element))     /* can be collected and dropped */
12305     {
12306       int i;
12307
12308       if (collect_count == 0)
12309         player->inventory_infinite_element = element;
12310       else
12311         for (i = 0; i < collect_count; i++)
12312           if (player->inventory_size < MAX_INVENTORY_SIZE)
12313             player->inventory_element[player->inventory_size++] = element;
12314
12315       DrawGameDoorValues();
12316     }
12317     else if (collect_count > 0)
12318     {
12319       local_player->gems_still_needed -= collect_count;
12320       if (local_player->gems_still_needed < 0)
12321         local_player->gems_still_needed = 0;
12322
12323       DrawGameValue_Emeralds(local_player->gems_still_needed);
12324     }
12325
12326     RaiseScoreElement(element);
12327     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12328
12329     if (is_player)
12330       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12331                                           player->index_bit, dig_side);
12332
12333     if (mode == DF_SNAP)
12334     {
12335 #if USE_NEW_SNAP_DELAY
12336       if (level.block_snap_field)
12337         setFieldForSnapping(x, y, element, move_direction);
12338       else
12339         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12340 #else
12341       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12342 #endif
12343
12344       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12345                                           player->index_bit, dig_side);
12346     }
12347   }
12348   else if (player_can_move_or_snap && IS_PUSHABLE(element))
12349   {
12350     if (mode == DF_SNAP && element != EL_BD_ROCK)
12351       return MP_NO_ACTION;
12352
12353     if (CAN_FALL(element) && dy)
12354       return MP_NO_ACTION;
12355
12356     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12357         !(element == EL_SPRING && level.use_spring_bug))
12358       return MP_NO_ACTION;
12359
12360     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12361         ((move_direction & MV_VERTICAL &&
12362           ((element_info[element].move_pattern & MV_LEFT &&
12363             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12364            (element_info[element].move_pattern & MV_RIGHT &&
12365             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12366          (move_direction & MV_HORIZONTAL &&
12367           ((element_info[element].move_pattern & MV_UP &&
12368             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12369            (element_info[element].move_pattern & MV_DOWN &&
12370             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12371       return MP_NO_ACTION;
12372
12373     /* do not push elements already moving away faster than player */
12374     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12375         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12376       return MP_NO_ACTION;
12377
12378     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12379     {
12380       if (player->push_delay_value == -1 || !player_was_pushing)
12381         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12382     }
12383     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12384     {
12385       if (player->push_delay_value == -1)
12386         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12387     }
12388     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12389     {
12390       if (!player->is_pushing)
12391         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12392     }
12393
12394     player->is_pushing = TRUE;
12395     player->is_active = TRUE;
12396
12397     if (!(IN_LEV_FIELD(nextx, nexty) &&
12398           (IS_FREE(nextx, nexty) ||
12399            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12400             IS_SB_ELEMENT(element)))))
12401       return MP_NO_ACTION;
12402
12403     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12404       return MP_NO_ACTION;
12405
12406     if (player->push_delay == -1)       /* new pushing; restart delay */
12407       player->push_delay = 0;
12408
12409     if (player->push_delay < player->push_delay_value &&
12410         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12411         element != EL_SPRING && element != EL_BALLOON)
12412     {
12413       /* make sure that there is no move delay before next try to push */
12414       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12415         player->move_delay = 0;
12416
12417       return MP_NO_ACTION;
12418     }
12419
12420     if (IS_SB_ELEMENT(element))
12421     {
12422       if (element == EL_SOKOBAN_FIELD_FULL)
12423       {
12424         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12425         local_player->sokobanfields_still_needed++;
12426       }
12427
12428       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12429       {
12430         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12431         local_player->sokobanfields_still_needed--;
12432       }
12433
12434       Feld[x][y] = EL_SOKOBAN_OBJECT;
12435
12436       if (Back[x][y] == Back[nextx][nexty])
12437         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12438       else if (Back[x][y] != 0)
12439         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12440                                     ACTION_EMPTYING);
12441       else
12442         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12443                                     ACTION_FILLING);
12444
12445       if (local_player->sokobanfields_still_needed == 0 &&
12446           game.emulation == EMU_SOKOBAN)
12447       {
12448         PlayerWins(player);
12449
12450         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12451       }
12452     }
12453     else
12454       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12455
12456     InitMovingField(x, y, move_direction);
12457     GfxAction[x][y] = ACTION_PUSHING;
12458
12459     if (mode == DF_SNAP)
12460       ContinueMoving(x, y);
12461     else
12462       MovPos[x][y] = (dx != 0 ? dx : dy);
12463
12464     Pushed[x][y] = TRUE;
12465     Pushed[nextx][nexty] = TRUE;
12466
12467     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12468       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12469     else
12470       player->push_delay_value = -1;    /* get new value later */
12471
12472     /* check for element change _after_ element has been pushed */
12473     if (game.use_change_when_pushing_bug)
12474     {
12475       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12476                                  player->index_bit, dig_side);
12477       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12478                                           player->index_bit, dig_side);
12479     }
12480   }
12481   else if (IS_SWITCHABLE(element))
12482   {
12483     if (PLAYER_SWITCHING(player, x, y))
12484     {
12485       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12486                                           player->index_bit, dig_side);
12487
12488       return MP_ACTION;
12489     }
12490
12491     player->is_switching = TRUE;
12492     player->switch_x = x;
12493     player->switch_y = y;
12494
12495     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12496
12497     if (element == EL_ROBOT_WHEEL)
12498     {
12499       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12500       ZX = x;
12501       ZY = y;
12502
12503       DrawLevelField(x, y);
12504     }
12505     else if (element == EL_SP_TERMINAL)
12506     {
12507       int xx, yy;
12508
12509       SCAN_PLAYFIELD(xx, yy)
12510       {
12511         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12512           Bang(xx, yy);
12513         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12514           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12515       }
12516     }
12517     else if (IS_BELT_SWITCH(element))
12518     {
12519       ToggleBeltSwitch(x, y);
12520     }
12521     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12522              element == EL_SWITCHGATE_SWITCH_DOWN ||
12523              element == EL_DC_SWITCHGATE_SWITCH_UP ||
12524              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12525     {
12526       ToggleSwitchgateSwitch(x, y);
12527     }
12528     else if (element == EL_LIGHT_SWITCH ||
12529              element == EL_LIGHT_SWITCH_ACTIVE)
12530     {
12531       ToggleLightSwitch(x, y);
12532     }
12533     else if (element == EL_TIMEGATE_SWITCH ||
12534              element == EL_DC_TIMEGATE_SWITCH)
12535     {
12536       ActivateTimegateSwitch(x, y);
12537     }
12538     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12539              element == EL_BALLOON_SWITCH_RIGHT ||
12540              element == EL_BALLOON_SWITCH_UP    ||
12541              element == EL_BALLOON_SWITCH_DOWN  ||
12542              element == EL_BALLOON_SWITCH_NONE  ||
12543              element == EL_BALLOON_SWITCH_ANY)
12544     {
12545       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12546                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12547                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12548                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12549                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12550                              move_direction);
12551     }
12552     else if (element == EL_LAMP)
12553     {
12554       Feld[x][y] = EL_LAMP_ACTIVE;
12555       local_player->lights_still_needed--;
12556
12557       ResetGfxAnimation(x, y);
12558       DrawLevelField(x, y);
12559     }
12560     else if (element == EL_TIME_ORB_FULL)
12561     {
12562       Feld[x][y] = EL_TIME_ORB_EMPTY;
12563
12564       if (level.time > 0 || level.use_time_orb_bug)
12565       {
12566         TimeLeft += level.time_orb_time;
12567         DrawGameValue_Time(TimeLeft);
12568       }
12569
12570       ResetGfxAnimation(x, y);
12571       DrawLevelField(x, y);
12572     }
12573     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12574              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12575     {
12576       int xx, yy;
12577
12578       game.ball_state = !game.ball_state;
12579
12580       SCAN_PLAYFIELD(xx, yy)
12581       {
12582         int e = Feld[xx][yy];
12583
12584         if (game.ball_state)
12585         {
12586           if (e == EL_EMC_MAGIC_BALL)
12587             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12588           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12589             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12590         }
12591         else
12592         {
12593           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12594             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12595           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12596             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12597         }
12598       }
12599     }
12600
12601     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12602                                         player->index_bit, dig_side);
12603
12604     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12605                                         player->index_bit, dig_side);
12606
12607     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12608                                         player->index_bit, dig_side);
12609
12610     return MP_ACTION;
12611   }
12612   else
12613   {
12614     if (!PLAYER_SWITCHING(player, x, y))
12615     {
12616       player->is_switching = TRUE;
12617       player->switch_x = x;
12618       player->switch_y = y;
12619
12620       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12621                                  player->index_bit, dig_side);
12622       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12623                                           player->index_bit, dig_side);
12624
12625       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12626                                  player->index_bit, dig_side);
12627       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12628                                           player->index_bit, dig_side);
12629     }
12630
12631     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12632                                player->index_bit, dig_side);
12633     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12634                                         player->index_bit, dig_side);
12635
12636     return MP_NO_ACTION;
12637   }
12638
12639   player->push_delay = -1;
12640
12641   if (is_player)                /* function can also be called by EL_PENGUIN */
12642   {
12643     if (Feld[x][y] != element)          /* really digged/collected something */
12644     {
12645       player->is_collecting = !player->is_digging;
12646       player->is_active = TRUE;
12647     }
12648   }
12649
12650   return MP_MOVING;
12651 }
12652
12653 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12654 {
12655   int jx = player->jx, jy = player->jy;
12656   int x = jx + dx, y = jy + dy;
12657   int snap_direction = (dx == -1 ? MV_LEFT  :
12658                         dx == +1 ? MV_RIGHT :
12659                         dy == -1 ? MV_UP    :
12660                         dy == +1 ? MV_DOWN  : MV_NONE);
12661   boolean can_continue_snapping = (level.continuous_snapping &&
12662                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12663
12664   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12665     return FALSE;
12666
12667   if (!player->active || !IN_LEV_FIELD(x, y))
12668     return FALSE;
12669
12670   if (dx && dy)
12671     return FALSE;
12672
12673   if (!dx && !dy)
12674   {
12675     if (player->MovPos == 0)
12676       player->is_pushing = FALSE;
12677
12678     player->is_snapping = FALSE;
12679
12680     if (player->MovPos == 0)
12681     {
12682       player->is_moving = FALSE;
12683       player->is_digging = FALSE;
12684       player->is_collecting = FALSE;
12685     }
12686
12687     return FALSE;
12688   }
12689
12690 #if USE_NEW_CONTINUOUS_SNAPPING
12691   /* prevent snapping with already pressed snap key when not allowed */
12692   if (player->is_snapping && !can_continue_snapping)
12693     return FALSE;
12694 #else
12695   if (player->is_snapping)
12696     return FALSE;
12697 #endif
12698
12699   player->MovDir = snap_direction;
12700
12701   if (player->MovPos == 0)
12702   {
12703     player->is_moving = FALSE;
12704     player->is_digging = FALSE;
12705     player->is_collecting = FALSE;
12706   }
12707
12708   player->is_dropping = FALSE;
12709   player->is_dropping_pressed = FALSE;
12710   player->drop_pressed_delay = 0;
12711
12712   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12713     return FALSE;
12714
12715   player->is_snapping = TRUE;
12716   player->is_active = TRUE;
12717
12718   if (player->MovPos == 0)
12719   {
12720     player->is_moving = FALSE;
12721     player->is_digging = FALSE;
12722     player->is_collecting = FALSE;
12723   }
12724
12725   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12726     DrawLevelField(player->last_jx, player->last_jy);
12727
12728   DrawLevelField(x, y);
12729
12730   return TRUE;
12731 }
12732
12733 boolean DropElement(struct PlayerInfo *player)
12734 {
12735   int old_element, new_element;
12736   int dropx = player->jx, dropy = player->jy;
12737   int drop_direction = player->MovDir;
12738   int drop_side = drop_direction;
12739   int drop_element = (player->inventory_size > 0 ?
12740                       player->inventory_element[player->inventory_size - 1] :
12741                       player->inventory_infinite_element != EL_UNDEFINED ?
12742                       player->inventory_infinite_element :
12743                       player->dynabombs_left > 0 ?
12744                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12745                       EL_UNDEFINED);
12746
12747   player->is_dropping_pressed = TRUE;
12748
12749   /* do not drop an element on top of another element; when holding drop key
12750      pressed without moving, dropped element must move away before the next
12751      element can be dropped (this is especially important if the next element
12752      is dynamite, which can be placed on background for historical reasons) */
12753   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12754     return MP_ACTION;
12755
12756   if (IS_THROWABLE(drop_element))
12757   {
12758     dropx += GET_DX_FROM_DIR(drop_direction);
12759     dropy += GET_DY_FROM_DIR(drop_direction);
12760
12761     if (!IN_LEV_FIELD(dropx, dropy))
12762       return FALSE;
12763   }
12764
12765   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12766   new_element = drop_element;           /* default: no change when dropping */
12767
12768   /* check if player is active, not moving and ready to drop */
12769   if (!player->active || player->MovPos || player->drop_delay > 0)
12770     return FALSE;
12771
12772   /* check if player has anything that can be dropped */
12773   if (new_element == EL_UNDEFINED)
12774     return FALSE;
12775
12776   /* check if drop key was pressed long enough for EM style dynamite */
12777   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12778     return FALSE;
12779
12780   /* check if anything can be dropped at the current position */
12781   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12782     return FALSE;
12783
12784   /* collected custom elements can only be dropped on empty fields */
12785   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12786     return FALSE;
12787
12788   if (old_element != EL_EMPTY)
12789     Back[dropx][dropy] = old_element;   /* store old element on this field */
12790
12791   ResetGfxAnimation(dropx, dropy);
12792   ResetRandomAnimationValue(dropx, dropy);
12793
12794   if (player->inventory_size > 0 ||
12795       player->inventory_infinite_element != EL_UNDEFINED)
12796   {
12797     if (player->inventory_size > 0)
12798     {
12799       player->inventory_size--;
12800
12801       DrawGameDoorValues();
12802
12803       if (new_element == EL_DYNAMITE)
12804         new_element = EL_DYNAMITE_ACTIVE;
12805       else if (new_element == EL_EM_DYNAMITE)
12806         new_element = EL_EM_DYNAMITE_ACTIVE;
12807       else if (new_element == EL_SP_DISK_RED)
12808         new_element = EL_SP_DISK_RED_ACTIVE;
12809     }
12810
12811     Feld[dropx][dropy] = new_element;
12812
12813     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12814       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12815                           el2img(Feld[dropx][dropy]), 0);
12816
12817     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12818
12819     /* needed if previous element just changed to "empty" in the last frame */
12820     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12821
12822     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12823                                player->index_bit, drop_side);
12824     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12825                                         CE_PLAYER_DROPS_X,
12826                                         player->index_bit, drop_side);
12827
12828     TestIfElementTouchesCustomElement(dropx, dropy);
12829   }
12830   else          /* player is dropping a dyna bomb */
12831   {
12832     player->dynabombs_left--;
12833
12834     Feld[dropx][dropy] = new_element;
12835
12836     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12837       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12838                           el2img(Feld[dropx][dropy]), 0);
12839
12840     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12841   }
12842
12843   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12844     InitField_WithBug1(dropx, dropy, FALSE);
12845
12846   new_element = Feld[dropx][dropy];     /* element might have changed */
12847
12848   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12849       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12850   {
12851     int move_direction, nextx, nexty;
12852
12853     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12854       MovDir[dropx][dropy] = drop_direction;
12855
12856     move_direction = MovDir[dropx][dropy];
12857     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12858     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12859
12860     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12861
12862 #if USE_FIX_IMPACT_COLLISION
12863     /* do not cause impact style collision by dropping elements that can fall */
12864     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12865 #else
12866     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12867 #endif
12868   }
12869
12870   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12871   player->is_dropping = TRUE;
12872
12873   player->drop_pressed_delay = 0;
12874   player->is_dropping_pressed = FALSE;
12875
12876   player->drop_x = dropx;
12877   player->drop_y = dropy;
12878
12879   return TRUE;
12880 }
12881
12882 /* ------------------------------------------------------------------------- */
12883 /* game sound playing functions                                              */
12884 /* ------------------------------------------------------------------------- */
12885
12886 static int *loop_sound_frame = NULL;
12887 static int *loop_sound_volume = NULL;
12888
12889 void InitPlayLevelSound()
12890 {
12891   int num_sounds = getSoundListSize();
12892
12893   checked_free(loop_sound_frame);
12894   checked_free(loop_sound_volume);
12895
12896   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12897   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12898 }
12899
12900 static void PlayLevelSound(int x, int y, int nr)
12901 {
12902   int sx = SCREENX(x), sy = SCREENY(y);
12903   int volume, stereo_position;
12904   int max_distance = 8;
12905   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12906
12907   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12908       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12909     return;
12910
12911   if (!IN_LEV_FIELD(x, y) ||
12912       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12913       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12914     return;
12915
12916   volume = SOUND_MAX_VOLUME;
12917
12918   if (!IN_SCR_FIELD(sx, sy))
12919   {
12920     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12921     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12922
12923     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12924   }
12925
12926   stereo_position = (SOUND_MAX_LEFT +
12927                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12928                      (SCR_FIELDX + 2 * max_distance));
12929
12930   if (IS_LOOP_SOUND(nr))
12931   {
12932     /* This assures that quieter loop sounds do not overwrite louder ones,
12933        while restarting sound volume comparison with each new game frame. */
12934
12935     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12936       return;
12937
12938     loop_sound_volume[nr] = volume;
12939     loop_sound_frame[nr] = FrameCounter;
12940   }
12941
12942   PlaySoundExt(nr, volume, stereo_position, type);
12943 }
12944
12945 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12946 {
12947   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12948                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12949                  y < LEVELY(BY1) ? LEVELY(BY1) :
12950                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12951                  sound_action);
12952 }
12953
12954 static void PlayLevelSoundAction(int x, int y, int action)
12955 {
12956   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12957 }
12958
12959 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12960 {
12961   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12962
12963   if (sound_effect != SND_UNDEFINED)
12964     PlayLevelSound(x, y, sound_effect);
12965 }
12966
12967 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12968                                               int action)
12969 {
12970   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12971
12972   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12973     PlayLevelSound(x, y, sound_effect);
12974 }
12975
12976 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12977 {
12978   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12979
12980   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12981     PlayLevelSound(x, y, sound_effect);
12982 }
12983
12984 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12985 {
12986   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12987
12988   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12989     StopSound(sound_effect);
12990 }
12991
12992 static void PlayLevelMusic()
12993 {
12994   if (levelset.music[level_nr] != MUS_UNDEFINED)
12995     PlayMusic(levelset.music[level_nr]);        /* from config file */
12996   else
12997     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
12998 }
12999
13000 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13001 {
13002   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13003   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13004   int x = xx - 1 - offset;
13005   int y = yy - 1 - offset;
13006
13007   switch (sample)
13008   {
13009     case SAMPLE_blank:
13010       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13011       break;
13012
13013     case SAMPLE_roll:
13014       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13015       break;
13016
13017     case SAMPLE_stone:
13018       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13019       break;
13020
13021     case SAMPLE_nut:
13022       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13023       break;
13024
13025     case SAMPLE_crack:
13026       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13027       break;
13028
13029     case SAMPLE_bug:
13030       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13031       break;
13032
13033     case SAMPLE_tank:
13034       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13035       break;
13036
13037     case SAMPLE_android_clone:
13038       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13039       break;
13040
13041     case SAMPLE_android_move:
13042       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13043       break;
13044
13045     case SAMPLE_spring:
13046       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13047       break;
13048
13049     case SAMPLE_slurp:
13050       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13051       break;
13052
13053     case SAMPLE_eater:
13054       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13055       break;
13056
13057     case SAMPLE_eater_eat:
13058       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13059       break;
13060
13061     case SAMPLE_alien:
13062       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13063       break;
13064
13065     case SAMPLE_collect:
13066       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13067       break;
13068
13069     case SAMPLE_diamond:
13070       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13071       break;
13072
13073     case SAMPLE_squash:
13074       /* !!! CHECK THIS !!! */
13075 #if 1
13076       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13077 #else
13078       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13079 #endif
13080       break;
13081
13082     case SAMPLE_wonderfall:
13083       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13084       break;
13085
13086     case SAMPLE_drip:
13087       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13088       break;
13089
13090     case SAMPLE_push:
13091       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13092       break;
13093
13094     case SAMPLE_dirt:
13095       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13096       break;
13097
13098     case SAMPLE_acid:
13099       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13100       break;
13101
13102     case SAMPLE_ball:
13103       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13104       break;
13105
13106     case SAMPLE_grow:
13107       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13108       break;
13109
13110     case SAMPLE_wonder:
13111       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13112       break;
13113
13114     case SAMPLE_door:
13115       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13116       break;
13117
13118     case SAMPLE_exit_open:
13119       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13120       break;
13121
13122     case SAMPLE_exit_leave:
13123       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13124       break;
13125
13126     case SAMPLE_dynamite:
13127       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13128       break;
13129
13130     case SAMPLE_tick:
13131       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13132       break;
13133
13134     case SAMPLE_press:
13135       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13136       break;
13137
13138     case SAMPLE_wheel:
13139       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13140       break;
13141
13142     case SAMPLE_boom:
13143       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13144       break;
13145
13146     case SAMPLE_die:
13147       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13148       break;
13149
13150     case SAMPLE_time:
13151       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13152       break;
13153
13154     default:
13155       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13156       break;
13157   }
13158 }
13159
13160 #if 0
13161 void ChangeTime(int value)
13162 {
13163   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13164
13165   *time += value;
13166
13167   /* EMC game engine uses value from time counter of RND game engine */
13168   level.native_em_level->lev->time = *time;
13169
13170   DrawGameValue_Time(*time);
13171 }
13172
13173 void RaiseScore(int value)
13174 {
13175   /* EMC game engine and RND game engine have separate score counters */
13176   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13177                 &level.native_em_level->lev->score : &local_player->score);
13178
13179   *score += value;
13180
13181   DrawGameValue_Score(*score);
13182 }
13183 #endif
13184
13185 void RaiseScore(int value)
13186 {
13187   local_player->score += value;
13188
13189   DrawGameValue_Score(local_player->score);
13190 }
13191
13192 void RaiseScoreElement(int element)
13193 {
13194   switch (element)
13195   {
13196     case EL_EMERALD:
13197     case EL_BD_DIAMOND:
13198     case EL_EMERALD_YELLOW:
13199     case EL_EMERALD_RED:
13200     case EL_EMERALD_PURPLE:
13201     case EL_SP_INFOTRON:
13202       RaiseScore(level.score[SC_EMERALD]);
13203       break;
13204     case EL_DIAMOND:
13205       RaiseScore(level.score[SC_DIAMOND]);
13206       break;
13207     case EL_CRYSTAL:
13208       RaiseScore(level.score[SC_CRYSTAL]);
13209       break;
13210     case EL_PEARL:
13211       RaiseScore(level.score[SC_PEARL]);
13212       break;
13213     case EL_BUG:
13214     case EL_BD_BUTTERFLY:
13215     case EL_SP_ELECTRON:
13216       RaiseScore(level.score[SC_BUG]);
13217       break;
13218     case EL_SPACESHIP:
13219     case EL_BD_FIREFLY:
13220     case EL_SP_SNIKSNAK:
13221       RaiseScore(level.score[SC_SPACESHIP]);
13222       break;
13223     case EL_YAMYAM:
13224     case EL_DARK_YAMYAM:
13225       RaiseScore(level.score[SC_YAMYAM]);
13226       break;
13227     case EL_ROBOT:
13228       RaiseScore(level.score[SC_ROBOT]);
13229       break;
13230     case EL_PACMAN:
13231       RaiseScore(level.score[SC_PACMAN]);
13232       break;
13233     case EL_NUT:
13234       RaiseScore(level.score[SC_NUT]);
13235       break;
13236     case EL_DYNAMITE:
13237     case EL_EM_DYNAMITE:
13238     case EL_SP_DISK_RED:
13239     case EL_DYNABOMB_INCREASE_NUMBER:
13240     case EL_DYNABOMB_INCREASE_SIZE:
13241     case EL_DYNABOMB_INCREASE_POWER:
13242       RaiseScore(level.score[SC_DYNAMITE]);
13243       break;
13244     case EL_SHIELD_NORMAL:
13245     case EL_SHIELD_DEADLY:
13246       RaiseScore(level.score[SC_SHIELD]);
13247       break;
13248     case EL_EXTRA_TIME:
13249       RaiseScore(level.extra_time_score);
13250       break;
13251     case EL_KEY_1:
13252     case EL_KEY_2:
13253     case EL_KEY_3:
13254     case EL_KEY_4:
13255     case EL_EM_KEY_1:
13256     case EL_EM_KEY_2:
13257     case EL_EM_KEY_3:
13258     case EL_EM_KEY_4:
13259     case EL_EMC_KEY_5:
13260     case EL_EMC_KEY_6:
13261     case EL_EMC_KEY_7:
13262     case EL_EMC_KEY_8:
13263     case EL_DC_KEY_WHITE:
13264       RaiseScore(level.score[SC_KEY]);
13265       break;
13266     default:
13267       RaiseScore(element_info[element].collect_score);
13268       break;
13269   }
13270 }
13271
13272 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13273 {
13274   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13275   {
13276 #if defined(NETWORK_AVALIABLE)
13277     if (options.network)
13278       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13279     else
13280 #endif
13281     {
13282       if (quick_quit)
13283       {
13284         game_status = GAME_MODE_MAIN;
13285
13286         DrawMainMenu();
13287       }
13288       else
13289       {
13290         FadeOut(REDRAW_FIELD);
13291
13292         game_status = GAME_MODE_MAIN;
13293
13294         DrawAndFadeInMainMenu(REDRAW_FIELD);
13295       }
13296     }
13297   }
13298   else          /* continue playing the game */
13299   {
13300     if (tape.playing && tape.deactivate_display)
13301       TapeDeactivateDisplayOff(TRUE);
13302
13303     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13304
13305     if (tape.playing && tape.deactivate_display)
13306       TapeDeactivateDisplayOn();
13307   }
13308 }
13309
13310 void RequestQuitGame(boolean ask_if_really_quit)
13311 {
13312   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13313   boolean skip_request = AllPlayersGone || quick_quit;
13314
13315   RequestQuitGameExt(skip_request, quick_quit,
13316                      "Do you really want to quit the game ?");
13317 }
13318
13319
13320 /* ------------------------------------------------------------------------- */
13321 /* random generator functions                                                */
13322 /* ------------------------------------------------------------------------- */
13323
13324 unsigned int InitEngineRandom_RND(long seed)
13325 {
13326   game.num_random_calls = 0;
13327
13328 #if 0
13329   unsigned int rnd_seed = InitEngineRandom(seed);
13330
13331   printf("::: START RND: %d\n", rnd_seed);
13332
13333   return rnd_seed;
13334 #else
13335
13336   return InitEngineRandom(seed);
13337
13338 #endif
13339
13340 }
13341
13342 unsigned int RND(int max)
13343 {
13344   if (max > 0)
13345   {
13346     game.num_random_calls++;
13347
13348     return GetEngineRandom(max);
13349   }
13350
13351   return 0;
13352 }
13353
13354
13355 /* ------------------------------------------------------------------------- */
13356 /* game engine snapshot handling functions                                   */
13357 /* ------------------------------------------------------------------------- */
13358
13359 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
13360
13361 struct EngineSnapshotInfo
13362 {
13363   /* runtime values for custom element collect score */
13364   int collect_score[NUM_CUSTOM_ELEMENTS];
13365
13366   /* runtime values for group element choice position */
13367   int choice_pos[NUM_GROUP_ELEMENTS];
13368
13369   /* runtime values for belt position animations */
13370   int belt_graphic[4 * NUM_BELT_PARTS];
13371   int belt_anim_mode[4 * NUM_BELT_PARTS];
13372 };
13373
13374 struct EngineSnapshotNodeInfo
13375 {
13376   void *buffer_orig;
13377   void *buffer_copy;
13378   int size;
13379 };
13380
13381 static struct EngineSnapshotInfo engine_snapshot_rnd;
13382 static ListNode *engine_snapshot_list = NULL;
13383 static char *snapshot_level_identifier = NULL;
13384 static int snapshot_level_nr = -1;
13385
13386 void FreeEngineSnapshot()
13387 {
13388   while (engine_snapshot_list != NULL)
13389     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13390                        checked_free);
13391
13392   setString(&snapshot_level_identifier, NULL);
13393   snapshot_level_nr = -1;
13394 }
13395
13396 static void SaveEngineSnapshotValues_RND()
13397 {
13398   static int belt_base_active_element[4] =
13399   {
13400     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13401     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13402     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13403     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13404   };
13405   int i, j;
13406
13407   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13408   {
13409     int element = EL_CUSTOM_START + i;
13410
13411     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13412   }
13413
13414   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13415   {
13416     int element = EL_GROUP_START + i;
13417
13418     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13419   }
13420
13421   for (i = 0; i < 4; i++)
13422   {
13423     for (j = 0; j < NUM_BELT_PARTS; j++)
13424     {
13425       int element = belt_base_active_element[i] + j;
13426       int graphic = el2img(element);
13427       int anim_mode = graphic_info[graphic].anim_mode;
13428
13429       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13430       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13431     }
13432   }
13433 }
13434
13435 static void LoadEngineSnapshotValues_RND()
13436 {
13437   unsigned long num_random_calls = game.num_random_calls;
13438   int i, j;
13439
13440   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13441   {
13442     int element = EL_CUSTOM_START + i;
13443
13444     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13445   }
13446
13447   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13448   {
13449     int element = EL_GROUP_START + i;
13450
13451     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13452   }
13453
13454   for (i = 0; i < 4; i++)
13455   {
13456     for (j = 0; j < NUM_BELT_PARTS; j++)
13457     {
13458       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13459       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13460
13461       graphic_info[graphic].anim_mode = anim_mode;
13462     }
13463   }
13464
13465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13466   {
13467     InitRND(tape.random_seed);
13468     for (i = 0; i < num_random_calls; i++)
13469       RND(1);
13470   }
13471
13472   if (game.num_random_calls != num_random_calls)
13473   {
13474     Error(ERR_RETURN, "number of random calls out of sync");
13475     Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
13476     Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
13477     Error(ERR_EXIT, "this should not happen -- please debug");
13478   }
13479 }
13480
13481 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13482 {
13483   struct EngineSnapshotNodeInfo *bi =
13484     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13485
13486   bi->buffer_orig = buffer;
13487   bi->buffer_copy = checked_malloc(size);
13488   bi->size = size;
13489
13490   memcpy(bi->buffer_copy, buffer, size);
13491
13492   addNodeToList(&engine_snapshot_list, NULL, bi);
13493 }
13494
13495 void SaveEngineSnapshot()
13496 {
13497   FreeEngineSnapshot();         /* free previous snapshot, if needed */
13498
13499   if (level_editor_test_game)   /* do not save snapshots from editor */
13500     return;
13501
13502   /* copy some special values to a structure better suited for the snapshot */
13503
13504   SaveEngineSnapshotValues_RND();
13505   SaveEngineSnapshotValues_EM();
13506
13507   /* save values stored in special snapshot structure */
13508
13509   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13510   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13511
13512   /* save further RND engine values */
13513
13514   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13515   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13516   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13517
13518   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13519   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13520   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13521   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13522
13523   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13524   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13525   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13526   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13527   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13528
13529   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13530   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13531   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13532
13533   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13534
13535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13536
13537   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13538   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13539
13540   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13541   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13542   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13543   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13545   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13547   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13548   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13549   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13551   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13554   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13555   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13558
13559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13560   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13561
13562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13565
13566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13567   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13568
13569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13570   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13572   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13574
13575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13577
13578   /* save level identification information */
13579
13580   setString(&snapshot_level_identifier, leveldir_current->identifier);
13581   snapshot_level_nr = level_nr;
13582
13583 #if 0
13584   ListNode *node = engine_snapshot_list;
13585   int num_bytes = 0;
13586
13587   while (node != NULL)
13588   {
13589     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13590
13591     node = node->next;
13592   }
13593
13594   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13595 #endif
13596 }
13597
13598 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13599 {
13600   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13601 }
13602
13603 void LoadEngineSnapshot()
13604 {
13605   ListNode *node = engine_snapshot_list;
13606
13607   if (engine_snapshot_list == NULL)
13608     return;
13609
13610   while (node != NULL)
13611   {
13612     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13613
13614     node = node->next;
13615   }
13616
13617   /* restore special values from snapshot structure */
13618
13619   LoadEngineSnapshotValues_RND();
13620   LoadEngineSnapshotValues_EM();
13621 }
13622
13623 boolean CheckEngineSnapshot()
13624 {
13625   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13626           snapshot_level_nr == level_nr);
13627 }
13628
13629
13630 /* ---------- new game button stuff ---------------------------------------- */
13631
13632 /* graphic position values for game buttons */
13633 #define GAME_BUTTON_XSIZE       30
13634 #define GAME_BUTTON_YSIZE       30
13635 #define GAME_BUTTON_XPOS        5
13636 #define GAME_BUTTON_YPOS        215
13637 #define SOUND_BUTTON_XPOS       5
13638 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13639
13640 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13641 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13642 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13643 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13644 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13645 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13646
13647 static struct
13648 {
13649   int x, y;
13650   int gadget_id;
13651   char *infotext;
13652 } gamebutton_info[NUM_GAME_BUTTONS] =
13653 {
13654   {
13655     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13656     GAME_CTRL_ID_STOP,
13657     "stop game"
13658   },
13659   {
13660     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13661     GAME_CTRL_ID_PAUSE,
13662     "pause game"
13663   },
13664   {
13665     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13666     GAME_CTRL_ID_PLAY,
13667     "play game"
13668   },
13669   {
13670     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13671     SOUND_CTRL_ID_MUSIC,
13672     "background music on/off"
13673   },
13674   {
13675     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13676     SOUND_CTRL_ID_LOOPS,
13677     "sound loops on/off"
13678   },
13679   {
13680     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13681     SOUND_CTRL_ID_SIMPLE,
13682     "normal sounds on/off"
13683   }
13684 };
13685
13686 void CreateGameButtons()
13687 {
13688   int i;
13689
13690   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13691   {
13692     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13693     struct GadgetInfo *gi;
13694     int button_type;
13695     boolean checked;
13696     unsigned long event_mask;
13697     int gd_xoffset, gd_yoffset;
13698     int gd_x1, gd_x2, gd_y1, gd_y2;
13699     int id = i;
13700
13701     gd_xoffset = gamebutton_info[i].x;
13702     gd_yoffset = gamebutton_info[i].y;
13703     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13704     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13705
13706     if (id == GAME_CTRL_ID_STOP ||
13707         id == GAME_CTRL_ID_PAUSE ||
13708         id == GAME_CTRL_ID_PLAY)
13709     {
13710       button_type = GD_TYPE_NORMAL_BUTTON;
13711       checked = FALSE;
13712       event_mask = GD_EVENT_RELEASED;
13713       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13714       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13715     }
13716     else
13717     {
13718       button_type = GD_TYPE_CHECK_BUTTON;
13719       checked =
13720         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13721          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13722          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13723       event_mask = GD_EVENT_PRESSED;
13724       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
13725       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13726     }
13727
13728     gi = CreateGadget(GDI_CUSTOM_ID, id,
13729                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
13730                       GDI_X, DX + gd_xoffset,
13731                       GDI_Y, DY + gd_yoffset,
13732                       GDI_WIDTH, GAME_BUTTON_XSIZE,
13733                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
13734                       GDI_TYPE, button_type,
13735                       GDI_STATE, GD_BUTTON_UNPRESSED,
13736                       GDI_CHECKED, checked,
13737                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13738                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13739                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13740                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13741                       GDI_EVENT_MASK, event_mask,
13742                       GDI_CALLBACK_ACTION, HandleGameButtons,
13743                       GDI_END);
13744
13745     if (gi == NULL)
13746       Error(ERR_EXIT, "cannot create gadget");
13747
13748     game_gadget[id] = gi;
13749   }
13750 }
13751
13752 void FreeGameButtons()
13753 {
13754   int i;
13755
13756   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13757     FreeGadget(game_gadget[i]);
13758 }
13759
13760 static void MapGameButtons()
13761 {
13762   int i;
13763
13764   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13765     MapGadget(game_gadget[i]);
13766 }
13767
13768 void UnmapGameButtons()
13769 {
13770   int i;
13771
13772   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13773     UnmapGadget(game_gadget[i]);
13774 }
13775
13776 static void HandleGameButtons(struct GadgetInfo *gi)
13777 {
13778   int id = gi->custom_id;
13779
13780   if (game_status != GAME_MODE_PLAYING)
13781     return;
13782
13783   switch (id)
13784   {
13785     case GAME_CTRL_ID_STOP:
13786       if (tape.playing)
13787         TapeStop();
13788       else
13789         RequestQuitGame(TRUE);
13790       break;
13791
13792     case GAME_CTRL_ID_PAUSE:
13793       if (options.network)
13794       {
13795 #if defined(NETWORK_AVALIABLE)
13796         if (tape.pausing)
13797           SendToServer_ContinuePlaying();
13798         else
13799           SendToServer_PausePlaying();
13800 #endif
13801       }
13802       else
13803         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13804       break;
13805
13806     case GAME_CTRL_ID_PLAY:
13807       if (tape.pausing)
13808       {
13809 #if defined(NETWORK_AVALIABLE)
13810         if (options.network)
13811           SendToServer_ContinuePlaying();
13812         else
13813 #endif
13814         {
13815           tape.pausing = FALSE;
13816           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13817         }
13818       }
13819       break;
13820
13821     case SOUND_CTRL_ID_MUSIC:
13822       if (setup.sound_music)
13823       { 
13824         setup.sound_music = FALSE;
13825         FadeMusic();
13826       }
13827       else if (audio.music_available)
13828       { 
13829         setup.sound = setup.sound_music = TRUE;
13830
13831         SetAudioMode(setup.sound);
13832
13833         PlayLevelMusic();
13834       }
13835       break;
13836
13837     case SOUND_CTRL_ID_LOOPS:
13838       if (setup.sound_loops)
13839         setup.sound_loops = FALSE;
13840       else if (audio.loops_available)
13841       {
13842         setup.sound = setup.sound_loops = TRUE;
13843         SetAudioMode(setup.sound);
13844       }
13845       break;
13846
13847     case SOUND_CTRL_ID_SIMPLE:
13848       if (setup.sound_simple)
13849         setup.sound_simple = FALSE;
13850       else if (audio.sound_available)
13851       {
13852         setup.sound = setup.sound_simple = TRUE;
13853         SetAudioMode(setup.sound);
13854       }
13855       break;
13856
13857     default:
13858       break;
13859   }
13860 }