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