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