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