rnd-20060804-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #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 #if 0
9677   printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
9678 #endif
9679
9680   GameActions_EM(effective_action, warp_mode);
9681
9682   CheckLevelTime();
9683
9684   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9685 }
9686
9687 void GameActions_RND()
9688 {
9689   int magic_wall_x = 0, magic_wall_y = 0;
9690   int i, x, y, element, graphic;
9691
9692   InitPlayfieldScanModeVars();
9693
9694 #if USE_ONE_MORE_CHANGE_PER_FRAME
9695   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9696   {
9697     SCAN_PLAYFIELD(x, y)
9698     {
9699       ChangeCount[x][y] = 0;
9700       ChangeEvent[x][y] = -1;
9701     }
9702   }
9703 #endif
9704
9705 #if 1
9706   if (game.set_centered_player)
9707   {
9708     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9709
9710     /* switching to "all players" only possible if all players fit to screen */
9711     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9712     {
9713       game.centered_player_nr_next = game.centered_player_nr;
9714       game.set_centered_player = FALSE;
9715     }
9716
9717     /* do not switch focus to non-existing (or non-active) player */
9718     if (game.centered_player_nr_next >= 0 &&
9719         !stored_player[game.centered_player_nr_next].active)
9720     {
9721       game.centered_player_nr_next = game.centered_player_nr;
9722       game.set_centered_player = FALSE;
9723     }
9724   }
9725
9726   if (game.set_centered_player &&
9727       ScreenMovPos == 0)        /* screen currently aligned at tile position */
9728   {
9729     int sx, sy;
9730
9731     if (game.centered_player_nr_next == -1)
9732     {
9733       setScreenCenteredToAllPlayers(&sx, &sy);
9734     }
9735     else
9736     {
9737       sx = stored_player[game.centered_player_nr_next].jx;
9738       sy = stored_player[game.centered_player_nr_next].jy;
9739     }
9740
9741     game.centered_player_nr = game.centered_player_nr_next;
9742     game.set_centered_player = FALSE;
9743
9744     DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9745     DrawGameDoorValues();
9746   }
9747 #endif
9748
9749   for (i = 0; i < MAX_PLAYERS; i++)
9750   {
9751     int actual_player_action = stored_player[i].effective_action;
9752
9753 #if 1
9754     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9755        - rnd_equinox_tetrachloride 048
9756        - rnd_equinox_tetrachloride_ii 096
9757        - rnd_emanuel_schmieg 002
9758        - doctor_sloan_ww 001, 020
9759     */
9760     if (stored_player[i].MovPos == 0)
9761       CheckGravityMovement(&stored_player[i]);
9762 #endif
9763
9764     /* overwrite programmed action with tape action */
9765     if (stored_player[i].programmed_action)
9766       actual_player_action = stored_player[i].programmed_action;
9767
9768 #if 1
9769     PlayerActions(&stored_player[i], actual_player_action);
9770 #else
9771     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9772
9773     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9774       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9775 #endif
9776
9777     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9778   }
9779
9780 #if 0
9781   network_player_action_received = FALSE;
9782 #endif
9783
9784   ScrollScreen(NULL, SCROLL_GO_ON);
9785
9786   /* for backwards compatibility, the following code emulates a fixed bug that
9787      occured when pushing elements (causing elements that just made their last
9788      pushing step to already (if possible) make their first falling step in the
9789      same game frame, which is bad); this code is also needed to use the famous
9790      "spring push bug" which is used in older levels and might be wanted to be
9791      used also in newer levels, but in this case the buggy pushing code is only
9792      affecting the "spring" element and no other elements */
9793
9794   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9795   {
9796     for (i = 0; i < MAX_PLAYERS; i++)
9797     {
9798       struct PlayerInfo *player = &stored_player[i];
9799       int x = player->jx;
9800       int y = player->jy;
9801
9802       if (player->active && player->is_pushing && player->is_moving &&
9803           IS_MOVING(x, y) &&
9804           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9805            Feld[x][y] == EL_SPRING))
9806       {
9807         ContinueMoving(x, y);
9808
9809         /* continue moving after pushing (this is actually a bug) */
9810         if (!IS_MOVING(x, y))
9811         {
9812           Stop[x][y] = FALSE;
9813         }
9814       }
9815     }
9816   }
9817
9818 #if 1
9819   SCAN_PLAYFIELD(x, y)
9820 #else
9821   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9822 #endif
9823   {
9824     ChangeCount[x][y] = 0;
9825     ChangeEvent[x][y] = -1;
9826
9827     /* this must be handled before main playfield loop */
9828     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9829     {
9830       MovDelay[x][y]--;
9831       if (MovDelay[x][y] <= 0)
9832         RemoveField(x, y);
9833     }
9834
9835 #if USE_NEW_SNAP_DELAY
9836     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9837     {
9838       MovDelay[x][y]--;
9839       if (MovDelay[x][y] <= 0)
9840       {
9841         RemoveField(x, y);
9842         DrawLevelField(x, y);
9843
9844         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
9845       }
9846     }
9847 #endif
9848
9849 #if DEBUG
9850     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9851     {
9852       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9853       printf("GameActions(): This should never happen!\n");
9854
9855       ChangePage[x][y] = -1;
9856     }
9857 #endif
9858
9859     Stop[x][y] = FALSE;
9860     if (WasJustMoving[x][y] > 0)
9861       WasJustMoving[x][y]--;
9862     if (WasJustFalling[x][y] > 0)
9863       WasJustFalling[x][y]--;
9864     if (CheckCollision[x][y] > 0)
9865       CheckCollision[x][y]--;
9866
9867     GfxFrame[x][y]++;
9868
9869     /* reset finished pushing action (not done in ContinueMoving() to allow
9870        continuous pushing animation for elements with zero push delay) */
9871     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9872     {
9873       ResetGfxAnimation(x, y);
9874       DrawLevelField(x, y);
9875     }
9876
9877 #if DEBUG
9878     if (IS_BLOCKED(x, y))
9879     {
9880       int oldx, oldy;
9881
9882       Blocked2Moving(x, y, &oldx, &oldy);
9883       if (!IS_MOVING(oldx, oldy))
9884       {
9885         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9886         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9887         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9888         printf("GameActions(): This should never happen!\n");
9889       }
9890     }
9891 #endif
9892   }
9893
9894 #if 1
9895   SCAN_PLAYFIELD(x, y)
9896 #else
9897   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9898 #endif
9899   {
9900     element = Feld[x][y];
9901     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9902
9903 #if 0
9904     printf("::: %d,%d\n", x, y);
9905
9906     if (element == EL_ROCK)
9907       printf("::: Yo man! Rocks can fall!\n");
9908 #endif
9909
9910 #if 1
9911     ResetGfxFrame(x, y, TRUE);
9912 #else
9913     if (graphic_info[graphic].anim_global_sync)
9914       GfxFrame[x][y] = FrameCounter;
9915     else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9916     {
9917       int old_gfx_frame = GfxFrame[x][y];
9918
9919       GfxFrame[x][y] = CustomValue[x][y];
9920
9921 #if 1
9922       if (GfxFrame[x][y] != old_gfx_frame)
9923 #endif
9924         DrawLevelGraphicAnimation(x, y, graphic);
9925     }
9926     else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9927     {
9928       int old_gfx_frame = GfxFrame[x][y];
9929
9930       GfxFrame[x][y] = element_info[element].collect_score;
9931
9932 #if 1
9933       if (GfxFrame[x][y] != old_gfx_frame)
9934 #endif
9935         DrawLevelGraphicAnimation(x, y, graphic);
9936     }
9937     else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
9938     {
9939       int old_gfx_frame = GfxFrame[x][y];
9940
9941       GfxFrame[x][y] = ChangeDelay[x][y];
9942
9943 #if 1
9944       if (GfxFrame[x][y] != old_gfx_frame)
9945 #endif
9946         DrawLevelGraphicAnimation(x, y, graphic);
9947     }
9948 #endif
9949
9950     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9951         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9952       ResetRandomAnimationValue(x, y);
9953
9954     SetRandomAnimationValue(x, y);
9955
9956     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9957
9958     if (IS_INACTIVE(element))
9959     {
9960       if (IS_ANIMATED(graphic))
9961         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9962
9963       continue;
9964     }
9965
9966     /* this may take place after moving, so 'element' may have changed */
9967     if (IS_CHANGING(x, y) &&
9968         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9969     {
9970       int page = element_info[element].event_page_nr[CE_DELAY];
9971 #if 0
9972       HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9973 #else
9974
9975 #if 0
9976       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9977 #endif
9978
9979 #if 0
9980       if (element == EL_CUSTOM_255)
9981         printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9982 #endif
9983
9984 #if 1
9985       HandleElementChange(x, y, page);
9986 #else
9987       if (CAN_CHANGE(element))
9988         HandleElementChange(x, y, page);
9989
9990       if (HAS_ACTION(element))
9991         ExecuteCustomElementAction(x, y, element, page);
9992 #endif
9993
9994 #endif
9995
9996       element = Feld[x][y];
9997       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9998     }
9999
10000     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10001     {
10002       StartMoving(x, y);
10003
10004       element = Feld[x][y];
10005       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10006
10007       if (IS_ANIMATED(graphic) &&
10008           !IS_MOVING(x, y) &&
10009           !Stop[x][y])
10010         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10011
10012       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10013         EdelsteinFunkeln(x, y);
10014     }
10015     else if ((element == EL_ACID ||
10016               element == EL_EXIT_OPEN ||
10017               element == EL_SP_EXIT_OPEN ||
10018               element == EL_SP_TERMINAL ||
10019               element == EL_SP_TERMINAL_ACTIVE ||
10020               element == EL_EXTRA_TIME ||
10021               element == EL_SHIELD_NORMAL ||
10022               element == EL_SHIELD_DEADLY) &&
10023              IS_ANIMATED(graphic))
10024       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10025     else if (IS_MOVING(x, y))
10026       ContinueMoving(x, y);
10027     else if (IS_ACTIVE_BOMB(element))
10028       CheckDynamite(x, y);
10029     else if (element == EL_AMOEBA_GROWING)
10030       AmoebeWaechst(x, y);
10031     else if (element == EL_AMOEBA_SHRINKING)
10032       AmoebaDisappearing(x, y);
10033
10034 #if !USE_NEW_AMOEBA_CODE
10035     else if (IS_AMOEBALIVE(element))
10036       AmoebeAbleger(x, y);
10037 #endif
10038
10039     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10040       Life(x, y);
10041     else if (element == EL_EXIT_CLOSED)
10042       CheckExit(x, y);
10043     else if (element == EL_SP_EXIT_CLOSED)
10044       CheckExitSP(x, y);
10045     else if (element == EL_EXPANDABLE_WALL_GROWING)
10046       MauerWaechst(x, y);
10047     else if (element == EL_EXPANDABLE_WALL ||
10048              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10049              element == EL_EXPANDABLE_WALL_VERTICAL ||
10050              element == EL_EXPANDABLE_WALL_ANY ||
10051              element == EL_BD_EXPANDABLE_WALL)
10052       MauerAbleger(x, y);
10053     else if (element == EL_FLAMES)
10054       CheckForDragon(x, y);
10055     else if (element == EL_EXPLOSION)
10056       ; /* drawing of correct explosion animation is handled separately */
10057     else if (element == EL_ELEMENT_SNAPPING ||
10058              element == EL_DIAGONAL_SHRINKING ||
10059              element == EL_DIAGONAL_GROWING)
10060     {
10061 #if 1
10062       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10063
10064       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10065 #endif
10066     }
10067     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10068       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10069
10070 #if 0
10071     if (element == EL_CUSTOM_255 ||
10072         element == EL_CUSTOM_256)
10073       DrawLevelGraphicAnimation(x, y, graphic);
10074 #endif
10075
10076     if (IS_BELT_ACTIVE(element))
10077       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10078
10079     if (game.magic_wall_active)
10080     {
10081       int jx = local_player->jx, jy = local_player->jy;
10082
10083       /* play the element sound at the position nearest to the player */
10084       if ((element == EL_MAGIC_WALL_FULL ||
10085            element == EL_MAGIC_WALL_ACTIVE ||
10086            element == EL_MAGIC_WALL_EMPTYING ||
10087            element == EL_BD_MAGIC_WALL_FULL ||
10088            element == EL_BD_MAGIC_WALL_ACTIVE ||
10089            element == EL_BD_MAGIC_WALL_EMPTYING) &&
10090           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10091       {
10092         magic_wall_x = x;
10093         magic_wall_y = y;
10094       }
10095     }
10096   }
10097
10098 #if USE_NEW_AMOEBA_CODE
10099   /* new experimental amoeba growth stuff */
10100   if (!(FrameCounter % 8))
10101   {
10102     static unsigned long random = 1684108901;
10103
10104     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10105     {
10106       x = RND(lev_fieldx);
10107       y = RND(lev_fieldy);
10108       element = Feld[x][y];
10109
10110       if (!IS_PLAYER(x,y) &&
10111           (element == EL_EMPTY ||
10112            CAN_GROW_INTO(element) ||
10113            element == EL_QUICKSAND_EMPTY ||
10114            element == EL_ACID_SPLASH_LEFT ||
10115            element == EL_ACID_SPLASH_RIGHT))
10116       {
10117         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10118             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10119             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10120             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10121           Feld[x][y] = EL_AMOEBA_DROP;
10122       }
10123
10124       random = random * 129 + 1;
10125     }
10126   }
10127 #endif
10128
10129 #if 0
10130   if (game.explosions_delayed)
10131 #endif
10132   {
10133     game.explosions_delayed = FALSE;
10134
10135 #if 1
10136     SCAN_PLAYFIELD(x, y)
10137 #else
10138     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10139 #endif
10140     {
10141       element = Feld[x][y];
10142
10143       if (ExplodeField[x][y])
10144         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10145       else if (element == EL_EXPLOSION)
10146         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10147
10148       ExplodeField[x][y] = EX_TYPE_NONE;
10149     }
10150
10151     game.explosions_delayed = TRUE;
10152   }
10153
10154   if (game.magic_wall_active)
10155   {
10156     if (!(game.magic_wall_time_left % 4))
10157     {
10158       int element = Feld[magic_wall_x][magic_wall_y];
10159
10160       if (element == EL_BD_MAGIC_WALL_FULL ||
10161           element == EL_BD_MAGIC_WALL_ACTIVE ||
10162           element == EL_BD_MAGIC_WALL_EMPTYING)
10163         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10164       else
10165         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10166     }
10167
10168     if (game.magic_wall_time_left > 0)
10169     {
10170       game.magic_wall_time_left--;
10171       if (!game.magic_wall_time_left)
10172       {
10173 #if 1
10174         SCAN_PLAYFIELD(x, y)
10175 #else
10176         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10177 #endif
10178         {
10179           element = Feld[x][y];
10180
10181           if (element == EL_MAGIC_WALL_ACTIVE ||
10182               element == EL_MAGIC_WALL_FULL)
10183           {
10184             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10185             DrawLevelField(x, y);
10186           }
10187           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10188                    element == EL_BD_MAGIC_WALL_FULL)
10189           {
10190             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10191             DrawLevelField(x, y);
10192           }
10193         }
10194
10195         game.magic_wall_active = FALSE;
10196       }
10197     }
10198   }
10199
10200   if (game.light_time_left > 0)
10201   {
10202     game.light_time_left--;
10203
10204     if (game.light_time_left == 0)
10205       RedrawAllLightSwitchesAndInvisibleElements();
10206   }
10207
10208   if (game.timegate_time_left > 0)
10209   {
10210     game.timegate_time_left--;
10211
10212     if (game.timegate_time_left == 0)
10213       CloseAllOpenTimegates();
10214   }
10215
10216   if (game.lenses_time_left > 0)
10217   {
10218     game.lenses_time_left--;
10219
10220     if (game.lenses_time_left == 0)
10221       RedrawAllInvisibleElementsForLenses();
10222   }
10223
10224   if (game.magnify_time_left > 0)
10225   {
10226     game.magnify_time_left--;
10227
10228     if (game.magnify_time_left == 0)
10229       RedrawAllInvisibleElementsForMagnifier();
10230   }
10231
10232   for (i = 0; i < MAX_PLAYERS; i++)
10233   {
10234     struct PlayerInfo *player = &stored_player[i];
10235
10236     if (SHIELD_ON(player))
10237     {
10238       if (player->shield_deadly_time_left)
10239         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10240       else if (player->shield_normal_time_left)
10241         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10242     }
10243   }
10244
10245   CheckLevelTime();
10246
10247   DrawAllPlayers();
10248   PlayAllPlayersSound();
10249
10250   if (options.debug)                    /* calculate frames per second */
10251   {
10252     static unsigned long fps_counter = 0;
10253     static int fps_frames = 0;
10254     unsigned long fps_delay_ms = Counter() - fps_counter;
10255
10256     fps_frames++;
10257
10258     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10259     {
10260       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10261
10262       fps_frames = 0;
10263       fps_counter = Counter();
10264     }
10265
10266     redraw_mask |= REDRAW_FPS;
10267   }
10268
10269   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10270
10271   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10272   {
10273     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10274
10275     local_player->show_envelope = 0;
10276   }
10277
10278   /* use random number generator in every frame to make it less predictable */
10279   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10280     RND(1);
10281 }
10282
10283 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10284 {
10285   int min_x = x, min_y = y, max_x = x, max_y = y;
10286   int i;
10287
10288   for (i = 0; i < MAX_PLAYERS; i++)
10289   {
10290     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10291
10292     if (!stored_player[i].active || &stored_player[i] == player)
10293       continue;
10294
10295     min_x = MIN(min_x, jx);
10296     min_y = MIN(min_y, jy);
10297     max_x = MAX(max_x, jx);
10298     max_y = MAX(max_y, jy);
10299   }
10300
10301   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10302 }
10303
10304 static boolean AllPlayersInVisibleScreen()
10305 {
10306   int i;
10307
10308   for (i = 0; i < MAX_PLAYERS; i++)
10309   {
10310     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10311
10312     if (!stored_player[i].active)
10313       continue;
10314
10315     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10316       return FALSE;
10317   }
10318
10319   return TRUE;
10320 }
10321
10322 void ScrollLevel(int dx, int dy)
10323 {
10324   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10325   int x, y;
10326
10327   BlitBitmap(drawto_field, drawto_field,
10328              FX + TILEX * (dx == -1) - softscroll_offset,
10329              FY + TILEY * (dy == -1) - softscroll_offset,
10330              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10331              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10332              FX + TILEX * (dx == 1) - softscroll_offset,
10333              FY + TILEY * (dy == 1) - softscroll_offset);
10334
10335   if (dx)
10336   {
10337     x = (dx == 1 ? BX1 : BX2);
10338     for (y = BY1; y <= BY2; y++)
10339       DrawScreenField(x, y);
10340   }
10341
10342   if (dy)
10343   {
10344     y = (dy == 1 ? BY1 : BY2);
10345     for (x = BX1; x <= BX2; x++)
10346       DrawScreenField(x, y);
10347   }
10348
10349   redraw_mask |= REDRAW_FIELD;
10350 }
10351
10352 static boolean canFallDown(struct PlayerInfo *player)
10353 {
10354   int jx = player->jx, jy = player->jy;
10355
10356   return (IN_LEV_FIELD(jx, jy + 1) &&
10357           (IS_FREE(jx, jy + 1) ||
10358            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10359           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10360           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10361 }
10362
10363 static boolean canPassField(int x, int y, int move_dir)
10364 {
10365   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10366   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10367   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10368   int nextx = x + dx;
10369   int nexty = y + dy;
10370   int element = Feld[x][y];
10371
10372   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10373           !CAN_MOVE(element) &&
10374           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10375           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10376           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10377 }
10378
10379 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10380 {
10381   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10382   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10383   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10384   int newx = x + dx;
10385   int newy = y + dy;
10386
10387   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10388           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10389           (IS_DIGGABLE(Feld[newx][newy]) ||
10390            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10391            canPassField(newx, newy, move_dir)));
10392 }
10393
10394 static void CheckGravityMovement(struct PlayerInfo *player)
10395 {
10396 #if USE_PLAYER_GRAVITY
10397   if (player->gravity && !player->programmed_action)
10398 #else
10399   if (game.gravity && !player->programmed_action)
10400 #endif
10401   {
10402     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10403     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10404     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10405     int jx = player->jx, jy = player->jy;
10406     boolean player_is_moving_to_valid_field =
10407       (!player_is_snapping &&
10408        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10409         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10410     boolean player_can_fall_down = canFallDown(player);
10411
10412     if (player_can_fall_down &&
10413         !player_is_moving_to_valid_field)
10414       player->programmed_action = MV_DOWN;
10415   }
10416 }
10417
10418 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10419 {
10420   return CheckGravityMovement(player);
10421
10422 #if USE_PLAYER_GRAVITY
10423   if (player->gravity && !player->programmed_action)
10424 #else
10425   if (game.gravity && !player->programmed_action)
10426 #endif
10427   {
10428     int jx = player->jx, jy = player->jy;
10429     boolean field_under_player_is_free =
10430       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10431     boolean player_is_standing_on_valid_field =
10432       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10433        (IS_WALKABLE(Feld[jx][jy]) &&
10434         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10435
10436     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10437       player->programmed_action = MV_DOWN;
10438   }
10439 }
10440
10441 /*
10442   MovePlayerOneStep()
10443   -----------------------------------------------------------------------------
10444   dx, dy:               direction (non-diagonal) to try to move the player to
10445   real_dx, real_dy:     direction as read from input device (can be diagonal)
10446 */
10447
10448 boolean MovePlayerOneStep(struct PlayerInfo *player,
10449                           int dx, int dy, int real_dx, int real_dy)
10450 {
10451   int jx = player->jx, jy = player->jy;
10452   int new_jx = jx + dx, new_jy = jy + dy;
10453 #if !USE_FIXED_DONT_RUN_INTO
10454   int element;
10455 #endif
10456   int can_move;
10457   boolean player_can_move = !player->cannot_move;
10458
10459   if (!player->active || (!dx && !dy))
10460     return MP_NO_ACTION;
10461
10462   player->MovDir = (dx < 0 ? MV_LEFT :
10463                     dx > 0 ? MV_RIGHT :
10464                     dy < 0 ? MV_UP :
10465                     dy > 0 ? MV_DOWN :  MV_NONE);
10466
10467   if (!IN_LEV_FIELD(new_jx, new_jy))
10468     return MP_NO_ACTION;
10469
10470   if (!player_can_move)
10471   {
10472 #if 1
10473     if (player->MovPos == 0)
10474     {
10475       player->is_moving = FALSE;
10476       player->is_digging = FALSE;
10477       player->is_collecting = FALSE;
10478       player->is_snapping = FALSE;
10479       player->is_pushing = FALSE;
10480     }
10481 #else
10482     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10483     SnapField(player, 0, 0);
10484 #endif
10485
10486 #if 0
10487     return MP_NO_ACTION;
10488 #endif
10489   }
10490
10491 #if 1
10492   if (!options.network && game.centered_player_nr == -1 &&
10493       !AllPlayersInSight(player, new_jx, new_jy))
10494     return MP_NO_ACTION;
10495 #else
10496   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10497     return MP_NO_ACTION;
10498 #endif
10499
10500 #if !USE_FIXED_DONT_RUN_INTO
10501   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10502
10503   /* (moved to DigField()) */
10504   if (player_can_move && DONT_RUN_INTO(element))
10505   {
10506     if (element == EL_ACID && dx == 0 && dy == 1)
10507     {
10508       SplashAcid(new_jx, new_jy);
10509       Feld[jx][jy] = EL_PLAYER_1;
10510       InitMovingField(jx, jy, MV_DOWN);
10511       Store[jx][jy] = EL_ACID;
10512       ContinueMoving(jx, jy);
10513       BuryPlayer(player);
10514     }
10515     else
10516       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10517
10518     return MP_MOVING;
10519   }
10520 #endif
10521
10522   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10523 #if 0
10524 #if USE_FIXED_DONT_RUN_INTO
10525   if (can_move == MP_DONT_RUN_INTO)
10526     return MP_MOVING;
10527 #endif
10528 #endif
10529   if (can_move != MP_MOVING)
10530     return can_move;
10531
10532 #if USE_FIXED_DONT_RUN_INTO
10533 #endif
10534
10535   /* check if DigField() has caused relocation of the player */
10536   if (player->jx != jx || player->jy != jy)
10537     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10538
10539   StorePlayer[jx][jy] = 0;
10540   player->last_jx = jx;
10541   player->last_jy = jy;
10542   player->jx = new_jx;
10543   player->jy = new_jy;
10544   StorePlayer[new_jx][new_jy] = player->element_nr;
10545
10546   if (player->move_delay_value_next != -1)
10547   {
10548     player->move_delay_value = player->move_delay_value_next;
10549     player->move_delay_value_next = -1;
10550   }
10551
10552   player->MovPos =
10553     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10554
10555   player->step_counter++;
10556
10557   PlayerVisit[jx][jy] = FrameCounter;
10558
10559   ScrollPlayer(player, SCROLL_INIT);
10560
10561   return MP_MOVING;
10562 }
10563
10564 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10565 {
10566   int jx = player->jx, jy = player->jy;
10567   int old_jx = jx, old_jy = jy;
10568   int moved = MP_NO_ACTION;
10569
10570   if (!player->active)
10571     return FALSE;
10572
10573   if (!dx && !dy)
10574   {
10575     if (player->MovPos == 0)
10576     {
10577       player->is_moving = FALSE;
10578       player->is_digging = FALSE;
10579       player->is_collecting = FALSE;
10580       player->is_snapping = FALSE;
10581       player->is_pushing = FALSE;
10582     }
10583
10584     return FALSE;
10585   }
10586
10587   if (player->move_delay > 0)
10588     return FALSE;
10589
10590   player->move_delay = -1;              /* set to "uninitialized" value */
10591
10592   /* store if player is automatically moved to next field */
10593   player->is_auto_moving = (player->programmed_action != MV_NONE);
10594
10595   /* remove the last programmed player action */
10596   player->programmed_action = 0;
10597
10598   if (player->MovPos)
10599   {
10600     /* should only happen if pre-1.2 tape recordings are played */
10601     /* this is only for backward compatibility */
10602
10603     int original_move_delay_value = player->move_delay_value;
10604
10605 #if DEBUG
10606     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10607            tape.counter);
10608 #endif
10609
10610     /* scroll remaining steps with finest movement resolution */
10611     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10612
10613     while (player->MovPos)
10614     {
10615       ScrollPlayer(player, SCROLL_GO_ON);
10616       ScrollScreen(NULL, SCROLL_GO_ON);
10617
10618       AdvanceFrameAndPlayerCounters(player->index_nr);
10619
10620       DrawAllPlayers();
10621       BackToFront();
10622     }
10623
10624     player->move_delay_value = original_move_delay_value;
10625   }
10626
10627   player->is_active = FALSE;
10628
10629   if (player->last_move_dir & MV_HORIZONTAL)
10630   {
10631     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10632       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10633   }
10634   else
10635   {
10636     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10637       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10638   }
10639
10640 #if USE_FIXED_BORDER_RUNNING_GFX
10641   if (!moved && !player->is_active)
10642   {
10643     player->is_moving = FALSE;
10644     player->is_digging = FALSE;
10645     player->is_collecting = FALSE;
10646     player->is_snapping = FALSE;
10647     player->is_pushing = FALSE;
10648   }
10649 #endif
10650
10651   jx = player->jx;
10652   jy = player->jy;
10653
10654 #if 1
10655   if (moved & MP_MOVING && !ScreenMovPos &&
10656       (player->index_nr == game.centered_player_nr ||
10657        game.centered_player_nr == -1))
10658 #else
10659   if (moved & MP_MOVING && !ScreenMovPos &&
10660       (player == local_player || !options.network))
10661 #endif
10662   {
10663     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10664     int offset = (setup.scroll_delay ? 3 : 0);
10665
10666     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10667     {
10668       /* actual player has left the screen -- scroll in that direction */
10669       if (jx != old_jx)         /* player has moved horizontally */
10670         scroll_x += (jx - old_jx);
10671       else                      /* player has moved vertically */
10672         scroll_y += (jy - old_jy);
10673     }
10674     else
10675     {
10676       if (jx != old_jx)         /* player has moved horizontally */
10677       {
10678         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10679             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10680           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10681
10682         /* don't scroll over playfield boundaries */
10683         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10684           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10685
10686         /* don't scroll more than one field at a time */
10687         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10688
10689         /* don't scroll against the player's moving direction */
10690         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10691             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10692           scroll_x = old_scroll_x;
10693       }
10694       else                      /* player has moved vertically */
10695       {
10696         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10697             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10698           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10699
10700         /* don't scroll over playfield boundaries */
10701         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10702           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10703
10704         /* don't scroll more than one field at a time */
10705         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10706
10707         /* don't scroll against the player's moving direction */
10708         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10709             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10710           scroll_y = old_scroll_y;
10711       }
10712     }
10713
10714     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10715     {
10716 #if 1
10717       if (!options.network && game.centered_player_nr == -1 &&
10718           !AllPlayersInVisibleScreen())
10719       {
10720         scroll_x = old_scroll_x;
10721         scroll_y = old_scroll_y;
10722       }
10723       else
10724 #else
10725       if (!options.network && !AllPlayersInVisibleScreen())
10726       {
10727         scroll_x = old_scroll_x;
10728         scroll_y = old_scroll_y;
10729       }
10730       else
10731 #endif
10732       {
10733         ScrollScreen(player, SCROLL_INIT);
10734         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10735       }
10736     }
10737   }
10738
10739   player->StepFrame = 0;
10740
10741   if (moved & MP_MOVING)
10742   {
10743     if (old_jx != jx && old_jy == jy)
10744       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10745     else if (old_jx == jx && old_jy != jy)
10746       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10747
10748     DrawLevelField(jx, jy);     /* for "crumbled sand" */
10749
10750     player->last_move_dir = player->MovDir;
10751     player->is_moving = TRUE;
10752     player->is_snapping = FALSE;
10753     player->is_switching = FALSE;
10754     player->is_dropping = FALSE;
10755     player->is_dropping_pressed = FALSE;
10756     player->drop_pressed_delay = 0;
10757   }
10758   else
10759   {
10760     CheckGravityMovementWhenNotMoving(player);
10761
10762     player->is_moving = FALSE;
10763
10764     /* at this point, the player is allowed to move, but cannot move right now
10765        (e.g. because of something blocking the way) -- ensure that the player
10766        is also allowed to move in the next frame (in old versions before 3.1.1,
10767        the player was forced to wait again for eight frames before next try) */
10768
10769     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10770       player->move_delay = 0;   /* allow direct movement in the next frame */
10771   }
10772
10773   if (player->move_delay == -1)         /* not yet initialized by DigField() */
10774     player->move_delay = player->move_delay_value;
10775
10776   if (game.engine_version < VERSION_IDENT(3,0,7,0))
10777   {
10778     TestIfPlayerTouchesBadThing(jx, jy);
10779     TestIfPlayerTouchesCustomElement(jx, jy);
10780   }
10781
10782   if (!player->active)
10783     RemovePlayer(player);
10784
10785   return moved;
10786 }
10787
10788 void ScrollPlayer(struct PlayerInfo *player, int mode)
10789 {
10790   int jx = player->jx, jy = player->jy;
10791   int last_jx = player->last_jx, last_jy = player->last_jy;
10792   int move_stepsize = TILEX / player->move_delay_value;
10793
10794 #if USE_NEW_PLAYER_SPEED
10795   if (!player->active)
10796     return;
10797
10798   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
10799     return;
10800 #else
10801   if (!player->active || player->MovPos == 0)
10802     return;
10803 #endif
10804
10805   if (mode == SCROLL_INIT)
10806   {
10807     player->actual_frame_counter = FrameCounter;
10808     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10809
10810     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10811         Feld[last_jx][last_jy] == EL_EMPTY)
10812     {
10813       int last_field_block_delay = 0;   /* start with no blocking at all */
10814       int block_delay_adjustment = player->block_delay_adjustment;
10815
10816       /* if player blocks last field, add delay for exactly one move */
10817       if (player->block_last_field)
10818       {
10819         last_field_block_delay += player->move_delay_value;
10820
10821         /* when blocking enabled, prevent moving up despite gravity */
10822 #if USE_PLAYER_GRAVITY
10823         if (player->gravity && player->MovDir == MV_UP)
10824           block_delay_adjustment = -1;
10825 #else
10826         if (game.gravity && player->MovDir == MV_UP)
10827           block_delay_adjustment = -1;
10828 #endif
10829       }
10830
10831       /* add block delay adjustment (also possible when not blocking) */
10832       last_field_block_delay += block_delay_adjustment;
10833
10834       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10835       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10836     }
10837
10838 #if USE_NEW_PLAYER_SPEED
10839     if (player->MovPos != 0)    /* player has not yet reached destination */
10840       return;
10841 #else
10842     return;
10843 #endif
10844   }
10845   else if (!FrameReached(&player->actual_frame_counter, 1))
10846     return;
10847
10848 #if 0
10849   printf("::: player->MovPos: %d -> %d\n",
10850          player->MovPos,
10851          player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10852 #endif
10853
10854 #if USE_NEW_PLAYER_SPEED
10855   if (player->MovPos != 0)
10856   {
10857     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10858     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10859
10860     /* before DrawPlayer() to draw correct player graphic for this case */
10861     if (player->MovPos == 0)
10862       CheckGravityMovement(player);
10863   }
10864 #else
10865   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10866   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10867
10868   /* before DrawPlayer() to draw correct player graphic for this case */
10869   if (player->MovPos == 0)
10870     CheckGravityMovement(player);
10871 #endif
10872
10873   if (player->MovPos == 0)      /* player reached destination field */
10874   {
10875 #if 0
10876     printf("::: player reached destination field\n");
10877 #endif
10878
10879     if (player->move_delay_reset_counter > 0)
10880     {
10881       player->move_delay_reset_counter--;
10882
10883       if (player->move_delay_reset_counter == 0)
10884       {
10885         /* continue with normal speed after quickly moving through gate */
10886         HALVE_PLAYER_SPEED(player);
10887
10888         /* be able to make the next move without delay */
10889         player->move_delay = 0;
10890       }
10891     }
10892
10893     player->last_jx = jx;
10894     player->last_jy = jy;
10895
10896     if (Feld[jx][jy] == EL_EXIT_OPEN ||
10897         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10898         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
10899     {
10900       DrawPlayer(player);       /* needed here only to cleanup last field */
10901       RemovePlayer(player);
10902
10903       if (local_player->friends_still_needed == 0 ||
10904           IS_SP_ELEMENT(Feld[jx][jy]))
10905         player->LevelSolved = player->GameOver = TRUE;
10906     }
10907
10908     /* this breaks one level: "machine", level 000 */
10909     {
10910       int move_direction = player->MovDir;
10911       int enter_side = MV_DIR_OPPOSITE(move_direction);
10912       int leave_side = move_direction;
10913       int old_jx = last_jx;
10914       int old_jy = last_jy;
10915       int old_element = Feld[old_jx][old_jy];
10916       int new_element = Feld[jx][jy];
10917
10918       if (IS_CUSTOM_ELEMENT(old_element))
10919         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10920                                    CE_LEFT_BY_PLAYER,
10921                                    player->index_bit, leave_side);
10922
10923       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10924                                           CE_PLAYER_LEAVES_X,
10925                                           player->index_bit, leave_side);
10926
10927       if (IS_CUSTOM_ELEMENT(new_element))
10928         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10929                                    player->index_bit, enter_side);
10930
10931       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10932                                           CE_PLAYER_ENTERS_X,
10933                                           player->index_bit, enter_side);
10934
10935       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10936                                         CE_MOVE_OF_X, move_direction);
10937     }
10938
10939     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10940     {
10941       TestIfPlayerTouchesBadThing(jx, jy);
10942       TestIfPlayerTouchesCustomElement(jx, jy);
10943
10944       /* needed because pushed element has not yet reached its destination,
10945          so it would trigger a change event at its previous field location */
10946       if (!player->is_pushing)
10947         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
10948
10949       if (!player->active)
10950         RemovePlayer(player);
10951     }
10952
10953     if (level.use_step_counter)
10954     {
10955       int i;
10956
10957       TimePlayed++;
10958
10959       if (TimeLeft > 0)
10960       {
10961         TimeLeft--;
10962
10963         if (TimeLeft <= 10 && setup.time_limit)
10964           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10965
10966         DrawGameValue_Time(TimeLeft);
10967
10968         if (!TimeLeft && setup.time_limit)
10969           for (i = 0; i < MAX_PLAYERS; i++)
10970             KillPlayer(&stored_player[i]);
10971       }
10972       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10973         DrawGameValue_Time(TimePlayed);
10974     }
10975
10976     if (tape.single_step && tape.recording && !tape.pausing &&
10977         !player->programmed_action)
10978       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10979   }
10980 }
10981
10982 void ScrollScreen(struct PlayerInfo *player, int mode)
10983 {
10984   static unsigned long screen_frame_counter = 0;
10985
10986   if (mode == SCROLL_INIT)
10987   {
10988     /* set scrolling step size according to actual player's moving speed */
10989     ScrollStepSize = TILEX / player->move_delay_value;
10990
10991     screen_frame_counter = FrameCounter;
10992     ScreenMovDir = player->MovDir;
10993     ScreenMovPos = player->MovPos;
10994     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10995     return;
10996   }
10997   else if (!FrameReached(&screen_frame_counter, 1))
10998     return;
10999
11000   if (ScreenMovPos)
11001   {
11002     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11003     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11004     redraw_mask |= REDRAW_FIELD;
11005   }
11006   else
11007     ScreenMovDir = MV_NONE;
11008 }
11009
11010 void TestIfPlayerTouchesCustomElement(int x, int y)
11011 {
11012   static int xy[4][2] =
11013   {
11014     { 0, -1 },
11015     { -1, 0 },
11016     { +1, 0 },
11017     { 0, +1 }
11018   };
11019   static int trigger_sides[4][2] =
11020   {
11021     /* center side       border side */
11022     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11023     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11024     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11025     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11026   };
11027   static int touch_dir[4] =
11028   {
11029     MV_LEFT | MV_RIGHT,
11030     MV_UP   | MV_DOWN,
11031     MV_UP   | MV_DOWN,
11032     MV_LEFT | MV_RIGHT
11033   };
11034   int center_element = Feld[x][y];      /* should always be non-moving! */
11035   int i;
11036
11037   for (i = 0; i < NUM_DIRECTIONS; i++)
11038   {
11039     int xx = x + xy[i][0];
11040     int yy = y + xy[i][1];
11041     int center_side = trigger_sides[i][0];
11042     int border_side = trigger_sides[i][1];
11043     int border_element;
11044
11045     if (!IN_LEV_FIELD(xx, yy))
11046       continue;
11047
11048     if (IS_PLAYER(x, y))
11049     {
11050       struct PlayerInfo *player = PLAYERINFO(x, y);
11051
11052       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11053         border_element = Feld[xx][yy];          /* may be moving! */
11054       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11055         border_element = Feld[xx][yy];
11056       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11057         border_element = MovingOrBlocked2Element(xx, yy);
11058       else
11059         continue;               /* center and border element do not touch */
11060
11061       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11062                                  player->index_bit, border_side);
11063       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11064                                           CE_PLAYER_TOUCHES_X,
11065                                           player->index_bit, border_side);
11066     }
11067     else if (IS_PLAYER(xx, yy))
11068     {
11069       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11070
11071       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11072       {
11073         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11074           continue;             /* center and border element do not touch */
11075       }
11076
11077       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11078                                  player->index_bit, center_side);
11079       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11080                                           CE_PLAYER_TOUCHES_X,
11081                                           player->index_bit, center_side);
11082       break;
11083     }
11084   }
11085 }
11086
11087 #if USE_ELEMENT_TOUCHING_BUGFIX
11088
11089 void TestIfElementTouchesCustomElement(int x, int y)
11090 {
11091   static int xy[4][2] =
11092   {
11093     { 0, -1 },
11094     { -1, 0 },
11095     { +1, 0 },
11096     { 0, +1 }
11097   };
11098   static int trigger_sides[4][2] =
11099   {
11100     /* center side      border side */
11101     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11102     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11103     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11104     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11105   };
11106   static int touch_dir[4] =
11107   {
11108     MV_LEFT | MV_RIGHT,
11109     MV_UP   | MV_DOWN,
11110     MV_UP   | MV_DOWN,
11111     MV_LEFT | MV_RIGHT
11112   };
11113   boolean change_center_element = FALSE;
11114   int center_element = Feld[x][y];      /* should always be non-moving! */
11115   int border_element_old[NUM_DIRECTIONS];
11116   int i;
11117
11118   for (i = 0; i < NUM_DIRECTIONS; i++)
11119   {
11120     int xx = x + xy[i][0];
11121     int yy = y + xy[i][1];
11122     int border_element;
11123
11124     border_element_old[i] = -1;
11125
11126     if (!IN_LEV_FIELD(xx, yy))
11127       continue;
11128
11129     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11130       border_element = Feld[xx][yy];    /* may be moving! */
11131     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11132       border_element = Feld[xx][yy];
11133     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11134       border_element = MovingOrBlocked2Element(xx, yy);
11135     else
11136       continue;                 /* center and border element do not touch */
11137
11138     border_element_old[i] = border_element;
11139   }
11140
11141   for (i = 0; i < NUM_DIRECTIONS; i++)
11142   {
11143     int xx = x + xy[i][0];
11144     int yy = y + xy[i][1];
11145     int center_side = trigger_sides[i][0];
11146     int border_element = border_element_old[i];
11147
11148     if (border_element == -1)
11149       continue;
11150
11151     /* check for change of border element */
11152     CheckElementChangeBySide(xx, yy, border_element, center_element,
11153                              CE_TOUCHING_X, center_side);
11154   }
11155
11156   for (i = 0; i < NUM_DIRECTIONS; i++)
11157   {
11158     int border_side = trigger_sides[i][1];
11159     int border_element = border_element_old[i];
11160
11161     if (border_element == -1)
11162       continue;
11163
11164     /* check for change of center element (but change it only once) */
11165     if (!change_center_element)
11166       change_center_element =
11167         CheckElementChangeBySide(x, y, center_element, border_element,
11168                                  CE_TOUCHING_X, border_side);
11169   }
11170 }
11171
11172 #else
11173
11174 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11175 {
11176   static int xy[4][2] =
11177   {
11178     { 0, -1 },
11179     { -1, 0 },
11180     { +1, 0 },
11181     { 0, +1 }
11182   };
11183   static int trigger_sides[4][2] =
11184   {
11185     /* center side      border side */
11186     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11187     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11188     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11189     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11190   };
11191   static int touch_dir[4] =
11192   {
11193     MV_LEFT | MV_RIGHT,
11194     MV_UP   | MV_DOWN,
11195     MV_UP   | MV_DOWN,
11196     MV_LEFT | MV_RIGHT
11197   };
11198   boolean change_center_element = FALSE;
11199   int center_element = Feld[x][y];      /* should always be non-moving! */
11200   int i;
11201
11202   for (i = 0; i < NUM_DIRECTIONS; i++)
11203   {
11204     int xx = x + xy[i][0];
11205     int yy = y + xy[i][1];
11206     int center_side = trigger_sides[i][0];
11207     int border_side = trigger_sides[i][1];
11208     int border_element;
11209
11210     if (!IN_LEV_FIELD(xx, yy))
11211       continue;
11212
11213     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11214       border_element = Feld[xx][yy];    /* may be moving! */
11215     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11216       border_element = Feld[xx][yy];
11217     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11218       border_element = MovingOrBlocked2Element(xx, yy);
11219     else
11220       continue;                 /* center and border element do not touch */
11221
11222     /* check for change of center element (but change it only once) */
11223     if (!change_center_element)
11224       change_center_element =
11225         CheckElementChangeBySide(x, y, center_element, border_element,
11226                                  CE_TOUCHING_X, border_side);
11227
11228     /* check for change of border element */
11229     CheckElementChangeBySide(xx, yy, border_element, center_element,
11230                              CE_TOUCHING_X, center_side);
11231   }
11232 }
11233
11234 #endif
11235
11236 void TestIfElementHitsCustomElement(int x, int y, int direction)
11237 {
11238   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11239   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11240   int hitx = x + dx, hity = y + dy;
11241   int hitting_element = Feld[x][y];
11242   int touched_element;
11243
11244   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11245     return;
11246
11247   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11248                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11249
11250   if (IN_LEV_FIELD(hitx, hity))
11251   {
11252     int opposite_direction = MV_DIR_OPPOSITE(direction);
11253     int hitting_side = direction;
11254     int touched_side = opposite_direction;
11255     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11256                           MovDir[hitx][hity] != direction ||
11257                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11258
11259     object_hit = TRUE;
11260
11261     if (object_hit)
11262     {
11263       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11264                                CE_HITTING_X, touched_side);
11265
11266       CheckElementChangeBySide(hitx, hity, touched_element,
11267                                hitting_element, CE_HIT_BY_X, hitting_side);
11268
11269       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11270                                CE_HIT_BY_SOMETHING, opposite_direction);
11271     }
11272   }
11273
11274   /* "hitting something" is also true when hitting the playfield border */
11275   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11276                            CE_HITTING_SOMETHING, direction);
11277 }
11278
11279 #if 0
11280 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11281 {
11282   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11283   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11284   int hitx = x + dx, hity = y + dy;
11285   int hitting_element = Feld[x][y];
11286   int touched_element;
11287 #if 0
11288   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11289                         !IS_FREE(hitx, hity) &&
11290                         (!IS_MOVING(hitx, hity) ||
11291                          MovDir[hitx][hity] != direction ||
11292                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11293 #endif
11294
11295   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11296     return;
11297
11298 #if 0
11299   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11300     return;
11301 #endif
11302
11303   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11304                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11305
11306   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11307                            EP_CAN_SMASH_EVERYTHING, direction);
11308
11309   if (IN_LEV_FIELD(hitx, hity))
11310   {
11311     int opposite_direction = MV_DIR_OPPOSITE(direction);
11312     int hitting_side = direction;
11313     int touched_side = opposite_direction;
11314 #if 0
11315     int touched_element = MovingOrBlocked2Element(hitx, hity);
11316 #endif
11317 #if 1
11318     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11319                           MovDir[hitx][hity] != direction ||
11320                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11321
11322     object_hit = TRUE;
11323 #endif
11324
11325     if (object_hit)
11326     {
11327       int i;
11328
11329       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11330                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11331
11332       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11333                                CE_OTHER_IS_SMASHING, touched_side);
11334
11335       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11336                                CE_OTHER_GETS_SMASHED, hitting_side);
11337     }
11338   }
11339 }
11340 #endif
11341
11342 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11343 {
11344   int i, kill_x = -1, kill_y = -1;
11345
11346   int bad_element = -1;
11347   static int test_xy[4][2] =
11348   {
11349     { 0, -1 },
11350     { -1, 0 },
11351     { +1, 0 },
11352     { 0, +1 }
11353   };
11354   static int test_dir[4] =
11355   {
11356     MV_UP,
11357     MV_LEFT,
11358     MV_RIGHT,
11359     MV_DOWN
11360   };
11361
11362   for (i = 0; i < NUM_DIRECTIONS; i++)
11363   {
11364     int test_x, test_y, test_move_dir, test_element;
11365
11366     test_x = good_x + test_xy[i][0];
11367     test_y = good_y + test_xy[i][1];
11368
11369     if (!IN_LEV_FIELD(test_x, test_y))
11370       continue;
11371
11372     test_move_dir =
11373       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11374
11375     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11376
11377     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11378        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11379     */
11380     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11381         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11382     {
11383       kill_x = test_x;
11384       kill_y = test_y;
11385       bad_element = test_element;
11386
11387       break;
11388     }
11389   }
11390
11391   if (kill_x != -1 || kill_y != -1)
11392   {
11393     if (IS_PLAYER(good_x, good_y))
11394     {
11395       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11396
11397       if (player->shield_deadly_time_left > 0 &&
11398           !IS_INDESTRUCTIBLE(bad_element))
11399         Bang(kill_x, kill_y);
11400       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11401         KillPlayer(player);
11402     }
11403     else
11404       Bang(good_x, good_y);
11405   }
11406 }
11407
11408 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11409 {
11410   int i, kill_x = -1, kill_y = -1;
11411   int bad_element = Feld[bad_x][bad_y];
11412   static int test_xy[4][2] =
11413   {
11414     { 0, -1 },
11415     { -1, 0 },
11416     { +1, 0 },
11417     { 0, +1 }
11418   };
11419   static int touch_dir[4] =
11420   {
11421     MV_LEFT | MV_RIGHT,
11422     MV_UP   | MV_DOWN,
11423     MV_UP   | MV_DOWN,
11424     MV_LEFT | MV_RIGHT
11425   };
11426   static int test_dir[4] =
11427   {
11428     MV_UP,
11429     MV_LEFT,
11430     MV_RIGHT,
11431     MV_DOWN
11432   };
11433
11434   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11435     return;
11436
11437   for (i = 0; i < NUM_DIRECTIONS; i++)
11438   {
11439     int test_x, test_y, test_move_dir, test_element;
11440
11441     test_x = bad_x + test_xy[i][0];
11442     test_y = bad_y + test_xy[i][1];
11443     if (!IN_LEV_FIELD(test_x, test_y))
11444       continue;
11445
11446     test_move_dir =
11447       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11448
11449     test_element = Feld[test_x][test_y];
11450
11451     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11452        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11453     */
11454     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11455         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11456     {
11457       /* good thing is player or penguin that does not move away */
11458       if (IS_PLAYER(test_x, test_y))
11459       {
11460         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11461
11462         if (bad_element == EL_ROBOT && player->is_moving)
11463           continue;     /* robot does not kill player if he is moving */
11464
11465         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11466         {
11467           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11468             continue;           /* center and border element do not touch */
11469         }
11470
11471         kill_x = test_x;
11472         kill_y = test_y;
11473         break;
11474       }
11475       else if (test_element == EL_PENGUIN)
11476       {
11477         kill_x = test_x;
11478         kill_y = test_y;
11479         break;
11480       }
11481     }
11482   }
11483
11484   if (kill_x != -1 || kill_y != -1)
11485   {
11486     if (IS_PLAYER(kill_x, kill_y))
11487     {
11488       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11489
11490       if (player->shield_deadly_time_left > 0 &&
11491           !IS_INDESTRUCTIBLE(bad_element))
11492         Bang(bad_x, bad_y);
11493       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11494         KillPlayer(player);
11495     }
11496     else
11497       Bang(kill_x, kill_y);
11498   }
11499 }
11500
11501 void TestIfPlayerTouchesBadThing(int x, int y)
11502 {
11503   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11504 }
11505
11506 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11507 {
11508   TestIfGoodThingHitsBadThing(x, y, move_dir);
11509 }
11510
11511 void TestIfBadThingTouchesPlayer(int x, int y)
11512 {
11513   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11514 }
11515
11516 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11517 {
11518   TestIfBadThingHitsGoodThing(x, y, move_dir);
11519 }
11520
11521 void TestIfFriendTouchesBadThing(int x, int y)
11522 {
11523   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11524 }
11525
11526 void TestIfBadThingTouchesFriend(int x, int y)
11527 {
11528   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11529 }
11530
11531 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11532 {
11533   int i, kill_x = bad_x, kill_y = bad_y;
11534   static int xy[4][2] =
11535   {
11536     { 0, -1 },
11537     { -1, 0 },
11538     { +1, 0 },
11539     { 0, +1 }
11540   };
11541
11542   for (i = 0; i < NUM_DIRECTIONS; i++)
11543   {
11544     int x, y, element;
11545
11546     x = bad_x + xy[i][0];
11547     y = bad_y + xy[i][1];
11548     if (!IN_LEV_FIELD(x, y))
11549       continue;
11550
11551     element = Feld[x][y];
11552     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11553         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11554     {
11555       kill_x = x;
11556       kill_y = y;
11557       break;
11558     }
11559   }
11560
11561   if (kill_x != bad_x || kill_y != bad_y)
11562     Bang(bad_x, bad_y);
11563 }
11564
11565 void KillPlayer(struct PlayerInfo *player)
11566 {
11567   int jx = player->jx, jy = player->jy;
11568
11569   if (!player->active)
11570     return;
11571
11572   /* remove accessible field at the player's position */
11573   Feld[jx][jy] = EL_EMPTY;
11574
11575   /* deactivate shield (else Bang()/Explode() would not work right) */
11576   player->shield_normal_time_left = 0;
11577   player->shield_deadly_time_left = 0;
11578
11579   Bang(jx, jy);
11580   BuryPlayer(player);
11581 }
11582
11583 static void KillPlayerUnlessEnemyProtected(int x, int y)
11584 {
11585   if (!PLAYER_ENEMY_PROTECTED(x, y))
11586     KillPlayer(PLAYERINFO(x, y));
11587 }
11588
11589 static void KillPlayerUnlessExplosionProtected(int x, int y)
11590 {
11591   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11592     KillPlayer(PLAYERINFO(x, y));
11593 }
11594
11595 void BuryPlayer(struct PlayerInfo *player)
11596 {
11597   int jx = player->jx, jy = player->jy;
11598
11599   if (!player->active)
11600     return;
11601
11602   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11603   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11604
11605   player->GameOver = TRUE;
11606   RemovePlayer(player);
11607 }
11608
11609 void RemovePlayer(struct PlayerInfo *player)
11610 {
11611   int jx = player->jx, jy = player->jy;
11612   int i, found = FALSE;
11613
11614   player->present = FALSE;
11615   player->active = FALSE;
11616
11617   if (!ExplodeField[jx][jy])
11618     StorePlayer[jx][jy] = 0;
11619
11620   if (player->is_moving)
11621     DrawLevelField(player->last_jx, player->last_jy);
11622
11623   for (i = 0; i < MAX_PLAYERS; i++)
11624     if (stored_player[i].active)
11625       found = TRUE;
11626
11627   if (!found)
11628     AllPlayersGone = TRUE;
11629
11630   ExitX = ZX = jx;
11631   ExitY = ZY = jy;
11632 }
11633
11634 #if USE_NEW_SNAP_DELAY
11635 static void setFieldForSnapping(int x, int y, int element, int direction)
11636 {
11637   struct ElementInfo *ei = &element_info[element];
11638   int direction_bit = MV_DIR_TO_BIT(direction);
11639   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11640   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11641                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11642
11643   Feld[x][y] = EL_ELEMENT_SNAPPING;
11644   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11645
11646   ResetGfxAnimation(x, y);
11647
11648   GfxElement[x][y] = element;
11649   GfxAction[x][y] = action;
11650   GfxDir[x][y] = direction;
11651   GfxFrame[x][y] = -1;
11652 }
11653 #endif
11654
11655 /*
11656   =============================================================================
11657   checkDiagonalPushing()
11658   -----------------------------------------------------------------------------
11659   check if diagonal input device direction results in pushing of object
11660   (by checking if the alternative direction is walkable, diggable, ...)
11661   =============================================================================
11662 */
11663
11664 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11665                                     int x, int y, int real_dx, int real_dy)
11666 {
11667   int jx, jy, dx, dy, xx, yy;
11668
11669   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11670     return TRUE;
11671
11672   /* diagonal direction: check alternative direction */
11673   jx = player->jx;
11674   jy = player->jy;
11675   dx = x - jx;
11676   dy = y - jy;
11677   xx = jx + (dx == 0 ? real_dx : 0);
11678   yy = jy + (dy == 0 ? real_dy : 0);
11679
11680   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11681 }
11682
11683 /*
11684   =============================================================================
11685   DigField()
11686   -----------------------------------------------------------------------------
11687   x, y:                 field next to player (non-diagonal) to try to dig to
11688   real_dx, real_dy:     direction as read from input device (can be diagonal)
11689   =============================================================================
11690 */
11691
11692 int DigField(struct PlayerInfo *player,
11693              int oldx, int oldy, int x, int y,
11694              int real_dx, int real_dy, int mode)
11695 {
11696   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11697   boolean player_was_pushing = player->is_pushing;
11698   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11699   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11700   int jx = oldx, jy = oldy;
11701   int dx = x - jx, dy = y - jy;
11702   int nextx = x + dx, nexty = y + dy;
11703   int move_direction = (dx == -1 ? MV_LEFT  :
11704                         dx == +1 ? MV_RIGHT :
11705                         dy == -1 ? MV_UP    :
11706                         dy == +1 ? MV_DOWN  : MV_NONE);
11707   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11708   int dig_side = MV_DIR_OPPOSITE(move_direction);
11709   int old_element = Feld[jx][jy];
11710 #if USE_FIXED_DONT_RUN_INTO
11711   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11712 #else
11713   int element;
11714 #endif
11715   int collect_count;
11716
11717   if (is_player)                /* function can also be called by EL_PENGUIN */
11718   {
11719     if (player->MovPos == 0)
11720     {
11721       player->is_digging = FALSE;
11722       player->is_collecting = FALSE;
11723     }
11724
11725     if (player->MovPos == 0)    /* last pushing move finished */
11726       player->is_pushing = FALSE;
11727
11728     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11729     {
11730       player->is_switching = FALSE;
11731       player->push_delay = -1;
11732
11733       return MP_NO_ACTION;
11734     }
11735   }
11736
11737 #if !USE_FIXED_DONT_RUN_INTO
11738   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11739     return MP_NO_ACTION;
11740 #endif
11741
11742   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11743     old_element = Back[jx][jy];
11744
11745   /* in case of element dropped at player position, check background */
11746   else if (Back[jx][jy] != EL_EMPTY &&
11747            game.engine_version >= VERSION_IDENT(2,2,0,0))
11748     old_element = Back[jx][jy];
11749
11750   /* checking here causes player to move into acid even if the current field
11751      cannot be left to that direction */
11752 #if 0
11753 #if USE_FIXED_DONT_RUN_INTO
11754   if (player_can_move && DONT_RUN_INTO(element))
11755   {
11756     if (element == EL_ACID && dx == 0 && dy == 1)
11757     {
11758       SplashAcid(x, y);
11759       Feld[jx][jy] = EL_PLAYER_1;
11760       InitMovingField(jx, jy, MV_DOWN);
11761       Store[jx][jy] = EL_ACID;
11762       ContinueMoving(jx, jy);
11763       BuryPlayer(player);
11764     }
11765     else
11766       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11767
11768     return MP_DONT_RUN_INTO;
11769   }
11770 #endif
11771 #endif
11772
11773 #if 1   /* ------------------------------ NEW ------------------------------ */
11774
11775   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11776     return MP_NO_ACTION;        /* field has no opening in this direction */
11777
11778   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11779     return MP_NO_ACTION;        /* field has no opening in this direction */
11780
11781 #if USE_FIXED_DONT_RUN_INTO
11782   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11783   {
11784     SplashAcid(x, y);
11785 #if 1
11786     Feld[jx][jy] = player->artwork_element;
11787 #else
11788     Feld[jx][jy] = EL_PLAYER_1;
11789 #endif
11790     InitMovingField(jx, jy, MV_DOWN);
11791     Store[jx][jy] = EL_ACID;
11792     ContinueMoving(jx, jy);
11793     BuryPlayer(player);
11794
11795     return MP_DONT_RUN_INTO;
11796   }
11797 #endif
11798
11799 #if USE_FIXED_DONT_RUN_INTO
11800   if (player_can_move && DONT_RUN_INTO(element))
11801   {
11802     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11803
11804     return MP_DONT_RUN_INTO;
11805   }
11806 #endif
11807
11808 #else   /* ------------------------------ OLD ------------------------------ */
11809
11810 #if 1
11811 #if USE_FIXED_DONT_RUN_INTO
11812   if (player_can_move && DONT_RUN_INTO(element))
11813   {
11814     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11815
11816     return MP_DONT_RUN_INTO;
11817   }
11818 #endif
11819 #endif
11820
11821   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11822     return MP_NO_ACTION;        /* field has no opening in this direction */
11823
11824   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11825     return MP_NO_ACTION;        /* field has no opening in this direction */
11826
11827   /* checking here causes player to explode when moving into acid */
11828 #if 1
11829 #if USE_FIXED_DONT_RUN_INTO
11830   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11831   {
11832     SplashAcid(x, y);
11833     Feld[jx][jy] = EL_PLAYER_1;
11834     InitMovingField(jx, jy, MV_DOWN);
11835     Store[jx][jy] = EL_ACID;
11836     ContinueMoving(jx, jy);
11837     BuryPlayer(player);
11838
11839     return MP_DONT_RUN_INTO;
11840   }
11841 #endif
11842 #endif
11843
11844 #endif  /* ------------------------------ END ------------------------------ */
11845
11846 #if 0
11847 #if USE_FIXED_DONT_RUN_INTO
11848   if (player_can_move && DONT_RUN_INTO(element))
11849   {
11850     if (element == EL_ACID && dx == 0 && dy == 1)
11851     {
11852       SplashAcid(x, y);
11853       Feld[jx][jy] = EL_PLAYER_1;
11854       InitMovingField(jx, jy, MV_DOWN);
11855       Store[jx][jy] = EL_ACID;
11856       ContinueMoving(jx, jy);
11857       BuryPlayer(player);
11858     }
11859     else
11860       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11861
11862     return MP_DONT_RUN_INTO;
11863   }
11864 #endif
11865 #endif
11866
11867 #if USE_FIXED_DONT_RUN_INTO
11868   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11869     return MP_NO_ACTION;
11870 #endif
11871
11872 #if !USE_FIXED_DONT_RUN_INTO
11873   element = Feld[x][y];
11874 #endif
11875
11876   collect_count = element_info[element].collect_count_initial;
11877
11878   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
11879     return MP_NO_ACTION;
11880
11881   if (game.engine_version < VERSION_IDENT(2,2,0,0))
11882     player_can_move = player_can_move_or_snap;
11883
11884   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11885       game.engine_version >= VERSION_IDENT(2,2,0,0))
11886   {
11887     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11888                                player->index_bit, dig_side);
11889     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11890                                         player->index_bit, dig_side);
11891
11892     if (Feld[x][y] != element)          /* field changed by snapping */
11893       return MP_ACTION;
11894
11895     return MP_NO_ACTION;
11896   }
11897
11898 #if USE_PLAYER_GRAVITY
11899   if (player->gravity && is_player && !player->is_auto_moving &&
11900       canFallDown(player) && move_direction != MV_DOWN &&
11901       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11902     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
11903 #else
11904   if (game.gravity && is_player && !player->is_auto_moving &&
11905       canFallDown(player) && move_direction != MV_DOWN &&
11906       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11907     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
11908 #endif
11909
11910   if (player_can_move &&
11911       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11912   {
11913     int sound_element = SND_ELEMENT(element);
11914     int sound_action = ACTION_WALKING;
11915
11916     if (IS_RND_GATE(element))
11917     {
11918       if (!player->key[RND_GATE_NR(element)])
11919         return MP_NO_ACTION;
11920     }
11921     else if (IS_RND_GATE_GRAY(element))
11922     {
11923       if (!player->key[RND_GATE_GRAY_NR(element)])
11924         return MP_NO_ACTION;
11925     }
11926     else if (IS_RND_GATE_GRAY_ACTIVE(element))
11927     {
11928       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11929         return MP_NO_ACTION;
11930     }
11931     else if (element == EL_EXIT_OPEN ||
11932              element == EL_SP_EXIT_OPEN ||
11933              element == EL_SP_EXIT_OPENING)
11934     {
11935       sound_action = ACTION_PASSING;    /* player is passing exit */
11936     }
11937     else if (element == EL_EMPTY)
11938     {
11939       sound_action = ACTION_MOVING;             /* nothing to walk on */
11940     }
11941
11942     /* play sound from background or player, whatever is available */
11943     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11944       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11945     else
11946       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11947   }
11948   else if (player_can_move &&
11949            IS_PASSABLE(element) && canPassField(x, y, move_direction))
11950   {
11951     if (!ACCESS_FROM(element, opposite_direction))
11952       return MP_NO_ACTION;      /* field not accessible from this direction */
11953
11954     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
11955       return MP_NO_ACTION;
11956
11957     if (IS_EM_GATE(element))
11958     {
11959       if (!player->key[EM_GATE_NR(element)])
11960         return MP_NO_ACTION;
11961     }
11962     else if (IS_EM_GATE_GRAY(element))
11963     {
11964       if (!player->key[EM_GATE_GRAY_NR(element)])
11965         return MP_NO_ACTION;
11966     }
11967     else if (IS_EM_GATE_GRAY_ACTIVE(element))
11968     {
11969       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11970         return MP_NO_ACTION;
11971     }
11972     else if (IS_EMC_GATE(element))
11973     {
11974       if (!player->key[EMC_GATE_NR(element)])
11975         return MP_NO_ACTION;
11976     }
11977     else if (IS_EMC_GATE_GRAY(element))
11978     {
11979       if (!player->key[EMC_GATE_GRAY_NR(element)])
11980         return MP_NO_ACTION;
11981     }
11982     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
11983     {
11984       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
11985         return MP_NO_ACTION;
11986     }
11987     else if (IS_SP_PORT(element))
11988     {
11989       if (element == EL_SP_GRAVITY_PORT_LEFT ||
11990           element == EL_SP_GRAVITY_PORT_RIGHT ||
11991           element == EL_SP_GRAVITY_PORT_UP ||
11992           element == EL_SP_GRAVITY_PORT_DOWN)
11993 #if USE_PLAYER_GRAVITY
11994         player->gravity = !player->gravity;
11995 #else
11996         game.gravity = !game.gravity;
11997 #endif
11998       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11999                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12000                element == EL_SP_GRAVITY_ON_PORT_UP ||
12001                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12002 #if USE_PLAYER_GRAVITY
12003         player->gravity = TRUE;
12004 #else
12005         game.gravity = TRUE;
12006 #endif
12007       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12008                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12009                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12010                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12011 #if USE_PLAYER_GRAVITY
12012         player->gravity = FALSE;
12013 #else
12014         game.gravity = FALSE;
12015 #endif
12016     }
12017
12018     /* automatically move to the next field with double speed */
12019     player->programmed_action = move_direction;
12020
12021     if (player->move_delay_reset_counter == 0)
12022     {
12023       player->move_delay_reset_counter = 2;     /* two double speed steps */
12024
12025       DOUBLE_PLAYER_SPEED(player);
12026     }
12027
12028     PlayLevelSoundAction(x, y, ACTION_PASSING);
12029   }
12030   else if (player_can_move_or_snap && IS_DIGGABLE(element))
12031   {
12032     RemoveField(x, y);
12033
12034     if (mode != DF_SNAP)
12035     {
12036       GfxElement[x][y] = GFX_ELEMENT(element);
12037       player->is_digging = TRUE;
12038     }
12039
12040     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12041
12042     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12043                                         player->index_bit, dig_side);
12044
12045     if (mode == DF_SNAP)
12046     {
12047 #if USE_NEW_SNAP_DELAY
12048       if (level.block_snap_field)
12049         setFieldForSnapping(x, y, element, move_direction);
12050       else
12051         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12052 #else
12053       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12054 #endif
12055
12056       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12057                                           player->index_bit, dig_side);
12058     }
12059   }
12060   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12061   {
12062     RemoveField(x, y);
12063
12064     if (is_player && mode != DF_SNAP)
12065     {
12066       GfxElement[x][y] = element;
12067       player->is_collecting = TRUE;
12068     }
12069
12070     if (element == EL_SPEED_PILL)
12071     {
12072       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12073     }
12074     else if (element == EL_EXTRA_TIME && level.time > 0)
12075     {
12076       TimeLeft += level.extra_time;
12077       DrawGameValue_Time(TimeLeft);
12078     }
12079     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12080     {
12081       player->shield_normal_time_left += level.shield_normal_time;
12082       if (element == EL_SHIELD_DEADLY)
12083         player->shield_deadly_time_left += level.shield_deadly_time;
12084     }
12085     else if (element == EL_DYNAMITE ||
12086              element == EL_EM_DYNAMITE ||
12087              element == EL_SP_DISK_RED)
12088     {
12089       if (player->inventory_size < MAX_INVENTORY_SIZE)
12090         player->inventory_element[player->inventory_size++] = element;
12091
12092 #if 1
12093       DrawGameDoorValues();
12094 #else
12095       DrawGameValue_Dynamite(local_player->inventory_size);
12096 #endif
12097     }
12098     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12099     {
12100       player->dynabomb_count++;
12101       player->dynabombs_left++;
12102     }
12103     else if (element == EL_DYNABOMB_INCREASE_SIZE)
12104     {
12105       player->dynabomb_size++;
12106     }
12107     else if (element == EL_DYNABOMB_INCREASE_POWER)
12108     {
12109       player->dynabomb_xl = TRUE;
12110     }
12111     else if (IS_KEY(element))
12112     {
12113       player->key[KEY_NR(element)] = TRUE;
12114
12115 #if 1
12116       DrawGameDoorValues();
12117 #else
12118       DrawGameValue_Keys(player->key);
12119 #endif
12120
12121       redraw_mask |= REDRAW_DOOR_1;
12122     }
12123     else if (IS_ENVELOPE(element))
12124     {
12125       player->show_envelope = element;
12126     }
12127     else if (element == EL_EMC_LENSES)
12128     {
12129       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12130
12131       RedrawAllInvisibleElementsForLenses();
12132     }
12133     else if (element == EL_EMC_MAGNIFIER)
12134     {
12135       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12136
12137       RedrawAllInvisibleElementsForMagnifier();
12138     }
12139     else if (IS_DROPPABLE(element) ||
12140              IS_THROWABLE(element))     /* can be collected and dropped */
12141     {
12142       int i;
12143
12144       if (collect_count == 0)
12145         player->inventory_infinite_element = element;
12146       else
12147         for (i = 0; i < collect_count; i++)
12148           if (player->inventory_size < MAX_INVENTORY_SIZE)
12149             player->inventory_element[player->inventory_size++] = element;
12150
12151 #if 1
12152       DrawGameDoorValues();
12153 #else
12154       DrawGameValue_Dynamite(local_player->inventory_size);
12155 #endif
12156     }
12157     else if (collect_count > 0)
12158     {
12159       local_player->gems_still_needed -= collect_count;
12160       if (local_player->gems_still_needed < 0)
12161         local_player->gems_still_needed = 0;
12162
12163       DrawGameValue_Emeralds(local_player->gems_still_needed);
12164     }
12165
12166     RaiseScoreElement(element);
12167     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12168
12169     if (is_player)
12170       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12171                                           player->index_bit, dig_side);
12172
12173     if (mode == DF_SNAP)
12174     {
12175 #if USE_NEW_SNAP_DELAY
12176       if (level.block_snap_field)
12177         setFieldForSnapping(x, y, element, move_direction);
12178       else
12179         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12180 #else
12181       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12182 #endif
12183
12184       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12185                                           player->index_bit, dig_side);
12186     }
12187   }
12188   else if (player_can_move_or_snap && IS_PUSHABLE(element))
12189   {
12190     if (mode == DF_SNAP && element != EL_BD_ROCK)
12191       return MP_NO_ACTION;
12192
12193     if (CAN_FALL(element) && dy)
12194       return MP_NO_ACTION;
12195
12196     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12197         !(element == EL_SPRING && level.use_spring_bug))
12198       return MP_NO_ACTION;
12199
12200     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12201         ((move_direction & MV_VERTICAL &&
12202           ((element_info[element].move_pattern & MV_LEFT &&
12203             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12204            (element_info[element].move_pattern & MV_RIGHT &&
12205             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12206          (move_direction & MV_HORIZONTAL &&
12207           ((element_info[element].move_pattern & MV_UP &&
12208             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12209            (element_info[element].move_pattern & MV_DOWN &&
12210             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12211       return MP_NO_ACTION;
12212
12213     /* do not push elements already moving away faster than player */
12214     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12215         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12216       return MP_NO_ACTION;
12217
12218     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12219     {
12220       if (player->push_delay_value == -1 || !player_was_pushing)
12221         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12222     }
12223     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12224     {
12225       if (player->push_delay_value == -1)
12226         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12227     }
12228     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12229     {
12230       if (!player->is_pushing)
12231         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12232     }
12233
12234     player->is_pushing = TRUE;
12235     player->is_active = TRUE;
12236
12237     if (!(IN_LEV_FIELD(nextx, nexty) &&
12238           (IS_FREE(nextx, nexty) ||
12239            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12240             IS_SB_ELEMENT(element)))))
12241       return MP_NO_ACTION;
12242
12243     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12244       return MP_NO_ACTION;
12245
12246     if (player->push_delay == -1)       /* new pushing; restart delay */
12247       player->push_delay = 0;
12248
12249     if (player->push_delay < player->push_delay_value &&
12250         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12251         element != EL_SPRING && element != EL_BALLOON)
12252     {
12253       /* make sure that there is no move delay before next try to push */
12254       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12255         player->move_delay = 0;
12256
12257       return MP_NO_ACTION;
12258     }
12259
12260     if (IS_SB_ELEMENT(element))
12261     {
12262       if (element == EL_SOKOBAN_FIELD_FULL)
12263       {
12264         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12265         local_player->sokobanfields_still_needed++;
12266       }
12267
12268       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12269       {
12270         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12271         local_player->sokobanfields_still_needed--;
12272       }
12273
12274       Feld[x][y] = EL_SOKOBAN_OBJECT;
12275
12276       if (Back[x][y] == Back[nextx][nexty])
12277         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12278       else if (Back[x][y] != 0)
12279         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12280                                     ACTION_EMPTYING);
12281       else
12282         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12283                                     ACTION_FILLING);
12284
12285       if (local_player->sokobanfields_still_needed == 0 &&
12286           game.emulation == EMU_SOKOBAN)
12287       {
12288         player->LevelSolved = player->GameOver = TRUE;
12289         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12290       }
12291     }
12292     else
12293       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12294
12295     InitMovingField(x, y, move_direction);
12296     GfxAction[x][y] = ACTION_PUSHING;
12297
12298     if (mode == DF_SNAP)
12299       ContinueMoving(x, y);
12300     else
12301       MovPos[x][y] = (dx != 0 ? dx : dy);
12302
12303     Pushed[x][y] = TRUE;
12304     Pushed[nextx][nexty] = TRUE;
12305
12306     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12307       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12308     else
12309       player->push_delay_value = -1;    /* get new value later */
12310
12311     /* check for element change _after_ element has been pushed */
12312     if (game.use_change_when_pushing_bug)
12313     {
12314       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12315                                  player->index_bit, dig_side);
12316       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12317                                           player->index_bit, dig_side);
12318     }
12319   }
12320   else if (IS_SWITCHABLE(element))
12321   {
12322     if (PLAYER_SWITCHING(player, x, y))
12323     {
12324       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12325                                           player->index_bit, dig_side);
12326
12327       return MP_ACTION;
12328     }
12329
12330     player->is_switching = TRUE;
12331     player->switch_x = x;
12332     player->switch_y = y;
12333
12334     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12335
12336     if (element == EL_ROBOT_WHEEL)
12337     {
12338       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12339       ZX = x;
12340       ZY = y;
12341
12342       DrawLevelField(x, y);
12343     }
12344     else if (element == EL_SP_TERMINAL)
12345     {
12346       int xx, yy;
12347
12348 #if 1
12349       SCAN_PLAYFIELD(xx, yy)
12350 #else
12351       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12352 #endif
12353       {
12354         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12355           Bang(xx, yy);
12356         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12357           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12358       }
12359     }
12360     else if (IS_BELT_SWITCH(element))
12361     {
12362       ToggleBeltSwitch(x, y);
12363     }
12364     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12365              element == EL_SWITCHGATE_SWITCH_DOWN)
12366     {
12367       ToggleSwitchgateSwitch(x, y);
12368     }
12369     else if (element == EL_LIGHT_SWITCH ||
12370              element == EL_LIGHT_SWITCH_ACTIVE)
12371     {
12372       ToggleLightSwitch(x, y);
12373     }
12374     else if (element == EL_TIMEGATE_SWITCH)
12375     {
12376       ActivateTimegateSwitch(x, y);
12377     }
12378     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12379              element == EL_BALLOON_SWITCH_RIGHT ||
12380              element == EL_BALLOON_SWITCH_UP    ||
12381              element == EL_BALLOON_SWITCH_DOWN  ||
12382              element == EL_BALLOON_SWITCH_NONE  ||
12383              element == EL_BALLOON_SWITCH_ANY)
12384     {
12385       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12386                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12387                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12388                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12389                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12390                              move_direction);
12391     }
12392     else if (element == EL_LAMP)
12393     {
12394       Feld[x][y] = EL_LAMP_ACTIVE;
12395       local_player->lights_still_needed--;
12396
12397       ResetGfxAnimation(x, y);
12398       DrawLevelField(x, y);
12399     }
12400     else if (element == EL_TIME_ORB_FULL)
12401     {
12402       Feld[x][y] = EL_TIME_ORB_EMPTY;
12403
12404       if (level.time > 0 || level.use_time_orb_bug)
12405       {
12406         TimeLeft += level.time_orb_time;
12407         DrawGameValue_Time(TimeLeft);
12408       }
12409
12410       ResetGfxAnimation(x, y);
12411       DrawLevelField(x, y);
12412     }
12413     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12414              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12415     {
12416       int xx, yy;
12417
12418       game.ball_state = !game.ball_state;
12419
12420 #if 1
12421       SCAN_PLAYFIELD(xx, yy)
12422 #else
12423       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12424 #endif
12425       {
12426         int e = Feld[xx][yy];
12427
12428         if (game.ball_state)
12429         {
12430           if (e == EL_EMC_MAGIC_BALL)
12431             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12432           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12433             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12434         }
12435         else
12436         {
12437           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12438             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12439           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12440             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12441         }
12442       }
12443     }
12444
12445     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12446                                         player->index_bit, dig_side);
12447
12448     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12449                                         player->index_bit, dig_side);
12450
12451     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12452                                         player->index_bit, dig_side);
12453
12454     return MP_ACTION;
12455   }
12456   else
12457   {
12458     if (!PLAYER_SWITCHING(player, x, y))
12459     {
12460       player->is_switching = TRUE;
12461       player->switch_x = x;
12462       player->switch_y = y;
12463
12464       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12465                                  player->index_bit, dig_side);
12466       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12467                                           player->index_bit, dig_side);
12468
12469       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12470                                  player->index_bit, dig_side);
12471       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12472                                           player->index_bit, dig_side);
12473     }
12474
12475     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12476                                player->index_bit, dig_side);
12477     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12478                                         player->index_bit, dig_side);
12479
12480     return MP_NO_ACTION;
12481   }
12482
12483   player->push_delay = -1;
12484
12485   if (is_player)                /* function can also be called by EL_PENGUIN */
12486   {
12487     if (Feld[x][y] != element)          /* really digged/collected something */
12488     {
12489       player->is_collecting = !player->is_digging;
12490       player->is_active = TRUE;
12491     }
12492   }
12493
12494   return MP_MOVING;
12495 }
12496
12497 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12498 {
12499   int jx = player->jx, jy = player->jy;
12500   int x = jx + dx, y = jy + dy;
12501   int snap_direction = (dx == -1 ? MV_LEFT  :
12502                         dx == +1 ? MV_RIGHT :
12503                         dy == -1 ? MV_UP    :
12504                         dy == +1 ? MV_DOWN  : MV_NONE);
12505   boolean can_continue_snapping = (level.continuous_snapping &&
12506                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12507
12508   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12509     return FALSE;
12510
12511   if (!player->active || !IN_LEV_FIELD(x, y))
12512     return FALSE;
12513
12514   if (dx && dy)
12515     return FALSE;
12516
12517   if (!dx && !dy)
12518   {
12519     if (player->MovPos == 0)
12520       player->is_pushing = FALSE;
12521
12522     player->is_snapping = FALSE;
12523
12524     if (player->MovPos == 0)
12525     {
12526       player->is_moving = FALSE;
12527       player->is_digging = FALSE;
12528       player->is_collecting = FALSE;
12529     }
12530
12531     return FALSE;
12532   }
12533
12534 #if USE_NEW_CONTINUOUS_SNAPPING
12535   /* prevent snapping with already pressed snap key when not allowed */
12536   if (player->is_snapping && !can_continue_snapping)
12537     return FALSE;
12538 #else
12539   if (player->is_snapping)
12540     return FALSE;
12541 #endif
12542
12543   player->MovDir = snap_direction;
12544
12545   if (player->MovPos == 0)
12546   {
12547     player->is_moving = FALSE;
12548     player->is_digging = FALSE;
12549     player->is_collecting = FALSE;
12550   }
12551
12552   player->is_dropping = FALSE;
12553   player->is_dropping_pressed = FALSE;
12554   player->drop_pressed_delay = 0;
12555
12556   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12557     return FALSE;
12558
12559   player->is_snapping = TRUE;
12560   player->is_active = TRUE;
12561
12562   if (player->MovPos == 0)
12563   {
12564     player->is_moving = FALSE;
12565     player->is_digging = FALSE;
12566     player->is_collecting = FALSE;
12567   }
12568
12569   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12570     DrawLevelField(player->last_jx, player->last_jy);
12571
12572   DrawLevelField(x, y);
12573
12574   return TRUE;
12575 }
12576
12577 boolean DropElement(struct PlayerInfo *player)
12578 {
12579   int old_element, new_element;
12580   int dropx = player->jx, dropy = player->jy;
12581   int drop_direction = player->MovDir;
12582   int drop_side = drop_direction;
12583   int drop_element = (player->inventory_size > 0 ?
12584                       player->inventory_element[player->inventory_size - 1] :
12585                       player->inventory_infinite_element != EL_UNDEFINED ?
12586                       player->inventory_infinite_element :
12587                       player->dynabombs_left > 0 ?
12588                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12589                       EL_UNDEFINED);
12590
12591   player->is_dropping_pressed = TRUE;
12592
12593   /* do not drop an element on top of another element; when holding drop key
12594      pressed without moving, dropped element must move away before the next
12595      element can be dropped (this is especially important if the next element
12596      is dynamite, which can be placed on background for historical reasons) */
12597   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12598     return MP_ACTION;
12599
12600   if (IS_THROWABLE(drop_element))
12601   {
12602     dropx += GET_DX_FROM_DIR(drop_direction);
12603     dropy += GET_DY_FROM_DIR(drop_direction);
12604
12605     if (!IN_LEV_FIELD(dropx, dropy))
12606       return FALSE;
12607   }
12608
12609   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12610   new_element = drop_element;           /* default: no change when dropping */
12611
12612   /* check if player is active, not moving and ready to drop */
12613   if (!player->active || player->MovPos || player->drop_delay > 0)
12614     return FALSE;
12615
12616   /* check if player has anything that can be dropped */
12617   if (new_element == EL_UNDEFINED)
12618     return FALSE;
12619
12620   /* check if drop key was pressed long enough for EM style dynamite */
12621   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12622     return FALSE;
12623
12624   /* check if anything can be dropped at the current position */
12625   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12626     return FALSE;
12627
12628   /* collected custom elements can only be dropped on empty fields */
12629   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12630     return FALSE;
12631
12632   if (old_element != EL_EMPTY)
12633     Back[dropx][dropy] = old_element;   /* store old element on this field */
12634
12635   ResetGfxAnimation(dropx, dropy);
12636   ResetRandomAnimationValue(dropx, dropy);
12637
12638   if (player->inventory_size > 0 ||
12639       player->inventory_infinite_element != EL_UNDEFINED)
12640   {
12641     if (player->inventory_size > 0)
12642     {
12643       player->inventory_size--;
12644
12645 #if 1
12646       DrawGameDoorValues();
12647 #else
12648       DrawGameValue_Dynamite(local_player->inventory_size);
12649 #endif
12650
12651       if (new_element == EL_DYNAMITE)
12652         new_element = EL_DYNAMITE_ACTIVE;
12653       else if (new_element == EL_EM_DYNAMITE)
12654         new_element = EL_EM_DYNAMITE_ACTIVE;
12655       else if (new_element == EL_SP_DISK_RED)
12656         new_element = EL_SP_DISK_RED_ACTIVE;
12657     }
12658
12659     Feld[dropx][dropy] = new_element;
12660
12661     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12662       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12663                           el2img(Feld[dropx][dropy]), 0);
12664
12665     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12666
12667     /* needed if previous element just changed to "empty" in the last frame */
12668     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12669
12670     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12671                                player->index_bit, drop_side);
12672     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12673                                         CE_PLAYER_DROPS_X,
12674                                         player->index_bit, drop_side);
12675
12676     TestIfElementTouchesCustomElement(dropx, dropy);
12677   }
12678   else          /* player is dropping a dyna bomb */
12679   {
12680     player->dynabombs_left--;
12681
12682     Feld[dropx][dropy] = new_element;
12683
12684     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12685       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12686                           el2img(Feld[dropx][dropy]), 0);
12687
12688     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12689   }
12690
12691   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12692     InitField_WithBug1(dropx, dropy, FALSE);
12693
12694   new_element = Feld[dropx][dropy];     /* element might have changed */
12695
12696   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12697       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12698   {
12699     int move_direction, nextx, nexty;
12700
12701     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12702       MovDir[dropx][dropy] = drop_direction;
12703
12704     move_direction = MovDir[dropx][dropy];
12705     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12706     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12707
12708     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12709     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12710   }
12711
12712   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12713   player->is_dropping = TRUE;
12714
12715   player->drop_pressed_delay = 0;
12716   player->is_dropping_pressed = FALSE;
12717
12718   player->drop_x = dropx;
12719   player->drop_y = dropy;
12720
12721   return TRUE;
12722 }
12723
12724 /* ------------------------------------------------------------------------- */
12725 /* game sound playing functions                                              */
12726 /* ------------------------------------------------------------------------- */
12727
12728 static int *loop_sound_frame = NULL;
12729 static int *loop_sound_volume = NULL;
12730
12731 void InitPlayLevelSound()
12732 {
12733   int num_sounds = getSoundListSize();
12734
12735   checked_free(loop_sound_frame);
12736   checked_free(loop_sound_volume);
12737
12738   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12739   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12740 }
12741
12742 static void PlayLevelSound(int x, int y, int nr)
12743 {
12744   int sx = SCREENX(x), sy = SCREENY(y);
12745   int volume, stereo_position;
12746   int max_distance = 8;
12747   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12748
12749   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12750       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12751     return;
12752
12753   if (!IN_LEV_FIELD(x, y) ||
12754       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12755       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12756     return;
12757
12758   volume = SOUND_MAX_VOLUME;
12759
12760   if (!IN_SCR_FIELD(sx, sy))
12761   {
12762     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12763     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12764
12765     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12766   }
12767
12768   stereo_position = (SOUND_MAX_LEFT +
12769                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12770                      (SCR_FIELDX + 2 * max_distance));
12771
12772   if (IS_LOOP_SOUND(nr))
12773   {
12774     /* This assures that quieter loop sounds do not overwrite louder ones,
12775        while restarting sound volume comparison with each new game frame. */
12776
12777     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12778       return;
12779
12780     loop_sound_volume[nr] = volume;
12781     loop_sound_frame[nr] = FrameCounter;
12782   }
12783
12784   PlaySoundExt(nr, volume, stereo_position, type);
12785 }
12786
12787 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12788 {
12789   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12790                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12791                  y < LEVELY(BY1) ? LEVELY(BY1) :
12792                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12793                  sound_action);
12794 }
12795
12796 static void PlayLevelSoundAction(int x, int y, int action)
12797 {
12798   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12799 }
12800
12801 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12802 {
12803   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12804
12805   if (sound_effect != SND_UNDEFINED)
12806     PlayLevelSound(x, y, sound_effect);
12807 }
12808
12809 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12810                                               int action)
12811 {
12812   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12813
12814   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12815     PlayLevelSound(x, y, sound_effect);
12816 }
12817
12818 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12819 {
12820   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12821
12822   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12823     PlayLevelSound(x, y, sound_effect);
12824 }
12825
12826 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12827 {
12828   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12829
12830   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12831     StopSound(sound_effect);
12832 }
12833
12834 static void PlayLevelMusic()
12835 {
12836   if (levelset.music[level_nr] != MUS_UNDEFINED)
12837     PlayMusic(levelset.music[level_nr]);        /* from config file */
12838   else
12839     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
12840 }
12841
12842 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12843 {
12844   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12845
12846   switch (sample)
12847   {
12848     case SAMPLE_blank:
12849       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12850       break;
12851
12852     case SAMPLE_roll:
12853       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12854       break;
12855
12856     case SAMPLE_stone:
12857       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12858       break;
12859
12860     case SAMPLE_nut:
12861       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12862       break;
12863
12864     case SAMPLE_crack:
12865       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12866       break;
12867
12868     case SAMPLE_bug:
12869       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12870       break;
12871
12872     case SAMPLE_tank:
12873       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12874       break;
12875
12876     case SAMPLE_android_clone:
12877       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12878       break;
12879
12880     case SAMPLE_android_move:
12881       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12882       break;
12883
12884     case SAMPLE_spring:
12885       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12886       break;
12887
12888     case SAMPLE_slurp:
12889       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12890       break;
12891
12892     case SAMPLE_eater:
12893       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12894       break;
12895
12896     case SAMPLE_eater_eat:
12897       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12898       break;
12899
12900     case SAMPLE_alien:
12901       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12902       break;
12903
12904     case SAMPLE_collect:
12905       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12906       break;
12907
12908     case SAMPLE_diamond:
12909       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12910       break;
12911
12912     case SAMPLE_squash:
12913       /* !!! CHECK THIS !!! */
12914 #if 1
12915       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12916 #else
12917       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12918 #endif
12919       break;
12920
12921     case SAMPLE_wonderfall:
12922       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12923       break;
12924
12925     case SAMPLE_drip:
12926       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12927       break;
12928
12929     case SAMPLE_push:
12930       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12931       break;
12932
12933     case SAMPLE_dirt:
12934       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12935       break;
12936
12937     case SAMPLE_acid:
12938       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12939       break;
12940
12941     case SAMPLE_ball:
12942       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12943       break;
12944
12945     case SAMPLE_grow:
12946       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12947       break;
12948
12949     case SAMPLE_wonder:
12950       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12951       break;
12952
12953     case SAMPLE_door:
12954       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12955       break;
12956
12957     case SAMPLE_exit_open:
12958       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12959       break;
12960
12961     case SAMPLE_exit_leave:
12962       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12963       break;
12964
12965     case SAMPLE_dynamite:
12966       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12967       break;
12968
12969     case SAMPLE_tick:
12970       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12971       break;
12972
12973     case SAMPLE_press:
12974       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12975       break;
12976
12977     case SAMPLE_wheel:
12978       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12979       break;
12980
12981     case SAMPLE_boom:
12982       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12983       break;
12984
12985     case SAMPLE_die:
12986       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12987       break;
12988
12989     case SAMPLE_time:
12990       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12991       break;
12992
12993     default:
12994       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12995       break;
12996   }
12997 }
12998
12999 void RaiseScore(int value)
13000 {
13001   local_player->score += value;
13002
13003   DrawGameValue_Score(local_player->score);
13004 }
13005
13006 void RaiseScoreElement(int element)
13007 {
13008   switch(element)
13009   {
13010     case EL_EMERALD:
13011     case EL_BD_DIAMOND:
13012     case EL_EMERALD_YELLOW:
13013     case EL_EMERALD_RED:
13014     case EL_EMERALD_PURPLE:
13015     case EL_SP_INFOTRON:
13016       RaiseScore(level.score[SC_EMERALD]);
13017       break;
13018     case EL_DIAMOND:
13019       RaiseScore(level.score[SC_DIAMOND]);
13020       break;
13021     case EL_CRYSTAL:
13022       RaiseScore(level.score[SC_CRYSTAL]);
13023       break;
13024     case EL_PEARL:
13025       RaiseScore(level.score[SC_PEARL]);
13026       break;
13027     case EL_BUG:
13028     case EL_BD_BUTTERFLY:
13029     case EL_SP_ELECTRON:
13030       RaiseScore(level.score[SC_BUG]);
13031       break;
13032     case EL_SPACESHIP:
13033     case EL_BD_FIREFLY:
13034     case EL_SP_SNIKSNAK:
13035       RaiseScore(level.score[SC_SPACESHIP]);
13036       break;
13037     case EL_YAMYAM:
13038     case EL_DARK_YAMYAM:
13039       RaiseScore(level.score[SC_YAMYAM]);
13040       break;
13041     case EL_ROBOT:
13042       RaiseScore(level.score[SC_ROBOT]);
13043       break;
13044     case EL_PACMAN:
13045       RaiseScore(level.score[SC_PACMAN]);
13046       break;
13047     case EL_NUT:
13048       RaiseScore(level.score[SC_NUT]);
13049       break;
13050     case EL_DYNAMITE:
13051     case EL_EM_DYNAMITE:
13052     case EL_SP_DISK_RED:
13053     case EL_DYNABOMB_INCREASE_NUMBER:
13054     case EL_DYNABOMB_INCREASE_SIZE:
13055     case EL_DYNABOMB_INCREASE_POWER:
13056       RaiseScore(level.score[SC_DYNAMITE]);
13057       break;
13058     case EL_SHIELD_NORMAL:
13059     case EL_SHIELD_DEADLY:
13060       RaiseScore(level.score[SC_SHIELD]);
13061       break;
13062     case EL_EXTRA_TIME:
13063       RaiseScore(level.extra_time_score);
13064       break;
13065     case EL_KEY_1:
13066     case EL_KEY_2:
13067     case EL_KEY_3:
13068     case EL_KEY_4:
13069     case EL_EM_KEY_1:
13070     case EL_EM_KEY_2:
13071     case EL_EM_KEY_3:
13072     case EL_EM_KEY_4:
13073     case EL_EMC_KEY_5:
13074     case EL_EMC_KEY_6:
13075     case EL_EMC_KEY_7:
13076     case EL_EMC_KEY_8:
13077       RaiseScore(level.score[SC_KEY]);
13078       break;
13079     default:
13080       RaiseScore(element_info[element].collect_score);
13081       break;
13082   }
13083 }
13084
13085 void RequestQuitGame(boolean ask_if_really_quit)
13086 {
13087   if (AllPlayersGone ||
13088       !ask_if_really_quit ||
13089       level_editor_test_game ||
13090       Request("Do you really want to quit the game ?",
13091               REQ_ASK | REQ_STAY_CLOSED))
13092   {
13093 #if defined(NETWORK_AVALIABLE)
13094     if (options.network)
13095       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13096     else
13097 #endif
13098     {
13099       game_status = GAME_MODE_MAIN;
13100       DrawMainMenu();
13101     }
13102   }
13103   else
13104   {
13105     if (tape.playing && tape.deactivate_display)
13106       TapeDeactivateDisplayOff(TRUE);
13107
13108     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13109
13110     if (tape.playing && tape.deactivate_display)
13111       TapeDeactivateDisplayOn();
13112   }
13113 }
13114
13115
13116 /* ---------- new game button stuff ---------------------------------------- */
13117
13118 /* graphic position values for game buttons */
13119 #define GAME_BUTTON_XSIZE       30
13120 #define GAME_BUTTON_YSIZE       30
13121 #define GAME_BUTTON_XPOS        5
13122 #define GAME_BUTTON_YPOS        215
13123 #define SOUND_BUTTON_XPOS       5
13124 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13125
13126 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13127 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13128 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13129 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13130 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13131 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13132
13133 static struct
13134 {
13135   int x, y;
13136   int gadget_id;
13137   char *infotext;
13138 } gamebutton_info[NUM_GAME_BUTTONS] =
13139 {
13140   {
13141     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13142     GAME_CTRL_ID_STOP,
13143     "stop game"
13144   },
13145   {
13146     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13147     GAME_CTRL_ID_PAUSE,
13148     "pause game"
13149   },
13150   {
13151     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13152     GAME_CTRL_ID_PLAY,
13153     "play game"
13154   },
13155   {
13156     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13157     SOUND_CTRL_ID_MUSIC,
13158     "background music on/off"
13159   },
13160   {
13161     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13162     SOUND_CTRL_ID_LOOPS,
13163     "sound loops on/off"
13164   },
13165   {
13166     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13167     SOUND_CTRL_ID_SIMPLE,
13168     "normal sounds on/off"
13169   }
13170 };
13171
13172 void CreateGameButtons()
13173 {
13174   int i;
13175
13176   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13177   {
13178     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13179     struct GadgetInfo *gi;
13180     int button_type;
13181     boolean checked;
13182     unsigned long event_mask;
13183     int gd_xoffset, gd_yoffset;
13184     int gd_x1, gd_x2, gd_y1, gd_y2;
13185     int id = i;
13186
13187     gd_xoffset = gamebutton_info[i].x;
13188     gd_yoffset = gamebutton_info[i].y;
13189     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13190     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13191
13192     if (id == GAME_CTRL_ID_STOP ||
13193         id == GAME_CTRL_ID_PAUSE ||
13194         id == GAME_CTRL_ID_PLAY)
13195     {
13196       button_type = GD_TYPE_NORMAL_BUTTON;
13197       checked = FALSE;
13198       event_mask = GD_EVENT_RELEASED;
13199       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13200       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13201     }
13202     else
13203     {
13204       button_type = GD_TYPE_CHECK_BUTTON;
13205       checked =
13206         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13207          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13208          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13209       event_mask = GD_EVENT_PRESSED;
13210       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
13211       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13212     }
13213
13214     gi = CreateGadget(GDI_CUSTOM_ID, id,
13215                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
13216                       GDI_X, DX + gd_xoffset,
13217                       GDI_Y, DY + gd_yoffset,
13218                       GDI_WIDTH, GAME_BUTTON_XSIZE,
13219                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
13220                       GDI_TYPE, button_type,
13221                       GDI_STATE, GD_BUTTON_UNPRESSED,
13222                       GDI_CHECKED, checked,
13223                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13224                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13225                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13226                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13227                       GDI_EVENT_MASK, event_mask,
13228                       GDI_CALLBACK_ACTION, HandleGameButtons,
13229                       GDI_END);
13230
13231     if (gi == NULL)
13232       Error(ERR_EXIT, "cannot create gadget");
13233
13234     game_gadget[id] = gi;
13235   }
13236 }
13237
13238 void FreeGameButtons()
13239 {
13240   int i;
13241
13242   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13243     FreeGadget(game_gadget[i]);
13244 }
13245
13246 static void MapGameButtons()
13247 {
13248   int i;
13249
13250   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13251     MapGadget(game_gadget[i]);
13252 }
13253
13254 void UnmapGameButtons()
13255 {
13256   int i;
13257
13258   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13259     UnmapGadget(game_gadget[i]);
13260 }
13261
13262 static void HandleGameButtons(struct GadgetInfo *gi)
13263 {
13264   int id = gi->custom_id;
13265
13266   if (game_status != GAME_MODE_PLAYING)
13267     return;
13268
13269   switch (id)
13270   {
13271     case GAME_CTRL_ID_STOP:
13272       if (tape.playing)
13273         TapeStop();
13274       else
13275         RequestQuitGame(TRUE);
13276       break;
13277
13278     case GAME_CTRL_ID_PAUSE:
13279       if (options.network)
13280       {
13281 #if defined(NETWORK_AVALIABLE)
13282         if (tape.pausing)
13283           SendToServer_ContinuePlaying();
13284         else
13285           SendToServer_PausePlaying();
13286 #endif
13287       }
13288       else
13289         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13290       break;
13291
13292     case GAME_CTRL_ID_PLAY:
13293       if (tape.pausing)
13294       {
13295 #if defined(NETWORK_AVALIABLE)
13296         if (options.network)
13297           SendToServer_ContinuePlaying();
13298         else
13299 #endif
13300         {
13301           tape.pausing = FALSE;
13302           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13303         }
13304       }
13305       break;
13306
13307     case SOUND_CTRL_ID_MUSIC:
13308       if (setup.sound_music)
13309       { 
13310         setup.sound_music = FALSE;
13311         FadeMusic();
13312       }
13313       else if (audio.music_available)
13314       { 
13315         setup.sound = setup.sound_music = TRUE;
13316
13317         SetAudioMode(setup.sound);
13318
13319         PlayLevelMusic();
13320       }
13321       break;
13322
13323     case SOUND_CTRL_ID_LOOPS:
13324       if (setup.sound_loops)
13325         setup.sound_loops = FALSE;
13326       else if (audio.loops_available)
13327       {
13328         setup.sound = setup.sound_loops = TRUE;
13329         SetAudioMode(setup.sound);
13330       }
13331       break;
13332
13333     case SOUND_CTRL_ID_SIMPLE:
13334       if (setup.sound_simple)
13335         setup.sound_simple = FALSE;
13336       else if (audio.sound_available)
13337       {
13338         setup.sound = setup.sound_simple = TRUE;
13339         SetAudioMode(setup.sound);
13340       }
13341       break;
13342
13343     default:
13344       break;
13345   }
13346 }