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