rnd-20060603-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 add_player = (ELEM_IS_PLAYER(new_element) &&
8454                         IS_WALKABLE(old_element));
8455
8456 #if 0
8457   /* check if element under the player changes from accessible to unaccessible
8458      (needed for special case of dropping element which then changes) */
8459   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8460       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8461   {
8462     Bang(x, y);
8463
8464     return;
8465   }
8466 #endif
8467
8468   if (!add_player)
8469   {
8470     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8471       RemoveMovingField(x, y);
8472     else
8473       RemoveField(x, y);
8474
8475     Feld[x][y] = new_element;
8476
8477 #if !USE_GFX_RESET_GFX_ANIMATION
8478     ResetGfxAnimation(x, y);
8479     ResetRandomAnimationValue(x, y);
8480 #endif
8481
8482     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8483       MovDir[x][y] = previous_move_direction;
8484
8485 #if USE_NEW_CUSTOM_VALUE
8486     if (element_info[new_element].use_last_ce_value)
8487       CustomValue[x][y] = last_ce_value;
8488 #endif
8489
8490     InitField_WithBug1(x, y, FALSE);
8491
8492     new_element = Feld[x][y];   /* element may have changed */
8493
8494 #if USE_GFX_RESET_GFX_ANIMATION
8495     ResetGfxAnimation(x, y);
8496     ResetRandomAnimationValue(x, y);
8497 #endif
8498
8499     DrawLevelField(x, y);
8500
8501     if (GFX_CRUMBLED(new_element))
8502       DrawLevelFieldCrumbledSandNeighbours(x, y);
8503   }
8504
8505 #if 1
8506   /* check if element under the player changes from accessible to unaccessible
8507      (needed for special case of dropping element which then changes) */
8508   /* (must be checked after creating new element for walkable group elements) */
8509   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8510       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8511   {
8512     Bang(x, y);
8513
8514     return;
8515   }
8516 #endif
8517
8518   /* "ChangeCount" not set yet to allow "entered by player" change one time */
8519   if (ELEM_IS_PLAYER(new_element))
8520     RelocatePlayer(x, y, new_element);
8521
8522   if (is_change)
8523     ChangeCount[x][y]++;        /* count number of changes in the same frame */
8524
8525   TestIfBadThingTouchesPlayer(x, y);
8526   TestIfPlayerTouchesCustomElement(x, y);
8527   TestIfElementTouchesCustomElement(x, y);
8528 }
8529
8530 static void CreateField(int x, int y, int element)
8531 {
8532   CreateFieldExt(x, y, element, FALSE);
8533 }
8534
8535 static void CreateElementFromChange(int x, int y, int element)
8536 {
8537   element = GET_VALID_RUNTIME_ELEMENT(element);
8538
8539 #if USE_STOP_CHANGED_ELEMENTS
8540   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8541   {
8542     int old_element = Feld[x][y];
8543
8544     /* prevent changed element from moving in same engine frame
8545        unless both old and new element can either fall or move */
8546     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8547         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8548       Stop[x][y] = TRUE;
8549   }
8550 #endif
8551
8552   CreateFieldExt(x, y, element, TRUE);
8553 }
8554
8555 static boolean ChangeElement(int x, int y, int element, int page)
8556 {
8557   struct ElementInfo *ei = &element_info[element];
8558   struct ElementChangeInfo *change = &ei->change_page[page];
8559   int ce_value = CustomValue[x][y];
8560   int ce_score = ei->collect_score;
8561   int target_element;
8562   int old_element = Feld[x][y];
8563
8564   /* always use default change event to prevent running into a loop */
8565   if (ChangeEvent[x][y] == -1)
8566     ChangeEvent[x][y] = CE_DELAY;
8567
8568   if (ChangeEvent[x][y] == CE_DELAY)
8569   {
8570     /* reset actual trigger element, trigger player and action element */
8571     change->actual_trigger_element = EL_EMPTY;
8572     change->actual_trigger_player = EL_PLAYER_1;
8573     change->actual_trigger_side = CH_SIDE_NONE;
8574     change->actual_trigger_ce_value = 0;
8575     change->actual_trigger_ce_score = 0;
8576   }
8577
8578   /* do not change elements more than a specified maximum number of changes */
8579   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8580     return FALSE;
8581
8582   ChangeCount[x][y]++;          /* count number of changes in the same frame */
8583
8584   if (change->explode)
8585   {
8586     Bang(x, y);
8587
8588     return TRUE;
8589   }
8590
8591   if (change->use_target_content)
8592   {
8593     boolean complete_replace = TRUE;
8594     boolean can_replace[3][3];
8595     int xx, yy;
8596
8597     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8598     {
8599       boolean is_empty;
8600       boolean is_walkable;
8601       boolean is_diggable;
8602       boolean is_collectible;
8603       boolean is_removable;
8604       boolean is_destructible;
8605       int ex = x + xx - 1;
8606       int ey = y + yy - 1;
8607       int content_element = change->target_content.e[xx][yy];
8608       int e;
8609
8610       can_replace[xx][yy] = TRUE;
8611
8612       if (ex == x && ey == y)   /* do not check changing element itself */
8613         continue;
8614
8615       if (content_element == EL_EMPTY_SPACE)
8616       {
8617         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8618
8619         continue;
8620       }
8621
8622       if (!IN_LEV_FIELD(ex, ey))
8623       {
8624         can_replace[xx][yy] = FALSE;
8625         complete_replace = FALSE;
8626
8627         continue;
8628       }
8629
8630       e = Feld[ex][ey];
8631
8632       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8633         e = MovingOrBlocked2Element(ex, ey);
8634
8635       is_empty = (IS_FREE(ex, ey) ||
8636                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8637
8638       is_walkable     = (is_empty || IS_WALKABLE(e));
8639       is_diggable     = (is_empty || IS_DIGGABLE(e));
8640       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8641       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8642       is_removable    = (is_diggable || is_collectible);
8643
8644       can_replace[xx][yy] =
8645         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
8646           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
8647           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
8648           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
8649           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
8650           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8651          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8652
8653       if (!can_replace[xx][yy])
8654         complete_replace = FALSE;
8655     }
8656
8657     if (!change->only_if_complete || complete_replace)
8658     {
8659       boolean something_has_changed = FALSE;
8660
8661       if (change->only_if_complete && change->use_random_replace &&
8662           RND(100) < change->random_percentage)
8663         return FALSE;
8664
8665       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8666       {
8667         int ex = x + xx - 1;
8668         int ey = y + yy - 1;
8669         int content_element;
8670
8671         if (can_replace[xx][yy] && (!change->use_random_replace ||
8672                                     RND(100) < change->random_percentage))
8673         {
8674           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8675             RemoveMovingField(ex, ey);
8676
8677           ChangeEvent[ex][ey] = ChangeEvent[x][y];
8678
8679           content_element = change->target_content.e[xx][yy];
8680           target_element = GET_TARGET_ELEMENT(content_element, change,
8681                                               ce_value, ce_score);
8682
8683           CreateElementFromChange(ex, ey, target_element);
8684
8685           something_has_changed = TRUE;
8686
8687           /* for symmetry reasons, freeze newly created border elements */
8688           if (ex != x || ey != y)
8689             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
8690         }
8691       }
8692
8693       if (something_has_changed)
8694       {
8695         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8696         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8697       }
8698     }
8699   }
8700   else
8701   {
8702     target_element = GET_TARGET_ELEMENT(change->target_element, change,
8703                                         ce_value, ce_score);
8704
8705     if (element == EL_DIAGONAL_GROWING ||
8706         element == EL_DIAGONAL_SHRINKING)
8707     {
8708       target_element = Store[x][y];
8709
8710       Store[x][y] = EL_EMPTY;
8711     }
8712
8713     CreateElementFromChange(x, y, target_element);
8714
8715     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8716     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8717   }
8718
8719   /* this uses direct change before indirect change */
8720   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8721
8722   return TRUE;
8723 }
8724
8725 #if USE_NEW_DELAYED_ACTION
8726
8727 static void HandleElementChange(int x, int y, int page)
8728 {
8729   int element = MovingOrBlocked2Element(x, y);
8730   struct ElementInfo *ei = &element_info[element];
8731   struct ElementChangeInfo *change = &ei->change_page[page];
8732
8733 #ifdef DEBUG
8734   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8735       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8736   {
8737     printf("\n\n");
8738     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8739            x, y, element, element_info[element].token_name);
8740     printf("HandleElementChange(): This should never happen!\n");
8741     printf("\n\n");
8742   }
8743 #endif
8744
8745   /* this can happen with classic bombs on walkable, changing elements */
8746   if (!CAN_CHANGE_OR_HAS_ACTION(element))
8747   {
8748 #if 0
8749     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
8750       ChangeDelay[x][y] = 0;
8751 #endif
8752
8753     return;
8754   }
8755
8756   if (ChangeDelay[x][y] == 0)           /* initialize element change */
8757   {
8758     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8759
8760     if (change->can_change)
8761     {
8762       ResetGfxAnimation(x, y);
8763       ResetRandomAnimationValue(x, y);
8764
8765       if (change->pre_change_function)
8766         change->pre_change_function(x, y);
8767     }
8768   }
8769
8770   ChangeDelay[x][y]--;
8771
8772   if (ChangeDelay[x][y] != 0)           /* continue element change */
8773   {
8774     if (change->can_change)
8775     {
8776       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8777
8778       if (IS_ANIMATED(graphic))
8779         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8780
8781       if (change->change_function)
8782         change->change_function(x, y);
8783     }
8784   }
8785   else                                  /* finish element change */
8786   {
8787     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
8788     {
8789       page = ChangePage[x][y];
8790       ChangePage[x][y] = -1;
8791
8792       change = &ei->change_page[page];
8793     }
8794
8795     if (IS_MOVING(x, y))                /* never change a running system ;-) */
8796     {
8797       ChangeDelay[x][y] = 1;            /* try change after next move step */
8798       ChangePage[x][y] = page;          /* remember page to use for change */
8799
8800       return;
8801     }
8802
8803     if (change->can_change)
8804     {
8805       if (ChangeElement(x, y, element, page))
8806       {
8807         if (change->post_change_function)
8808           change->post_change_function(x, y);
8809       }
8810     }
8811
8812     if (change->has_action)
8813       ExecuteCustomElementAction(x, y, element, page);
8814   }
8815 }
8816
8817 #else
8818
8819 static void HandleElementChange(int x, int y, int page)
8820 {
8821   int element = MovingOrBlocked2Element(x, y);
8822   struct ElementInfo *ei = &element_info[element];
8823   struct ElementChangeInfo *change = &ei->change_page[page];
8824
8825 #ifdef DEBUG
8826   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8827   {
8828     printf("\n\n");
8829     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8830            x, y, element, element_info[element].token_name);
8831     printf("HandleElementChange(): This should never happen!\n");
8832     printf("\n\n");
8833   }
8834 #endif
8835
8836   /* this can happen with classic bombs on walkable, changing elements */
8837   if (!CAN_CHANGE(element))
8838   {
8839 #if 0
8840     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
8841       ChangeDelay[x][y] = 0;
8842 #endif
8843
8844     return;
8845   }
8846
8847   if (ChangeDelay[x][y] == 0)           /* initialize element change */
8848   {
8849     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8850
8851     ResetGfxAnimation(x, y);
8852     ResetRandomAnimationValue(x, y);
8853
8854     if (change->pre_change_function)
8855       change->pre_change_function(x, y);
8856   }
8857
8858   ChangeDelay[x][y]--;
8859
8860   if (ChangeDelay[x][y] != 0)           /* continue element change */
8861   {
8862     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8863
8864     if (IS_ANIMATED(graphic))
8865       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8866
8867     if (change->change_function)
8868       change->change_function(x, y);
8869   }
8870   else                                  /* finish element change */
8871   {
8872     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
8873     {
8874       page = ChangePage[x][y];
8875       ChangePage[x][y] = -1;
8876
8877       change = &ei->change_page[page];
8878     }
8879
8880     if (IS_MOVING(x, y))                /* never change a running system ;-) */
8881     {
8882       ChangeDelay[x][y] = 1;            /* try change after next move step */
8883       ChangePage[x][y] = page;          /* remember page to use for change */
8884
8885       return;
8886     }
8887
8888     if (ChangeElement(x, y, element, page))
8889     {
8890       if (change->post_change_function)
8891         change->post_change_function(x, y);
8892     }
8893   }
8894 }
8895
8896 #endif
8897
8898 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8899                                               int trigger_element,
8900                                               int trigger_event,
8901                                               int trigger_player,
8902                                               int trigger_side,
8903                                               int trigger_page)
8904 {
8905   boolean change_done_any = FALSE;
8906   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8907   int i;
8908
8909   if (!(trigger_events[trigger_element][trigger_event]))
8910     return FALSE;
8911
8912   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8913   {
8914     int element = EL_CUSTOM_START + i;
8915     boolean change_done = FALSE;
8916     int p;
8917
8918     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8919         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8920       continue;
8921
8922     for (p = 0; p < element_info[element].num_change_pages; p++)
8923     {
8924       struct ElementChangeInfo *change = &element_info[element].change_page[p];
8925
8926       if (change->can_change_or_has_action &&
8927           change->has_event[trigger_event] &&
8928           change->trigger_side & trigger_side &&
8929           change->trigger_player & trigger_player &&
8930           change->trigger_page & trigger_page_bits &&
8931           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8932       {
8933         change->actual_trigger_element = trigger_element;
8934         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8935         change->actual_trigger_side = trigger_side;
8936         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8937         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8938
8939         if ((change->can_change && !change_done) || change->has_action)
8940         {
8941           int x, y;
8942
8943 #if 1
8944           SCAN_PLAYFIELD(x, y)
8945 #else
8946           for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8947 #endif
8948           {
8949             if (Feld[x][y] == element)
8950             {
8951               if (change->can_change && !change_done)
8952               {
8953                 ChangeDelay[x][y] = 1;
8954                 ChangeEvent[x][y] = trigger_event;
8955
8956                 HandleElementChange(x, y, p);
8957               }
8958 #if USE_NEW_DELAYED_ACTION
8959               else if (change->has_action)
8960               {
8961                 ExecuteCustomElementAction(x, y, element, p);
8962                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8963               }
8964 #else
8965               if (change->has_action)
8966               {
8967                 ExecuteCustomElementAction(x, y, element, p);
8968                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8969               }
8970 #endif
8971             }
8972           }
8973
8974           if (change->can_change)
8975           {
8976             change_done = TRUE;
8977             change_done_any = TRUE;
8978           }
8979         }
8980       }
8981     }
8982   }
8983
8984   return change_done_any;
8985 }
8986
8987 static boolean CheckElementChangeExt(int x, int y,
8988                                      int element,
8989                                      int trigger_element,
8990                                      int trigger_event,
8991                                      int trigger_player,
8992                                      int trigger_side)
8993 {
8994   boolean change_done = FALSE;
8995   int p;
8996
8997   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8998       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8999     return FALSE;
9000
9001   if (Feld[x][y] == EL_BLOCKED)
9002   {
9003     Blocked2Moving(x, y, &x, &y);
9004     element = Feld[x][y];
9005   }
9006
9007 #if 0
9008   /* check if element has already changed */
9009   if (Feld[x][y] != element)
9010     return FALSE;
9011 #else
9012   /* check if element has already changed or is about to change after moving */
9013   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9014        Feld[x][y] != element) ||
9015
9016       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9017        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9018         ChangePage[x][y] != -1)))
9019     return FALSE;
9020 #endif
9021
9022   for (p = 0; p < element_info[element].num_change_pages; p++)
9023   {
9024     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9025
9026     boolean check_trigger_element =
9027       (trigger_event == CE_TOUCHING_X ||
9028        trigger_event == CE_HITTING_X ||
9029        trigger_event == CE_HIT_BY_X);
9030
9031     if (change->can_change_or_has_action &&
9032         change->has_event[trigger_event] &&
9033         change->trigger_side & trigger_side &&
9034         change->trigger_player & trigger_player &&
9035         (!check_trigger_element ||
9036          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9037     {
9038       change->actual_trigger_element = trigger_element;
9039       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9040       change->actual_trigger_side = trigger_side;
9041       change->actual_trigger_ce_value = CustomValue[x][y];
9042       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9043
9044       /* special case: trigger element not at (x,y) position for some events */
9045       if (check_trigger_element)
9046       {
9047         static struct
9048         {
9049           int dx, dy;
9050         } move_xy[] =
9051           {
9052             {  0,  0 },
9053             { -1,  0 },
9054             { +1,  0 },
9055             {  0,  0 },
9056             {  0, -1 },
9057             {  0,  0 }, { 0, 0 }, { 0, 0 },
9058             {  0, +1 }
9059           };
9060
9061         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9062         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9063
9064         change->actual_trigger_ce_value = CustomValue[xx][yy];
9065         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9066       }
9067
9068       if (change->can_change && !change_done)
9069       {
9070         ChangeDelay[x][y] = 1;
9071         ChangeEvent[x][y] = trigger_event;
9072
9073         HandleElementChange(x, y, p);
9074
9075         change_done = TRUE;
9076       }
9077 #if USE_NEW_DELAYED_ACTION
9078       else if (change->has_action)
9079       {
9080         ExecuteCustomElementAction(x, y, element, p);
9081         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9082       }
9083 #else
9084       if (change->has_action)
9085       {
9086         ExecuteCustomElementAction(x, y, element, p);
9087         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9088       }
9089 #endif
9090     }
9091   }
9092
9093   return change_done;
9094 }
9095
9096 static void PlayPlayerSound(struct PlayerInfo *player)
9097 {
9098   int jx = player->jx, jy = player->jy;
9099   int sound_element = player->artwork_element;
9100   int last_action = player->last_action_waiting;
9101   int action = player->action_waiting;
9102
9103   if (player->is_waiting)
9104   {
9105     if (action != last_action)
9106       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9107     else
9108       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9109   }
9110   else
9111   {
9112     if (action != last_action)
9113       StopSound(element_info[sound_element].sound[last_action]);
9114
9115     if (last_action == ACTION_SLEEPING)
9116       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9117   }
9118 }
9119
9120 static void PlayAllPlayersSound()
9121 {
9122   int i;
9123
9124   for (i = 0; i < MAX_PLAYERS; i++)
9125     if (stored_player[i].active)
9126       PlayPlayerSound(&stored_player[i]);
9127 }
9128
9129 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9130 {
9131   boolean last_waiting = player->is_waiting;
9132   int move_dir = player->MovDir;
9133
9134   player->dir_waiting = move_dir;
9135   player->last_action_waiting = player->action_waiting;
9136
9137   if (is_waiting)
9138   {
9139     if (!last_waiting)          /* not waiting -> waiting */
9140     {
9141       player->is_waiting = TRUE;
9142
9143       player->frame_counter_bored =
9144         FrameCounter +
9145         game.player_boring_delay_fixed +
9146         SimpleRND(game.player_boring_delay_random);
9147       player->frame_counter_sleeping =
9148         FrameCounter +
9149         game.player_sleeping_delay_fixed +
9150         SimpleRND(game.player_sleeping_delay_random);
9151
9152 #if 1
9153       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9154 #else
9155       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9156 #endif
9157     }
9158
9159     if (game.player_sleeping_delay_fixed +
9160         game.player_sleeping_delay_random > 0 &&
9161         player->anim_delay_counter == 0 &&
9162         player->post_delay_counter == 0 &&
9163         FrameCounter >= player->frame_counter_sleeping)
9164       player->is_sleeping = TRUE;
9165     else if (game.player_boring_delay_fixed +
9166              game.player_boring_delay_random > 0 &&
9167              FrameCounter >= player->frame_counter_bored)
9168       player->is_bored = TRUE;
9169
9170     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9171                               player->is_bored ? ACTION_BORING :
9172                               ACTION_WAITING);
9173
9174 #if 1
9175     if (player->is_sleeping && player->use_murphy)
9176     {
9177       /* special case for sleeping Murphy when leaning against non-free tile */
9178
9179       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9180           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9181            !IS_MOVING(player->jx - 1, player->jy)))
9182         move_dir = MV_LEFT;
9183       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9184                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9185                 !IS_MOVING(player->jx + 1, player->jy)))
9186         move_dir = MV_RIGHT;
9187       else
9188         player->is_sleeping = FALSE;
9189
9190       player->dir_waiting = move_dir;
9191     }
9192 #endif
9193
9194     if (player->is_sleeping)
9195     {
9196       if (player->num_special_action_sleeping > 0)
9197       {
9198         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9199         {
9200           int last_special_action = player->special_action_sleeping;
9201           int num_special_action = player->num_special_action_sleeping;
9202           int special_action =
9203             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9204              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9205              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9206              last_special_action + 1 : ACTION_SLEEPING);
9207           int special_graphic =
9208             el_act_dir2img(player->artwork_element, special_action, move_dir);
9209
9210           player->anim_delay_counter =
9211             graphic_info[special_graphic].anim_delay_fixed +
9212             SimpleRND(graphic_info[special_graphic].anim_delay_random);
9213           player->post_delay_counter =
9214             graphic_info[special_graphic].post_delay_fixed +
9215             SimpleRND(graphic_info[special_graphic].post_delay_random);
9216
9217           player->special_action_sleeping = special_action;
9218         }
9219
9220         if (player->anim_delay_counter > 0)
9221         {
9222           player->action_waiting = player->special_action_sleeping;
9223           player->anim_delay_counter--;
9224         }
9225         else if (player->post_delay_counter > 0)
9226         {
9227           player->post_delay_counter--;
9228         }
9229       }
9230     }
9231     else if (player->is_bored)
9232     {
9233       if (player->num_special_action_bored > 0)
9234       {
9235         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9236         {
9237           int special_action =
9238             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9239           int special_graphic =
9240             el_act_dir2img(player->artwork_element, special_action, move_dir);
9241
9242           player->anim_delay_counter =
9243             graphic_info[special_graphic].anim_delay_fixed +
9244             SimpleRND(graphic_info[special_graphic].anim_delay_random);
9245           player->post_delay_counter =
9246             graphic_info[special_graphic].post_delay_fixed +
9247             SimpleRND(graphic_info[special_graphic].post_delay_random);
9248
9249           player->special_action_bored = special_action;
9250         }
9251
9252         if (player->anim_delay_counter > 0)
9253         {
9254           player->action_waiting = player->special_action_bored;
9255           player->anim_delay_counter--;
9256         }
9257         else if (player->post_delay_counter > 0)
9258         {
9259           player->post_delay_counter--;
9260         }
9261       }
9262     }
9263   }
9264   else if (last_waiting)        /* waiting -> not waiting */
9265   {
9266     player->is_waiting = FALSE;
9267     player->is_bored = FALSE;
9268     player->is_sleeping = FALSE;
9269
9270     player->frame_counter_bored = -1;
9271     player->frame_counter_sleeping = -1;
9272
9273     player->anim_delay_counter = 0;
9274     player->post_delay_counter = 0;
9275
9276     player->dir_waiting = player->MovDir;
9277     player->action_waiting = ACTION_DEFAULT;
9278
9279     player->special_action_bored = ACTION_DEFAULT;
9280     player->special_action_sleeping = ACTION_DEFAULT;
9281   }
9282 }
9283
9284 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9285 {
9286   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9287   int left      = player_action & JOY_LEFT;
9288   int right     = player_action & JOY_RIGHT;
9289   int up        = player_action & JOY_UP;
9290   int down      = player_action & JOY_DOWN;
9291   int button1   = player_action & JOY_BUTTON_1;
9292   int button2   = player_action & JOY_BUTTON_2;
9293   int dx        = (left ? -1 : right ? 1 : 0);
9294   int dy        = (up   ? -1 : down  ? 1 : 0);
9295
9296   if (!player->active || tape.pausing)
9297     return 0;
9298
9299   if (player_action)
9300   {
9301     if (button1)
9302       snapped = SnapField(player, dx, dy);
9303     else
9304     {
9305       if (button2)
9306         dropped = DropElement(player);
9307
9308       moved = MovePlayer(player, dx, dy);
9309     }
9310
9311     if (tape.single_step && tape.recording && !tape.pausing)
9312     {
9313       if (button1 || (dropped && !moved))
9314       {
9315         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9316         SnapField(player, 0, 0);                /* stop snapping */
9317       }
9318     }
9319
9320     SetPlayerWaiting(player, FALSE);
9321
9322     return player_action;
9323   }
9324   else
9325   {
9326     /* no actions for this player (no input at player's configured device) */
9327
9328     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9329     SnapField(player, 0, 0);
9330     CheckGravityMovementWhenNotMoving(player);
9331
9332     if (player->MovPos == 0)
9333       SetPlayerWaiting(player, TRUE);
9334
9335     if (player->MovPos == 0)    /* needed for tape.playing */
9336       player->is_moving = FALSE;
9337
9338     player->is_dropping = FALSE;
9339     player->is_dropping_pressed = FALSE;
9340     player->drop_pressed_delay = 0;
9341
9342     return 0;
9343   }
9344 }
9345
9346 static void CheckLevelTime()
9347 {
9348   int i;
9349
9350   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9351   {
9352     if (level.native_em_level->lev->home == 0)  /* all players at home */
9353     {
9354       local_player->LevelSolved = TRUE;
9355       AllPlayersGone = TRUE;
9356
9357       level.native_em_level->lev->home = -1;
9358     }
9359
9360     if (level.native_em_level->ply[0]->alive == 0 &&
9361         level.native_em_level->ply[1]->alive == 0 &&
9362         level.native_em_level->ply[2]->alive == 0 &&
9363         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9364       AllPlayersGone = TRUE;
9365   }
9366
9367   if (TimeFrames >= FRAMES_PER_SECOND)
9368   {
9369     TimeFrames = 0;
9370     TapeTime++;
9371
9372     for (i = 0; i < MAX_PLAYERS; i++)
9373     {
9374       struct PlayerInfo *player = &stored_player[i];
9375
9376       if (SHIELD_ON(player))
9377       {
9378         player->shield_normal_time_left--;
9379
9380         if (player->shield_deadly_time_left > 0)
9381           player->shield_deadly_time_left--;
9382       }
9383     }
9384
9385     if (!level.use_step_counter)
9386     {
9387       TimePlayed++;
9388
9389       if (TimeLeft > 0)
9390       {
9391         TimeLeft--;
9392
9393         if (TimeLeft <= 10 && setup.time_limit)
9394           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9395
9396         DrawGameValue_Time(TimeLeft);
9397
9398         if (!TimeLeft && setup.time_limit)
9399         {
9400           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9401             level.native_em_level->lev->killed_out_of_time = TRUE;
9402           else
9403             for (i = 0; i < MAX_PLAYERS; i++)
9404               KillPlayer(&stored_player[i]);
9405         }
9406       }
9407       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9408         DrawGameValue_Time(TimePlayed);
9409
9410       level.native_em_level->lev->time =
9411         (level.time == 0 ? TimePlayed : TimeLeft);
9412     }
9413
9414     if (tape.recording || tape.playing)
9415       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9416   }
9417 }
9418
9419 void AdvanceFrameAndPlayerCounters(int player_nr)
9420 {
9421   int i;
9422
9423 #if 0
9424   Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9425         FrameCounter, FrameCounter + 1);
9426 #endif
9427
9428   /* advance frame counters (global frame counter and time frame counter) */
9429   FrameCounter++;
9430   TimeFrames++;
9431
9432   /* advance player counters (counters for move delay, move animation etc.) */
9433   for (i = 0; i < MAX_PLAYERS; i++)
9434   {
9435     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9436     int move_delay_value = stored_player[i].move_delay_value;
9437     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9438
9439     if (!advance_player_counters)       /* not all players may be affected */
9440       continue;
9441
9442 #if USE_NEW_PLAYER_ANIM
9443     if (move_frames == 0)       /* less than one move per game frame */
9444     {
9445       int stepsize = TILEX / move_delay_value;
9446       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9447       int count = (stored_player[i].is_moving ?
9448                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9449
9450       if (count % delay == 0)
9451         move_frames = 1;
9452     }
9453 #endif
9454
9455     stored_player[i].Frame += move_frames;
9456
9457     if (stored_player[i].MovPos != 0)
9458       stored_player[i].StepFrame += move_frames;
9459
9460     if (stored_player[i].move_delay > 0)
9461       stored_player[i].move_delay--;
9462
9463     /* due to bugs in previous versions, counter must count up, not down */
9464     if (stored_player[i].push_delay != -1)
9465       stored_player[i].push_delay++;
9466
9467     if (stored_player[i].drop_delay > 0)
9468       stored_player[i].drop_delay--;
9469
9470     if (stored_player[i].is_dropping_pressed)
9471       stored_player[i].drop_pressed_delay++;
9472   }
9473 }
9474
9475 void StartGameActions(boolean init_network_game, boolean record_tape,
9476                       long random_seed)
9477 {
9478   unsigned long new_random_seed = InitRND(random_seed);
9479
9480   if (record_tape)
9481     TapeStartRecording(new_random_seed);
9482
9483 #if defined(NETWORK_AVALIABLE)
9484   if (init_network_game)
9485   {
9486     SendToServer_StartPlaying();
9487
9488     return;
9489   }
9490 #endif
9491
9492   StopAnimation();
9493
9494   game_status = GAME_MODE_PLAYING;
9495
9496   InitGame();
9497 }
9498
9499 void GameActions()
9500 {
9501   static unsigned long game_frame_delay = 0;
9502   unsigned long game_frame_delay_value;
9503   byte *recorded_player_action;
9504   byte summarized_player_action = 0;
9505   byte tape_action[MAX_PLAYERS];
9506   int i;
9507
9508   if (game.restart_level)
9509     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9510
9511   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9512   {
9513     if (level.native_em_level->lev->home == 0)  /* all players at home */
9514     {
9515       local_player->LevelSolved = TRUE;
9516       AllPlayersGone = TRUE;
9517
9518       level.native_em_level->lev->home = -1;
9519     }
9520
9521     if (level.native_em_level->ply[0]->alive == 0 &&
9522         level.native_em_level->ply[1]->alive == 0 &&
9523         level.native_em_level->ply[2]->alive == 0 &&
9524         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9525       AllPlayersGone = TRUE;
9526   }
9527
9528   if (local_player->LevelSolved)
9529     GameWon();
9530
9531   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9532     TapeStop();
9533
9534   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9535     return;
9536
9537   game_frame_delay_value =
9538     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9539
9540   if (tape.playing && tape.warp_forward && !tape.pausing)
9541     game_frame_delay_value = 0;
9542
9543   /* ---------- main game synchronization point ---------- */
9544
9545   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9546
9547   if (network_playing && !network_player_action_received)
9548   {
9549     /* try to get network player actions in time */
9550
9551 #if defined(NETWORK_AVALIABLE)
9552     /* last chance to get network player actions without main loop delay */
9553     HandleNetworking();
9554 #endif
9555
9556     /* game was quit by network peer */
9557     if (game_status != GAME_MODE_PLAYING)
9558       return;
9559
9560     if (!network_player_action_received)
9561       return;           /* failed to get network player actions in time */
9562
9563     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9564   }
9565
9566   if (tape.pausing)
9567     return;
9568
9569   /* at this point we know that we really continue executing the game */
9570
9571 #if 1
9572   network_player_action_received = FALSE;
9573 #endif
9574
9575   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9576
9577   if (tape.set_centered_player)
9578   {
9579     game.centered_player_nr_next = tape.centered_player_nr_next;
9580     game.set_centered_player = TRUE;
9581   }
9582
9583   for (i = 0; i < MAX_PLAYERS; i++)
9584   {
9585     summarized_player_action |= stored_player[i].action;
9586
9587     if (!network_playing)
9588       stored_player[i].effective_action = stored_player[i].action;
9589   }
9590
9591 #if defined(NETWORK_AVALIABLE)
9592   if (network_playing)
9593     SendToServer_MovePlayer(summarized_player_action);
9594 #endif
9595
9596   if (!options.network && !setup.team_mode)
9597     local_player->effective_action = summarized_player_action;
9598
9599   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9600   {
9601     for (i = 0; i < MAX_PLAYERS; i++)
9602       stored_player[i].effective_action =
9603         (i == game.centered_player_nr ? summarized_player_action : 0);
9604   }
9605
9606   if (recorded_player_action != NULL)
9607     for (i = 0; i < MAX_PLAYERS; i++)
9608       stored_player[i].effective_action = recorded_player_action[i];
9609
9610   for (i = 0; i < MAX_PLAYERS; i++)
9611   {
9612     tape_action[i] = stored_player[i].effective_action;
9613
9614     /* (this can only happen in the R'n'D game engine) */
9615     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9616       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9617   }
9618
9619   /* only record actions from input devices, but not programmed actions */
9620   if (tape.recording)
9621     TapeRecordAction(tape_action);
9622
9623   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9624   {
9625     GameActions_EM_Main();
9626   }
9627   else
9628   {
9629     GameActions_RND();
9630   }
9631 }
9632
9633 void GameActions_EM_Main()
9634 {
9635   byte effective_action[MAX_PLAYERS];
9636   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9637   int i;
9638
9639   for (i = 0; i < MAX_PLAYERS; i++)
9640     effective_action[i] = stored_player[i].effective_action;
9641
9642   GameActions_EM(effective_action, warp_mode);
9643
9644   CheckLevelTime();
9645
9646   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9647 }
9648
9649 void GameActions_RND()
9650 {
9651   int magic_wall_x = 0, magic_wall_y = 0;
9652   int i, x, y, element, graphic;
9653
9654   InitPlayfieldScanModeVars();
9655
9656 #if USE_ONE_MORE_CHANGE_PER_FRAME
9657   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9658   {
9659     SCAN_PLAYFIELD(x, y)
9660     {
9661       ChangeCount[x][y] = 0;
9662       ChangeEvent[x][y] = -1;
9663     }
9664   }
9665 #endif
9666
9667 #if 1
9668   if (game.set_centered_player)
9669   {
9670     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9671
9672     /* switching to "all players" only possible if all players fit to screen */
9673     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9674     {
9675       game.centered_player_nr_next = game.centered_player_nr;
9676       game.set_centered_player = FALSE;
9677     }
9678
9679     /* do not switch focus to non-existing (or non-active) player */
9680     if (game.centered_player_nr_next >= 0 &&
9681         !stored_player[game.centered_player_nr_next].active)
9682     {
9683       game.centered_player_nr_next = game.centered_player_nr;
9684       game.set_centered_player = FALSE;
9685     }
9686   }
9687
9688   if (game.set_centered_player &&
9689       ScreenMovPos == 0)        /* screen currently aligned at tile position */
9690   {
9691     int sx, sy;
9692
9693     if (game.centered_player_nr_next == -1)
9694     {
9695       setScreenCenteredToAllPlayers(&sx, &sy);
9696     }
9697     else
9698     {
9699       sx = stored_player[game.centered_player_nr_next].jx;
9700       sy = stored_player[game.centered_player_nr_next].jy;
9701     }
9702
9703     game.centered_player_nr = game.centered_player_nr_next;
9704     game.set_centered_player = FALSE;
9705
9706     DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9707     DrawGameDoorValues();
9708   }
9709 #endif
9710
9711   for (i = 0; i < MAX_PLAYERS; i++)
9712   {
9713     int actual_player_action = stored_player[i].effective_action;
9714
9715 #if 1
9716     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9717        - rnd_equinox_tetrachloride 048
9718        - rnd_equinox_tetrachloride_ii 096
9719        - rnd_emanuel_schmieg 002
9720        - doctor_sloan_ww 001, 020
9721     */
9722     if (stored_player[i].MovPos == 0)
9723       CheckGravityMovement(&stored_player[i]);
9724 #endif
9725
9726     /* overwrite programmed action with tape action */
9727     if (stored_player[i].programmed_action)
9728       actual_player_action = stored_player[i].programmed_action;
9729
9730 #if 1
9731     PlayerActions(&stored_player[i], actual_player_action);
9732 #else
9733     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9734
9735     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9736       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9737 #endif
9738
9739     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9740   }
9741
9742 #if 0
9743   network_player_action_received = FALSE;
9744 #endif
9745
9746   ScrollScreen(NULL, SCROLL_GO_ON);
9747
9748   /* for backwards compatibility, the following code emulates a fixed bug that
9749      occured when pushing elements (causing elements that just made their last
9750      pushing step to already (if possible) make their first falling step in the
9751      same game frame, which is bad); this code is also needed to use the famous
9752      "spring push bug" which is used in older levels and might be wanted to be
9753      used also in newer levels, but in this case the buggy pushing code is only
9754      affecting the "spring" element and no other elements */
9755
9756   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9757   {
9758     for (i = 0; i < MAX_PLAYERS; i++)
9759     {
9760       struct PlayerInfo *player = &stored_player[i];
9761       int x = player->jx;
9762       int y = player->jy;
9763
9764       if (player->active && player->is_pushing && player->is_moving &&
9765           IS_MOVING(x, y) &&
9766           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9767            Feld[x][y] == EL_SPRING))
9768       {
9769         ContinueMoving(x, y);
9770
9771         /* continue moving after pushing (this is actually a bug) */
9772         if (!IS_MOVING(x, y))
9773         {
9774           Stop[x][y] = FALSE;
9775         }
9776       }
9777     }
9778   }
9779
9780 #if 1
9781   SCAN_PLAYFIELD(x, y)
9782 #else
9783   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9784 #endif
9785   {
9786     ChangeCount[x][y] = 0;
9787     ChangeEvent[x][y] = -1;
9788
9789     /* this must be handled before main playfield loop */
9790     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9791     {
9792       MovDelay[x][y]--;
9793       if (MovDelay[x][y] <= 0)
9794         RemoveField(x, y);
9795     }
9796
9797 #if USE_NEW_SNAP_DELAY
9798     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9799     {
9800       MovDelay[x][y]--;
9801       if (MovDelay[x][y] <= 0)
9802       {
9803         RemoveField(x, y);
9804         DrawLevelField(x, y);
9805
9806         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
9807       }
9808     }
9809 #endif
9810
9811 #if DEBUG
9812     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9813     {
9814       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9815       printf("GameActions(): This should never happen!\n");
9816
9817       ChangePage[x][y] = -1;
9818     }
9819 #endif
9820
9821     Stop[x][y] = FALSE;
9822     if (WasJustMoving[x][y] > 0)
9823       WasJustMoving[x][y]--;
9824     if (WasJustFalling[x][y] > 0)
9825       WasJustFalling[x][y]--;
9826     if (CheckCollision[x][y] > 0)
9827       CheckCollision[x][y]--;
9828
9829     GfxFrame[x][y]++;
9830
9831     /* reset finished pushing action (not done in ContinueMoving() to allow
9832        continuous pushing animation for elements with zero push delay) */
9833     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9834     {
9835       ResetGfxAnimation(x, y);
9836       DrawLevelField(x, y);
9837     }
9838
9839 #if DEBUG
9840     if (IS_BLOCKED(x, y))
9841     {
9842       int oldx, oldy;
9843
9844       Blocked2Moving(x, y, &oldx, &oldy);
9845       if (!IS_MOVING(oldx, oldy))
9846       {
9847         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9848         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9849         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9850         printf("GameActions(): This should never happen!\n");
9851       }
9852     }
9853 #endif
9854   }
9855
9856 #if 1
9857   SCAN_PLAYFIELD(x, y)
9858 #else
9859   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9860 #endif
9861   {
9862     element = Feld[x][y];
9863     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9864
9865 #if 0
9866     printf("::: %d,%d\n", x, y);
9867
9868     if (element == EL_ROCK)
9869       printf("::: Yo man! Rocks can fall!\n");
9870 #endif
9871
9872 #if 1
9873     ResetGfxFrame(x, y, TRUE);
9874 #else
9875     if (graphic_info[graphic].anim_global_sync)
9876       GfxFrame[x][y] = FrameCounter;
9877     else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9878     {
9879       int old_gfx_frame = GfxFrame[x][y];
9880
9881       GfxFrame[x][y] = CustomValue[x][y];
9882
9883 #if 1
9884       if (GfxFrame[x][y] != old_gfx_frame)
9885 #endif
9886         DrawLevelGraphicAnimation(x, y, graphic);
9887     }
9888     else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9889     {
9890       int old_gfx_frame = GfxFrame[x][y];
9891
9892       GfxFrame[x][y] = element_info[element].collect_score;
9893
9894 #if 1
9895       if (GfxFrame[x][y] != old_gfx_frame)
9896 #endif
9897         DrawLevelGraphicAnimation(x, y, graphic);
9898     }
9899     else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
9900     {
9901       int old_gfx_frame = GfxFrame[x][y];
9902
9903       GfxFrame[x][y] = ChangeDelay[x][y];
9904
9905 #if 1
9906       if (GfxFrame[x][y] != old_gfx_frame)
9907 #endif
9908         DrawLevelGraphicAnimation(x, y, graphic);
9909     }
9910 #endif
9911
9912     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9913         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9914       ResetRandomAnimationValue(x, y);
9915
9916     SetRandomAnimationValue(x, y);
9917
9918     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9919
9920     if (IS_INACTIVE(element))
9921     {
9922       if (IS_ANIMATED(graphic))
9923         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9924
9925       continue;
9926     }
9927
9928     /* this may take place after moving, so 'element' may have changed */
9929     if (IS_CHANGING(x, y) &&
9930         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9931     {
9932       int page = element_info[element].event_page_nr[CE_DELAY];
9933 #if 0
9934       HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9935 #else
9936
9937 #if 0
9938       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9939 #endif
9940
9941 #if 0
9942       if (element == EL_CUSTOM_255)
9943         printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9944 #endif
9945
9946 #if 1
9947       HandleElementChange(x, y, page);
9948 #else
9949       if (CAN_CHANGE(element))
9950         HandleElementChange(x, y, page);
9951
9952       if (HAS_ACTION(element))
9953         ExecuteCustomElementAction(x, y, element, page);
9954 #endif
9955
9956 #endif
9957
9958       element = Feld[x][y];
9959       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9960     }
9961
9962     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9963     {
9964       StartMoving(x, y);
9965
9966       element = Feld[x][y];
9967       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9968
9969       if (IS_ANIMATED(graphic) &&
9970           !IS_MOVING(x, y) &&
9971           !Stop[x][y])
9972         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9973
9974       if (IS_GEM(element) || element == EL_SP_INFOTRON)
9975         EdelsteinFunkeln(x, y);
9976     }
9977     else if ((element == EL_ACID ||
9978               element == EL_EXIT_OPEN ||
9979               element == EL_SP_EXIT_OPEN ||
9980               element == EL_SP_TERMINAL ||
9981               element == EL_SP_TERMINAL_ACTIVE ||
9982               element == EL_EXTRA_TIME ||
9983               element == EL_SHIELD_NORMAL ||
9984               element == EL_SHIELD_DEADLY) &&
9985              IS_ANIMATED(graphic))
9986       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9987     else if (IS_MOVING(x, y))
9988       ContinueMoving(x, y);
9989     else if (IS_ACTIVE_BOMB(element))
9990       CheckDynamite(x, y);
9991     else if (element == EL_AMOEBA_GROWING)
9992       AmoebeWaechst(x, y);
9993     else if (element == EL_AMOEBA_SHRINKING)
9994       AmoebaDisappearing(x, y);
9995
9996 #if !USE_NEW_AMOEBA_CODE
9997     else if (IS_AMOEBALIVE(element))
9998       AmoebeAbleger(x, y);
9999 #endif
10000
10001     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10002       Life(x, y);
10003     else if (element == EL_EXIT_CLOSED)
10004       CheckExit(x, y);
10005     else if (element == EL_SP_EXIT_CLOSED)
10006       CheckExitSP(x, y);
10007     else if (element == EL_EXPANDABLE_WALL_GROWING)
10008       MauerWaechst(x, y);
10009     else if (element == EL_EXPANDABLE_WALL ||
10010              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10011              element == EL_EXPANDABLE_WALL_VERTICAL ||
10012              element == EL_EXPANDABLE_WALL_ANY)
10013       MauerAbleger(x, y);
10014     else if (element == EL_FLAMES)
10015       CheckForDragon(x, y);
10016     else if (element == EL_EXPLOSION)
10017       ; /* drawing of correct explosion animation is handled separately */
10018     else if (element == EL_ELEMENT_SNAPPING ||
10019              element == EL_DIAGONAL_SHRINKING ||
10020              element == EL_DIAGONAL_GROWING)
10021     {
10022 #if 1
10023       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10024
10025       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10026 #endif
10027     }
10028     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10029       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10030
10031 #if 0
10032     if (element == EL_CUSTOM_255 ||
10033         element == EL_CUSTOM_256)
10034       DrawLevelGraphicAnimation(x, y, graphic);
10035 #endif
10036
10037     if (IS_BELT_ACTIVE(element))
10038       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10039
10040     if (game.magic_wall_active)
10041     {
10042       int jx = local_player->jx, jy = local_player->jy;
10043
10044       /* play the element sound at the position nearest to the player */
10045       if ((element == EL_MAGIC_WALL_FULL ||
10046            element == EL_MAGIC_WALL_ACTIVE ||
10047            element == EL_MAGIC_WALL_EMPTYING ||
10048            element == EL_BD_MAGIC_WALL_FULL ||
10049            element == EL_BD_MAGIC_WALL_ACTIVE ||
10050            element == EL_BD_MAGIC_WALL_EMPTYING) &&
10051           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10052       {
10053         magic_wall_x = x;
10054         magic_wall_y = y;
10055       }
10056     }
10057   }
10058
10059 #if USE_NEW_AMOEBA_CODE
10060   /* new experimental amoeba growth stuff */
10061   if (!(FrameCounter % 8))
10062   {
10063     static unsigned long random = 1684108901;
10064
10065     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10066     {
10067       x = RND(lev_fieldx);
10068       y = RND(lev_fieldy);
10069       element = Feld[x][y];
10070
10071       if (!IS_PLAYER(x,y) &&
10072           (element == EL_EMPTY ||
10073            CAN_GROW_INTO(element) ||
10074            element == EL_QUICKSAND_EMPTY ||
10075            element == EL_ACID_SPLASH_LEFT ||
10076            element == EL_ACID_SPLASH_RIGHT))
10077       {
10078         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10079             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10080             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10081             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10082           Feld[x][y] = EL_AMOEBA_DROP;
10083       }
10084
10085       random = random * 129 + 1;
10086     }
10087   }
10088 #endif
10089
10090 #if 0
10091   if (game.explosions_delayed)
10092 #endif
10093   {
10094     game.explosions_delayed = FALSE;
10095
10096 #if 1
10097     SCAN_PLAYFIELD(x, y)
10098 #else
10099     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10100 #endif
10101     {
10102       element = Feld[x][y];
10103
10104       if (ExplodeField[x][y])
10105         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10106       else if (element == EL_EXPLOSION)
10107         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10108
10109       ExplodeField[x][y] = EX_TYPE_NONE;
10110     }
10111
10112     game.explosions_delayed = TRUE;
10113   }
10114
10115   if (game.magic_wall_active)
10116   {
10117     if (!(game.magic_wall_time_left % 4))
10118     {
10119       int element = Feld[magic_wall_x][magic_wall_y];
10120
10121       if (element == EL_BD_MAGIC_WALL_FULL ||
10122           element == EL_BD_MAGIC_WALL_ACTIVE ||
10123           element == EL_BD_MAGIC_WALL_EMPTYING)
10124         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10125       else
10126         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10127     }
10128
10129     if (game.magic_wall_time_left > 0)
10130     {
10131       game.magic_wall_time_left--;
10132       if (!game.magic_wall_time_left)
10133       {
10134 #if 1
10135         SCAN_PLAYFIELD(x, y)
10136 #else
10137         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10138 #endif
10139         {
10140           element = Feld[x][y];
10141
10142           if (element == EL_MAGIC_WALL_ACTIVE ||
10143               element == EL_MAGIC_WALL_FULL)
10144           {
10145             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10146             DrawLevelField(x, y);
10147           }
10148           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10149                    element == EL_BD_MAGIC_WALL_FULL)
10150           {
10151             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10152             DrawLevelField(x, y);
10153           }
10154         }
10155
10156         game.magic_wall_active = FALSE;
10157       }
10158     }
10159   }
10160
10161   if (game.light_time_left > 0)
10162   {
10163     game.light_time_left--;
10164
10165     if (game.light_time_left == 0)
10166       RedrawAllLightSwitchesAndInvisibleElements();
10167   }
10168
10169   if (game.timegate_time_left > 0)
10170   {
10171     game.timegate_time_left--;
10172
10173     if (game.timegate_time_left == 0)
10174       CloseAllOpenTimegates();
10175   }
10176
10177   if (game.lenses_time_left > 0)
10178   {
10179     game.lenses_time_left--;
10180
10181     if (game.lenses_time_left == 0)
10182       RedrawAllInvisibleElementsForLenses();
10183   }
10184
10185   if (game.magnify_time_left > 0)
10186   {
10187     game.magnify_time_left--;
10188
10189     if (game.magnify_time_left == 0)
10190       RedrawAllInvisibleElementsForMagnifier();
10191   }
10192
10193   for (i = 0; i < MAX_PLAYERS; i++)
10194   {
10195     struct PlayerInfo *player = &stored_player[i];
10196
10197     if (SHIELD_ON(player))
10198     {
10199       if (player->shield_deadly_time_left)
10200         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10201       else if (player->shield_normal_time_left)
10202         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10203     }
10204   }
10205
10206   CheckLevelTime();
10207
10208   DrawAllPlayers();
10209   PlayAllPlayersSound();
10210
10211   if (options.debug)                    /* calculate frames per second */
10212   {
10213     static unsigned long fps_counter = 0;
10214     static int fps_frames = 0;
10215     unsigned long fps_delay_ms = Counter() - fps_counter;
10216
10217     fps_frames++;
10218
10219     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10220     {
10221       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10222
10223       fps_frames = 0;
10224       fps_counter = Counter();
10225     }
10226
10227     redraw_mask |= REDRAW_FPS;
10228   }
10229
10230   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10231
10232   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10233   {
10234     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10235
10236     local_player->show_envelope = 0;
10237   }
10238
10239   /* use random number generator in every frame to make it less predictable */
10240   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10241     RND(1);
10242 }
10243
10244 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10245 {
10246   int min_x = x, min_y = y, max_x = x, max_y = y;
10247   int i;
10248
10249   for (i = 0; i < MAX_PLAYERS; i++)
10250   {
10251     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10252
10253     if (!stored_player[i].active || &stored_player[i] == player)
10254       continue;
10255
10256     min_x = MIN(min_x, jx);
10257     min_y = MIN(min_y, jy);
10258     max_x = MAX(max_x, jx);
10259     max_y = MAX(max_y, jy);
10260   }
10261
10262   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10263 }
10264
10265 static boolean AllPlayersInVisibleScreen()
10266 {
10267   int i;
10268
10269   for (i = 0; i < MAX_PLAYERS; i++)
10270   {
10271     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10272
10273     if (!stored_player[i].active)
10274       continue;
10275
10276     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10277       return FALSE;
10278   }
10279
10280   return TRUE;
10281 }
10282
10283 void ScrollLevel(int dx, int dy)
10284 {
10285   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10286   int x, y;
10287
10288   BlitBitmap(drawto_field, drawto_field,
10289              FX + TILEX * (dx == -1) - softscroll_offset,
10290              FY + TILEY * (dy == -1) - softscroll_offset,
10291              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10292              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10293              FX + TILEX * (dx == 1) - softscroll_offset,
10294              FY + TILEY * (dy == 1) - softscroll_offset);
10295
10296   if (dx)
10297   {
10298     x = (dx == 1 ? BX1 : BX2);
10299     for (y = BY1; y <= BY2; y++)
10300       DrawScreenField(x, y);
10301   }
10302
10303   if (dy)
10304   {
10305     y = (dy == 1 ? BY1 : BY2);
10306     for (x = BX1; x <= BX2; x++)
10307       DrawScreenField(x, y);
10308   }
10309
10310   redraw_mask |= REDRAW_FIELD;
10311 }
10312
10313 static boolean canFallDown(struct PlayerInfo *player)
10314 {
10315   int jx = player->jx, jy = player->jy;
10316
10317   return (IN_LEV_FIELD(jx, jy + 1) &&
10318           (IS_FREE(jx, jy + 1) ||
10319            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10320           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10321           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10322 }
10323
10324 static boolean canPassField(int x, int y, int move_dir)
10325 {
10326   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10327   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10328   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10329   int nextx = x + dx;
10330   int nexty = y + dy;
10331   int element = Feld[x][y];
10332
10333   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10334           !CAN_MOVE(element) &&
10335           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10336           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10337           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10338 }
10339
10340 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10341 {
10342   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10343   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10344   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10345   int newx = x + dx;
10346   int newy = y + dy;
10347
10348   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10349           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10350           (IS_DIGGABLE(Feld[newx][newy]) ||
10351            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10352            canPassField(newx, newy, move_dir)));
10353 }
10354
10355 static void CheckGravityMovement(struct PlayerInfo *player)
10356 {
10357 #if USE_PLAYER_GRAVITY
10358   if (player->gravity && !player->programmed_action)
10359 #else
10360   if (game.gravity && !player->programmed_action)
10361 #endif
10362   {
10363     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10364     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10365     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10366     int jx = player->jx, jy = player->jy;
10367     boolean player_is_moving_to_valid_field =
10368       (!player_is_snapping &&
10369        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10370         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10371     boolean player_can_fall_down = canFallDown(player);
10372
10373     if (player_can_fall_down &&
10374         !player_is_moving_to_valid_field)
10375       player->programmed_action = MV_DOWN;
10376   }
10377 }
10378
10379 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10380 {
10381   return CheckGravityMovement(player);
10382
10383 #if USE_PLAYER_GRAVITY
10384   if (player->gravity && !player->programmed_action)
10385 #else
10386   if (game.gravity && !player->programmed_action)
10387 #endif
10388   {
10389     int jx = player->jx, jy = player->jy;
10390     boolean field_under_player_is_free =
10391       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10392     boolean player_is_standing_on_valid_field =
10393       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10394        (IS_WALKABLE(Feld[jx][jy]) &&
10395         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10396
10397     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10398       player->programmed_action = MV_DOWN;
10399   }
10400 }
10401
10402 /*
10403   MovePlayerOneStep()
10404   -----------------------------------------------------------------------------
10405   dx, dy:               direction (non-diagonal) to try to move the player to
10406   real_dx, real_dy:     direction as read from input device (can be diagonal)
10407 */
10408
10409 boolean MovePlayerOneStep(struct PlayerInfo *player,
10410                           int dx, int dy, int real_dx, int real_dy)
10411 {
10412   int jx = player->jx, jy = player->jy;
10413   int new_jx = jx + dx, new_jy = jy + dy;
10414 #if !USE_FIXED_DONT_RUN_INTO
10415   int element;
10416 #endif
10417   int can_move;
10418   boolean player_can_move = !player->cannot_move;
10419
10420   if (!player->active || (!dx && !dy))
10421     return MP_NO_ACTION;
10422
10423   player->MovDir = (dx < 0 ? MV_LEFT :
10424                     dx > 0 ? MV_RIGHT :
10425                     dy < 0 ? MV_UP :
10426                     dy > 0 ? MV_DOWN :  MV_NONE);
10427
10428   if (!IN_LEV_FIELD(new_jx, new_jy))
10429     return MP_NO_ACTION;
10430
10431   if (!player_can_move)
10432   {
10433 #if 1
10434     if (player->MovPos == 0)
10435     {
10436       player->is_moving = FALSE;
10437       player->is_digging = FALSE;
10438       player->is_collecting = FALSE;
10439       player->is_snapping = FALSE;
10440       player->is_pushing = FALSE;
10441     }
10442 #else
10443     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10444     SnapField(player, 0, 0);
10445 #endif
10446
10447 #if 0
10448     return MP_NO_ACTION;
10449 #endif
10450   }
10451
10452 #if 1
10453   if (!options.network && game.centered_player_nr == -1 &&
10454       !AllPlayersInSight(player, new_jx, new_jy))
10455     return MP_NO_ACTION;
10456 #else
10457   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10458     return MP_NO_ACTION;
10459 #endif
10460
10461 #if !USE_FIXED_DONT_RUN_INTO
10462   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10463
10464   /* (moved to DigField()) */
10465   if (player_can_move && DONT_RUN_INTO(element))
10466   {
10467     if (element == EL_ACID && dx == 0 && dy == 1)
10468     {
10469       SplashAcid(new_jx, new_jy);
10470       Feld[jx][jy] = EL_PLAYER_1;
10471       InitMovingField(jx, jy, MV_DOWN);
10472       Store[jx][jy] = EL_ACID;
10473       ContinueMoving(jx, jy);
10474       BuryPlayer(player);
10475     }
10476     else
10477       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10478
10479     return MP_MOVING;
10480   }
10481 #endif
10482
10483   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10484 #if 0
10485 #if USE_FIXED_DONT_RUN_INTO
10486   if (can_move == MP_DONT_RUN_INTO)
10487     return MP_MOVING;
10488 #endif
10489 #endif
10490   if (can_move != MP_MOVING)
10491     return can_move;
10492
10493 #if USE_FIXED_DONT_RUN_INTO
10494 #endif
10495
10496   /* check if DigField() has caused relocation of the player */
10497   if (player->jx != jx || player->jy != jy)
10498     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10499
10500   StorePlayer[jx][jy] = 0;
10501   player->last_jx = jx;
10502   player->last_jy = jy;
10503   player->jx = new_jx;
10504   player->jy = new_jy;
10505   StorePlayer[new_jx][new_jy] = player->element_nr;
10506
10507   if (player->move_delay_value_next != -1)
10508   {
10509     player->move_delay_value = player->move_delay_value_next;
10510     player->move_delay_value_next = -1;
10511   }
10512
10513   player->MovPos =
10514     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10515
10516   player->step_counter++;
10517
10518   PlayerVisit[jx][jy] = FrameCounter;
10519
10520   ScrollPlayer(player, SCROLL_INIT);
10521
10522   return MP_MOVING;
10523 }
10524
10525 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10526 {
10527   int jx = player->jx, jy = player->jy;
10528   int old_jx = jx, old_jy = jy;
10529   int moved = MP_NO_ACTION;
10530
10531   if (!player->active)
10532     return FALSE;
10533
10534   if (!dx && !dy)
10535   {
10536     if (player->MovPos == 0)
10537     {
10538       player->is_moving = FALSE;
10539       player->is_digging = FALSE;
10540       player->is_collecting = FALSE;
10541       player->is_snapping = FALSE;
10542       player->is_pushing = FALSE;
10543     }
10544
10545     return FALSE;
10546   }
10547
10548   if (player->move_delay > 0)
10549     return FALSE;
10550
10551   player->move_delay = -1;              /* set to "uninitialized" value */
10552
10553   /* store if player is automatically moved to next field */
10554   player->is_auto_moving = (player->programmed_action != MV_NONE);
10555
10556   /* remove the last programmed player action */
10557   player->programmed_action = 0;
10558
10559   if (player->MovPos)
10560   {
10561     /* should only happen if pre-1.2 tape recordings are played */
10562     /* this is only for backward compatibility */
10563
10564     int original_move_delay_value = player->move_delay_value;
10565
10566 #if DEBUG
10567     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10568            tape.counter);
10569 #endif
10570
10571     /* scroll remaining steps with finest movement resolution */
10572     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10573
10574     while (player->MovPos)
10575     {
10576       ScrollPlayer(player, SCROLL_GO_ON);
10577       ScrollScreen(NULL, SCROLL_GO_ON);
10578
10579       AdvanceFrameAndPlayerCounters(player->index_nr);
10580
10581       DrawAllPlayers();
10582       BackToFront();
10583     }
10584
10585     player->move_delay_value = original_move_delay_value;
10586   }
10587
10588   if (player->last_move_dir & MV_HORIZONTAL)
10589   {
10590     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10591       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10592   }
10593   else
10594   {
10595     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10596       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10597   }
10598
10599   jx = player->jx;
10600   jy = player->jy;
10601
10602 #if 1
10603   if (moved & MP_MOVING && !ScreenMovPos &&
10604       (player->index_nr == game.centered_player_nr ||
10605        game.centered_player_nr == -1))
10606 #else
10607   if (moved & MP_MOVING && !ScreenMovPos &&
10608       (player == local_player || !options.network))
10609 #endif
10610   {
10611     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10612     int offset = (setup.scroll_delay ? 3 : 0);
10613
10614     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10615     {
10616       /* actual player has left the screen -- scroll in that direction */
10617       if (jx != old_jx)         /* player has moved horizontally */
10618         scroll_x += (jx - old_jx);
10619       else                      /* player has moved vertically */
10620         scroll_y += (jy - old_jy);
10621     }
10622     else
10623     {
10624       if (jx != old_jx)         /* player has moved horizontally */
10625       {
10626         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10627             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10628           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10629
10630         /* don't scroll over playfield boundaries */
10631         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10632           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10633
10634         /* don't scroll more than one field at a time */
10635         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10636
10637         /* don't scroll against the player's moving direction */
10638         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10639             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10640           scroll_x = old_scroll_x;
10641       }
10642       else                      /* player has moved vertically */
10643       {
10644         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10645             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10646           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10647
10648         /* don't scroll over playfield boundaries */
10649         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10650           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10651
10652         /* don't scroll more than one field at a time */
10653         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10654
10655         /* don't scroll against the player's moving direction */
10656         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10657             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10658           scroll_y = old_scroll_y;
10659       }
10660     }
10661
10662     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10663     {
10664 #if 1
10665       if (!options.network && game.centered_player_nr == -1 &&
10666           !AllPlayersInVisibleScreen())
10667       {
10668         scroll_x = old_scroll_x;
10669         scroll_y = old_scroll_y;
10670       }
10671       else
10672 #else
10673       if (!options.network && !AllPlayersInVisibleScreen())
10674       {
10675         scroll_x = old_scroll_x;
10676         scroll_y = old_scroll_y;
10677       }
10678       else
10679 #endif
10680       {
10681         ScrollScreen(player, SCROLL_INIT);
10682         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10683       }
10684     }
10685   }
10686
10687   player->StepFrame = 0;
10688
10689   if (moved & MP_MOVING)
10690   {
10691     if (old_jx != jx && old_jy == jy)
10692       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10693     else if (old_jx == jx && old_jy != jy)
10694       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10695
10696     DrawLevelField(jx, jy);     /* for "crumbled sand" */
10697
10698     player->last_move_dir = player->MovDir;
10699     player->is_moving = TRUE;
10700     player->is_snapping = FALSE;
10701     player->is_switching = FALSE;
10702     player->is_dropping = FALSE;
10703     player->is_dropping_pressed = FALSE;
10704     player->drop_pressed_delay = 0;
10705   }
10706   else
10707   {
10708     CheckGravityMovementWhenNotMoving(player);
10709
10710     player->is_moving = FALSE;
10711
10712     /* at this point, the player is allowed to move, but cannot move right now
10713        (e.g. because of something blocking the way) -- ensure that the player
10714        is also allowed to move in the next frame (in old versions before 3.1.1,
10715        the player was forced to wait again for eight frames before next try) */
10716
10717     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10718       player->move_delay = 0;   /* allow direct movement in the next frame */
10719   }
10720
10721   if (player->move_delay == -1)         /* not yet initialized by DigField() */
10722     player->move_delay = player->move_delay_value;
10723
10724   if (game.engine_version < VERSION_IDENT(3,0,7,0))
10725   {
10726     TestIfPlayerTouchesBadThing(jx, jy);
10727     TestIfPlayerTouchesCustomElement(jx, jy);
10728   }
10729
10730   if (!player->active)
10731     RemovePlayer(player);
10732
10733   return moved;
10734 }
10735
10736 void ScrollPlayer(struct PlayerInfo *player, int mode)
10737 {
10738   int jx = player->jx, jy = player->jy;
10739   int last_jx = player->last_jx, last_jy = player->last_jy;
10740   int move_stepsize = TILEX / player->move_delay_value;
10741
10742 #if USE_NEW_PLAYER_SPEED
10743   if (!player->active)
10744     return;
10745
10746   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
10747     return;
10748 #else
10749   if (!player->active || player->MovPos == 0)
10750     return;
10751 #endif
10752
10753   if (mode == SCROLL_INIT)
10754   {
10755     player->actual_frame_counter = FrameCounter;
10756     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10757
10758     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10759         Feld[last_jx][last_jy] == EL_EMPTY)
10760     {
10761       int last_field_block_delay = 0;   /* start with no blocking at all */
10762       int block_delay_adjustment = player->block_delay_adjustment;
10763
10764       /* if player blocks last field, add delay for exactly one move */
10765       if (player->block_last_field)
10766       {
10767         last_field_block_delay += player->move_delay_value;
10768
10769         /* when blocking enabled, prevent moving up despite gravity */
10770 #if USE_PLAYER_GRAVITY
10771         if (player->gravity && player->MovDir == MV_UP)
10772           block_delay_adjustment = -1;
10773 #else
10774         if (game.gravity && player->MovDir == MV_UP)
10775           block_delay_adjustment = -1;
10776 #endif
10777       }
10778
10779       /* add block delay adjustment (also possible when not blocking) */
10780       last_field_block_delay += block_delay_adjustment;
10781
10782       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10783       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10784     }
10785
10786 #if USE_NEW_PLAYER_SPEED
10787     if (player->MovPos != 0)    /* player has not yet reached destination */
10788       return;
10789 #else
10790     return;
10791 #endif
10792   }
10793   else if (!FrameReached(&player->actual_frame_counter, 1))
10794     return;
10795
10796 #if 0
10797   printf("::: player->MovPos: %d -> %d\n",
10798          player->MovPos,
10799          player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10800 #endif
10801
10802 #if USE_NEW_PLAYER_SPEED
10803   if (player->MovPos != 0)
10804   {
10805     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10806     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10807
10808     /* before DrawPlayer() to draw correct player graphic for this case */
10809     if (player->MovPos == 0)
10810       CheckGravityMovement(player);
10811   }
10812 #else
10813   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10814   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10815
10816   /* before DrawPlayer() to draw correct player graphic for this case */
10817   if (player->MovPos == 0)
10818     CheckGravityMovement(player);
10819 #endif
10820
10821   if (player->MovPos == 0)      /* player reached destination field */
10822   {
10823 #if 0
10824     printf("::: player reached destination field\n");
10825 #endif
10826
10827     if (player->move_delay_reset_counter > 0)
10828     {
10829       player->move_delay_reset_counter--;
10830
10831       if (player->move_delay_reset_counter == 0)
10832       {
10833         /* continue with normal speed after quickly moving through gate */
10834         HALVE_PLAYER_SPEED(player);
10835
10836         /* be able to make the next move without delay */
10837         player->move_delay = 0;
10838       }
10839     }
10840
10841     player->last_jx = jx;
10842     player->last_jy = jy;
10843
10844     if (Feld[jx][jy] == EL_EXIT_OPEN ||
10845         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10846         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
10847     {
10848       DrawPlayer(player);       /* needed here only to cleanup last field */
10849       RemovePlayer(player);
10850
10851       if (local_player->friends_still_needed == 0 ||
10852           IS_SP_ELEMENT(Feld[jx][jy]))
10853         player->LevelSolved = player->GameOver = TRUE;
10854     }
10855
10856     /* this breaks one level: "machine", level 000 */
10857     {
10858       int move_direction = player->MovDir;
10859       int enter_side = MV_DIR_OPPOSITE(move_direction);
10860       int leave_side = move_direction;
10861       int old_jx = last_jx;
10862       int old_jy = last_jy;
10863       int old_element = Feld[old_jx][old_jy];
10864       int new_element = Feld[jx][jy];
10865
10866       if (IS_CUSTOM_ELEMENT(old_element))
10867         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10868                                    CE_LEFT_BY_PLAYER,
10869                                    player->index_bit, leave_side);
10870
10871       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10872                                           CE_PLAYER_LEAVES_X,
10873                                           player->index_bit, leave_side);
10874
10875       if (IS_CUSTOM_ELEMENT(new_element))
10876         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10877                                    player->index_bit, enter_side);
10878
10879       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10880                                           CE_PLAYER_ENTERS_X,
10881                                           player->index_bit, enter_side);
10882
10883       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10884                                         CE_MOVE_OF_X, move_direction);
10885     }
10886
10887     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10888     {
10889       TestIfPlayerTouchesBadThing(jx, jy);
10890       TestIfPlayerTouchesCustomElement(jx, jy);
10891
10892       /* needed because pushed element has not yet reached its destination,
10893          so it would trigger a change event at its previous field location */
10894       if (!player->is_pushing)
10895         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
10896
10897       if (!player->active)
10898         RemovePlayer(player);
10899     }
10900
10901     if (level.use_step_counter)
10902     {
10903       int i;
10904
10905       TimePlayed++;
10906
10907       if (TimeLeft > 0)
10908       {
10909         TimeLeft--;
10910
10911         if (TimeLeft <= 10 && setup.time_limit)
10912           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10913
10914         DrawGameValue_Time(TimeLeft);
10915
10916         if (!TimeLeft && setup.time_limit)
10917           for (i = 0; i < MAX_PLAYERS; i++)
10918             KillPlayer(&stored_player[i]);
10919       }
10920       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10921         DrawGameValue_Time(TimePlayed);
10922     }
10923
10924     if (tape.single_step && tape.recording && !tape.pausing &&
10925         !player->programmed_action)
10926       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10927   }
10928 }
10929
10930 void ScrollScreen(struct PlayerInfo *player, int mode)
10931 {
10932   static unsigned long screen_frame_counter = 0;
10933
10934   if (mode == SCROLL_INIT)
10935   {
10936     /* set scrolling step size according to actual player's moving speed */
10937     ScrollStepSize = TILEX / player->move_delay_value;
10938
10939     screen_frame_counter = FrameCounter;
10940     ScreenMovDir = player->MovDir;
10941     ScreenMovPos = player->MovPos;
10942     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10943     return;
10944   }
10945   else if (!FrameReached(&screen_frame_counter, 1))
10946     return;
10947
10948   if (ScreenMovPos)
10949   {
10950     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10951     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10952     redraw_mask |= REDRAW_FIELD;
10953   }
10954   else
10955     ScreenMovDir = MV_NONE;
10956 }
10957
10958 void TestIfPlayerTouchesCustomElement(int x, int y)
10959 {
10960   static int xy[4][2] =
10961   {
10962     { 0, -1 },
10963     { -1, 0 },
10964     { +1, 0 },
10965     { 0, +1 }
10966   };
10967   static int trigger_sides[4][2] =
10968   {
10969     /* center side       border side */
10970     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10971     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10972     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10973     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10974   };
10975   static int touch_dir[4] =
10976   {
10977     MV_LEFT | MV_RIGHT,
10978     MV_UP   | MV_DOWN,
10979     MV_UP   | MV_DOWN,
10980     MV_LEFT | MV_RIGHT
10981   };
10982   int center_element = Feld[x][y];      /* should always be non-moving! */
10983   int i;
10984
10985   for (i = 0; i < NUM_DIRECTIONS; i++)
10986   {
10987     int xx = x + xy[i][0];
10988     int yy = y + xy[i][1];
10989     int center_side = trigger_sides[i][0];
10990     int border_side = trigger_sides[i][1];
10991     int border_element;
10992
10993     if (!IN_LEV_FIELD(xx, yy))
10994       continue;
10995
10996     if (IS_PLAYER(x, y))
10997     {
10998       struct PlayerInfo *player = PLAYERINFO(x, y);
10999
11000       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11001         border_element = Feld[xx][yy];          /* may be moving! */
11002       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11003         border_element = Feld[xx][yy];
11004       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11005         border_element = MovingOrBlocked2Element(xx, yy);
11006       else
11007         continue;               /* center and border element do not touch */
11008
11009       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11010                                  player->index_bit, border_side);
11011       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11012                                           CE_PLAYER_TOUCHES_X,
11013                                           player->index_bit, border_side);
11014     }
11015     else if (IS_PLAYER(xx, yy))
11016     {
11017       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11018
11019       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11020       {
11021         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11022           continue;             /* center and border element do not touch */
11023       }
11024
11025       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11026                                  player->index_bit, center_side);
11027       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11028                                           CE_PLAYER_TOUCHES_X,
11029                                           player->index_bit, center_side);
11030       break;
11031     }
11032   }
11033 }
11034
11035 #if USE_ELEMENT_TOUCHING_BUGFIX
11036
11037 void TestIfElementTouchesCustomElement(int x, int y)
11038 {
11039   static int xy[4][2] =
11040   {
11041     { 0, -1 },
11042     { -1, 0 },
11043     { +1, 0 },
11044     { 0, +1 }
11045   };
11046   static int trigger_sides[4][2] =
11047   {
11048     /* center side      border side */
11049     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11050     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11051     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11052     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11053   };
11054   static int touch_dir[4] =
11055   {
11056     MV_LEFT | MV_RIGHT,
11057     MV_UP   | MV_DOWN,
11058     MV_UP   | MV_DOWN,
11059     MV_LEFT | MV_RIGHT
11060   };
11061   boolean change_center_element = FALSE;
11062   int center_element = Feld[x][y];      /* should always be non-moving! */
11063   int border_element_old[NUM_DIRECTIONS];
11064   int i;
11065
11066   for (i = 0; i < NUM_DIRECTIONS; i++)
11067   {
11068     int xx = x + xy[i][0];
11069     int yy = y + xy[i][1];
11070     int border_element;
11071
11072     border_element_old[i] = -1;
11073
11074     if (!IN_LEV_FIELD(xx, yy))
11075       continue;
11076
11077     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11078       border_element = Feld[xx][yy];    /* may be moving! */
11079     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11080       border_element = Feld[xx][yy];
11081     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11082       border_element = MovingOrBlocked2Element(xx, yy);
11083     else
11084       continue;                 /* center and border element do not touch */
11085
11086     border_element_old[i] = border_element;
11087   }
11088
11089   for (i = 0; i < NUM_DIRECTIONS; i++)
11090   {
11091     int xx = x + xy[i][0];
11092     int yy = y + xy[i][1];
11093     int center_side = trigger_sides[i][0];
11094     int border_element = border_element_old[i];
11095
11096     if (border_element == -1)
11097       continue;
11098
11099     /* check for change of border element */
11100     CheckElementChangeBySide(xx, yy, border_element, center_element,
11101                              CE_TOUCHING_X, center_side);
11102   }
11103
11104   for (i = 0; i < NUM_DIRECTIONS; i++)
11105   {
11106     int border_side = trigger_sides[i][1];
11107     int border_element = border_element_old[i];
11108
11109     if (border_element == -1)
11110       continue;
11111
11112     /* check for change of center element (but change it only once) */
11113     if (!change_center_element)
11114       change_center_element =
11115         CheckElementChangeBySide(x, y, center_element, border_element,
11116                                  CE_TOUCHING_X, border_side);
11117   }
11118 }
11119
11120 #else
11121
11122 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11123 {
11124   static int xy[4][2] =
11125   {
11126     { 0, -1 },
11127     { -1, 0 },
11128     { +1, 0 },
11129     { 0, +1 }
11130   };
11131   static int trigger_sides[4][2] =
11132   {
11133     /* center side      border side */
11134     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11135     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11136     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11137     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11138   };
11139   static int touch_dir[4] =
11140   {
11141     MV_LEFT | MV_RIGHT,
11142     MV_UP   | MV_DOWN,
11143     MV_UP   | MV_DOWN,
11144     MV_LEFT | MV_RIGHT
11145   };
11146   boolean change_center_element = FALSE;
11147   int center_element = Feld[x][y];      /* should always be non-moving! */
11148   int i;
11149
11150   for (i = 0; i < NUM_DIRECTIONS; i++)
11151   {
11152     int xx = x + xy[i][0];
11153     int yy = y + xy[i][1];
11154     int center_side = trigger_sides[i][0];
11155     int border_side = trigger_sides[i][1];
11156     int border_element;
11157
11158     if (!IN_LEV_FIELD(xx, yy))
11159       continue;
11160
11161     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11162       border_element = Feld[xx][yy];    /* may be moving! */
11163     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11164       border_element = Feld[xx][yy];
11165     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11166       border_element = MovingOrBlocked2Element(xx, yy);
11167     else
11168       continue;                 /* center and border element do not touch */
11169
11170     /* check for change of center element (but change it only once) */
11171     if (!change_center_element)
11172       change_center_element =
11173         CheckElementChangeBySide(x, y, center_element, border_element,
11174                                  CE_TOUCHING_X, border_side);
11175
11176     /* check for change of border element */
11177     CheckElementChangeBySide(xx, yy, border_element, center_element,
11178                              CE_TOUCHING_X, center_side);
11179   }
11180 }
11181
11182 #endif
11183
11184 void TestIfElementHitsCustomElement(int x, int y, int direction)
11185 {
11186   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11187   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11188   int hitx = x + dx, hity = y + dy;
11189   int hitting_element = Feld[x][y];
11190   int touched_element;
11191
11192   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11193     return;
11194
11195   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11196                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11197
11198   if (IN_LEV_FIELD(hitx, hity))
11199   {
11200     int opposite_direction = MV_DIR_OPPOSITE(direction);
11201     int hitting_side = direction;
11202     int touched_side = opposite_direction;
11203     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11204                           MovDir[hitx][hity] != direction ||
11205                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11206
11207     object_hit = TRUE;
11208
11209     if (object_hit)
11210     {
11211       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11212                                CE_HITTING_X, touched_side);
11213
11214       CheckElementChangeBySide(hitx, hity, touched_element,
11215                                hitting_element, CE_HIT_BY_X, hitting_side);
11216
11217       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11218                                CE_HIT_BY_SOMETHING, opposite_direction);
11219     }
11220   }
11221
11222   /* "hitting something" is also true when hitting the playfield border */
11223   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11224                            CE_HITTING_SOMETHING, direction);
11225 }
11226
11227 #if 0
11228 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11229 {
11230   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11231   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11232   int hitx = x + dx, hity = y + dy;
11233   int hitting_element = Feld[x][y];
11234   int touched_element;
11235 #if 0
11236   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11237                         !IS_FREE(hitx, hity) &&
11238                         (!IS_MOVING(hitx, hity) ||
11239                          MovDir[hitx][hity] != direction ||
11240                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11241 #endif
11242
11243   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11244     return;
11245
11246 #if 0
11247   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11248     return;
11249 #endif
11250
11251   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11252                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11253
11254   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11255                            EP_CAN_SMASH_EVERYTHING, direction);
11256
11257   if (IN_LEV_FIELD(hitx, hity))
11258   {
11259     int opposite_direction = MV_DIR_OPPOSITE(direction);
11260     int hitting_side = direction;
11261     int touched_side = opposite_direction;
11262 #if 0
11263     int touched_element = MovingOrBlocked2Element(hitx, hity);
11264 #endif
11265 #if 1
11266     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11267                           MovDir[hitx][hity] != direction ||
11268                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11269
11270     object_hit = TRUE;
11271 #endif
11272
11273     if (object_hit)
11274     {
11275       int i;
11276
11277       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11278                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11279
11280       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11281                                CE_OTHER_IS_SMASHING, touched_side);
11282
11283       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11284                                CE_OTHER_GETS_SMASHED, hitting_side);
11285     }
11286   }
11287 }
11288 #endif
11289
11290 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11291 {
11292   int i, kill_x = -1, kill_y = -1;
11293
11294   int bad_element = -1;
11295   static int test_xy[4][2] =
11296   {
11297     { 0, -1 },
11298     { -1, 0 },
11299     { +1, 0 },
11300     { 0, +1 }
11301   };
11302   static int test_dir[4] =
11303   {
11304     MV_UP,
11305     MV_LEFT,
11306     MV_RIGHT,
11307     MV_DOWN
11308   };
11309
11310   for (i = 0; i < NUM_DIRECTIONS; i++)
11311   {
11312     int test_x, test_y, test_move_dir, test_element;
11313
11314     test_x = good_x + test_xy[i][0];
11315     test_y = good_y + test_xy[i][1];
11316
11317     if (!IN_LEV_FIELD(test_x, test_y))
11318       continue;
11319
11320     test_move_dir =
11321       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11322
11323     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11324
11325     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11326        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11327     */
11328     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11329         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11330     {
11331       kill_x = test_x;
11332       kill_y = test_y;
11333       bad_element = test_element;
11334
11335       break;
11336     }
11337   }
11338
11339   if (kill_x != -1 || kill_y != -1)
11340   {
11341     if (IS_PLAYER(good_x, good_y))
11342     {
11343       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11344
11345       if (player->shield_deadly_time_left > 0 &&
11346           !IS_INDESTRUCTIBLE(bad_element))
11347         Bang(kill_x, kill_y);
11348       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11349         KillPlayer(player);
11350     }
11351     else
11352       Bang(good_x, good_y);
11353   }
11354 }
11355
11356 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11357 {
11358   int i, kill_x = -1, kill_y = -1;
11359   int bad_element = Feld[bad_x][bad_y];
11360   static int test_xy[4][2] =
11361   {
11362     { 0, -1 },
11363     { -1, 0 },
11364     { +1, 0 },
11365     { 0, +1 }
11366   };
11367   static int touch_dir[4] =
11368   {
11369     MV_LEFT | MV_RIGHT,
11370     MV_UP   | MV_DOWN,
11371     MV_UP   | MV_DOWN,
11372     MV_LEFT | MV_RIGHT
11373   };
11374   static int test_dir[4] =
11375   {
11376     MV_UP,
11377     MV_LEFT,
11378     MV_RIGHT,
11379     MV_DOWN
11380   };
11381
11382   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11383     return;
11384
11385   for (i = 0; i < NUM_DIRECTIONS; i++)
11386   {
11387     int test_x, test_y, test_move_dir, test_element;
11388
11389     test_x = bad_x + test_xy[i][0];
11390     test_y = bad_y + test_xy[i][1];
11391     if (!IN_LEV_FIELD(test_x, test_y))
11392       continue;
11393
11394     test_move_dir =
11395       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11396
11397     test_element = Feld[test_x][test_y];
11398
11399     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11400        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11401     */
11402     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11403         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11404     {
11405       /* good thing is player or penguin that does not move away */
11406       if (IS_PLAYER(test_x, test_y))
11407       {
11408         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11409
11410         if (bad_element == EL_ROBOT && player->is_moving)
11411           continue;     /* robot does not kill player if he is moving */
11412
11413         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11414         {
11415           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11416             continue;           /* center and border element do not touch */
11417         }
11418
11419         kill_x = test_x;
11420         kill_y = test_y;
11421         break;
11422       }
11423       else if (test_element == EL_PENGUIN)
11424       {
11425         kill_x = test_x;
11426         kill_y = test_y;
11427         break;
11428       }
11429     }
11430   }
11431
11432   if (kill_x != -1 || kill_y != -1)
11433   {
11434     if (IS_PLAYER(kill_x, kill_y))
11435     {
11436       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11437
11438       if (player->shield_deadly_time_left > 0 &&
11439           !IS_INDESTRUCTIBLE(bad_element))
11440         Bang(bad_x, bad_y);
11441       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11442         KillPlayer(player);
11443     }
11444     else
11445       Bang(kill_x, kill_y);
11446   }
11447 }
11448
11449 void TestIfPlayerTouchesBadThing(int x, int y)
11450 {
11451   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11452 }
11453
11454 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11455 {
11456   TestIfGoodThingHitsBadThing(x, y, move_dir);
11457 }
11458
11459 void TestIfBadThingTouchesPlayer(int x, int y)
11460 {
11461   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11462 }
11463
11464 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11465 {
11466   TestIfBadThingHitsGoodThing(x, y, move_dir);
11467 }
11468
11469 void TestIfFriendTouchesBadThing(int x, int y)
11470 {
11471   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11472 }
11473
11474 void TestIfBadThingTouchesFriend(int x, int y)
11475 {
11476   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11477 }
11478
11479 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11480 {
11481   int i, kill_x = bad_x, kill_y = bad_y;
11482   static int xy[4][2] =
11483   {
11484     { 0, -1 },
11485     { -1, 0 },
11486     { +1, 0 },
11487     { 0, +1 }
11488   };
11489
11490   for (i = 0; i < NUM_DIRECTIONS; i++)
11491   {
11492     int x, y, element;
11493
11494     x = bad_x + xy[i][0];
11495     y = bad_y + xy[i][1];
11496     if (!IN_LEV_FIELD(x, y))
11497       continue;
11498
11499     element = Feld[x][y];
11500     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11501         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11502     {
11503       kill_x = x;
11504       kill_y = y;
11505       break;
11506     }
11507   }
11508
11509   if (kill_x != bad_x || kill_y != bad_y)
11510     Bang(bad_x, bad_y);
11511 }
11512
11513 void KillPlayer(struct PlayerInfo *player)
11514 {
11515   int jx = player->jx, jy = player->jy;
11516
11517   if (!player->active)
11518     return;
11519
11520   /* remove accessible field at the player's position */
11521   Feld[jx][jy] = EL_EMPTY;
11522
11523   /* deactivate shield (else Bang()/Explode() would not work right) */
11524   player->shield_normal_time_left = 0;
11525   player->shield_deadly_time_left = 0;
11526
11527   Bang(jx, jy);
11528   BuryPlayer(player);
11529 }
11530
11531 static void KillPlayerUnlessEnemyProtected(int x, int y)
11532 {
11533   if (!PLAYER_ENEMY_PROTECTED(x, y))
11534     KillPlayer(PLAYERINFO(x, y));
11535 }
11536
11537 static void KillPlayerUnlessExplosionProtected(int x, int y)
11538 {
11539   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11540     KillPlayer(PLAYERINFO(x, y));
11541 }
11542
11543 void BuryPlayer(struct PlayerInfo *player)
11544 {
11545   int jx = player->jx, jy = player->jy;
11546
11547   if (!player->active)
11548     return;
11549
11550   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11551   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11552
11553   player->GameOver = TRUE;
11554   RemovePlayer(player);
11555 }
11556
11557 void RemovePlayer(struct PlayerInfo *player)
11558 {
11559   int jx = player->jx, jy = player->jy;
11560   int i, found = FALSE;
11561
11562   player->present = FALSE;
11563   player->active = FALSE;
11564
11565   if (!ExplodeField[jx][jy])
11566     StorePlayer[jx][jy] = 0;
11567
11568   if (player->is_moving)
11569     DrawLevelField(player->last_jx, player->last_jy);
11570
11571   for (i = 0; i < MAX_PLAYERS; i++)
11572     if (stored_player[i].active)
11573       found = TRUE;
11574
11575   if (!found)
11576     AllPlayersGone = TRUE;
11577
11578   ExitX = ZX = jx;
11579   ExitY = ZY = jy;
11580 }
11581
11582 #if USE_NEW_SNAP_DELAY
11583 static void setFieldForSnapping(int x, int y, int element, int direction)
11584 {
11585   struct ElementInfo *ei = &element_info[element];
11586   int direction_bit = MV_DIR_TO_BIT(direction);
11587   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11588   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11589                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11590
11591   Feld[x][y] = EL_ELEMENT_SNAPPING;
11592   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11593
11594   ResetGfxAnimation(x, y);
11595
11596   GfxElement[x][y] = element;
11597   GfxAction[x][y] = action;
11598   GfxDir[x][y] = direction;
11599   GfxFrame[x][y] = -1;
11600 }
11601 #endif
11602
11603 /*
11604   =============================================================================
11605   checkDiagonalPushing()
11606   -----------------------------------------------------------------------------
11607   check if diagonal input device direction results in pushing of object
11608   (by checking if the alternative direction is walkable, diggable, ...)
11609   =============================================================================
11610 */
11611
11612 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11613                                     int x, int y, int real_dx, int real_dy)
11614 {
11615   int jx, jy, dx, dy, xx, yy;
11616
11617   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11618     return TRUE;
11619
11620   /* diagonal direction: check alternative direction */
11621   jx = player->jx;
11622   jy = player->jy;
11623   dx = x - jx;
11624   dy = y - jy;
11625   xx = jx + (dx == 0 ? real_dx : 0);
11626   yy = jy + (dy == 0 ? real_dy : 0);
11627
11628   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11629 }
11630
11631 /*
11632   =============================================================================
11633   DigField()
11634   -----------------------------------------------------------------------------
11635   x, y:                 field next to player (non-diagonal) to try to dig to
11636   real_dx, real_dy:     direction as read from input device (can be diagonal)
11637   =============================================================================
11638 */
11639
11640 int DigField(struct PlayerInfo *player,
11641              int oldx, int oldy, int x, int y,
11642              int real_dx, int real_dy, int mode)
11643 {
11644   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11645   boolean player_was_pushing = player->is_pushing;
11646   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11647   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11648   int jx = oldx, jy = oldy;
11649   int dx = x - jx, dy = y - jy;
11650   int nextx = x + dx, nexty = y + dy;
11651   int move_direction = (dx == -1 ? MV_LEFT  :
11652                         dx == +1 ? MV_RIGHT :
11653                         dy == -1 ? MV_UP    :
11654                         dy == +1 ? MV_DOWN  : MV_NONE);
11655   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11656   int dig_side = MV_DIR_OPPOSITE(move_direction);
11657   int old_element = Feld[jx][jy];
11658 #if USE_FIXED_DONT_RUN_INTO
11659   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11660 #else
11661   int element;
11662 #endif
11663   int collect_count;
11664
11665   if (is_player)                /* function can also be called by EL_PENGUIN */
11666   {
11667     if (player->MovPos == 0)
11668     {
11669       player->is_digging = FALSE;
11670       player->is_collecting = FALSE;
11671     }
11672
11673     if (player->MovPos == 0)    /* last pushing move finished */
11674       player->is_pushing = FALSE;
11675
11676     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11677     {
11678       player->is_switching = FALSE;
11679       player->push_delay = -1;
11680
11681       return MP_NO_ACTION;
11682     }
11683   }
11684
11685 #if !USE_FIXED_DONT_RUN_INTO
11686   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11687     return MP_NO_ACTION;
11688 #endif
11689
11690   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11691     old_element = Back[jx][jy];
11692
11693   /* in case of element dropped at player position, check background */
11694   else if (Back[jx][jy] != EL_EMPTY &&
11695            game.engine_version >= VERSION_IDENT(2,2,0,0))
11696     old_element = Back[jx][jy];
11697
11698   /* checking here causes player to move into acid even if the current field
11699      cannot be left to that direction */
11700 #if 0
11701 #if USE_FIXED_DONT_RUN_INTO
11702   if (player_can_move && DONT_RUN_INTO(element))
11703   {
11704     if (element == EL_ACID && dx == 0 && dy == 1)
11705     {
11706       SplashAcid(x, y);
11707       Feld[jx][jy] = EL_PLAYER_1;
11708       InitMovingField(jx, jy, MV_DOWN);
11709       Store[jx][jy] = EL_ACID;
11710       ContinueMoving(jx, jy);
11711       BuryPlayer(player);
11712     }
11713     else
11714       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11715
11716     return MP_DONT_RUN_INTO;
11717   }
11718 #endif
11719 #endif
11720
11721 #if 1   /* ------------------------------ NEW ------------------------------ */
11722
11723   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11724     return MP_NO_ACTION;        /* field has no opening in this direction */
11725
11726   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11727     return MP_NO_ACTION;        /* field has no opening in this direction */
11728
11729 #if USE_FIXED_DONT_RUN_INTO
11730   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11731   {
11732     SplashAcid(x, y);
11733     Feld[jx][jy] = EL_PLAYER_1;
11734     InitMovingField(jx, jy, MV_DOWN);
11735     Store[jx][jy] = EL_ACID;
11736     ContinueMoving(jx, jy);
11737     BuryPlayer(player);
11738
11739     return MP_DONT_RUN_INTO;
11740   }
11741 #endif
11742
11743 #if USE_FIXED_DONT_RUN_INTO
11744   if (player_can_move && DONT_RUN_INTO(element))
11745   {
11746     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11747
11748     return MP_DONT_RUN_INTO;
11749   }
11750 #endif
11751
11752 #else   /* ------------------------------ OLD ------------------------------ */
11753
11754 #if 1
11755 #if USE_FIXED_DONT_RUN_INTO
11756   if (player_can_move && DONT_RUN_INTO(element))
11757   {
11758     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11759
11760     return MP_DONT_RUN_INTO;
11761   }
11762 #endif
11763 #endif
11764
11765   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11766     return MP_NO_ACTION;        /* field has no opening in this direction */
11767
11768   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11769     return MP_NO_ACTION;        /* field has no opening in this direction */
11770
11771   /* checking here causes player to explode when moving into acid */
11772 #if 1
11773 #if USE_FIXED_DONT_RUN_INTO
11774   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11775   {
11776     SplashAcid(x, y);
11777     Feld[jx][jy] = EL_PLAYER_1;
11778     InitMovingField(jx, jy, MV_DOWN);
11779     Store[jx][jy] = EL_ACID;
11780     ContinueMoving(jx, jy);
11781     BuryPlayer(player);
11782
11783     return MP_DONT_RUN_INTO;
11784   }
11785 #endif
11786 #endif
11787
11788 #endif  /* ------------------------------ END ------------------------------ */
11789
11790 #if 0
11791 #if USE_FIXED_DONT_RUN_INTO
11792   if (player_can_move && DONT_RUN_INTO(element))
11793   {
11794     if (element == EL_ACID && dx == 0 && dy == 1)
11795     {
11796       SplashAcid(x, y);
11797       Feld[jx][jy] = EL_PLAYER_1;
11798       InitMovingField(jx, jy, MV_DOWN);
11799       Store[jx][jy] = EL_ACID;
11800       ContinueMoving(jx, jy);
11801       BuryPlayer(player);
11802     }
11803     else
11804       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11805
11806     return MP_DONT_RUN_INTO;
11807   }
11808 #endif
11809 #endif
11810
11811 #if USE_FIXED_DONT_RUN_INTO
11812   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11813     return MP_NO_ACTION;
11814 #endif
11815
11816 #if !USE_FIXED_DONT_RUN_INTO
11817   element = Feld[x][y];
11818 #endif
11819
11820   collect_count = element_info[element].collect_count_initial;
11821
11822   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
11823     return MP_NO_ACTION;
11824
11825   if (game.engine_version < VERSION_IDENT(2,2,0,0))
11826     player_can_move = player_can_move_or_snap;
11827
11828   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11829       game.engine_version >= VERSION_IDENT(2,2,0,0))
11830   {
11831     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11832                                player->index_bit, dig_side);
11833     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11834                                         player->index_bit, dig_side);
11835
11836     if (Feld[x][y] != element)          /* field changed by snapping */
11837       return MP_ACTION;
11838
11839     return MP_NO_ACTION;
11840   }
11841
11842 #if USE_PLAYER_GRAVITY
11843   if (player->gravity && is_player && !player->is_auto_moving &&
11844       canFallDown(player) && move_direction != MV_DOWN &&
11845       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11846     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
11847 #else
11848   if (game.gravity && is_player && !player->is_auto_moving &&
11849       canFallDown(player) && move_direction != MV_DOWN &&
11850       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11851     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
11852 #endif
11853
11854   if (player_can_move &&
11855       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11856   {
11857     int sound_element = SND_ELEMENT(element);
11858     int sound_action = ACTION_WALKING;
11859
11860     if (IS_RND_GATE(element))
11861     {
11862       if (!player->key[RND_GATE_NR(element)])
11863         return MP_NO_ACTION;
11864     }
11865     else if (IS_RND_GATE_GRAY(element))
11866     {
11867       if (!player->key[RND_GATE_GRAY_NR(element)])
11868         return MP_NO_ACTION;
11869     }
11870     else if (IS_RND_GATE_GRAY_ACTIVE(element))
11871     {
11872       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11873         return MP_NO_ACTION;
11874     }
11875     else if (element == EL_EXIT_OPEN ||
11876              element == EL_SP_EXIT_OPEN ||
11877              element == EL_SP_EXIT_OPENING)
11878     {
11879       sound_action = ACTION_PASSING;    /* player is passing exit */
11880     }
11881     else if (element == EL_EMPTY)
11882     {
11883       sound_action = ACTION_MOVING;             /* nothing to walk on */
11884     }
11885
11886     /* play sound from background or player, whatever is available */
11887     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11888       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11889     else
11890       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11891   }
11892   else if (player_can_move &&
11893            IS_PASSABLE(element) && canPassField(x, y, move_direction))
11894   {
11895     if (!ACCESS_FROM(element, opposite_direction))
11896       return MP_NO_ACTION;      /* field not accessible from this direction */
11897
11898     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
11899       return MP_NO_ACTION;
11900
11901     if (IS_EM_GATE(element))
11902     {
11903       if (!player->key[EM_GATE_NR(element)])
11904         return MP_NO_ACTION;
11905     }
11906     else if (IS_EM_GATE_GRAY(element))
11907     {
11908       if (!player->key[EM_GATE_GRAY_NR(element)])
11909         return MP_NO_ACTION;
11910     }
11911     else if (IS_EM_GATE_GRAY_ACTIVE(element))
11912     {
11913       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11914         return MP_NO_ACTION;
11915     }
11916     else if (IS_EMC_GATE(element))
11917     {
11918       if (!player->key[EMC_GATE_NR(element)])
11919         return MP_NO_ACTION;
11920     }
11921     else if (IS_EMC_GATE_GRAY(element))
11922     {
11923       if (!player->key[EMC_GATE_GRAY_NR(element)])
11924         return MP_NO_ACTION;
11925     }
11926     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11927     {
11928       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11929         return MP_NO_ACTION;
11930     }
11931     else if (IS_SP_PORT(element))
11932     {
11933       if (element == EL_SP_GRAVITY_PORT_LEFT ||
11934           element == EL_SP_GRAVITY_PORT_RIGHT ||
11935           element == EL_SP_GRAVITY_PORT_UP ||
11936           element == EL_SP_GRAVITY_PORT_DOWN)
11937 #if USE_PLAYER_GRAVITY
11938         player->gravity = !player->gravity;
11939 #else
11940         game.gravity = !game.gravity;
11941 #endif
11942       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11943                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11944                element == EL_SP_GRAVITY_ON_PORT_UP ||
11945                element == EL_SP_GRAVITY_ON_PORT_DOWN)
11946 #if USE_PLAYER_GRAVITY
11947         player->gravity = TRUE;
11948 #else
11949         game.gravity = TRUE;
11950 #endif
11951       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11952                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11953                element == EL_SP_GRAVITY_OFF_PORT_UP ||
11954                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11955 #if USE_PLAYER_GRAVITY
11956         player->gravity = FALSE;
11957 #else
11958         game.gravity = FALSE;
11959 #endif
11960     }
11961
11962     /* automatically move to the next field with double speed */
11963     player->programmed_action = move_direction;
11964
11965     if (player->move_delay_reset_counter == 0)
11966     {
11967       player->move_delay_reset_counter = 2;     /* two double speed steps */
11968
11969       DOUBLE_PLAYER_SPEED(player);
11970     }
11971
11972     PlayLevelSoundAction(x, y, ACTION_PASSING);
11973   }
11974   else if (player_can_move_or_snap && IS_DIGGABLE(element))
11975   {
11976     RemoveField(x, y);
11977
11978     if (mode != DF_SNAP)
11979     {
11980       GfxElement[x][y] = GFX_ELEMENT(element);
11981       player->is_digging = TRUE;
11982     }
11983
11984     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11985
11986     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11987                                         player->index_bit, dig_side);
11988
11989     if (mode == DF_SNAP)
11990     {
11991 #if USE_NEW_SNAP_DELAY
11992       if (level.block_snap_field)
11993         setFieldForSnapping(x, y, element, move_direction);
11994       else
11995         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11996 #else
11997       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
11998 #endif
11999
12000       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12001                                           player->index_bit, dig_side);
12002     }
12003   }
12004   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12005   {
12006     RemoveField(x, y);
12007
12008     if (is_player && mode != DF_SNAP)
12009     {
12010       GfxElement[x][y] = element;
12011       player->is_collecting = TRUE;
12012     }
12013
12014     if (element == EL_SPEED_PILL)
12015     {
12016       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12017     }
12018     else if (element == EL_EXTRA_TIME && level.time > 0)
12019     {
12020       TimeLeft += level.extra_time;
12021       DrawGameValue_Time(TimeLeft);
12022     }
12023     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12024     {
12025       player->shield_normal_time_left += level.shield_normal_time;
12026       if (element == EL_SHIELD_DEADLY)
12027         player->shield_deadly_time_left += level.shield_deadly_time;
12028     }
12029     else if (element == EL_DYNAMITE ||
12030              element == EL_EM_DYNAMITE ||
12031              element == EL_SP_DISK_RED)
12032     {
12033       if (player->inventory_size < MAX_INVENTORY_SIZE)
12034         player->inventory_element[player->inventory_size++] = element;
12035
12036 #if 1
12037       DrawGameDoorValues();
12038 #else
12039       DrawGameValue_Dynamite(local_player->inventory_size);
12040 #endif
12041     }
12042     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12043     {
12044       player->dynabomb_count++;
12045       player->dynabombs_left++;
12046     }
12047     else if (element == EL_DYNABOMB_INCREASE_SIZE)
12048     {
12049       player->dynabomb_size++;
12050     }
12051     else if (element == EL_DYNABOMB_INCREASE_POWER)
12052     {
12053       player->dynabomb_xl = TRUE;
12054     }
12055     else if (IS_KEY(element))
12056     {
12057       player->key[KEY_NR(element)] = TRUE;
12058
12059 #if 1
12060       DrawGameDoorValues();
12061 #else
12062       DrawGameValue_Keys(player->key);
12063 #endif
12064
12065       redraw_mask |= REDRAW_DOOR_1;
12066     }
12067     else if (IS_ENVELOPE(element))
12068     {
12069       player->show_envelope = element;
12070     }
12071     else if (element == EL_EMC_LENSES)
12072     {
12073       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12074
12075       RedrawAllInvisibleElementsForLenses();
12076     }
12077     else if (element == EL_EMC_MAGNIFIER)
12078     {
12079       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12080
12081       RedrawAllInvisibleElementsForMagnifier();
12082     }
12083     else if (IS_DROPPABLE(element) ||
12084              IS_THROWABLE(element))     /* can be collected and dropped */
12085     {
12086       int i;
12087
12088       if (collect_count == 0)
12089         player->inventory_infinite_element = element;
12090       else
12091         for (i = 0; i < collect_count; i++)
12092           if (player->inventory_size < MAX_INVENTORY_SIZE)
12093             player->inventory_element[player->inventory_size++] = element;
12094
12095 #if 1
12096       DrawGameDoorValues();
12097 #else
12098       DrawGameValue_Dynamite(local_player->inventory_size);
12099 #endif
12100     }
12101     else if (collect_count > 0)
12102     {
12103       local_player->gems_still_needed -= collect_count;
12104       if (local_player->gems_still_needed < 0)
12105         local_player->gems_still_needed = 0;
12106
12107       DrawGameValue_Emeralds(local_player->gems_still_needed);
12108     }
12109
12110     RaiseScoreElement(element);
12111     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12112
12113     if (is_player)
12114       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12115                                           player->index_bit, dig_side);
12116
12117     if (mode == DF_SNAP)
12118     {
12119 #if USE_NEW_SNAP_DELAY
12120       if (level.block_snap_field)
12121         setFieldForSnapping(x, y, element, move_direction);
12122       else
12123         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12124 #else
12125       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12126 #endif
12127
12128       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12129                                           player->index_bit, dig_side);
12130     }
12131   }
12132   else if (player_can_move_or_snap && IS_PUSHABLE(element))
12133   {
12134     if (mode == DF_SNAP && element != EL_BD_ROCK)
12135       return MP_NO_ACTION;
12136
12137     if (CAN_FALL(element) && dy)
12138       return MP_NO_ACTION;
12139
12140     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12141         !(element == EL_SPRING && level.use_spring_bug))
12142       return MP_NO_ACTION;
12143
12144     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12145         ((move_direction & MV_VERTICAL &&
12146           ((element_info[element].move_pattern & MV_LEFT &&
12147             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12148            (element_info[element].move_pattern & MV_RIGHT &&
12149             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12150          (move_direction & MV_HORIZONTAL &&
12151           ((element_info[element].move_pattern & MV_UP &&
12152             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12153            (element_info[element].move_pattern & MV_DOWN &&
12154             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12155       return MP_NO_ACTION;
12156
12157     /* do not push elements already moving away faster than player */
12158     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12159         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12160       return MP_NO_ACTION;
12161
12162     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12163     {
12164       if (player->push_delay_value == -1 || !player_was_pushing)
12165         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12166     }
12167     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12168     {
12169       if (player->push_delay_value == -1)
12170         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12171     }
12172     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12173     {
12174       if (!player->is_pushing)
12175         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12176     }
12177
12178     player->is_pushing = TRUE;
12179
12180     if (!(IN_LEV_FIELD(nextx, nexty) &&
12181           (IS_FREE(nextx, nexty) ||
12182            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12183             IS_SB_ELEMENT(element)))))
12184       return MP_NO_ACTION;
12185
12186     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12187       return MP_NO_ACTION;
12188
12189     if (player->push_delay == -1)       /* new pushing; restart delay */
12190       player->push_delay = 0;
12191
12192     if (player->push_delay < player->push_delay_value &&
12193         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12194         element != EL_SPRING && element != EL_BALLOON)
12195     {
12196       /* make sure that there is no move delay before next try to push */
12197       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12198         player->move_delay = 0;
12199
12200       return MP_NO_ACTION;
12201     }
12202
12203     if (IS_SB_ELEMENT(element))
12204     {
12205       if (element == EL_SOKOBAN_FIELD_FULL)
12206       {
12207         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12208         local_player->sokobanfields_still_needed++;
12209       }
12210
12211       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12212       {
12213         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12214         local_player->sokobanfields_still_needed--;
12215       }
12216
12217       Feld[x][y] = EL_SOKOBAN_OBJECT;
12218
12219       if (Back[x][y] == Back[nextx][nexty])
12220         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12221       else if (Back[x][y] != 0)
12222         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12223                                     ACTION_EMPTYING);
12224       else
12225         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12226                                     ACTION_FILLING);
12227
12228       if (local_player->sokobanfields_still_needed == 0 &&
12229           game.emulation == EMU_SOKOBAN)
12230       {
12231         player->LevelSolved = player->GameOver = TRUE;
12232         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12233       }
12234     }
12235     else
12236       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12237
12238     InitMovingField(x, y, move_direction);
12239     GfxAction[x][y] = ACTION_PUSHING;
12240
12241     if (mode == DF_SNAP)
12242       ContinueMoving(x, y);
12243     else
12244       MovPos[x][y] = (dx != 0 ? dx : dy);
12245
12246     Pushed[x][y] = TRUE;
12247     Pushed[nextx][nexty] = TRUE;
12248
12249     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12250       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12251     else
12252       player->push_delay_value = -1;    /* get new value later */
12253
12254     /* check for element change _after_ element has been pushed */
12255     if (game.use_change_when_pushing_bug)
12256     {
12257       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12258                                  player->index_bit, dig_side);
12259       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12260                                           player->index_bit, dig_side);
12261     }
12262   }
12263   else if (IS_SWITCHABLE(element))
12264   {
12265     if (PLAYER_SWITCHING(player, x, y))
12266     {
12267       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12268                                           player->index_bit, dig_side);
12269
12270       return MP_ACTION;
12271     }
12272
12273     player->is_switching = TRUE;
12274     player->switch_x = x;
12275     player->switch_y = y;
12276
12277     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12278
12279     if (element == EL_ROBOT_WHEEL)
12280     {
12281       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12282       ZX = x;
12283       ZY = y;
12284
12285       DrawLevelField(x, y);
12286     }
12287     else if (element == EL_SP_TERMINAL)
12288     {
12289       int xx, yy;
12290
12291 #if 1
12292       SCAN_PLAYFIELD(xx, yy)
12293 #else
12294       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12295 #endif
12296       {
12297         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12298           Bang(xx, yy);
12299         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12300           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12301       }
12302     }
12303     else if (IS_BELT_SWITCH(element))
12304     {
12305       ToggleBeltSwitch(x, y);
12306     }
12307     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12308              element == EL_SWITCHGATE_SWITCH_DOWN)
12309     {
12310       ToggleSwitchgateSwitch(x, y);
12311     }
12312     else if (element == EL_LIGHT_SWITCH ||
12313              element == EL_LIGHT_SWITCH_ACTIVE)
12314     {
12315       ToggleLightSwitch(x, y);
12316     }
12317     else if (element == EL_TIMEGATE_SWITCH)
12318     {
12319       ActivateTimegateSwitch(x, y);
12320     }
12321     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12322              element == EL_BALLOON_SWITCH_RIGHT ||
12323              element == EL_BALLOON_SWITCH_UP    ||
12324              element == EL_BALLOON_SWITCH_DOWN  ||
12325              element == EL_BALLOON_SWITCH_NONE  ||
12326              element == EL_BALLOON_SWITCH_ANY)
12327     {
12328       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12329                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12330                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12331                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12332                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12333                              move_direction);
12334     }
12335     else if (element == EL_LAMP)
12336     {
12337       Feld[x][y] = EL_LAMP_ACTIVE;
12338       local_player->lights_still_needed--;
12339
12340       ResetGfxAnimation(x, y);
12341       DrawLevelField(x, y);
12342     }
12343     else if (element == EL_TIME_ORB_FULL)
12344     {
12345       Feld[x][y] = EL_TIME_ORB_EMPTY;
12346
12347       if (level.time > 0 || level.use_time_orb_bug)
12348       {
12349         TimeLeft += level.time_orb_time;
12350         DrawGameValue_Time(TimeLeft);
12351       }
12352
12353       ResetGfxAnimation(x, y);
12354       DrawLevelField(x, y);
12355     }
12356     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12357              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12358     {
12359       int xx, yy;
12360
12361       game.ball_state = !game.ball_state;
12362
12363 #if 1
12364       SCAN_PLAYFIELD(xx, yy)
12365 #else
12366       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12367 #endif
12368       {
12369         int e = Feld[xx][yy];
12370
12371         if (game.ball_state)
12372         {
12373           if (e == EL_EMC_MAGIC_BALL)
12374             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12375           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12376             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12377         }
12378         else
12379         {
12380           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12381             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12382           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12383             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12384         }
12385       }
12386     }
12387
12388     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12389                                         player->index_bit, dig_side);
12390
12391     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12392                                         player->index_bit, dig_side);
12393
12394     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12395                                         player->index_bit, dig_side);
12396
12397     return MP_ACTION;
12398   }
12399   else
12400   {
12401     if (!PLAYER_SWITCHING(player, x, y))
12402     {
12403       player->is_switching = TRUE;
12404       player->switch_x = x;
12405       player->switch_y = y;
12406
12407       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12408                                  player->index_bit, dig_side);
12409       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12410                                           player->index_bit, dig_side);
12411
12412       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12413                                  player->index_bit, dig_side);
12414       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12415                                           player->index_bit, dig_side);
12416     }
12417
12418     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12419                                player->index_bit, dig_side);
12420     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12421                                         player->index_bit, dig_side);
12422
12423     return MP_NO_ACTION;
12424   }
12425
12426   player->push_delay = -1;
12427
12428   if (is_player)                /* function can also be called by EL_PENGUIN */
12429   {
12430     if (Feld[x][y] != element)          /* really digged/collected something */
12431       player->is_collecting = !player->is_digging;
12432   }
12433
12434   return MP_MOVING;
12435 }
12436
12437 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12438 {
12439   int jx = player->jx, jy = player->jy;
12440   int x = jx + dx, y = jy + dy;
12441   int snap_direction = (dx == -1 ? MV_LEFT  :
12442                         dx == +1 ? MV_RIGHT :
12443                         dy == -1 ? MV_UP    :
12444                         dy == +1 ? MV_DOWN  : MV_NONE);
12445   boolean can_continue_snapping = (level.continuous_snapping &&
12446                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12447
12448   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12449     return FALSE;
12450
12451   if (!player->active || !IN_LEV_FIELD(x, y))
12452     return FALSE;
12453
12454   if (dx && dy)
12455     return FALSE;
12456
12457   if (!dx && !dy)
12458   {
12459     if (player->MovPos == 0)
12460       player->is_pushing = FALSE;
12461
12462     player->is_snapping = FALSE;
12463
12464     if (player->MovPos == 0)
12465     {
12466       player->is_moving = FALSE;
12467       player->is_digging = FALSE;
12468       player->is_collecting = FALSE;
12469     }
12470
12471     return FALSE;
12472   }
12473
12474 #if USE_NEW_CONTINUOUS_SNAPPING
12475   /* prevent snapping with already pressed snap key when not allowed */
12476   if (player->is_snapping && !can_continue_snapping)
12477     return FALSE;
12478 #else
12479   if (player->is_snapping)
12480     return FALSE;
12481 #endif
12482
12483   player->MovDir = snap_direction;
12484
12485   if (player->MovPos == 0)
12486   {
12487     player->is_moving = FALSE;
12488     player->is_digging = FALSE;
12489     player->is_collecting = FALSE;
12490   }
12491
12492   player->is_dropping = FALSE;
12493   player->is_dropping_pressed = FALSE;
12494   player->drop_pressed_delay = 0;
12495
12496   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12497     return FALSE;
12498
12499   player->is_snapping = TRUE;
12500
12501   if (player->MovPos == 0)
12502   {
12503     player->is_moving = FALSE;
12504     player->is_digging = FALSE;
12505     player->is_collecting = FALSE;
12506   }
12507
12508   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12509     DrawLevelField(player->last_jx, player->last_jy);
12510
12511   DrawLevelField(x, y);
12512
12513   return TRUE;
12514 }
12515
12516 boolean DropElement(struct PlayerInfo *player)
12517 {
12518   int old_element, new_element;
12519   int dropx = player->jx, dropy = player->jy;
12520   int drop_direction = player->MovDir;
12521   int drop_side = drop_direction;
12522   int drop_element = (player->inventory_size > 0 ?
12523                       player->inventory_element[player->inventory_size - 1] :
12524                       player->inventory_infinite_element != EL_UNDEFINED ?
12525                       player->inventory_infinite_element :
12526                       player->dynabombs_left > 0 ?
12527                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12528                       EL_UNDEFINED);
12529
12530   player->is_dropping_pressed = TRUE;
12531
12532   /* do not drop an element on top of another element; when holding drop key
12533      pressed without moving, dropped element must move away before the next
12534      element can be dropped (this is especially important if the next element
12535      is dynamite, which can be placed on background for historical reasons) */
12536   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12537     return MP_ACTION;
12538
12539   if (IS_THROWABLE(drop_element))
12540   {
12541     dropx += GET_DX_FROM_DIR(drop_direction);
12542     dropy += GET_DY_FROM_DIR(drop_direction);
12543
12544     if (!IN_LEV_FIELD(dropx, dropy))
12545       return FALSE;
12546   }
12547
12548   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12549   new_element = drop_element;           /* default: no change when dropping */
12550
12551   /* check if player is active, not moving and ready to drop */
12552   if (!player->active || player->MovPos || player->drop_delay > 0)
12553     return FALSE;
12554
12555   /* check if player has anything that can be dropped */
12556   if (new_element == EL_UNDEFINED)
12557     return FALSE;
12558
12559   /* check if drop key was pressed long enough for EM style dynamite */
12560   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12561     return FALSE;
12562
12563   /* check if anything can be dropped at the current position */
12564   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12565     return FALSE;
12566
12567   /* collected custom elements can only be dropped on empty fields */
12568   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12569     return FALSE;
12570
12571   if (old_element != EL_EMPTY)
12572     Back[dropx][dropy] = old_element;   /* store old element on this field */
12573
12574   ResetGfxAnimation(dropx, dropy);
12575   ResetRandomAnimationValue(dropx, dropy);
12576
12577   if (player->inventory_size > 0 ||
12578       player->inventory_infinite_element != EL_UNDEFINED)
12579   {
12580     if (player->inventory_size > 0)
12581     {
12582       player->inventory_size--;
12583
12584 #if 1
12585       DrawGameDoorValues();
12586 #else
12587       DrawGameValue_Dynamite(local_player->inventory_size);
12588 #endif
12589
12590       if (new_element == EL_DYNAMITE)
12591         new_element = EL_DYNAMITE_ACTIVE;
12592       else if (new_element == EL_EM_DYNAMITE)
12593         new_element = EL_EM_DYNAMITE_ACTIVE;
12594       else if (new_element == EL_SP_DISK_RED)
12595         new_element = EL_SP_DISK_RED_ACTIVE;
12596     }
12597
12598     Feld[dropx][dropy] = new_element;
12599
12600     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12601       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12602                           el2img(Feld[dropx][dropy]), 0);
12603
12604     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12605
12606     /* needed if previous element just changed to "empty" in the last frame */
12607     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12608
12609     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12610                                player->index_bit, drop_side);
12611     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12612                                         CE_PLAYER_DROPS_X,
12613                                         player->index_bit, drop_side);
12614
12615     TestIfElementTouchesCustomElement(dropx, dropy);
12616   }
12617   else          /* player is dropping a dyna bomb */
12618   {
12619     player->dynabombs_left--;
12620
12621     Feld[dropx][dropy] = new_element;
12622
12623     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12624       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12625                           el2img(Feld[dropx][dropy]), 0);
12626
12627     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12628   }
12629
12630   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12631     InitField_WithBug1(dropx, dropy, FALSE);
12632
12633   new_element = Feld[dropx][dropy];     /* element might have changed */
12634
12635   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12636       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12637   {
12638     int move_direction, nextx, nexty;
12639
12640     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12641       MovDir[dropx][dropy] = drop_direction;
12642
12643     move_direction = MovDir[dropx][dropy];
12644     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12645     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12646
12647     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12648     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12649   }
12650
12651   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12652   player->is_dropping = TRUE;
12653
12654   player->drop_pressed_delay = 0;
12655   player->is_dropping_pressed = FALSE;
12656
12657   player->drop_x = dropx;
12658   player->drop_y = dropy;
12659
12660   return TRUE;
12661 }
12662
12663 /* ------------------------------------------------------------------------- */
12664 /* game sound playing functions                                              */
12665 /* ------------------------------------------------------------------------- */
12666
12667 static int *loop_sound_frame = NULL;
12668 static int *loop_sound_volume = NULL;
12669
12670 void InitPlayLevelSound()
12671 {
12672   int num_sounds = getSoundListSize();
12673
12674   checked_free(loop_sound_frame);
12675   checked_free(loop_sound_volume);
12676
12677   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12678   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12679 }
12680
12681 static void PlayLevelSound(int x, int y, int nr)
12682 {
12683   int sx = SCREENX(x), sy = SCREENY(y);
12684   int volume, stereo_position;
12685   int max_distance = 8;
12686   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12687
12688   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12689       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12690     return;
12691
12692   if (!IN_LEV_FIELD(x, y) ||
12693       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12694       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12695     return;
12696
12697   volume = SOUND_MAX_VOLUME;
12698
12699   if (!IN_SCR_FIELD(sx, sy))
12700   {
12701     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12702     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12703
12704     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12705   }
12706
12707   stereo_position = (SOUND_MAX_LEFT +
12708                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12709                      (SCR_FIELDX + 2 * max_distance));
12710
12711   if (IS_LOOP_SOUND(nr))
12712   {
12713     /* This assures that quieter loop sounds do not overwrite louder ones,
12714        while restarting sound volume comparison with each new game frame. */
12715
12716     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12717       return;
12718
12719     loop_sound_volume[nr] = volume;
12720     loop_sound_frame[nr] = FrameCounter;
12721   }
12722
12723   PlaySoundExt(nr, volume, stereo_position, type);
12724 }
12725
12726 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12727 {
12728   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12729                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12730                  y < LEVELY(BY1) ? LEVELY(BY1) :
12731                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12732                  sound_action);
12733 }
12734
12735 static void PlayLevelSoundAction(int x, int y, int action)
12736 {
12737   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12738 }
12739
12740 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12741 {
12742   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12743
12744   if (sound_effect != SND_UNDEFINED)
12745     PlayLevelSound(x, y, sound_effect);
12746 }
12747
12748 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12749                                               int action)
12750 {
12751   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12752
12753   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12754     PlayLevelSound(x, y, sound_effect);
12755 }
12756
12757 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12758 {
12759   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12760
12761   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12762     PlayLevelSound(x, y, sound_effect);
12763 }
12764
12765 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12766 {
12767   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12768
12769   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12770     StopSound(sound_effect);
12771 }
12772
12773 static void PlayLevelMusic()
12774 {
12775   if (levelset.music[level_nr] != MUS_UNDEFINED)
12776     PlayMusic(levelset.music[level_nr]);        /* from config file */
12777   else
12778     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
12779 }
12780
12781 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12782 {
12783   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12784
12785   switch (sample)
12786   {
12787     case SAMPLE_blank:
12788       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12789       break;
12790
12791     case SAMPLE_roll:
12792       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12793       break;
12794
12795     case SAMPLE_stone:
12796       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12797       break;
12798
12799     case SAMPLE_nut:
12800       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12801       break;
12802
12803     case SAMPLE_crack:
12804       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12805       break;
12806
12807     case SAMPLE_bug:
12808       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12809       break;
12810
12811     case SAMPLE_tank:
12812       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12813       break;
12814
12815     case SAMPLE_android_clone:
12816       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12817       break;
12818
12819     case SAMPLE_android_move:
12820       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12821       break;
12822
12823     case SAMPLE_spring:
12824       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12825       break;
12826
12827     case SAMPLE_slurp:
12828       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12829       break;
12830
12831     case SAMPLE_eater:
12832       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12833       break;
12834
12835     case SAMPLE_eater_eat:
12836       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12837       break;
12838
12839     case SAMPLE_alien:
12840       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12841       break;
12842
12843     case SAMPLE_collect:
12844       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12845       break;
12846
12847     case SAMPLE_diamond:
12848       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12849       break;
12850
12851     case SAMPLE_squash:
12852       /* !!! CHECK THIS !!! */
12853 #if 1
12854       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12855 #else
12856       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12857 #endif
12858       break;
12859
12860     case SAMPLE_wonderfall:
12861       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12862       break;
12863
12864     case SAMPLE_drip:
12865       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12866       break;
12867
12868     case SAMPLE_push:
12869       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12870       break;
12871
12872     case SAMPLE_dirt:
12873       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12874       break;
12875
12876     case SAMPLE_acid:
12877       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12878       break;
12879
12880     case SAMPLE_ball:
12881       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12882       break;
12883
12884     case SAMPLE_grow:
12885       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12886       break;
12887
12888     case SAMPLE_wonder:
12889       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12890       break;
12891
12892     case SAMPLE_door:
12893       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12894       break;
12895
12896     case SAMPLE_exit_open:
12897       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12898       break;
12899
12900     case SAMPLE_exit_leave:
12901       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12902       break;
12903
12904     case SAMPLE_dynamite:
12905       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12906       break;
12907
12908     case SAMPLE_tick:
12909       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12910       break;
12911
12912     case SAMPLE_press:
12913       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12914       break;
12915
12916     case SAMPLE_wheel:
12917       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12918       break;
12919
12920     case SAMPLE_boom:
12921       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12922       break;
12923
12924     case SAMPLE_die:
12925       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12926       break;
12927
12928     case SAMPLE_time:
12929       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12930       break;
12931
12932     default:
12933       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12934       break;
12935   }
12936 }
12937
12938 void RaiseScore(int value)
12939 {
12940   local_player->score += value;
12941
12942   DrawGameValue_Score(local_player->score);
12943 }
12944
12945 void RaiseScoreElement(int element)
12946 {
12947   switch(element)
12948   {
12949     case EL_EMERALD:
12950     case EL_BD_DIAMOND:
12951     case EL_EMERALD_YELLOW:
12952     case EL_EMERALD_RED:
12953     case EL_EMERALD_PURPLE:
12954     case EL_SP_INFOTRON:
12955       RaiseScore(level.score[SC_EMERALD]);
12956       break;
12957     case EL_DIAMOND:
12958       RaiseScore(level.score[SC_DIAMOND]);
12959       break;
12960     case EL_CRYSTAL:
12961       RaiseScore(level.score[SC_CRYSTAL]);
12962       break;
12963     case EL_PEARL:
12964       RaiseScore(level.score[SC_PEARL]);
12965       break;
12966     case EL_BUG:
12967     case EL_BD_BUTTERFLY:
12968     case EL_SP_ELECTRON:
12969       RaiseScore(level.score[SC_BUG]);
12970       break;
12971     case EL_SPACESHIP:
12972     case EL_BD_FIREFLY:
12973     case EL_SP_SNIKSNAK:
12974       RaiseScore(level.score[SC_SPACESHIP]);
12975       break;
12976     case EL_YAMYAM:
12977     case EL_DARK_YAMYAM:
12978       RaiseScore(level.score[SC_YAMYAM]);
12979       break;
12980     case EL_ROBOT:
12981       RaiseScore(level.score[SC_ROBOT]);
12982       break;
12983     case EL_PACMAN:
12984       RaiseScore(level.score[SC_PACMAN]);
12985       break;
12986     case EL_NUT:
12987       RaiseScore(level.score[SC_NUT]);
12988       break;
12989     case EL_DYNAMITE:
12990     case EL_EM_DYNAMITE:
12991     case EL_SP_DISK_RED:
12992     case EL_DYNABOMB_INCREASE_NUMBER:
12993     case EL_DYNABOMB_INCREASE_SIZE:
12994     case EL_DYNABOMB_INCREASE_POWER:
12995       RaiseScore(level.score[SC_DYNAMITE]);
12996       break;
12997     case EL_SHIELD_NORMAL:
12998     case EL_SHIELD_DEADLY:
12999       RaiseScore(level.score[SC_SHIELD]);
13000       break;
13001     case EL_EXTRA_TIME:
13002       RaiseScore(level.extra_time_score);
13003       break;
13004     case EL_KEY_1:
13005     case EL_KEY_2:
13006     case EL_KEY_3:
13007     case EL_KEY_4:
13008     case EL_EM_KEY_1:
13009     case EL_EM_KEY_2:
13010     case EL_EM_KEY_3:
13011     case EL_EM_KEY_4:
13012     case EL_EMC_KEY_5:
13013     case EL_EMC_KEY_6:
13014     case EL_EMC_KEY_7:
13015     case EL_EMC_KEY_8:
13016       RaiseScore(level.score[SC_KEY]);
13017       break;
13018     default:
13019       RaiseScore(element_info[element].collect_score);
13020       break;
13021   }
13022 }
13023
13024 void RequestQuitGame(boolean ask_if_really_quit)
13025 {
13026   if (AllPlayersGone ||
13027       !ask_if_really_quit ||
13028       level_editor_test_game ||
13029       Request("Do you really want to quit the game ?",
13030               REQ_ASK | REQ_STAY_CLOSED))
13031   {
13032 #if defined(NETWORK_AVALIABLE)
13033     if (options.network)
13034       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13035     else
13036 #endif
13037     {
13038       game_status = GAME_MODE_MAIN;
13039       DrawMainMenu();
13040     }
13041   }
13042   else
13043   {
13044     if (tape.playing && tape.deactivate_display)
13045       TapeDeactivateDisplayOff(TRUE);
13046
13047     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13048
13049     if (tape.playing && tape.deactivate_display)
13050       TapeDeactivateDisplayOn();
13051   }
13052 }
13053
13054
13055 /* ---------- new game button stuff ---------------------------------------- */
13056
13057 /* graphic position values for game buttons */
13058 #define GAME_BUTTON_XSIZE       30
13059 #define GAME_BUTTON_YSIZE       30
13060 #define GAME_BUTTON_XPOS        5
13061 #define GAME_BUTTON_YPOS        215
13062 #define SOUND_BUTTON_XPOS       5
13063 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13064
13065 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13066 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13067 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13068 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13069 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13070 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13071
13072 static struct
13073 {
13074   int x, y;
13075   int gadget_id;
13076   char *infotext;
13077 } gamebutton_info[NUM_GAME_BUTTONS] =
13078 {
13079   {
13080     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13081     GAME_CTRL_ID_STOP,
13082     "stop game"
13083   },
13084   {
13085     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13086     GAME_CTRL_ID_PAUSE,
13087     "pause game"
13088   },
13089   {
13090     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13091     GAME_CTRL_ID_PLAY,
13092     "play game"
13093   },
13094   {
13095     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13096     SOUND_CTRL_ID_MUSIC,
13097     "background music on/off"
13098   },
13099   {
13100     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13101     SOUND_CTRL_ID_LOOPS,
13102     "sound loops on/off"
13103   },
13104   {
13105     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13106     SOUND_CTRL_ID_SIMPLE,
13107     "normal sounds on/off"
13108   }
13109 };
13110
13111 void CreateGameButtons()
13112 {
13113   int i;
13114
13115   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13116   {
13117     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13118     struct GadgetInfo *gi;
13119     int button_type;
13120     boolean checked;
13121     unsigned long event_mask;
13122     int gd_xoffset, gd_yoffset;
13123     int gd_x1, gd_x2, gd_y1, gd_y2;
13124     int id = i;
13125
13126     gd_xoffset = gamebutton_info[i].x;
13127     gd_yoffset = gamebutton_info[i].y;
13128     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13129     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13130
13131     if (id == GAME_CTRL_ID_STOP ||
13132         id == GAME_CTRL_ID_PAUSE ||
13133         id == GAME_CTRL_ID_PLAY)
13134     {
13135       button_type = GD_TYPE_NORMAL_BUTTON;
13136       checked = FALSE;
13137       event_mask = GD_EVENT_RELEASED;
13138       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13139       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13140     }
13141     else
13142     {
13143       button_type = GD_TYPE_CHECK_BUTTON;
13144       checked =
13145         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13146          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13147          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13148       event_mask = GD_EVENT_PRESSED;
13149       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
13150       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13151     }
13152
13153     gi = CreateGadget(GDI_CUSTOM_ID, id,
13154                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
13155                       GDI_X, DX + gd_xoffset,
13156                       GDI_Y, DY + gd_yoffset,
13157                       GDI_WIDTH, GAME_BUTTON_XSIZE,
13158                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
13159                       GDI_TYPE, button_type,
13160                       GDI_STATE, GD_BUTTON_UNPRESSED,
13161                       GDI_CHECKED, checked,
13162                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13163                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13164                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13165                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13166                       GDI_EVENT_MASK, event_mask,
13167                       GDI_CALLBACK_ACTION, HandleGameButtons,
13168                       GDI_END);
13169
13170     if (gi == NULL)
13171       Error(ERR_EXIT, "cannot create gadget");
13172
13173     game_gadget[id] = gi;
13174   }
13175 }
13176
13177 void FreeGameButtons()
13178 {
13179   int i;
13180
13181   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13182     FreeGadget(game_gadget[i]);
13183 }
13184
13185 static void MapGameButtons()
13186 {
13187   int i;
13188
13189   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13190     MapGadget(game_gadget[i]);
13191 }
13192
13193 void UnmapGameButtons()
13194 {
13195   int i;
13196
13197   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13198     UnmapGadget(game_gadget[i]);
13199 }
13200
13201 static void HandleGameButtons(struct GadgetInfo *gi)
13202 {
13203   int id = gi->custom_id;
13204
13205   if (game_status != GAME_MODE_PLAYING)
13206     return;
13207
13208   switch (id)
13209   {
13210     case GAME_CTRL_ID_STOP:
13211       if (tape.playing)
13212         TapeStop();
13213       else
13214         RequestQuitGame(TRUE);
13215       break;
13216
13217     case GAME_CTRL_ID_PAUSE:
13218       if (options.network)
13219       {
13220 #if defined(NETWORK_AVALIABLE)
13221         if (tape.pausing)
13222           SendToServer_ContinuePlaying();
13223         else
13224           SendToServer_PausePlaying();
13225 #endif
13226       }
13227       else
13228         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13229       break;
13230
13231     case GAME_CTRL_ID_PLAY:
13232       if (tape.pausing)
13233       {
13234 #if defined(NETWORK_AVALIABLE)
13235         if (options.network)
13236           SendToServer_ContinuePlaying();
13237         else
13238 #endif
13239         {
13240           tape.pausing = FALSE;
13241           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13242         }
13243       }
13244       break;
13245
13246     case SOUND_CTRL_ID_MUSIC:
13247       if (setup.sound_music)
13248       { 
13249         setup.sound_music = FALSE;
13250         FadeMusic();
13251       }
13252       else if (audio.music_available)
13253       { 
13254         setup.sound = setup.sound_music = TRUE;
13255
13256         SetAudioMode(setup.sound);
13257
13258         PlayLevelMusic();
13259       }
13260       break;
13261
13262     case SOUND_CTRL_ID_LOOPS:
13263       if (setup.sound_loops)
13264         setup.sound_loops = FALSE;
13265       else if (audio.loops_available)
13266       {
13267         setup.sound = setup.sound_loops = TRUE;
13268         SetAudioMode(setup.sound);
13269       }
13270       break;
13271
13272     case SOUND_CTRL_ID_SIMPLE:
13273       if (setup.sound_simple)
13274         setup.sound_simple = FALSE;
13275       else if (audio.sound_available)
13276       {
13277         setup.sound = setup.sound_simple = TRUE;
13278         SetAudioMode(setup.sound);
13279       }
13280       break;
13281
13282     default:
13283       break;
13284   }
13285 }