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