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