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