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