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