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