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