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