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