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