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