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