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