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