3652cc99beaa7b21ce567dd691abd531f83d0081
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 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 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62
63 /* for DigField() */
64 #define DF_NO_PUSH              0
65 #define DF_DIG                  1
66 #define DF_SNAP                 2
67
68 /* for MovePlayer() */
69 #define MP_NO_ACTION            0
70 #define MP_MOVING               1
71 #define MP_ACTION               2
72 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
73
74 /* for ScrollPlayer() */
75 #define SCROLL_INIT             0
76 #define SCROLL_GO_ON            1
77
78 /* for Bang()/Explode() */
79 #define EX_PHASE_START          0
80 #define EX_TYPE_NONE            0
81 #define EX_TYPE_NORMAL          (1 << 0)
82 #define EX_TYPE_CENTER          (1 << 1)
83 #define EX_TYPE_BORDER          (1 << 2)
84 #define EX_TYPE_CROSS           (1 << 3)
85 #define EX_TYPE_DYNA            (1 << 4)
86 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
87
88 #if 1
89 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0)
90 #define PANEL_XPOS(p)           (DX + ALIGNED_MENU_XPOS(p))
91 #define PANEL_YPOS(p)           (DY + ALIGNED_MENU_YPOS(p))
92 #else
93 #define PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
94 #define PANEL_XPOS(p)           (ALIGNED_XPOS((p).x, (p).width, (p).align))
95 #define PANEL_YPOS(p)           ((p).y)
96 #endif
97
98 /* special positions in the game control window (relative to control window) */
99 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
100 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
101 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
102 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
103 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
104 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
105 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
106 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
107 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
108 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
109 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
110 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
111 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
112 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
113 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
114 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
115
116 /* special positions in the game control window (relative to main window) */
117 #define DX_LEVEL1               (DX + XX_LEVEL1)
118 #define DX_LEVEL2               (DX + XX_LEVEL2)
119 #define DX_LEVEL                (DX + XX_LEVEL)
120 #define DY_LEVEL                (DY + YY_LEVEL)
121 #define DX_EMERALDS             (DX + XX_EMERALDS)
122 #define DY_EMERALDS             (DY + YY_EMERALDS)
123 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
124 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
125 #define DX_KEYS                 (DX + XX_KEYS)
126 #define DY_KEYS                 (DY + YY_KEYS)
127 #define DX_SCORE                (DX + XX_SCORE)
128 #define DY_SCORE                (DY + YY_SCORE)
129 #define DX_TIME1                (DX + XX_TIME1)
130 #define DX_TIME2                (DX + XX_TIME2)
131 #define DX_TIME                 (DX + XX_TIME)
132 #define DY_TIME                 (DY + YY_TIME)
133
134 /* values for delayed check of falling and moving elements and for collision */
135 #define CHECK_DELAY_MOVING      3
136 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
137 #define CHECK_DELAY_COLLISION   2
138 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
139
140 /* values for initial player move delay (initial delay counter value) */
141 #define INITIAL_MOVE_DELAY_OFF  -1
142 #define INITIAL_MOVE_DELAY_ON   0
143
144 /* values for player movement speed (which is in fact a delay value) */
145 #define MOVE_DELAY_MIN_SPEED    32
146 #define MOVE_DELAY_NORMAL_SPEED 8
147 #define MOVE_DELAY_HIGH_SPEED   4
148 #define MOVE_DELAY_MAX_SPEED    1
149
150 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
151 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
152
153 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
154 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
155
156 /* values for other actions */
157 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
158 #define MOVE_STEPSIZE_MIN       (1)
159 #define MOVE_STEPSIZE_MAX       (TILEX)
160
161 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
162 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
163
164 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
165
166 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
167                                  RND(element_info[e].push_delay_random))
168 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
169                                  RND(element_info[e].drop_delay_random))
170 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
171                                  RND(element_info[e].move_delay_random))
172 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
173                                     (element_info[e].move_delay_random))
174 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
175                                  RND(element_info[e].ce_value_random_initial))
176 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
177 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
178                                  RND((c)->delay_random * (c)->delay_frames))
179 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
180                                  RND((c)->delay_random))
181
182
183 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
184          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
185
186 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
187         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
188          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
189          (be) + (e) - EL_SELF)
190
191 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
192         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
193          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
194          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
195          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
196          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
197          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
198          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
199          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
200          (e))
201
202 #define CAN_GROW_INTO(e)                                                \
203         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
204
205 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
206                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
207                                         (condition)))
208
209 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
210                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
211                                         (CAN_MOVE_INTO_ACID(e) &&       \
212                                          Feld[x][y] == EL_ACID) ||      \
213                                         (condition)))
214
215 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
216                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
217                                         (CAN_MOVE_INTO_ACID(e) &&       \
218                                          Feld[x][y] == EL_ACID) ||      \
219                                         (condition)))
220
221 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
222                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
223                                         (condition) ||                  \
224                                         (CAN_MOVE_INTO_ACID(e) &&       \
225                                          Feld[x][y] == EL_ACID) ||      \
226                                         (DONT_COLLIDE_WITH(e) &&        \
227                                          IS_PLAYER(x, y) &&             \
228                                          !PLAYER_ENEMY_PROTECTED(x, y))))
229
230 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
231         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
232
233 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
234         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
235
236 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
237         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
238
239 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
240         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
241                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
242
243 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
244         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
245
246 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
247         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
248
249 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
250         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
251
252 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
253         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
254
255 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
256         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
257
258 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
259         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
260                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
261                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
262                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
263                                                  IS_FOOD_PENGUIN(Feld[x][y])))
264 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
265         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
266
267 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
268         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
269
270 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
271         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
272
273 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
274         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
275                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
276
277 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
278
279 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
280                 (!IS_PLAYER(x, y) &&                                    \
281                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
282
283 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
284         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
285
286 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
287 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
288
289 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
290 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
291 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
292 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
293
294 /* game button identifiers */
295 #define GAME_CTRL_ID_STOP               0
296 #define GAME_CTRL_ID_PAUSE              1
297 #define GAME_CTRL_ID_PLAY               2
298 #define SOUND_CTRL_ID_MUSIC             3
299 #define SOUND_CTRL_ID_LOOPS             4
300 #define SOUND_CTRL_ID_SIMPLE            5
301
302 #define NUM_GAME_BUTTONS                6
303
304
305 /* forward declaration for internal use */
306
307 static void CreateField(int, int, int);
308
309 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
310 static void AdvanceFrameAndPlayerCounters(int);
311
312 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
313 static boolean MovePlayer(struct PlayerInfo *, int, int);
314 static void ScrollPlayer(struct PlayerInfo *, int);
315 static void ScrollScreen(struct PlayerInfo *, int);
316
317 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
318
319 static void InitBeltMovement(void);
320 static void CloseAllOpenTimegates(void);
321 static void CheckGravityMovement(struct PlayerInfo *);
322 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
323 static void KillPlayerUnlessEnemyProtected(int, int);
324 static void KillPlayerUnlessExplosionProtected(int, int);
325
326 static void TestIfPlayerTouchesCustomElement(int, int);
327 static void TestIfElementTouchesCustomElement(int, int);
328 static void TestIfElementHitsCustomElement(int, int, int);
329 #if 0
330 static void TestIfElementSmashesCustomElement(int, int, int);
331 #endif
332
333 static void HandleElementChange(int, int, int);
334 static void ExecuteCustomElementAction(int, int, int, int);
335 static boolean ChangeElement(int, int, int, int);
336
337 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
338 #define CheckTriggeredElementChange(x, y, e, ev)                        \
339         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
340 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
341         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
342 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
343         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
344 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
345         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
346
347 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
348 #define CheckElementChange(x, y, e, te, ev)                             \
349         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
350 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
351         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
352 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
353         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
354
355 static void PlayLevelSound(int, int, int);
356 static void PlayLevelSoundNearest(int, int, int);
357 static void PlayLevelSoundAction(int, int, int);
358 static void PlayLevelSoundElementAction(int, int, int, int);
359 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
360 static void PlayLevelSoundActionIfLoop(int, int, int);
361 static void StopLevelSoundActionIfLoop(int, int, int);
362 static void PlayLevelMusic();
363
364 static void MapGameButtons();
365 static void HandleGameButtons(struct GadgetInfo *);
366
367 int AmoebeNachbarNr(int, int);
368 void AmoebeUmwandeln(int, int);
369 void ContinueMoving(int, int);
370 void Bang(int, int);
371 void InitMovDir(int, int);
372 void InitAmoebaNr(int, int);
373 int NewHiScore(void);
374
375 void TestIfGoodThingHitsBadThing(int, int, int);
376 void TestIfBadThingHitsGoodThing(int, int, int);
377 void TestIfPlayerTouchesBadThing(int, int);
378 void TestIfPlayerRunsIntoBadThing(int, int, int);
379 void TestIfBadThingTouchesPlayer(int, int);
380 void TestIfBadThingRunsIntoPlayer(int, int, int);
381 void TestIfFriendTouchesBadThing(int, int);
382 void TestIfBadThingTouchesFriend(int, int);
383 void TestIfBadThingTouchesOtherBadThing(int, int);
384
385 void KillPlayer(struct PlayerInfo *);
386 void BuryPlayer(struct PlayerInfo *);
387 void RemovePlayer(struct PlayerInfo *);
388
389 boolean SnapField(struct PlayerInfo *, int, int);
390 boolean DropElement(struct PlayerInfo *);
391
392 static int getInvisibleActiveFromInvisibleElement(int);
393 static int getInvisibleFromInvisibleActiveElement(int);
394
395 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
396
397 /* for detection of endless loops, caused by custom element programming */
398 /* (using maximal playfield width x 10 is just a rough approximation) */
399 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
400
401 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
402 {                                                                       \
403   if (recursion_loop_detected)                                          \
404     return (rc);                                                        \
405                                                                         \
406   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
407   {                                                                     \
408     recursion_loop_detected = TRUE;                                     \
409     recursion_loop_element = (e);                                       \
410   }                                                                     \
411                                                                         \
412   recursion_loop_depth++;                                               \
413 }
414
415 #define RECURSION_LOOP_DETECTION_END()                                  \
416 {                                                                       \
417   recursion_loop_depth--;                                               \
418 }
419
420 static int recursion_loop_depth;
421 static boolean recursion_loop_detected;
422 static boolean recursion_loop_element;
423
424
425 /* ------------------------------------------------------------------------- */
426 /* definition of elements that automatically change to other elements after  */
427 /* a specified time, eventually calling a function when changing             */
428 /* ------------------------------------------------------------------------- */
429
430 /* forward declaration for changer functions */
431 static void InitBuggyBase(int, int);
432 static void WarnBuggyBase(int, int);
433
434 static void InitTrap(int, int);
435 static void ActivateTrap(int, int);
436 static void ChangeActiveTrap(int, int);
437
438 static void InitRobotWheel(int, int);
439 static void RunRobotWheel(int, int);
440 static void StopRobotWheel(int, int);
441
442 static void InitTimegateWheel(int, int);
443 static void RunTimegateWheel(int, int);
444
445 static void InitMagicBallDelay(int, int);
446 static void ActivateMagicBall(int, int);
447
448 struct ChangingElementInfo
449 {
450   int element;
451   int target_element;
452   int change_delay;
453   void (*pre_change_function)(int x, int y);
454   void (*change_function)(int x, int y);
455   void (*post_change_function)(int x, int y);
456 };
457
458 static struct ChangingElementInfo change_delay_list[] =
459 {
460   {
461     EL_NUT_BREAKING,
462     EL_EMERALD,
463     6,
464     NULL,
465     NULL,
466     NULL
467   },
468   {
469     EL_PEARL_BREAKING,
470     EL_EMPTY,
471     8,
472     NULL,
473     NULL,
474     NULL
475   },
476   {
477     EL_EXIT_OPENING,
478     EL_EXIT_OPEN,
479     29,
480     NULL,
481     NULL,
482     NULL
483   },
484   {
485     EL_EXIT_CLOSING,
486     EL_EXIT_CLOSED,
487     29,
488     NULL,
489     NULL,
490     NULL
491   },
492   {
493     EL_STEEL_EXIT_OPENING,
494     EL_STEEL_EXIT_OPEN,
495     29,
496     NULL,
497     NULL,
498     NULL
499   },
500   {
501     EL_STEEL_EXIT_CLOSING,
502     EL_STEEL_EXIT_CLOSED,
503     29,
504     NULL,
505     NULL,
506     NULL
507   },
508   {
509     EL_EM_EXIT_OPENING,
510     EL_EM_EXIT_OPEN,
511     29,
512     NULL,
513     NULL,
514     NULL
515   },
516   {
517     EL_EM_EXIT_CLOSING,
518 #if 1
519     EL_EMPTY,
520 #else
521     EL_EM_EXIT_CLOSED,
522 #endif
523     29,
524     NULL,
525     NULL,
526     NULL
527   },
528   {
529     EL_EM_STEEL_EXIT_OPENING,
530     EL_EM_STEEL_EXIT_OPEN,
531     29,
532     NULL,
533     NULL,
534     NULL
535   },
536   {
537     EL_EM_STEEL_EXIT_CLOSING,
538 #if 1
539     EL_STEELWALL,
540 #else
541     EL_EM_STEEL_EXIT_CLOSED,
542 #endif
543     29,
544     NULL,
545     NULL,
546     NULL
547   },
548   {
549     EL_SP_EXIT_OPENING,
550     EL_SP_EXIT_OPEN,
551     29,
552     NULL,
553     NULL,
554     NULL
555   },
556   {
557     EL_SP_EXIT_CLOSING,
558     EL_SP_EXIT_CLOSED,
559     29,
560     NULL,
561     NULL,
562     NULL
563   },
564   {
565     EL_SWITCHGATE_OPENING,
566     EL_SWITCHGATE_OPEN,
567     29,
568     NULL,
569     NULL,
570     NULL
571   },
572   {
573     EL_SWITCHGATE_CLOSING,
574     EL_SWITCHGATE_CLOSED,
575     29,
576     NULL,
577     NULL,
578     NULL
579   },
580   {
581     EL_TIMEGATE_OPENING,
582     EL_TIMEGATE_OPEN,
583     29,
584     NULL,
585     NULL,
586     NULL
587   },
588   {
589     EL_TIMEGATE_CLOSING,
590     EL_TIMEGATE_CLOSED,
591     29,
592     NULL,
593     NULL,
594     NULL
595   },
596
597   {
598     EL_ACID_SPLASH_LEFT,
599     EL_EMPTY,
600     8,
601     NULL,
602     NULL,
603     NULL
604   },
605   {
606     EL_ACID_SPLASH_RIGHT,
607     EL_EMPTY,
608     8,
609     NULL,
610     NULL,
611     NULL
612   },
613   {
614     EL_SP_BUGGY_BASE,
615     EL_SP_BUGGY_BASE_ACTIVATING,
616     0,
617     InitBuggyBase,
618     NULL,
619     NULL
620   },
621   {
622     EL_SP_BUGGY_BASE_ACTIVATING,
623     EL_SP_BUGGY_BASE_ACTIVE,
624     0,
625     InitBuggyBase,
626     NULL,
627     NULL
628   },
629   {
630     EL_SP_BUGGY_BASE_ACTIVE,
631     EL_SP_BUGGY_BASE,
632     0,
633     InitBuggyBase,
634     WarnBuggyBase,
635     NULL
636   },
637   {
638     EL_TRAP,
639     EL_TRAP_ACTIVE,
640     0,
641     InitTrap,
642     NULL,
643     ActivateTrap
644   },
645   {
646     EL_TRAP_ACTIVE,
647     EL_TRAP,
648     31,
649     NULL,
650     ChangeActiveTrap,
651     NULL
652   },
653   {
654     EL_ROBOT_WHEEL_ACTIVE,
655     EL_ROBOT_WHEEL,
656     0,
657     InitRobotWheel,
658     RunRobotWheel,
659     StopRobotWheel
660   },
661   {
662     EL_TIMEGATE_SWITCH_ACTIVE,
663     EL_TIMEGATE_SWITCH,
664     0,
665     InitTimegateWheel,
666     RunTimegateWheel,
667     NULL
668   },
669   {
670     EL_DC_TIMEGATE_SWITCH_ACTIVE,
671     EL_DC_TIMEGATE_SWITCH,
672     0,
673     InitTimegateWheel,
674     RunTimegateWheel,
675     NULL
676   },
677   {
678     EL_EMC_MAGIC_BALL_ACTIVE,
679     EL_EMC_MAGIC_BALL_ACTIVE,
680     0,
681     InitMagicBallDelay,
682     NULL,
683     ActivateMagicBall
684   },
685   {
686     EL_EMC_SPRING_BUMPER_ACTIVE,
687     EL_EMC_SPRING_BUMPER,
688     8,
689     NULL,
690     NULL,
691     NULL
692   },
693   {
694     EL_DIAGONAL_SHRINKING,
695     EL_UNDEFINED,
696     0,
697     NULL,
698     NULL,
699     NULL
700   },
701   {
702     EL_DIAGONAL_GROWING,
703     EL_UNDEFINED,
704     0,
705     NULL,
706     NULL,
707     NULL,
708   },
709
710   {
711     EL_UNDEFINED,
712     EL_UNDEFINED,
713     -1,
714     NULL,
715     NULL,
716     NULL
717   }
718 };
719
720 struct
721 {
722   int element;
723   int push_delay_fixed, push_delay_random;
724 }
725 push_delay_list[] =
726 {
727   { EL_SPRING,                  0, 0 },
728   { EL_BALLOON,                 0, 0 },
729
730   { EL_SOKOBAN_OBJECT,          2, 0 },
731   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
732   { EL_SATELLITE,               2, 0 },
733   { EL_SP_DISK_YELLOW,          2, 0 },
734
735   { EL_UNDEFINED,               0, 0 },
736 };
737
738 struct
739 {
740   int element;
741   int move_stepsize;
742 }
743 move_stepsize_list[] =
744 {
745   { EL_AMOEBA_DROP,             2 },
746   { EL_AMOEBA_DROPPING,         2 },
747   { EL_QUICKSAND_FILLING,       1 },
748   { EL_QUICKSAND_EMPTYING,      1 },
749   { EL_QUICKSAND_FAST_FILLING,  2 },
750   { EL_QUICKSAND_FAST_EMPTYING, 2 },
751   { EL_MAGIC_WALL_FILLING,      2 },
752   { EL_MAGIC_WALL_EMPTYING,     2 },
753   { EL_BD_MAGIC_WALL_FILLING,   2 },
754   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
755   { EL_DC_MAGIC_WALL_FILLING,   2 },
756   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
757
758   { EL_UNDEFINED,               0 },
759 };
760
761 struct
762 {
763   int element;
764   int count;
765 }
766 collect_count_list[] =
767 {
768   { EL_EMERALD,                 1 },
769   { EL_BD_DIAMOND,              1 },
770   { EL_EMERALD_YELLOW,          1 },
771   { EL_EMERALD_RED,             1 },
772   { EL_EMERALD_PURPLE,          1 },
773   { EL_DIAMOND,                 3 },
774   { EL_SP_INFOTRON,             1 },
775   { EL_PEARL,                   5 },
776   { EL_CRYSTAL,                 8 },
777
778   { EL_UNDEFINED,               0 },
779 };
780
781 struct
782 {
783   int element;
784   int direction;
785 }
786 access_direction_list[] =
787 {
788   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
789   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
790   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
791   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
792   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
793   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
794   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
795   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
796   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
797   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
798   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
799
800   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
801   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
802   { EL_SP_PORT_UP,                                                   MV_DOWN },
803   { EL_SP_PORT_DOWN,                                         MV_UP           },
804   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
805   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
806   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
807   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
808   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
809   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
810   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
811   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
812   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
813   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
814   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
815   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
816   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
817   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
818   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
819
820   { EL_UNDEFINED,                       MV_NONE                              }
821 };
822
823 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
824
825 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
826 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
827 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
828                                  IS_JUST_CHANGING(x, y))
829
830 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
831
832 /* static variables for playfield scan mode (scanning forward or backward) */
833 static int playfield_scan_start_x = 0;
834 static int playfield_scan_start_y = 0;
835 static int playfield_scan_delta_x = 1;
836 static int playfield_scan_delta_y = 1;
837
838 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
839                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
840                                      (y) += playfield_scan_delta_y)     \
841                                 for ((x) = playfield_scan_start_x;      \
842                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
843                                      (x) += playfield_scan_delta_x)     \
844
845 #ifdef DEBUG
846 void DEBUG_SetMaximumDynamite()
847 {
848   int i;
849
850   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
851     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
852       local_player->inventory_element[local_player->inventory_size++] =
853         EL_DYNAMITE;
854 }
855 #endif
856
857 static void InitPlayfieldScanModeVars()
858 {
859   if (game.use_reverse_scan_direction)
860   {
861     playfield_scan_start_x = lev_fieldx - 1;
862     playfield_scan_start_y = lev_fieldy - 1;
863
864     playfield_scan_delta_x = -1;
865     playfield_scan_delta_y = -1;
866   }
867   else
868   {
869     playfield_scan_start_x = 0;
870     playfield_scan_start_y = 0;
871
872     playfield_scan_delta_x = 1;
873     playfield_scan_delta_y = 1;
874   }
875 }
876
877 static void InitPlayfieldScanMode(int mode)
878 {
879   game.use_reverse_scan_direction =
880     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
881
882   InitPlayfieldScanModeVars();
883 }
884
885 static int get_move_delay_from_stepsize(int move_stepsize)
886 {
887   move_stepsize =
888     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
889
890   /* make sure that stepsize value is always a power of 2 */
891   move_stepsize = (1 << log_2(move_stepsize));
892
893   return TILEX / move_stepsize;
894 }
895
896 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
897                                boolean init_game)
898 {
899   int player_nr = player->index_nr;
900   int move_delay = get_move_delay_from_stepsize(move_stepsize);
901   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
902
903   /* do no immediately change move delay -- the player might just be moving */
904   player->move_delay_value_next = move_delay;
905
906   /* information if player can move must be set separately */
907   player->cannot_move = cannot_move;
908
909   if (init_game)
910   {
911     player->move_delay       = game.initial_move_delay[player_nr];
912     player->move_delay_value = game.initial_move_delay_value[player_nr];
913
914     player->move_delay_value_next = -1;
915
916     player->move_delay_reset_counter = 0;
917   }
918 }
919
920 void GetPlayerConfig()
921 {
922   if (!audio.sound_available)
923     setup.sound_simple = FALSE;
924
925   if (!audio.loops_available)
926     setup.sound_loops = FALSE;
927
928   if (!audio.music_available)
929     setup.sound_music = FALSE;
930
931   if (!video.fullscreen_available)
932     setup.fullscreen = FALSE;
933
934   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
935
936   SetAudioMode(setup.sound);
937   InitJoysticks();
938 }
939
940 static int getBeltNrFromBeltElement(int element)
941 {
942   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
943           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
944           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
945 }
946
947 static int getBeltNrFromBeltActiveElement(int element)
948 {
949   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
950           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
951           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
952 }
953
954 static int getBeltNrFromBeltSwitchElement(int element)
955 {
956   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
957           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
958           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
959 }
960
961 static int getBeltDirNrFromBeltSwitchElement(int element)
962 {
963   static int belt_base_element[4] =
964   {
965     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
966     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
967     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
968     EL_CONVEYOR_BELT_4_SWITCH_LEFT
969   };
970
971   int belt_nr = getBeltNrFromBeltSwitchElement(element);
972   int belt_dir_nr = element - belt_base_element[belt_nr];
973
974   return (belt_dir_nr % 3);
975 }
976
977 static int getBeltDirFromBeltSwitchElement(int element)
978 {
979   static int belt_move_dir[3] =
980   {
981     MV_LEFT,
982     MV_NONE,
983     MV_RIGHT
984   };
985
986   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
987
988   return belt_move_dir[belt_dir_nr];
989 }
990
991 static int get_element_from_group_element(int element)
992 {
993   if (IS_GROUP_ELEMENT(element))
994   {
995     struct ElementGroupInfo *group = element_info[element].group;
996     int last_anim_random_frame = gfx.anim_random_frame;
997     int element_pos;
998
999     if (group->choice_mode == ANIM_RANDOM)
1000       gfx.anim_random_frame = RND(group->num_elements_resolved);
1001
1002     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1003                                     group->choice_mode, 0,
1004                                     group->choice_pos);
1005
1006     if (group->choice_mode == ANIM_RANDOM)
1007       gfx.anim_random_frame = last_anim_random_frame;
1008
1009     group->choice_pos++;
1010
1011     element = group->element_resolved[element_pos];
1012   }
1013
1014   return element;
1015 }
1016
1017 static void InitPlayerField(int x, int y, int element, boolean init_game)
1018 {
1019   if (element == EL_SP_MURPHY)
1020   {
1021     if (init_game)
1022     {
1023       if (stored_player[0].present)
1024       {
1025         Feld[x][y] = EL_SP_MURPHY_CLONE;
1026
1027         return;
1028       }
1029       else
1030       {
1031         stored_player[0].use_murphy = TRUE;
1032
1033         if (!level.use_artwork_element[0])
1034           stored_player[0].artwork_element = EL_SP_MURPHY;
1035       }
1036
1037       Feld[x][y] = EL_PLAYER_1;
1038     }
1039   }
1040
1041   if (init_game)
1042   {
1043     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1044     int jx = player->jx, jy = player->jy;
1045
1046     player->present = TRUE;
1047
1048     player->block_last_field = (element == EL_SP_MURPHY ?
1049                                 level.sp_block_last_field :
1050                                 level.block_last_field);
1051
1052     /* ---------- initialize player's last field block delay --------------- */
1053
1054     /* always start with reliable default value (no adjustment needed) */
1055     player->block_delay_adjustment = 0;
1056
1057     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1058     if (player->block_last_field && element == EL_SP_MURPHY)
1059       player->block_delay_adjustment = 1;
1060
1061     /* special case 2: in game engines before 3.1.1, blocking was different */
1062     if (game.use_block_last_field_bug)
1063       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1064
1065     if (!options.network || player->connected)
1066     {
1067       player->active = TRUE;
1068
1069       /* remove potentially duplicate players */
1070       if (StorePlayer[jx][jy] == Feld[x][y])
1071         StorePlayer[jx][jy] = 0;
1072
1073       StorePlayer[x][y] = Feld[x][y];
1074
1075       if (options.debug)
1076       {
1077         printf("Player %d activated.\n", player->element_nr);
1078         printf("[Local player is %d and currently %s.]\n",
1079                local_player->element_nr,
1080                local_player->active ? "active" : "not active");
1081       }
1082     }
1083
1084     Feld[x][y] = EL_EMPTY;
1085
1086     player->jx = player->last_jx = x;
1087     player->jy = player->last_jy = y;
1088   }
1089 }
1090
1091 static void InitField(int x, int y, boolean init_game)
1092 {
1093   int element = Feld[x][y];
1094
1095   switch (element)
1096   {
1097     case EL_SP_MURPHY:
1098     case EL_PLAYER_1:
1099     case EL_PLAYER_2:
1100     case EL_PLAYER_3:
1101     case EL_PLAYER_4:
1102       InitPlayerField(x, y, element, init_game);
1103       break;
1104
1105     case EL_SOKOBAN_FIELD_PLAYER:
1106       element = Feld[x][y] = EL_PLAYER_1;
1107       InitField(x, y, init_game);
1108
1109       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1110       InitField(x, y, init_game);
1111       break;
1112
1113     case EL_SOKOBAN_FIELD_EMPTY:
1114       local_player->sokobanfields_still_needed++;
1115       break;
1116
1117     case EL_STONEBLOCK:
1118       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1119         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1120       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1121         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1122       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1123         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1124       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1125         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1126       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1127         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1128       break;
1129
1130     case EL_BUG:
1131     case EL_BUG_RIGHT:
1132     case EL_BUG_UP:
1133     case EL_BUG_LEFT:
1134     case EL_BUG_DOWN:
1135     case EL_SPACESHIP:
1136     case EL_SPACESHIP_RIGHT:
1137     case EL_SPACESHIP_UP:
1138     case EL_SPACESHIP_LEFT:
1139     case EL_SPACESHIP_DOWN:
1140     case EL_BD_BUTTERFLY:
1141     case EL_BD_BUTTERFLY_RIGHT:
1142     case EL_BD_BUTTERFLY_UP:
1143     case EL_BD_BUTTERFLY_LEFT:
1144     case EL_BD_BUTTERFLY_DOWN:
1145     case EL_BD_FIREFLY:
1146     case EL_BD_FIREFLY_RIGHT:
1147     case EL_BD_FIREFLY_UP:
1148     case EL_BD_FIREFLY_LEFT:
1149     case EL_BD_FIREFLY_DOWN:
1150     case EL_PACMAN_RIGHT:
1151     case EL_PACMAN_UP:
1152     case EL_PACMAN_LEFT:
1153     case EL_PACMAN_DOWN:
1154     case EL_YAMYAM:
1155     case EL_YAMYAM_LEFT:
1156     case EL_YAMYAM_RIGHT:
1157     case EL_YAMYAM_UP:
1158     case EL_YAMYAM_DOWN:
1159     case EL_DARK_YAMYAM:
1160     case EL_ROBOT:
1161     case EL_PACMAN:
1162     case EL_SP_SNIKSNAK:
1163     case EL_SP_ELECTRON:
1164     case EL_MOLE:
1165     case EL_MOLE_LEFT:
1166     case EL_MOLE_RIGHT:
1167     case EL_MOLE_UP:
1168     case EL_MOLE_DOWN:
1169       InitMovDir(x, y);
1170       break;
1171
1172     case EL_AMOEBA_FULL:
1173     case EL_BD_AMOEBA:
1174       InitAmoebaNr(x, y);
1175       break;
1176
1177     case EL_AMOEBA_DROP:
1178       if (y == lev_fieldy - 1)
1179       {
1180         Feld[x][y] = EL_AMOEBA_GROWING;
1181         Store[x][y] = EL_AMOEBA_WET;
1182       }
1183       break;
1184
1185     case EL_DYNAMITE_ACTIVE:
1186     case EL_SP_DISK_RED_ACTIVE:
1187     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1188     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1189     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1190     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1191       MovDelay[x][y] = 96;
1192       break;
1193
1194     case EL_EM_DYNAMITE_ACTIVE:
1195       MovDelay[x][y] = 32;
1196       break;
1197
1198     case EL_LAMP:
1199       local_player->lights_still_needed++;
1200       break;
1201
1202     case EL_PENGUIN:
1203       local_player->friends_still_needed++;
1204       break;
1205
1206     case EL_PIG:
1207     case EL_DRAGON:
1208       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1209       break;
1210
1211     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1212     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1213     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1214     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1215     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1216     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1217     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1218     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1219     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1220     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1221     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1222     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1223       if (init_game)
1224       {
1225         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1226         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1227         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1228
1229         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1230         {
1231           game.belt_dir[belt_nr] = belt_dir;
1232           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1233         }
1234         else    /* more than one switch -- set it like the first switch */
1235         {
1236           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1237         }
1238       }
1239       break;
1240
1241 #if !USE_BOTH_SWITCHGATE_SWITCHES
1242     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1243       if (init_game)
1244         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1245       break;
1246
1247     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1248       if (init_game)
1249         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1250       break;
1251 #endif
1252
1253     case EL_LIGHT_SWITCH_ACTIVE:
1254       if (init_game)
1255         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1256       break;
1257
1258     case EL_INVISIBLE_STEELWALL:
1259     case EL_INVISIBLE_WALL:
1260     case EL_INVISIBLE_SAND:
1261       if (game.light_time_left > 0 ||
1262           game.lenses_time_left > 0)
1263         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1264       break;
1265
1266     case EL_EMC_MAGIC_BALL:
1267       if (game.ball_state)
1268         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1269       break;
1270
1271     case EL_EMC_MAGIC_BALL_SWITCH:
1272       if (game.ball_state)
1273         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1274       break;
1275
1276     default:
1277       if (IS_CUSTOM_ELEMENT(element))
1278       {
1279         if (CAN_MOVE(element))
1280           InitMovDir(x, y);
1281
1282 #if USE_NEW_CUSTOM_VALUE
1283         if (!element_info[element].use_last_ce_value || init_game)
1284           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1285 #endif
1286       }
1287       else if (IS_GROUP_ELEMENT(element))
1288       {
1289         Feld[x][y] = get_element_from_group_element(element);
1290
1291         InitField(x, y, init_game);
1292       }
1293
1294       break;
1295   }
1296
1297   if (!init_game)
1298     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1299 }
1300
1301 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1302 {
1303   InitField(x, y, init_game);
1304
1305   /* not needed to call InitMovDir() -- already done by InitField()! */
1306   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1307       CAN_MOVE(Feld[x][y]))
1308     InitMovDir(x, y);
1309 }
1310
1311 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1312 {
1313   int old_element = Feld[x][y];
1314
1315   InitField(x, y, init_game);
1316
1317   /* not needed to call InitMovDir() -- already done by InitField()! */
1318   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1319       CAN_MOVE(old_element) &&
1320       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1321     InitMovDir(x, y);
1322
1323   /* this case is in fact a combination of not less than three bugs:
1324      first, it calls InitMovDir() for elements that can move, although this is
1325      already done by InitField(); then, it checks the element that was at this
1326      field _before_ the call to InitField() (which can change it); lastly, it
1327      was not called for "mole with direction" elements, which were treated as
1328      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1329   */
1330 }
1331
1332 #if 1
1333
1334 void DrawGameValue_Emeralds(int value)
1335 {
1336   struct TextPosInfo *pos = &game.panel.gems;
1337   int font_nr = FONT_TEXT_2;
1338   int font_width = getFontWidth(font_nr);
1339   int digits = pos->chars;
1340
1341   if (PANEL_DEACTIVATED(pos))
1342     return;
1343
1344   pos->width = digits * font_width;
1345
1346   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1347 }
1348
1349 void DrawGameValue_Dynamite(int value)
1350 {
1351   struct TextPosInfo *pos = &game.panel.inventory;
1352   int font_nr = FONT_TEXT_2;
1353   int font_width = getFontWidth(font_nr);
1354   int digits = pos->chars;
1355
1356   if (PANEL_DEACTIVATED(pos))
1357     return;
1358
1359   pos->width = digits * font_width;
1360
1361   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1362 }
1363
1364 void DrawGameValue_Score(int value)
1365 {
1366   struct TextPosInfo *pos = &game.panel.score;
1367   int font_nr = FONT_TEXT_2;
1368   int font_width = getFontWidth(font_nr);
1369   int digits = pos->chars;
1370
1371   if (PANEL_DEACTIVATED(pos))
1372     return;
1373
1374   pos->width = digits * font_width;
1375
1376   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1377 }
1378
1379 void DrawGameValue_Time(int value)
1380 {
1381   struct TextPosInfo *pos = &game.panel.time;
1382   static int last_value = -1;
1383   int digits1 = 3;
1384   int digits2 = 4;
1385   int digits = pos->chars;
1386   int font1_nr = FONT_TEXT_2;
1387   int font2_nr = FONT_TEXT_1;
1388   int font_nr = font1_nr;
1389   boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1390
1391   if (PANEL_DEACTIVATED(pos))
1392     return;
1393
1394   if (use_dynamic_digits)               /* use dynamic number of digits */
1395   {
1396     digits  = (value < 1000 ? digits1  : digits2);
1397     font_nr = (value < 1000 ? font1_nr : font2_nr);
1398   }
1399
1400   /* clear background if value just changed its size (dynamic digits only) */
1401   if (use_dynamic_digits && (last_value < 1000) != (value < 1000))
1402   {
1403     int width1 = digits1 * getFontWidth(font1_nr);
1404     int width2 = digits2 * getFontWidth(font2_nr);
1405     int max_width = MAX(width1, width2);
1406     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
1407
1408     pos->width = max_width;
1409
1410     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
1411                                max_width, max_height);
1412   }
1413
1414   pos->width = digits * getFontWidth(font_nr);
1415
1416   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1417
1418   last_value = value;
1419 }
1420
1421 void DrawGameValue_Level(int value)
1422 {
1423   struct TextPosInfo *pos = &game.panel.level;
1424   int digits1 = 2;
1425   int digits2 = 3;
1426   int digits = pos->chars;
1427   int font1_nr = FONT_TEXT_2;
1428   int font2_nr = FONT_TEXT_1;
1429   int font_nr = font1_nr;
1430   boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
1431
1432   if (PANEL_DEACTIVATED(pos))
1433     return;
1434
1435   if (use_dynamic_digits)               /* use dynamic number of digits */
1436   {
1437     digits  = (level_nr < 100 ? digits1  : digits2);
1438     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
1439   }
1440
1441   pos->width = digits * getFontWidth(font_nr);
1442
1443   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
1444 }
1445
1446 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1447 {
1448   struct TextPosInfo *pos = &game.panel.keys;
1449   int base_key_graphic = EL_KEY_1;
1450   int i;
1451
1452   if (PANEL_DEACTIVATED(pos))
1453     return;
1454
1455   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1456     base_key_graphic = EL_EM_KEY_1;
1457
1458   pos->width = 4 * MINI_TILEX;
1459
1460   /* currently only 4 of 8 possible keys are displayed */
1461   for (i = 0; i < STD_NUM_KEYS; i++)
1462   {
1463     int src_x = DOOR_GFX_PAGEX5 + 18;
1464     int src_y = DOOR_GFX_PAGEY1 + 123;
1465     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
1466     int dst_y = PANEL_YPOS(pos);
1467
1468     if (key[i])
1469       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
1470     else
1471       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
1472                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
1473   }
1474 }
1475
1476 #else
1477
1478 void DrawGameValue_Emeralds(int value)
1479 {
1480   int font_nr = FONT_TEXT_2;
1481   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1482
1483   if (PANEL_DEACTIVATED(game.panel.gems))
1484     return;
1485
1486   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
1487 }
1488
1489 void DrawGameValue_Dynamite(int value)
1490 {
1491   int font_nr = FONT_TEXT_2;
1492   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
1493
1494   if (PANEL_DEACTIVATED(game.panel.inventory))
1495     return;
1496
1497   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
1498 }
1499
1500 void DrawGameValue_Score(int value)
1501 {
1502   int font_nr = FONT_TEXT_2;
1503   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
1504
1505   if (PANEL_DEACTIVATED(game.panel.score))
1506     return;
1507
1508   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
1509 }
1510
1511 void DrawGameValue_Time(int value)
1512 {
1513   int font1_nr = FONT_TEXT_2;
1514 #if 1
1515   int font2_nr = FONT_TEXT_1;
1516 #else
1517   int font2_nr = FONT_LEVEL_NUMBER;
1518 #endif
1519   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
1520   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
1521
1522   if (PANEL_DEACTIVATED(game.panel.time))
1523     return;
1524
1525   /* clear background if value just changed its size */
1526   if (value == 999 || value == 1000)
1527     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1528
1529   if (value < 1000)
1530     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
1531   else
1532     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
1533 }
1534
1535 void DrawGameValue_Level(int value)
1536 {
1537   int font1_nr = FONT_TEXT_2;
1538 #if 1
1539   int font2_nr = FONT_TEXT_1;
1540 #else
1541   int font2_nr = FONT_LEVEL_NUMBER;
1542 #endif
1543
1544   if (PANEL_DEACTIVATED(game.panel.level))
1545     return;
1546
1547   if (level_nr < 100)
1548     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
1549   else
1550     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
1551 }
1552
1553 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1554 {
1555   int base_key_graphic = EL_KEY_1;
1556   int i;
1557
1558   if (PANEL_DEACTIVATED(game.panel.keys))
1559     return;
1560
1561   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1562     base_key_graphic = EL_EM_KEY_1;
1563
1564   /* currently only 4 of 8 possible keys are displayed */
1565   for (i = 0; i < STD_NUM_KEYS; i++)
1566   {
1567     int x = XX_KEYS + i * MINI_TILEX;
1568     int y = YY_KEYS;
1569
1570     if (key[i])
1571       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1572     else
1573       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1574                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1575   }
1576 }
1577
1578 #endif
1579
1580 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1581                        int key_bits)
1582 {
1583   int key[MAX_NUM_KEYS];
1584   int i;
1585
1586   /* prevent EM engine from updating time/score values parallel to GameWon() */
1587   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
1588       local_player->LevelSolved)
1589     return;
1590
1591   for (i = 0; i < MAX_NUM_KEYS; i++)
1592     key[i] = key_bits & (1 << i);
1593
1594   DrawGameValue_Level(level_nr);
1595
1596   DrawGameValue_Emeralds(emeralds);
1597   DrawGameValue_Dynamite(dynamite);
1598   DrawGameValue_Score(score);
1599   DrawGameValue_Time(time);
1600
1601   DrawGameValue_Keys(key);
1602 }
1603
1604 void DrawGameDoorValues()
1605 {
1606   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1607   int dynamite_value = 0;
1608   int score_value = (local_player->LevelSolved ? local_player->score_final :
1609                      local_player->score);
1610   int gems_value = local_player->gems_still_needed;
1611   int key_bits = 0;
1612   int i, j;
1613
1614   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1615   {
1616     DrawGameDoorValues_EM();
1617
1618     return;
1619   }
1620
1621   if (game.centered_player_nr == -1)
1622   {
1623     for (i = 0; i < MAX_PLAYERS; i++)
1624     {
1625       for (j = 0; j < MAX_NUM_KEYS; j++)
1626         if (stored_player[i].key[j])
1627           key_bits |= (1 << j);
1628
1629       dynamite_value += stored_player[i].inventory_size;
1630     }
1631   }
1632   else
1633   {
1634     int player_nr = game.centered_player_nr;
1635
1636     for (i = 0; i < MAX_NUM_KEYS; i++)
1637       if (stored_player[player_nr].key[i])
1638         key_bits |= (1 << i);
1639
1640     dynamite_value = stored_player[player_nr].inventory_size;
1641   }
1642
1643   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
1644                     key_bits);
1645 }
1646
1647
1648 /*
1649   =============================================================================
1650   InitGameEngine()
1651   -----------------------------------------------------------------------------
1652   initialize game engine due to level / tape version number
1653   =============================================================================
1654 */
1655
1656 static void InitGameEngine()
1657 {
1658   int i, j, k, l, x, y;
1659
1660   /* set game engine from tape file when re-playing, else from level file */
1661   game.engine_version = (tape.playing ? tape.engine_version :
1662                          level.game_version);
1663
1664   /* ---------------------------------------------------------------------- */
1665   /* set flags for bugs and changes according to active game engine version */
1666   /* ---------------------------------------------------------------------- */
1667
1668   /*
1669     Summary of bugfix/change:
1670     Fixed handling for custom elements that change when pushed by the player.
1671
1672     Fixed/changed in version:
1673     3.1.0
1674
1675     Description:
1676     Before 3.1.0, custom elements that "change when pushing" changed directly
1677     after the player started pushing them (until then handled in "DigField()").
1678     Since 3.1.0, these custom elements are not changed until the "pushing"
1679     move of the element is finished (now handled in "ContinueMoving()").
1680
1681     Affected levels/tapes:
1682     The first condition is generally needed for all levels/tapes before version
1683     3.1.0, which might use the old behaviour before it was changed; known tapes
1684     that are affected are some tapes from the level set "Walpurgis Gardens" by
1685     Jamie Cullen.
1686     The second condition is an exception from the above case and is needed for
1687     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1688     above (including some development versions of 3.1.0), but before it was
1689     known that this change would break tapes like the above and was fixed in
1690     3.1.1, so that the changed behaviour was active although the engine version
1691     while recording maybe was before 3.1.0. There is at least one tape that is
1692     affected by this exception, which is the tape for the one-level set "Bug
1693     Machine" by Juergen Bonhagen.
1694   */
1695
1696   game.use_change_when_pushing_bug =
1697     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1698      !(tape.playing &&
1699        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1700        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1701
1702   /*
1703     Summary of bugfix/change:
1704     Fixed handling for blocking the field the player leaves when moving.
1705
1706     Fixed/changed in version:
1707     3.1.1
1708
1709     Description:
1710     Before 3.1.1, when "block last field when moving" was enabled, the field
1711     the player is leaving when moving was blocked for the time of the move,
1712     and was directly unblocked afterwards. This resulted in the last field
1713     being blocked for exactly one less than the number of frames of one player
1714     move. Additionally, even when blocking was disabled, the last field was
1715     blocked for exactly one frame.
1716     Since 3.1.1, due to changes in player movement handling, the last field
1717     is not blocked at all when blocking is disabled. When blocking is enabled,
1718     the last field is blocked for exactly the number of frames of one player
1719     move. Additionally, if the player is Murphy, the hero of Supaplex, the
1720     last field is blocked for exactly one more than the number of frames of
1721     one player move.
1722
1723     Affected levels/tapes:
1724     (!!! yet to be determined -- probably many !!!)
1725   */
1726
1727   game.use_block_last_field_bug =
1728     (game.engine_version < VERSION_IDENT(3,1,1,0));
1729
1730   /*
1731     Summary of bugfix/change:
1732     Changed behaviour of CE changes with multiple changes per single frame.
1733
1734     Fixed/changed in version:
1735     3.2.0-6
1736
1737     Description:
1738     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1739     This resulted in race conditions where CEs seem to behave strange in some
1740     situations (where triggered CE changes were just skipped because there was
1741     already a CE change on that tile in the playfield in that engine frame).
1742     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1743     (The number of changes per frame must be limited in any case, because else
1744     it is easily possible to define CE changes that would result in an infinite
1745     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1746     should be set large enough so that it would only be reached in cases where
1747     the corresponding CE change conditions run into a loop. Therefore, it seems
1748     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1749     maximal number of change pages for custom elements.)
1750
1751     Affected levels/tapes:
1752     Probably many.
1753   */
1754
1755 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1756   game.max_num_changes_per_frame = 1;
1757 #else
1758   game.max_num_changes_per_frame =
1759     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1760 #endif
1761
1762   /* ---------------------------------------------------------------------- */
1763
1764   /* default scan direction: scan playfield from top/left to bottom/right */
1765   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1766
1767   /* dynamically adjust element properties according to game engine version */
1768   InitElementPropertiesEngine(game.engine_version);
1769
1770 #if 0
1771   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1772   printf("          tape version == %06d [%s] [file: %06d]\n",
1773          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1774          tape.file_version);
1775   printf("       => game.engine_version == %06d\n", game.engine_version);
1776 #endif
1777
1778   /* ---------- initialize player's initial move delay --------------------- */
1779
1780   /* dynamically adjust player properties according to level information */
1781   for (i = 0; i < MAX_PLAYERS; i++)
1782     game.initial_move_delay_value[i] =
1783       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1784
1785   /* dynamically adjust player properties according to game engine version */
1786   for (i = 0; i < MAX_PLAYERS; i++)
1787     game.initial_move_delay[i] =
1788       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1789        game.initial_move_delay_value[i] : 0);
1790
1791   /* ---------- initialize player's initial push delay --------------------- */
1792
1793   /* dynamically adjust player properties according to game engine version */
1794   game.initial_push_delay_value =
1795     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1796
1797   /* ---------- initialize changing elements ------------------------------- */
1798
1799   /* initialize changing elements information */
1800   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1801   {
1802     struct ElementInfo *ei = &element_info[i];
1803
1804     /* this pointer might have been changed in the level editor */
1805     ei->change = &ei->change_page[0];
1806
1807     if (!IS_CUSTOM_ELEMENT(i))
1808     {
1809       ei->change->target_element = EL_EMPTY_SPACE;
1810       ei->change->delay_fixed = 0;
1811       ei->change->delay_random = 0;
1812       ei->change->delay_frames = 1;
1813     }
1814
1815     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1816     {
1817       ei->has_change_event[j] = FALSE;
1818
1819       ei->event_page_nr[j] = 0;
1820       ei->event_page[j] = &ei->change_page[0];
1821     }
1822   }
1823
1824   /* add changing elements from pre-defined list */
1825   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1826   {
1827     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1828     struct ElementInfo *ei = &element_info[ch_delay->element];
1829
1830     ei->change->target_element       = ch_delay->target_element;
1831     ei->change->delay_fixed          = ch_delay->change_delay;
1832
1833     ei->change->pre_change_function  = ch_delay->pre_change_function;
1834     ei->change->change_function      = ch_delay->change_function;
1835     ei->change->post_change_function = ch_delay->post_change_function;
1836
1837     ei->change->can_change = TRUE;
1838     ei->change->can_change_or_has_action = TRUE;
1839
1840     ei->has_change_event[CE_DELAY] = TRUE;
1841
1842     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1843     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1844   }
1845
1846   /* ---------- initialize internal run-time variables ------------- */
1847
1848   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1849   {
1850     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1851
1852     for (j = 0; j < ei->num_change_pages; j++)
1853     {
1854       ei->change_page[j].can_change_or_has_action =
1855         (ei->change_page[j].can_change |
1856          ei->change_page[j].has_action);
1857     }
1858   }
1859
1860   /* add change events from custom element configuration */
1861   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1862   {
1863     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1864
1865     for (j = 0; j < ei->num_change_pages; j++)
1866     {
1867       if (!ei->change_page[j].can_change_or_has_action)
1868         continue;
1869
1870       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1871       {
1872         /* only add event page for the first page found with this event */
1873         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1874         {
1875           ei->has_change_event[k] = TRUE;
1876
1877           ei->event_page_nr[k] = j;
1878           ei->event_page[k] = &ei->change_page[j];
1879         }
1880       }
1881     }
1882   }
1883
1884   /* ---------- initialize run-time trigger player and element ------------- */
1885
1886   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1887   {
1888     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1889
1890     for (j = 0; j < ei->num_change_pages; j++)
1891     {
1892       ei->change_page[j].actual_trigger_element = EL_EMPTY;
1893       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1894       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1895       ei->change_page[j].actual_trigger_ce_value = 0;
1896       ei->change_page[j].actual_trigger_ce_score = 0;
1897     }
1898   }
1899
1900   /* ---------- initialize trigger events ---------------------------------- */
1901
1902   /* initialize trigger events information */
1903   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1904     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1905       trigger_events[i][j] = FALSE;
1906
1907   /* add trigger events from element change event properties */
1908   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1909   {
1910     struct ElementInfo *ei = &element_info[i];
1911
1912     for (j = 0; j < ei->num_change_pages; j++)
1913     {
1914       if (!ei->change_page[j].can_change_or_has_action)
1915         continue;
1916
1917       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1918       {
1919         int trigger_element = ei->change_page[j].trigger_element;
1920
1921         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1922         {
1923           if (ei->change_page[j].has_event[k])
1924           {
1925             if (IS_GROUP_ELEMENT(trigger_element))
1926             {
1927               struct ElementGroupInfo *group =
1928                 element_info[trigger_element].group;
1929
1930               for (l = 0; l < group->num_elements_resolved; l++)
1931                 trigger_events[group->element_resolved[l]][k] = TRUE;
1932             }
1933             else if (trigger_element == EL_ANY_ELEMENT)
1934               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1935                 trigger_events[l][k] = TRUE;
1936             else
1937               trigger_events[trigger_element][k] = TRUE;
1938           }
1939         }
1940       }
1941     }
1942   }
1943
1944   /* ---------- initialize push delay -------------------------------------- */
1945
1946   /* initialize push delay values to default */
1947   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1948   {
1949     if (!IS_CUSTOM_ELEMENT(i))
1950     {
1951       /* set default push delay values (corrected since version 3.0.7-1) */
1952       if (game.engine_version < VERSION_IDENT(3,0,7,1))
1953       {
1954         element_info[i].push_delay_fixed = 2;
1955         element_info[i].push_delay_random = 8;
1956       }
1957       else
1958       {
1959         element_info[i].push_delay_fixed = 8;
1960         element_info[i].push_delay_random = 8;
1961       }
1962     }
1963   }
1964
1965   /* set push delay value for certain elements from pre-defined list */
1966   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1967   {
1968     int e = push_delay_list[i].element;
1969
1970     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1971     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1972   }
1973
1974   /* set push delay value for Supaplex elements for newer engine versions */
1975   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1976   {
1977     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1978     {
1979       if (IS_SP_ELEMENT(i))
1980       {
1981         /* set SP push delay to just enough to push under a falling zonk */
1982         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1983
1984         element_info[i].push_delay_fixed  = delay;
1985         element_info[i].push_delay_random = 0;
1986       }
1987     }
1988   }
1989
1990   /* ---------- initialize move stepsize ----------------------------------- */
1991
1992   /* initialize move stepsize values to default */
1993   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1994     if (!IS_CUSTOM_ELEMENT(i))
1995       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1996
1997   /* set move stepsize value for certain elements from pre-defined list */
1998   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1999   {
2000     int e = move_stepsize_list[i].element;
2001
2002     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2003   }
2004
2005   /* ---------- initialize collect score ----------------------------------- */
2006
2007   /* initialize collect score values for custom elements from initial value */
2008   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2009     if (IS_CUSTOM_ELEMENT(i))
2010       element_info[i].collect_score = element_info[i].collect_score_initial;
2011
2012   /* ---------- initialize collect count ----------------------------------- */
2013
2014   /* initialize collect count values for non-custom elements */
2015   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2016     if (!IS_CUSTOM_ELEMENT(i))
2017       element_info[i].collect_count_initial = 0;
2018
2019   /* add collect count values for all elements from pre-defined list */
2020   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2021     element_info[collect_count_list[i].element].collect_count_initial =
2022       collect_count_list[i].count;
2023
2024   /* ---------- initialize access direction -------------------------------- */
2025
2026   /* initialize access direction values to default (access from every side) */
2027   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2028     if (!IS_CUSTOM_ELEMENT(i))
2029       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2030
2031   /* set access direction value for certain elements from pre-defined list */
2032   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2033     element_info[access_direction_list[i].element].access_direction =
2034       access_direction_list[i].direction;
2035
2036   /* ---------- initialize explosion content ------------------------------- */
2037   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2038   {
2039     if (IS_CUSTOM_ELEMENT(i))
2040       continue;
2041
2042     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
2043     {
2044       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
2045
2046       element_info[i].content.e[x][y] =
2047         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
2048          i == EL_PLAYER_2 ? EL_EMERALD_RED :
2049          i == EL_PLAYER_3 ? EL_EMERALD :
2050          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
2051          i == EL_MOLE ? EL_EMERALD_RED :
2052          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
2053          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
2054          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
2055          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
2056          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
2057          i == EL_WALL_EMERALD ? EL_EMERALD :
2058          i == EL_WALL_DIAMOND ? EL_DIAMOND :
2059          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
2060          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
2061          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
2062          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
2063          i == EL_WALL_PEARL ? EL_PEARL :
2064          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
2065          EL_EMPTY);
2066     }
2067   }
2068
2069   /* ---------- initialize recursion detection ------------------------------ */
2070   recursion_loop_depth = 0;
2071   recursion_loop_detected = FALSE;
2072   recursion_loop_element = EL_UNDEFINED;
2073 }
2074
2075 int get_num_special_action(int element, int action_first, int action_last)
2076 {
2077   int num_special_action = 0;
2078   int i, j;
2079
2080   for (i = action_first; i <= action_last; i++)
2081   {
2082     boolean found = FALSE;
2083
2084     for (j = 0; j < NUM_DIRECTIONS; j++)
2085       if (el_act_dir2img(element, i, j) !=
2086           el_act_dir2img(element, ACTION_DEFAULT, j))
2087         found = TRUE;
2088
2089     if (found)
2090       num_special_action++;
2091     else
2092       break;
2093   }
2094
2095   return num_special_action;
2096 }
2097
2098
2099 /*
2100   =============================================================================
2101   InitGame()
2102   -----------------------------------------------------------------------------
2103   initialize and start new game
2104   =============================================================================
2105 */
2106
2107 void InitGame()
2108 {
2109   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2110   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2111   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2112   boolean do_fading = (game_status == GAME_MODE_MAIN);
2113   int i, j, x, y;
2114
2115   game_status = GAME_MODE_PLAYING;
2116
2117   InitGameEngine();
2118
2119   /* don't play tapes over network */
2120   network_playing = (options.network && !tape.playing);
2121
2122   for (i = 0; i < MAX_PLAYERS; i++)
2123   {
2124     struct PlayerInfo *player = &stored_player[i];
2125
2126     player->index_nr = i;
2127     player->index_bit = (1 << i);
2128     player->element_nr = EL_PLAYER_1 + i;
2129
2130     player->present = FALSE;
2131     player->active = FALSE;
2132     player->killed = FALSE;
2133
2134     player->action = 0;
2135     player->effective_action = 0;
2136     player->programmed_action = 0;
2137
2138     player->score = 0;
2139     player->score_final = 0;
2140
2141     player->gems_still_needed = level.gems_needed;
2142     player->sokobanfields_still_needed = 0;
2143     player->lights_still_needed = 0;
2144     player->friends_still_needed = 0;
2145
2146     for (j = 0; j < MAX_NUM_KEYS; j++)
2147       player->key[j] = FALSE;
2148
2149     player->num_white_keys = 0;
2150
2151     player->dynabomb_count = 0;
2152     player->dynabomb_size = 1;
2153     player->dynabombs_left = 0;
2154     player->dynabomb_xl = FALSE;
2155
2156     player->MovDir = MV_NONE;
2157     player->MovPos = 0;
2158     player->GfxPos = 0;
2159     player->GfxDir = MV_NONE;
2160     player->GfxAction = ACTION_DEFAULT;
2161     player->Frame = 0;
2162     player->StepFrame = 0;
2163
2164     player->use_murphy = FALSE;
2165     player->artwork_element =
2166       (level.use_artwork_element[i] ? level.artwork_element[i] :
2167        player->element_nr);
2168
2169     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2170     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2171
2172     player->gravity = level.initial_player_gravity[i];
2173
2174     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2175
2176     player->actual_frame_counter = 0;
2177
2178     player->step_counter = 0;
2179
2180     player->last_move_dir = MV_NONE;
2181
2182     player->is_active = FALSE;
2183
2184     player->is_waiting = FALSE;
2185     player->is_moving = FALSE;
2186     player->is_auto_moving = FALSE;
2187     player->is_digging = FALSE;
2188     player->is_snapping = FALSE;
2189     player->is_collecting = FALSE;
2190     player->is_pushing = FALSE;
2191     player->is_switching = FALSE;
2192     player->is_dropping = FALSE;
2193     player->is_dropping_pressed = FALSE;
2194
2195     player->is_bored = FALSE;
2196     player->is_sleeping = FALSE;
2197
2198     player->frame_counter_bored = -1;
2199     player->frame_counter_sleeping = -1;
2200
2201     player->anim_delay_counter = 0;
2202     player->post_delay_counter = 0;
2203
2204     player->dir_waiting = MV_NONE;
2205     player->action_waiting = ACTION_DEFAULT;
2206     player->last_action_waiting = ACTION_DEFAULT;
2207     player->special_action_bored = ACTION_DEFAULT;
2208     player->special_action_sleeping = ACTION_DEFAULT;
2209
2210     player->switch_x = -1;
2211     player->switch_y = -1;
2212
2213     player->drop_x = -1;
2214     player->drop_y = -1;
2215
2216     player->show_envelope = 0;
2217
2218     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2219
2220     player->push_delay       = -1;      /* initialized when pushing starts */
2221     player->push_delay_value = game.initial_push_delay_value;
2222
2223     player->drop_delay = 0;
2224     player->drop_pressed_delay = 0;
2225
2226     player->last_jx = -1;
2227     player->last_jy = -1;
2228     player->jx = -1;
2229     player->jy = -1;
2230
2231     player->shield_normal_time_left = 0;
2232     player->shield_deadly_time_left = 0;
2233
2234     player->inventory_infinite_element = EL_UNDEFINED;
2235     player->inventory_size = 0;
2236
2237     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2238     SnapField(player, 0, 0);
2239
2240     player->LevelSolved = FALSE;
2241     player->GameOver = FALSE;
2242
2243     player->LevelSolved_GameEnd = FALSE;
2244     player->LevelSolved_SaveTape = FALSE;
2245     player->LevelSolved_SaveScore = FALSE;
2246   }
2247
2248   network_player_action_received = FALSE;
2249
2250 #if defined(NETWORK_AVALIABLE)
2251   /* initial null action */
2252   if (network_playing)
2253     SendToServer_MovePlayer(MV_NONE);
2254 #endif
2255
2256   ZX = ZY = -1;
2257   ExitX = ExitY = -1;
2258
2259   FrameCounter = 0;
2260   TimeFrames = 0;
2261   TimePlayed = 0;
2262   TimeLeft = level.time;
2263   TapeTime = 0;
2264
2265   ScreenMovDir = MV_NONE;
2266   ScreenMovPos = 0;
2267   ScreenGfxPos = 0;
2268
2269   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2270
2271   AllPlayersGone = FALSE;
2272
2273   game.yamyam_content_nr = 0;
2274   game.magic_wall_active = FALSE;
2275   game.magic_wall_time_left = 0;
2276   game.light_time_left = 0;
2277   game.timegate_time_left = 0;
2278   game.switchgate_pos = 0;
2279   game.wind_direction = level.wind_direction_initial;
2280
2281 #if !USE_PLAYER_GRAVITY
2282   game.gravity = FALSE;
2283   game.explosions_delayed = TRUE;
2284 #endif
2285
2286   game.lenses_time_left = 0;
2287   game.magnify_time_left = 0;
2288
2289   game.ball_state = level.ball_state_initial;
2290   game.ball_content_nr = 0;
2291
2292   game.envelope_active = FALSE;
2293
2294   /* set focus to local player for network games, else to all players */
2295   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2296   game.centered_player_nr_next = game.centered_player_nr;
2297   game.set_centered_player = FALSE;
2298
2299   if (network_playing && tape.recording)
2300   {
2301     /* store client dependent player focus when recording network games */
2302     tape.centered_player_nr_next = game.centered_player_nr_next;
2303     tape.set_centered_player = TRUE;
2304   }
2305
2306   for (i = 0; i < NUM_BELTS; i++)
2307   {
2308     game.belt_dir[i] = MV_NONE;
2309     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2310   }
2311
2312   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2313     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2314
2315   SCAN_PLAYFIELD(x, y)
2316   {
2317     Feld[x][y] = level.field[x][y];
2318     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2319     ChangeDelay[x][y] = 0;
2320     ChangePage[x][y] = -1;
2321 #if USE_NEW_CUSTOM_VALUE
2322     CustomValue[x][y] = 0;              /* initialized in InitField() */
2323 #endif
2324     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2325     AmoebaNr[x][y] = 0;
2326     WasJustMoving[x][y] = 0;
2327     WasJustFalling[x][y] = 0;
2328     CheckCollision[x][y] = 0;
2329     CheckImpact[x][y] = 0;
2330     Stop[x][y] = FALSE;
2331     Pushed[x][y] = FALSE;
2332
2333     ChangeCount[x][y] = 0;
2334     ChangeEvent[x][y] = -1;
2335
2336     ExplodePhase[x][y] = 0;
2337     ExplodeDelay[x][y] = 0;
2338     ExplodeField[x][y] = EX_TYPE_NONE;
2339
2340     RunnerVisit[x][y] = 0;
2341     PlayerVisit[x][y] = 0;
2342
2343     GfxFrame[x][y] = 0;
2344     GfxRandom[x][y] = INIT_GFX_RANDOM();
2345     GfxElement[x][y] = EL_UNDEFINED;
2346     GfxAction[x][y] = ACTION_DEFAULT;
2347     GfxDir[x][y] = MV_NONE;
2348   }
2349
2350   SCAN_PLAYFIELD(x, y)
2351   {
2352     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2353       emulate_bd = FALSE;
2354     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2355       emulate_sb = FALSE;
2356     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2357       emulate_sp = FALSE;
2358
2359     InitField(x, y, TRUE);
2360   }
2361
2362   InitBeltMovement();
2363
2364   for (i = 0; i < MAX_PLAYERS; i++)
2365   {
2366     struct PlayerInfo *player = &stored_player[i];
2367
2368     /* set number of special actions for bored and sleeping animation */
2369     player->num_special_action_bored =
2370       get_num_special_action(player->artwork_element,
2371                              ACTION_BORING_1, ACTION_BORING_LAST);
2372     player->num_special_action_sleeping =
2373       get_num_special_action(player->artwork_element,
2374                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2375   }
2376
2377   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2378                     emulate_sb ? EMU_SOKOBAN :
2379                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2380
2381 #if USE_NEW_ALL_SLIPPERY
2382   /* initialize type of slippery elements */
2383   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2384   {
2385     if (!IS_CUSTOM_ELEMENT(i))
2386     {
2387       /* default: elements slip down either to the left or right randomly */
2388       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2389
2390       /* SP style elements prefer to slip down on the left side */
2391       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2392         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2393
2394       /* BD style elements prefer to slip down on the left side */
2395       if (game.emulation == EMU_BOULDERDASH)
2396         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2397     }
2398   }
2399 #endif
2400
2401   /* initialize explosion and ignition delay */
2402   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2403   {
2404     if (!IS_CUSTOM_ELEMENT(i))
2405     {
2406       int num_phase = 8;
2407       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2408                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2409                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2410       int last_phase = (num_phase + 1) * delay;
2411       int half_phase = (num_phase / 2) * delay;
2412
2413       element_info[i].explosion_delay = last_phase - 1;
2414       element_info[i].ignition_delay = half_phase;
2415
2416       if (i == EL_BLACK_ORB)
2417         element_info[i].ignition_delay = 1;
2418     }
2419
2420 #if 0
2421     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2422       element_info[i].explosion_delay = 1;
2423
2424     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2425       element_info[i].ignition_delay = 1;
2426 #endif
2427   }
2428
2429   /* correct non-moving belts to start moving left */
2430   for (i = 0; i < NUM_BELTS; i++)
2431     if (game.belt_dir[i] == MV_NONE)
2432       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2433
2434   /* check if any connected player was not found in playfield */
2435   for (i = 0; i < MAX_PLAYERS; i++)
2436   {
2437     struct PlayerInfo *player = &stored_player[i];
2438
2439     if (player->connected && !player->present)
2440     {
2441       for (j = 0; j < MAX_PLAYERS; j++)
2442       {
2443         struct PlayerInfo *some_player = &stored_player[j];
2444         int jx = some_player->jx, jy = some_player->jy;
2445
2446         /* assign first free player found that is present in the playfield */
2447         if (some_player->present && !some_player->connected)
2448         {
2449           player->present = TRUE;
2450           player->active = TRUE;
2451
2452           some_player->present = FALSE;
2453           some_player->active = FALSE;
2454
2455           player->artwork_element = some_player->artwork_element;
2456
2457           player->block_last_field       = some_player->block_last_field;
2458           player->block_delay_adjustment = some_player->block_delay_adjustment;
2459
2460           StorePlayer[jx][jy] = player->element_nr;
2461           player->jx = player->last_jx = jx;
2462           player->jy = player->last_jy = jy;
2463
2464           break;
2465         }
2466       }
2467     }
2468   }
2469
2470   if (tape.playing)
2471   {
2472     /* when playing a tape, eliminate all players who do not participate */
2473
2474     for (i = 0; i < MAX_PLAYERS; i++)
2475     {
2476       if (stored_player[i].active && !tape.player_participates[i])
2477       {
2478         struct PlayerInfo *player = &stored_player[i];
2479         int jx = player->jx, jy = player->jy;
2480
2481         player->active = FALSE;
2482         StorePlayer[jx][jy] = 0;
2483         Feld[jx][jy] = EL_EMPTY;
2484       }
2485     }
2486   }
2487   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2488   {
2489     /* when in single player mode, eliminate all but the first active player */
2490
2491     for (i = 0; i < MAX_PLAYERS; i++)
2492     {
2493       if (stored_player[i].active)
2494       {
2495         for (j = i + 1; j < MAX_PLAYERS; j++)
2496         {
2497           if (stored_player[j].active)
2498           {
2499             struct PlayerInfo *player = &stored_player[j];
2500             int jx = player->jx, jy = player->jy;
2501
2502             player->active = FALSE;
2503             player->present = FALSE;
2504
2505             StorePlayer[jx][jy] = 0;
2506             Feld[jx][jy] = EL_EMPTY;
2507           }
2508         }
2509       }
2510     }
2511   }
2512
2513   /* when recording the game, store which players take part in the game */
2514   if (tape.recording)
2515   {
2516     for (i = 0; i < MAX_PLAYERS; i++)
2517       if (stored_player[i].active)
2518         tape.player_participates[i] = TRUE;
2519   }
2520
2521   if (options.debug)
2522   {
2523     for (i = 0; i < MAX_PLAYERS; i++)
2524     {
2525       struct PlayerInfo *player = &stored_player[i];
2526
2527       printf("Player %d: present == %d, connected == %d, active == %d.\n",
2528              i+1,
2529              player->present,
2530              player->connected,
2531              player->active);
2532       if (local_player == player)
2533         printf("Player  %d is local player.\n", i+1);
2534     }
2535   }
2536
2537   if (BorderElement == EL_EMPTY)
2538   {
2539     SBX_Left = 0;
2540     SBX_Right = lev_fieldx - SCR_FIELDX;
2541     SBY_Upper = 0;
2542     SBY_Lower = lev_fieldy - SCR_FIELDY;
2543   }
2544   else
2545   {
2546     SBX_Left = -1;
2547     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2548     SBY_Upper = -1;
2549     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2550   }
2551
2552   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2553     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2554
2555   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2556     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2557
2558   /* if local player not found, look for custom element that might create
2559      the player (make some assumptions about the right custom element) */
2560   if (!local_player->present)
2561   {
2562     int start_x = 0, start_y = 0;
2563     int found_rating = 0;
2564     int found_element = EL_UNDEFINED;
2565     int player_nr = local_player->index_nr;
2566
2567     SCAN_PLAYFIELD(x, y)
2568     {
2569       int element = Feld[x][y];
2570       int content;
2571       int xx, yy;
2572       boolean is_player;
2573
2574       if (level.use_start_element[player_nr] &&
2575           level.start_element[player_nr] == element &&
2576           found_rating < 4)
2577       {
2578         start_x = x;
2579         start_y = y;
2580
2581         found_rating = 4;
2582         found_element = element;
2583       }
2584
2585       if (!IS_CUSTOM_ELEMENT(element))
2586         continue;
2587
2588       if (CAN_CHANGE(element))
2589       {
2590         for (i = 0; i < element_info[element].num_change_pages; i++)
2591         {
2592           /* check for player created from custom element as single target */
2593           content = element_info[element].change_page[i].target_element;
2594           is_player = ELEM_IS_PLAYER(content);
2595
2596           if (is_player && (found_rating < 3 || element < found_element))
2597           {
2598             start_x = x;
2599             start_y = y;
2600
2601             found_rating = 3;
2602             found_element = element;
2603           }
2604         }
2605       }
2606
2607       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2608       {
2609         /* check for player created from custom element as explosion content */
2610         content = element_info[element].content.e[xx][yy];
2611         is_player = ELEM_IS_PLAYER(content);
2612
2613         if (is_player && (found_rating < 2 || element < found_element))
2614         {
2615           start_x = x + xx - 1;
2616           start_y = y + yy - 1;
2617
2618           found_rating = 2;
2619           found_element = element;
2620         }
2621
2622         if (!CAN_CHANGE(element))
2623           continue;
2624
2625         for (i = 0; i < element_info[element].num_change_pages; i++)
2626         {
2627           /* check for player created from custom element as extended target */
2628           content =
2629             element_info[element].change_page[i].target_content.e[xx][yy];
2630
2631           is_player = ELEM_IS_PLAYER(content);
2632
2633           if (is_player && (found_rating < 1 || element < found_element))
2634           {
2635             start_x = x + xx - 1;
2636             start_y = y + yy - 1;
2637
2638             found_rating = 1;
2639             found_element = element;
2640           }
2641         }
2642       }
2643     }
2644
2645     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
2646                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2647                 start_x - MIDPOSX);
2648
2649     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2650                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2651                 start_y - MIDPOSY);
2652   }
2653   else
2654   {
2655     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2656                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2657                 local_player->jx - MIDPOSX);
2658
2659     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2660                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2661                 local_player->jy - MIDPOSY);
2662   }
2663
2664   StopAnimation();
2665
2666   if (!game.restart_level)
2667     CloseDoor(DOOR_CLOSE_1);
2668
2669   if (do_fading)
2670     FadeOut(REDRAW_FIELD);
2671
2672   /* !!! FIX THIS (START) !!! */
2673   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2674   {
2675     InitGameEngine_EM();
2676
2677     /* blit playfield from scroll buffer to normal back buffer for fading in */
2678     BlitScreenToBitmap_EM(backbuffer);
2679   }
2680   else
2681   {
2682     DrawLevel();
2683     DrawAllPlayers();
2684
2685     /* after drawing the level, correct some elements */
2686     if (game.timegate_time_left == 0)
2687       CloseAllOpenTimegates();
2688
2689     /* blit playfield from scroll buffer to normal back buffer for fading in */
2690     if (setup.soft_scrolling)
2691       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2692
2693     redraw_mask |= REDRAW_FROM_BACKBUFFER;
2694   }
2695   /* !!! FIX THIS (END) !!! */
2696
2697   if (do_fading)
2698     FadeIn(REDRAW_FIELD);
2699
2700   BackToFront();
2701
2702   if (!game.restart_level)
2703   {
2704     /* copy default game door content to main double buffer */
2705     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2706                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2707   }
2708
2709   SetPanelBackground();
2710   SetDrawBackgroundMask(REDRAW_DOOR_1);
2711
2712   DrawGameDoorValues();
2713
2714   if (!game.restart_level)
2715   {
2716     UnmapGameButtons();
2717     UnmapTapeButtons();
2718     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2719     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2720     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2721     MapGameButtons();
2722     MapTapeButtons();
2723
2724     /* copy actual game door content to door double buffer for OpenDoor() */
2725     BlitBitmap(drawto, bitmap_db_door,
2726                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2727
2728     OpenDoor(DOOR_OPEN_ALL);
2729
2730     PlaySound(SND_GAME_STARTING);
2731
2732     if (setup.sound_music)
2733       PlayLevelMusic();
2734
2735     KeyboardAutoRepeatOffUnlessAutoplay();
2736
2737     if (options.debug)
2738     {
2739       for (i = 0; i < MAX_PLAYERS; i++)
2740         printf("Player %d %sactive.\n",
2741                i + 1, (stored_player[i].active ? "" : "not "));
2742     }
2743   }
2744
2745 #if 1
2746   UnmapAllGadgets();
2747
2748   MapGameButtons();
2749   MapTapeButtons();
2750 #endif
2751
2752   game.restart_level = FALSE;
2753 }
2754
2755 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2756 {
2757   /* this is used for non-R'n'D game engines to update certain engine values */
2758
2759   /* needed to determine if sounds are played within the visible screen area */
2760   scroll_x = actual_scroll_x;
2761   scroll_y = actual_scroll_y;
2762 }
2763
2764 void InitMovDir(int x, int y)
2765 {
2766   int i, element = Feld[x][y];
2767   static int xy[4][2] =
2768   {
2769     {  0, +1 },
2770     { +1,  0 },
2771     {  0, -1 },
2772     { -1,  0 }
2773   };
2774   static int direction[3][4] =
2775   {
2776     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
2777     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
2778     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
2779   };
2780
2781   switch (element)
2782   {
2783     case EL_BUG_RIGHT:
2784     case EL_BUG_UP:
2785     case EL_BUG_LEFT:
2786     case EL_BUG_DOWN:
2787       Feld[x][y] = EL_BUG;
2788       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2789       break;
2790
2791     case EL_SPACESHIP_RIGHT:
2792     case EL_SPACESHIP_UP:
2793     case EL_SPACESHIP_LEFT:
2794     case EL_SPACESHIP_DOWN:
2795       Feld[x][y] = EL_SPACESHIP;
2796       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2797       break;
2798
2799     case EL_BD_BUTTERFLY_RIGHT:
2800     case EL_BD_BUTTERFLY_UP:
2801     case EL_BD_BUTTERFLY_LEFT:
2802     case EL_BD_BUTTERFLY_DOWN:
2803       Feld[x][y] = EL_BD_BUTTERFLY;
2804       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2805       break;
2806
2807     case EL_BD_FIREFLY_RIGHT:
2808     case EL_BD_FIREFLY_UP:
2809     case EL_BD_FIREFLY_LEFT:
2810     case EL_BD_FIREFLY_DOWN:
2811       Feld[x][y] = EL_BD_FIREFLY;
2812       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2813       break;
2814
2815     case EL_PACMAN_RIGHT:
2816     case EL_PACMAN_UP:
2817     case EL_PACMAN_LEFT:
2818     case EL_PACMAN_DOWN:
2819       Feld[x][y] = EL_PACMAN;
2820       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2821       break;
2822
2823     case EL_YAMYAM_LEFT:
2824     case EL_YAMYAM_RIGHT:
2825     case EL_YAMYAM_UP:
2826     case EL_YAMYAM_DOWN:
2827       Feld[x][y] = EL_YAMYAM;
2828       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2829       break;
2830
2831     case EL_SP_SNIKSNAK:
2832       MovDir[x][y] = MV_UP;
2833       break;
2834
2835     case EL_SP_ELECTRON:
2836       MovDir[x][y] = MV_LEFT;
2837       break;
2838
2839     case EL_MOLE_LEFT:
2840     case EL_MOLE_RIGHT:
2841     case EL_MOLE_UP:
2842     case EL_MOLE_DOWN:
2843       Feld[x][y] = EL_MOLE;
2844       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2845       break;
2846
2847     default:
2848       if (IS_CUSTOM_ELEMENT(element))
2849       {
2850         struct ElementInfo *ei = &element_info[element];
2851         int move_direction_initial = ei->move_direction_initial;
2852         int move_pattern = ei->move_pattern;
2853
2854         if (move_direction_initial == MV_START_PREVIOUS)
2855         {
2856           if (MovDir[x][y] != MV_NONE)
2857             return;
2858
2859           move_direction_initial = MV_START_AUTOMATIC;
2860         }
2861
2862         if (move_direction_initial == MV_START_RANDOM)
2863           MovDir[x][y] = 1 << RND(4);
2864         else if (move_direction_initial & MV_ANY_DIRECTION)
2865           MovDir[x][y] = move_direction_initial;
2866         else if (move_pattern == MV_ALL_DIRECTIONS ||
2867                  move_pattern == MV_TURNING_LEFT ||
2868                  move_pattern == MV_TURNING_RIGHT ||
2869                  move_pattern == MV_TURNING_LEFT_RIGHT ||
2870                  move_pattern == MV_TURNING_RIGHT_LEFT ||
2871                  move_pattern == MV_TURNING_RANDOM)
2872           MovDir[x][y] = 1 << RND(4);
2873         else if (move_pattern == MV_HORIZONTAL)
2874           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2875         else if (move_pattern == MV_VERTICAL)
2876           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2877         else if (move_pattern & MV_ANY_DIRECTION)
2878           MovDir[x][y] = element_info[element].move_pattern;
2879         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2880                  move_pattern == MV_ALONG_RIGHT_SIDE)
2881         {
2882           /* use random direction as default start direction */
2883           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2884             MovDir[x][y] = 1 << RND(4);
2885
2886           for (i = 0; i < NUM_DIRECTIONS; i++)
2887           {
2888             int x1 = x + xy[i][0];
2889             int y1 = y + xy[i][1];
2890
2891             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2892             {
2893               if (move_pattern == MV_ALONG_RIGHT_SIDE)
2894                 MovDir[x][y] = direction[0][i];
2895               else
2896                 MovDir[x][y] = direction[1][i];
2897
2898               break;
2899             }
2900           }
2901         }                
2902       }
2903       else
2904       {
2905         MovDir[x][y] = 1 << RND(4);
2906
2907         if (element != EL_BUG &&
2908             element != EL_SPACESHIP &&
2909             element != EL_BD_BUTTERFLY &&
2910             element != EL_BD_FIREFLY)
2911           break;
2912
2913         for (i = 0; i < NUM_DIRECTIONS; i++)
2914         {
2915           int x1 = x + xy[i][0];
2916           int y1 = y + xy[i][1];
2917
2918           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2919           {
2920             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2921             {
2922               MovDir[x][y] = direction[0][i];
2923               break;
2924             }
2925             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2926                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2927             {
2928               MovDir[x][y] = direction[1][i];
2929               break;
2930             }
2931           }
2932         }
2933       }
2934       break;
2935   }
2936
2937   GfxDir[x][y] = MovDir[x][y];
2938 }
2939
2940 void InitAmoebaNr(int x, int y)
2941 {
2942   int i;
2943   int group_nr = AmoebeNachbarNr(x, y);
2944
2945   if (group_nr == 0)
2946   {
2947     for (i = 1; i < MAX_NUM_AMOEBA; i++)
2948     {
2949       if (AmoebaCnt[i] == 0)
2950       {
2951         group_nr = i;
2952         break;
2953       }
2954     }
2955   }
2956
2957   AmoebaNr[x][y] = group_nr;
2958   AmoebaCnt[group_nr]++;
2959   AmoebaCnt2[group_nr]++;
2960 }
2961
2962 static void PlayerWins(struct PlayerInfo *player)
2963 {
2964   player->LevelSolved = TRUE;
2965   player->GameOver = TRUE;
2966
2967   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2968                          level.native_em_level->lev->score : player->score);
2969 }
2970
2971 void GameWon()
2972 {
2973   static int time, time_final;
2974   static int score, score_final;
2975   static int game_over_delay = 0;
2976   int game_over_delay_value = 50;
2977
2978   if (!local_player->LevelSolved_GameEnd)
2979   {
2980     int i;
2981
2982     /* do not start end game actions before the player stops moving (to exit) */
2983     if (local_player->MovPos)
2984       return;
2985
2986     local_player->LevelSolved_GameEnd = TRUE;
2987     local_player->LevelSolved_SaveTape = tape.recording;
2988     local_player->LevelSolved_SaveScore = !tape.playing;
2989
2990     if (tape.auto_play)         /* tape might already be stopped here */
2991       tape.auto_play_level_solved = TRUE;
2992
2993 #if 1
2994     TapeStop();
2995 #endif
2996
2997     game_over_delay = game_over_delay_value;
2998
2999     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
3000     score = score_final = local_player->score_final;
3001
3002     if (TimeLeft > 0)
3003     {
3004       time_final = 0;
3005       score_final += TimeLeft * level.score[SC_TIME_BONUS];
3006     }
3007     else if (level.time == 0 && TimePlayed < 999)
3008     {
3009       time_final = 999;
3010       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
3011     }
3012
3013     local_player->score_final = score_final;
3014
3015     if (level_editor_test_game)
3016     {
3017       time = time_final;
3018       score = score_final;
3019
3020       DrawGameValue_Time(time);
3021       DrawGameValue_Score(score);
3022     }
3023
3024     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3025     {
3026       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
3027       {
3028         /* close exit door after last player */
3029         if ((AllPlayersGone &&
3030              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3031               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
3032               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
3033             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
3034             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
3035         {
3036           int element = Feld[ExitX][ExitY];
3037
3038 #if 0
3039           if (element == EL_EM_EXIT_OPEN ||
3040               element == EL_EM_STEEL_EXIT_OPEN)
3041           {
3042             Bang(ExitX, ExitY);
3043           }
3044           else
3045 #endif
3046           {
3047             Feld[ExitX][ExitY] =
3048               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
3049                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
3050                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
3051                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
3052                EL_EM_STEEL_EXIT_CLOSING);
3053
3054             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3055           }
3056         }
3057
3058         /* player disappears */
3059         DrawLevelField(ExitX, ExitY);
3060       }
3061
3062       for (i = 0; i < MAX_PLAYERS; i++)
3063       {
3064         struct PlayerInfo *player = &stored_player[i];
3065
3066         if (player->present)
3067         {
3068           RemovePlayer(player);
3069
3070           /* player disappears */
3071           DrawLevelField(player->jx, player->jy);
3072         }
3073       }
3074     }
3075
3076     PlaySound(SND_GAME_WINNING);
3077   }
3078
3079   if (game_over_delay > 0)
3080   {
3081     game_over_delay--;
3082
3083     return;
3084   }
3085
3086   if (time != time_final)
3087   {
3088     int time_to_go = ABS(time_final - time);
3089     int time_count_dir = (time < time_final ? +1 : -1);
3090     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
3091
3092     time  += time_count_steps * time_count_dir;
3093     score += time_count_steps * level.score[SC_TIME_BONUS];
3094
3095     DrawGameValue_Time(time);
3096     DrawGameValue_Score(score);
3097
3098     if (time == time_final)
3099       StopSound(SND_GAME_LEVELTIME_BONUS);
3100     else if (setup.sound_loops)
3101       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
3102     else
3103       PlaySound(SND_GAME_LEVELTIME_BONUS);
3104   }
3105 }
3106
3107 void GameEnd()
3108 {
3109   int hi_pos;
3110   boolean raise_level = FALSE;
3111
3112   CloseDoor(DOOR_CLOSE_1);
3113
3114   if (local_player->LevelSolved_SaveTape)
3115   {
3116 #if 0
3117     TapeStop();
3118 #endif
3119
3120 #if 1
3121     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
3122 #else
3123     SaveTape(tape.level_nr);            /* ask to save tape */
3124 #endif
3125   }
3126
3127   if (level_editor_test_game)
3128   {
3129     game_status = GAME_MODE_MAIN;
3130
3131     DrawMainMenu();
3132
3133     return;
3134   }
3135
3136   if (!local_player->LevelSolved_SaveScore)
3137   {
3138     FadeOut(REDRAW_FIELD);
3139
3140     game_status = GAME_MODE_MAIN;
3141
3142     DrawAndFadeInMainMenu(REDRAW_FIELD);
3143
3144     return;
3145   }
3146
3147   if (level_nr == leveldir_current->handicap_level)
3148   {
3149     leveldir_current->handicap_level++;
3150     SaveLevelSetup_SeriesInfo();
3151   }
3152
3153   if (level_nr < leveldir_current->last_level)
3154     raise_level = TRUE;                 /* advance to next level */
3155
3156   if ((hi_pos = NewHiScore()) >= 0) 
3157   {
3158     game_status = GAME_MODE_SCORES;
3159
3160     DrawHallOfFame(hi_pos);
3161
3162     if (raise_level)
3163     {
3164       level_nr++;
3165       TapeErase();
3166     }
3167   }
3168   else
3169   {
3170     FadeOut(REDRAW_FIELD);
3171
3172     game_status = GAME_MODE_MAIN;
3173
3174     if (raise_level)
3175     {
3176       level_nr++;
3177       TapeErase();
3178     }
3179
3180     DrawAndFadeInMainMenu(REDRAW_FIELD);
3181   }
3182 }
3183
3184 int NewHiScore()
3185 {
3186   int k, l;
3187   int position = -1;
3188
3189   LoadScore(level_nr);
3190
3191   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3192       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3193     return -1;
3194
3195   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3196   {
3197     if (local_player->score_final > highscore[k].Score)
3198     {
3199       /* player has made it to the hall of fame */
3200
3201       if (k < MAX_SCORE_ENTRIES - 1)
3202       {
3203         int m = MAX_SCORE_ENTRIES - 1;
3204
3205 #ifdef ONE_PER_NAME
3206         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3207           if (strEqual(setup.player_name, highscore[l].Name))
3208             m = l;
3209         if (m == k)     /* player's new highscore overwrites his old one */
3210           goto put_into_list;
3211 #endif
3212
3213         for (l = m; l > k; l--)
3214         {
3215           strcpy(highscore[l].Name, highscore[l - 1].Name);
3216           highscore[l].Score = highscore[l - 1].Score;
3217         }
3218       }
3219
3220 #ifdef ONE_PER_NAME
3221       put_into_list:
3222 #endif
3223       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3224       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3225       highscore[k].Score = local_player->score_final; 
3226       position = k;
3227       break;
3228     }
3229
3230 #ifdef ONE_PER_NAME
3231     else if (!strncmp(setup.player_name, highscore[k].Name,
3232                       MAX_PLAYER_NAME_LEN))
3233       break;    /* player already there with a higher score */
3234 #endif
3235
3236   }
3237
3238   if (position >= 0) 
3239     SaveScore(level_nr);
3240
3241   return position;
3242 }
3243
3244 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
3245 {
3246   int element = Feld[x][y];
3247   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3248   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3249   int horiz_move = (dx != 0);
3250   int sign = (horiz_move ? dx : dy);
3251   int step = sign * element_info[element].move_stepsize;
3252
3253   /* special values for move stepsize for spring and things on conveyor belt */
3254   if (horiz_move)
3255   {
3256     if (CAN_FALL(element) &&
3257         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3258       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3259     else if (element == EL_SPRING)
3260       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3261   }
3262
3263   return step;
3264 }
3265
3266 inline static int getElementMoveStepsize(int x, int y)
3267 {
3268   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
3269 }
3270
3271 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3272 {
3273   if (player->GfxAction != action || player->GfxDir != dir)
3274   {
3275 #if 0
3276     printf("Player frame reset! (%d => %d, %d => %d)\n",
3277            player->GfxAction, action, player->GfxDir, dir);
3278 #endif
3279
3280     player->GfxAction = action;
3281     player->GfxDir = dir;
3282     player->Frame = 0;
3283     player->StepFrame = 0;
3284   }
3285 }
3286
3287 #if USE_GFX_RESET_GFX_ANIMATION
3288 static void ResetGfxFrame(int x, int y, boolean redraw)
3289 {
3290   int element = Feld[x][y];
3291   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3292   int last_gfx_frame = GfxFrame[x][y];
3293
3294   if (graphic_info[graphic].anim_global_sync)
3295     GfxFrame[x][y] = FrameCounter;
3296   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3297     GfxFrame[x][y] = CustomValue[x][y];
3298   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3299     GfxFrame[x][y] = element_info[element].collect_score;
3300   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3301     GfxFrame[x][y] = ChangeDelay[x][y];
3302
3303   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3304     DrawLevelGraphicAnimation(x, y, graphic);
3305 }
3306 #endif
3307
3308 static void ResetGfxAnimation(int x, int y)
3309 {
3310   GfxAction[x][y] = ACTION_DEFAULT;
3311   GfxDir[x][y] = MovDir[x][y];
3312   GfxFrame[x][y] = 0;
3313
3314 #if USE_GFX_RESET_GFX_ANIMATION
3315   ResetGfxFrame(x, y, FALSE);
3316 #endif
3317 }
3318
3319 static void ResetRandomAnimationValue(int x, int y)
3320 {
3321   GfxRandom[x][y] = INIT_GFX_RANDOM();
3322 }
3323
3324 void InitMovingField(int x, int y, int direction)
3325 {
3326   int element = Feld[x][y];
3327   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3328   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3329   int newx = x + dx;
3330   int newy = y + dy;
3331   boolean is_moving_before, is_moving_after;
3332 #if 0
3333   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
3334 #endif
3335
3336   /* check if element was/is moving or being moved before/after mode change */
3337 #if 1
3338   is_moving_before = WasJustMoving[x][y];
3339 #else
3340   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
3341 #endif
3342   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
3343
3344   /* reset animation only for moving elements which change direction of moving
3345      or which just started or stopped moving
3346      (else CEs with property "can move" / "not moving" are reset each frame) */
3347 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3348 #if 1
3349   if (is_moving_before != is_moving_after ||
3350       direction != MovDir[x][y])
3351     ResetGfxAnimation(x, y);
3352 #else
3353   if ((is_moving_before || is_moving_after) && !continues_moving)
3354     ResetGfxAnimation(x, y);
3355 #endif
3356 #else
3357   if (!continues_moving)
3358     ResetGfxAnimation(x, y);
3359 #endif
3360
3361   MovDir[x][y] = direction;
3362   GfxDir[x][y] = direction;
3363
3364 #if USE_GFX_RESET_ONLY_WHEN_MOVING
3365   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
3366                      direction == MV_DOWN && CAN_FALL(element) ?
3367                      ACTION_FALLING : ACTION_MOVING);
3368 #else
3369   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3370                      ACTION_FALLING : ACTION_MOVING);
3371 #endif
3372
3373   /* this is needed for CEs with property "can move" / "not moving" */
3374
3375   if (is_moving_after)
3376   {
3377     if (Feld[newx][newy] == EL_EMPTY)
3378       Feld[newx][newy] = EL_BLOCKED;
3379
3380     MovDir[newx][newy] = MovDir[x][y];
3381
3382 #if USE_NEW_CUSTOM_VALUE
3383     CustomValue[newx][newy] = CustomValue[x][y];
3384 #endif
3385
3386     GfxFrame[newx][newy] = GfxFrame[x][y];
3387     GfxRandom[newx][newy] = GfxRandom[x][y];
3388     GfxAction[newx][newy] = GfxAction[x][y];
3389     GfxDir[newx][newy] = GfxDir[x][y];
3390   }
3391 }
3392
3393 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3394 {
3395   int direction = MovDir[x][y];
3396   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3397   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3398
3399   *goes_to_x = newx;
3400   *goes_to_y = newy;
3401 }
3402
3403 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3404 {
3405   int oldx = x, oldy = y;
3406   int direction = MovDir[x][y];
3407
3408   if (direction == MV_LEFT)
3409     oldx++;
3410   else if (direction == MV_RIGHT)
3411     oldx--;
3412   else if (direction == MV_UP)
3413     oldy++;
3414   else if (direction == MV_DOWN)
3415     oldy--;
3416
3417   *comes_from_x = oldx;
3418   *comes_from_y = oldy;
3419 }
3420
3421 int MovingOrBlocked2Element(int x, int y)
3422 {
3423   int element = Feld[x][y];
3424
3425   if (element == EL_BLOCKED)
3426   {
3427     int oldx, oldy;
3428
3429     Blocked2Moving(x, y, &oldx, &oldy);
3430     return Feld[oldx][oldy];
3431   }
3432   else
3433     return element;
3434 }
3435
3436 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3437 {
3438   /* like MovingOrBlocked2Element(), but if element is moving
3439      and (x,y) is the field the moving element is just leaving,
3440      return EL_BLOCKED instead of the element value */
3441   int element = Feld[x][y];
3442
3443   if (IS_MOVING(x, y))
3444   {
3445     if (element == EL_BLOCKED)
3446     {
3447       int oldx, oldy;
3448
3449       Blocked2Moving(x, y, &oldx, &oldy);
3450       return Feld[oldx][oldy];
3451     }
3452     else
3453       return EL_BLOCKED;
3454   }
3455   else
3456     return element;
3457 }
3458
3459 static void RemoveField(int x, int y)
3460 {
3461   Feld[x][y] = EL_EMPTY;
3462
3463   MovPos[x][y] = 0;
3464   MovDir[x][y] = 0;
3465   MovDelay[x][y] = 0;
3466
3467 #if USE_NEW_CUSTOM_VALUE
3468   CustomValue[x][y] = 0;
3469 #endif
3470
3471   AmoebaNr[x][y] = 0;
3472   ChangeDelay[x][y] = 0;
3473   ChangePage[x][y] = -1;
3474   Pushed[x][y] = FALSE;
3475
3476 #if 0
3477   ExplodeField[x][y] = EX_TYPE_NONE;
3478 #endif
3479
3480   GfxElement[x][y] = EL_UNDEFINED;
3481   GfxAction[x][y] = ACTION_DEFAULT;
3482   GfxDir[x][y] = MV_NONE;
3483 }
3484
3485 void RemoveMovingField(int x, int y)
3486 {
3487   int oldx = x, oldy = y, newx = x, newy = y;
3488   int element = Feld[x][y];
3489   int next_element = EL_UNDEFINED;
3490
3491   if (element != EL_BLOCKED && !IS_MOVING(x, y))
3492     return;
3493
3494   if (IS_MOVING(x, y))
3495   {
3496     Moving2Blocked(x, y, &newx, &newy);
3497
3498     if (Feld[newx][newy] != EL_BLOCKED)
3499     {
3500       /* element is moving, but target field is not free (blocked), but
3501          already occupied by something different (example: acid pool);
3502          in this case, only remove the moving field, but not the target */
3503
3504       RemoveField(oldx, oldy);
3505
3506       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3507
3508       DrawLevelField(oldx, oldy);
3509
3510       return;
3511     }
3512   }
3513   else if (element == EL_BLOCKED)
3514   {
3515     Blocked2Moving(x, y, &oldx, &oldy);
3516     if (!IS_MOVING(oldx, oldy))
3517       return;
3518   }
3519
3520   if (element == EL_BLOCKED &&
3521       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3522        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
3523        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3524        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3525        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
3526        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3527     next_element = get_next_element(Feld[oldx][oldy]);
3528
3529   RemoveField(oldx, oldy);
3530   RemoveField(newx, newy);
3531
3532   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3533
3534   if (next_element != EL_UNDEFINED)
3535     Feld[oldx][oldy] = next_element;
3536
3537   DrawLevelField(oldx, oldy);
3538   DrawLevelField(newx, newy);
3539 }
3540
3541 void DrawDynamite(int x, int y)
3542 {
3543   int sx = SCREENX(x), sy = SCREENY(y);
3544   int graphic = el2img(Feld[x][y]);
3545   int frame;
3546
3547   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3548     return;
3549
3550   if (IS_WALKABLE_INSIDE(Back[x][y]))
3551     return;
3552
3553   if (Back[x][y])
3554     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3555   else if (Store[x][y])
3556     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3557
3558   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3559
3560   if (Back[x][y] || Store[x][y])
3561     DrawGraphicThruMask(sx, sy, graphic, frame);
3562   else
3563     DrawGraphic(sx, sy, graphic, frame);
3564 }
3565
3566 void CheckDynamite(int x, int y)
3567 {
3568   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
3569   {
3570     MovDelay[x][y]--;
3571
3572     if (MovDelay[x][y] != 0)
3573     {
3574       DrawDynamite(x, y);
3575       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3576
3577       return;
3578     }
3579   }
3580
3581   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3582
3583   Bang(x, y);
3584 }
3585
3586 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3587 {
3588   boolean num_checked_players = 0;
3589   int i;
3590
3591   for (i = 0; i < MAX_PLAYERS; i++)
3592   {
3593     if (stored_player[i].active)
3594     {
3595       int sx = stored_player[i].jx;
3596       int sy = stored_player[i].jy;
3597
3598       if (num_checked_players == 0)
3599       {
3600         *sx1 = *sx2 = sx;
3601         *sy1 = *sy2 = sy;
3602       }
3603       else
3604       {
3605         *sx1 = MIN(*sx1, sx);
3606         *sy1 = MIN(*sy1, sy);
3607         *sx2 = MAX(*sx2, sx);
3608         *sy2 = MAX(*sy2, sy);
3609       }
3610
3611       num_checked_players++;
3612     }
3613   }
3614 }
3615
3616 static boolean checkIfAllPlayersFitToScreen_RND()
3617 {
3618   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3619
3620   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3621
3622   return (sx2 - sx1 < SCR_FIELDX &&
3623           sy2 - sy1 < SCR_FIELDY);
3624 }
3625
3626 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3627 {
3628   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3629
3630   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3631
3632   *sx = (sx1 + sx2) / 2;
3633   *sy = (sy1 + sy2) / 2;
3634 }
3635
3636 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
3637                         boolean center_screen, boolean quick_relocation)
3638 {
3639   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3640   boolean no_delay = (tape.warp_forward);
3641   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3642   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3643
3644   if (quick_relocation)
3645   {
3646     int offset = (setup.scroll_delay ? 3 : 0);
3647
3648     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3649     {
3650       if (center_screen)
3651       {
3652         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3653                     x > SBX_Right + MIDPOSX ? SBX_Right :
3654                     x - MIDPOSX);
3655
3656         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3657                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
3658                     y - MIDPOSY);
3659       }
3660       else
3661       {
3662         /* quick relocation (without scrolling), but do not center screen */
3663
3664         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
3665                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
3666                                old_x - MIDPOSX);
3667
3668         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3669                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3670                                old_y - MIDPOSY);
3671
3672         int offset_x = x + (scroll_x - center_scroll_x);
3673         int offset_y = y + (scroll_y - center_scroll_y);
3674
3675         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
3676                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
3677                     offset_x - MIDPOSX);
3678
3679         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3680                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3681                     offset_y - MIDPOSY);
3682       }
3683     }
3684     else
3685     {
3686       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
3687           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3688         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3689
3690       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
3691           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3692         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3693
3694       /* don't scroll over playfield boundaries */
3695       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3696         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3697
3698       /* don't scroll over playfield boundaries */
3699       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3700         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3701     }
3702
3703     RedrawPlayfield(TRUE, 0,0,0,0);
3704   }
3705   else
3706   {
3707     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3708                      x > SBX_Right + MIDPOSX ? SBX_Right :
3709                      x - MIDPOSX);
3710
3711     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3712                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
3713                      y - MIDPOSY);
3714
3715     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3716
3717     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3718     {
3719       int dx = 0, dy = 0;
3720       int fx = FX, fy = FY;
3721
3722       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3723       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3724
3725       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3726         break;
3727
3728       scroll_x -= dx;
3729       scroll_y -= dy;
3730
3731       fx += dx * TILEX / 2;
3732       fy += dy * TILEY / 2;
3733
3734       ScrollLevel(dx, dy);
3735       DrawAllPlayers();
3736
3737       /* scroll in two steps of half tile size to make things smoother */
3738       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3739       FlushDisplay();
3740       Delay(wait_delay_value);
3741
3742       /* scroll second step to align at full tile size */
3743       BackToFront();
3744       Delay(wait_delay_value);
3745     }
3746
3747     DrawAllPlayers();
3748     BackToFront();
3749     Delay(wait_delay_value);
3750   }
3751 }
3752
3753 void RelocatePlayer(int jx, int jy, int el_player_raw)
3754 {
3755   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3756   int player_nr = GET_PLAYER_NR(el_player);
3757   struct PlayerInfo *player = &stored_player[player_nr];
3758   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3759   boolean no_delay = (tape.warp_forward);
3760   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3761   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3762   int old_jx = player->jx;
3763   int old_jy = player->jy;
3764   int old_element = Feld[old_jx][old_jy];
3765   int element = Feld[jx][jy];
3766   boolean player_relocated = (old_jx != jx || old_jy != jy);
3767
3768   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3769   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
3770   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3771   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
3772   int leave_side_horiz = move_dir_horiz;
3773   int leave_side_vert  = move_dir_vert;
3774   int enter_side = enter_side_horiz | enter_side_vert;
3775   int leave_side = leave_side_horiz | leave_side_vert;
3776
3777   if (player->GameOver)         /* do not reanimate dead player */
3778     return;
3779
3780   if (!player_relocated)        /* no need to relocate the player */
3781     return;
3782
3783   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
3784   {
3785     RemoveField(jx, jy);        /* temporarily remove newly placed player */
3786     DrawLevelField(jx, jy);
3787   }
3788
3789   if (player->present)
3790   {
3791     while (player->MovPos)
3792     {
3793       ScrollPlayer(player, SCROLL_GO_ON);
3794       ScrollScreen(NULL, SCROLL_GO_ON);
3795
3796       AdvanceFrameAndPlayerCounters(player->index_nr);
3797
3798       DrawPlayer(player);
3799
3800       BackToFront();
3801       Delay(wait_delay_value);
3802     }
3803
3804     DrawPlayer(player);         /* needed here only to cleanup last field */
3805     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
3806
3807     player->is_moving = FALSE;
3808   }
3809
3810   if (IS_CUSTOM_ELEMENT(old_element))
3811     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3812                                CE_LEFT_BY_PLAYER,
3813                                player->index_bit, leave_side);
3814
3815   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3816                                       CE_PLAYER_LEAVES_X,
3817                                       player->index_bit, leave_side);
3818
3819   Feld[jx][jy] = el_player;
3820   InitPlayerField(jx, jy, el_player, TRUE);
3821
3822   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3823   {
3824     Feld[jx][jy] = element;
3825     InitField(jx, jy, FALSE);
3826   }
3827
3828   /* only visually relocate centered player */
3829   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
3830                      FALSE, level.instant_relocation);
3831
3832   TestIfPlayerTouchesBadThing(jx, jy);
3833   TestIfPlayerTouchesCustomElement(jx, jy);
3834
3835   if (IS_CUSTOM_ELEMENT(element))
3836     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3837                                player->index_bit, enter_side);
3838
3839   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3840                                       player->index_bit, enter_side);
3841 }
3842
3843 void Explode(int ex, int ey, int phase, int mode)
3844 {
3845   int x, y;
3846   int last_phase;
3847   int border_element;
3848
3849   /* !!! eliminate this variable !!! */
3850   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3851
3852   if (game.explosions_delayed)
3853   {
3854     ExplodeField[ex][ey] = mode;
3855     return;
3856   }
3857
3858   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
3859   {
3860     int center_element = Feld[ex][ey];
3861     int artwork_element, explosion_element;     /* set these values later */
3862
3863 #if 0
3864     /* --- This is only really needed (and now handled) in "Impact()". --- */
3865     /* do not explode moving elements that left the explode field in time */
3866     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3867         center_element == EL_EMPTY &&
3868         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3869       return;
3870 #endif
3871
3872 #if 0
3873     /* !!! at this place, the center element may be EL_BLOCKED !!! */
3874     if (mode == EX_TYPE_NORMAL ||
3875         mode == EX_TYPE_CENTER ||
3876         mode == EX_TYPE_CROSS)
3877       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3878 #endif
3879
3880     /* remove things displayed in background while burning dynamite */
3881     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3882       Back[ex][ey] = 0;
3883
3884     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3885     {
3886       /* put moving element to center field (and let it explode there) */
3887       center_element = MovingOrBlocked2Element(ex, ey);
3888       RemoveMovingField(ex, ey);
3889       Feld[ex][ey] = center_element;
3890     }
3891
3892     /* now "center_element" is finally determined -- set related values now */
3893     artwork_element = center_element;           /* for custom player artwork */
3894     explosion_element = center_element;         /* for custom player artwork */
3895
3896     if (IS_PLAYER(ex, ey))
3897     {
3898       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3899
3900       artwork_element = stored_player[player_nr].artwork_element;
3901
3902       if (level.use_explosion_element[player_nr])
3903       {
3904         explosion_element = level.explosion_element[player_nr];
3905         artwork_element = explosion_element;
3906       }
3907     }
3908
3909 #if 1
3910     if (mode == EX_TYPE_NORMAL ||
3911         mode == EX_TYPE_CENTER ||
3912         mode == EX_TYPE_CROSS)
3913       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3914 #endif
3915
3916     last_phase = element_info[explosion_element].explosion_delay + 1;
3917
3918     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3919     {
3920       int xx = x - ex + 1;
3921       int yy = y - ey + 1;
3922       int element;
3923
3924       if (!IN_LEV_FIELD(x, y) ||
3925           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3926           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
3927         continue;
3928
3929       element = Feld[x][y];
3930
3931       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3932       {
3933         element = MovingOrBlocked2Element(x, y);
3934
3935         if (!IS_EXPLOSION_PROOF(element))
3936           RemoveMovingField(x, y);
3937       }
3938
3939       /* indestructible elements can only explode in center (but not flames) */
3940       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3941                                            mode == EX_TYPE_BORDER)) ||
3942           element == EL_FLAMES)
3943         continue;
3944
3945       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3946          behaviour, for example when touching a yamyam that explodes to rocks
3947          with active deadly shield, a rock is created under the player !!! */
3948       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3949 #if 0
3950       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3951           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3952            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3953 #else
3954       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3955 #endif
3956       {
3957         if (IS_ACTIVE_BOMB(element))
3958         {
3959           /* re-activate things under the bomb like gate or penguin */
3960           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3961           Back[x][y] = 0;
3962         }
3963
3964         continue;
3965       }
3966
3967       /* save walkable background elements while explosion on same tile */
3968       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3969           (x != ex || y != ey || mode == EX_TYPE_BORDER))
3970         Back[x][y] = element;
3971
3972       /* ignite explodable elements reached by other explosion */
3973       if (element == EL_EXPLOSION)
3974         element = Store2[x][y];
3975
3976       if (AmoebaNr[x][y] &&
3977           (element == EL_AMOEBA_FULL ||
3978            element == EL_BD_AMOEBA ||
3979            element == EL_AMOEBA_GROWING))
3980       {
3981         AmoebaCnt[AmoebaNr[x][y]]--;
3982         AmoebaCnt2[AmoebaNr[x][y]]--;
3983       }
3984
3985       RemoveField(x, y);
3986
3987       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3988       {
3989         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3990
3991         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3992
3993         if (PLAYERINFO(ex, ey)->use_murphy)
3994           Store[x][y] = EL_EMPTY;
3995       }
3996
3997       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3998          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3999       else if (ELEM_IS_PLAYER(center_element))
4000         Store[x][y] = EL_EMPTY;
4001       else if (center_element == EL_YAMYAM)
4002         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4003       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4004         Store[x][y] = element_info[center_element].content.e[xx][yy];
4005 #if 1
4006       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4007          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4008          otherwise) -- FIX THIS !!! */
4009       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4010         Store[x][y] = element_info[element].content.e[1][1];
4011 #else
4012       else if (!CAN_EXPLODE(element))
4013         Store[x][y] = element_info[element].content.e[1][1];
4014 #endif
4015       else
4016         Store[x][y] = EL_EMPTY;
4017
4018       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4019           center_element == EL_AMOEBA_TO_DIAMOND)
4020         Store2[x][y] = element;
4021
4022       Feld[x][y] = EL_EXPLOSION;
4023       GfxElement[x][y] = artwork_element;
4024
4025       ExplodePhase[x][y] = 1;
4026       ExplodeDelay[x][y] = last_phase;
4027
4028       Stop[x][y] = TRUE;
4029     }
4030
4031     if (center_element == EL_YAMYAM)
4032       game.yamyam_content_nr =
4033         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4034
4035     return;
4036   }
4037
4038   if (Stop[ex][ey])
4039     return;
4040
4041   x = ex;
4042   y = ey;
4043
4044   if (phase == 1)
4045     GfxFrame[x][y] = 0;         /* restart explosion animation */
4046
4047   last_phase = ExplodeDelay[x][y];
4048
4049   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4050
4051 #ifdef DEBUG
4052
4053   /* activate this even in non-DEBUG version until cause for crash in
4054      getGraphicAnimationFrame() (see below) is found and eliminated */
4055
4056 #endif
4057 #if 1
4058
4059 #if 1
4060   /* this can happen if the player leaves an explosion just in time */
4061   if (GfxElement[x][y] == EL_UNDEFINED)
4062     GfxElement[x][y] = EL_EMPTY;
4063 #else
4064   if (GfxElement[x][y] == EL_UNDEFINED)
4065   {
4066     printf("\n\n");
4067     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4068     printf("Explode(): This should never happen!\n");
4069     printf("\n\n");
4070
4071     GfxElement[x][y] = EL_EMPTY;
4072   }
4073 #endif
4074
4075 #endif
4076
4077   border_element = Store2[x][y];
4078   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4079     border_element = StorePlayer[x][y];
4080
4081   if (phase == element_info[border_element].ignition_delay ||
4082       phase == last_phase)
4083   {
4084     boolean border_explosion = FALSE;
4085
4086     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4087         !PLAYER_EXPLOSION_PROTECTED(x, y))
4088     {
4089       KillPlayerUnlessExplosionProtected(x, y);
4090       border_explosion = TRUE;
4091     }
4092     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4093     {
4094       Feld[x][y] = Store2[x][y];
4095       Store2[x][y] = 0;
4096       Bang(x, y);
4097       border_explosion = TRUE;
4098     }
4099     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4100     {
4101       AmoebeUmwandeln(x, y);
4102       Store2[x][y] = 0;
4103       border_explosion = TRUE;
4104     }
4105
4106     /* if an element just explodes due to another explosion (chain-reaction),
4107        do not immediately end the new explosion when it was the last frame of
4108        the explosion (as it would be done in the following "if"-statement!) */
4109     if (border_explosion && phase == last_phase)
4110       return;
4111   }
4112
4113   if (phase == last_phase)
4114   {
4115     int element;
4116
4117     element = Feld[x][y] = Store[x][y];
4118     Store[x][y] = Store2[x][y] = 0;
4119     GfxElement[x][y] = EL_UNDEFINED;
4120
4121     /* player can escape from explosions and might therefore be still alive */
4122     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4123         element <= EL_PLAYER_IS_EXPLODING_4)
4124     {
4125       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4126       int explosion_element = EL_PLAYER_1 + player_nr;
4127       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4128       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4129
4130       if (level.use_explosion_element[player_nr])
4131         explosion_element = level.explosion_element[player_nr];
4132
4133       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4134                     element_info[explosion_element].content.e[xx][yy]);
4135     }
4136
4137     /* restore probably existing indestructible background element */
4138     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4139       element = Feld[x][y] = Back[x][y];
4140     Back[x][y] = 0;
4141
4142     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4143     GfxDir[x][y] = MV_NONE;
4144     ChangeDelay[x][y] = 0;
4145     ChangePage[x][y] = -1;
4146
4147 #if USE_NEW_CUSTOM_VALUE
4148     CustomValue[x][y] = 0;
4149 #endif
4150
4151     InitField_WithBug2(x, y, FALSE);
4152
4153     DrawLevelField(x, y);
4154
4155     TestIfElementTouchesCustomElement(x, y);
4156
4157     if (GFX_CRUMBLED(element))
4158       DrawLevelFieldCrumbledSandNeighbours(x, y);
4159
4160     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4161       StorePlayer[x][y] = 0;
4162
4163     if (ELEM_IS_PLAYER(element))
4164       RelocatePlayer(x, y, element);
4165   }
4166   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4167   {
4168     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4169     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4170
4171     if (phase == delay)
4172       DrawLevelFieldCrumbledSand(x, y);
4173
4174     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4175     {
4176       DrawLevelElement(x, y, Back[x][y]);
4177       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4178     }
4179     else if (IS_WALKABLE_UNDER(Back[x][y]))
4180     {
4181       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4182       DrawLevelElementThruMask(x, y, Back[x][y]);
4183     }
4184     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4185       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4186   }
4187 }
4188
4189 void DynaExplode(int ex, int ey)
4190 {
4191   int i, j;
4192   int dynabomb_element = Feld[ex][ey];
4193   int dynabomb_size = 1;
4194   boolean dynabomb_xl = FALSE;
4195   struct PlayerInfo *player;
4196   static int xy[4][2] =
4197   {
4198     { 0, -1 },
4199     { -1, 0 },
4200     { +1, 0 },
4201     { 0, +1 }
4202   };
4203
4204   if (IS_ACTIVE_BOMB(dynabomb_element))
4205   {
4206     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4207     dynabomb_size = player->dynabomb_size;
4208     dynabomb_xl = player->dynabomb_xl;
4209     player->dynabombs_left++;
4210   }
4211
4212   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4213
4214   for (i = 0; i < NUM_DIRECTIONS; i++)
4215   {
4216     for (j = 1; j <= dynabomb_size; j++)
4217     {
4218       int x = ex + j * xy[i][0];
4219       int y = ey + j * xy[i][1];
4220       int element;
4221
4222       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4223         break;
4224
4225       element = Feld[x][y];
4226
4227       /* do not restart explosions of fields with active bombs */
4228       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4229         continue;
4230
4231       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4232
4233       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4234           !IS_DIGGABLE(element) && !dynabomb_xl)
4235         break;
4236     }
4237   }
4238 }
4239
4240 void Bang(int x, int y)
4241 {
4242   int element = MovingOrBlocked2Element(x, y);
4243   int explosion_type = EX_TYPE_NORMAL;
4244
4245   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4246   {
4247     struct PlayerInfo *player = PLAYERINFO(x, y);
4248
4249     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4250                             player->element_nr);
4251
4252     if (level.use_explosion_element[player->index_nr])
4253     {
4254       int explosion_element = level.explosion_element[player->index_nr];
4255
4256       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4257         explosion_type = EX_TYPE_CROSS;
4258       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4259         explosion_type = EX_TYPE_CENTER;
4260     }
4261   }
4262
4263   switch (element)
4264   {
4265     case EL_BUG:
4266     case EL_SPACESHIP:
4267     case EL_BD_BUTTERFLY:
4268     case EL_BD_FIREFLY:
4269     case EL_YAMYAM:
4270     case EL_DARK_YAMYAM:
4271     case EL_ROBOT:
4272     case EL_PACMAN:
4273     case EL_MOLE:
4274       RaiseScoreElement(element);
4275       break;
4276
4277     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4278     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4279     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4280     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4281     case EL_DYNABOMB_INCREASE_NUMBER:
4282     case EL_DYNABOMB_INCREASE_SIZE:
4283     case EL_DYNABOMB_INCREASE_POWER:
4284       explosion_type = EX_TYPE_DYNA;
4285       break;
4286
4287     case EL_DC_LANDMINE:
4288 #if 0
4289     case EL_EM_EXIT_OPEN:
4290     case EL_EM_STEEL_EXIT_OPEN:
4291 #endif
4292       explosion_type = EX_TYPE_CENTER;
4293       break;
4294
4295     case EL_PENGUIN:
4296     case EL_LAMP:
4297     case EL_LAMP_ACTIVE:
4298     case EL_AMOEBA_TO_DIAMOND:
4299       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4300         explosion_type = EX_TYPE_CENTER;
4301       break;
4302
4303     default:
4304       if (element_info[element].explosion_type == EXPLODES_CROSS)
4305         explosion_type = EX_TYPE_CROSS;
4306       else if (element_info[element].explosion_type == EXPLODES_1X1)
4307         explosion_type = EX_TYPE_CENTER;
4308       break;
4309   }
4310
4311   if (explosion_type == EX_TYPE_DYNA)
4312     DynaExplode(x, y);
4313   else
4314     Explode(x, y, EX_PHASE_START, explosion_type);
4315
4316   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4317 }
4318
4319 void SplashAcid(int x, int y)
4320 {
4321   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4322       (!IN_LEV_FIELD(x - 1, y - 2) ||
4323        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4324     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4325
4326   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4327       (!IN_LEV_FIELD(x + 1, y - 2) ||
4328        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4329     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4330
4331   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4332 }
4333
4334 static void InitBeltMovement()
4335 {
4336   static int belt_base_element[4] =
4337   {
4338     EL_CONVEYOR_BELT_1_LEFT,
4339     EL_CONVEYOR_BELT_2_LEFT,
4340     EL_CONVEYOR_BELT_3_LEFT,
4341     EL_CONVEYOR_BELT_4_LEFT
4342   };
4343   static int belt_base_active_element[4] =
4344   {
4345     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4346     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4347     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4348     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4349   };
4350
4351   int x, y, i, j;
4352
4353   /* set frame order for belt animation graphic according to belt direction */
4354   for (i = 0; i < NUM_BELTS; i++)
4355   {
4356     int belt_nr = i;
4357
4358     for (j = 0; j < NUM_BELT_PARTS; j++)
4359     {
4360       int element = belt_base_active_element[belt_nr] + j;
4361       int graphic = el2img(element);
4362
4363       if (game.belt_dir[i] == MV_LEFT)
4364         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4365       else
4366         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4367     }
4368   }
4369
4370   SCAN_PLAYFIELD(x, y)
4371   {
4372     int element = Feld[x][y];
4373
4374     for (i = 0; i < NUM_BELTS; i++)
4375     {
4376       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4377       {
4378         int e_belt_nr = getBeltNrFromBeltElement(element);
4379         int belt_nr = i;
4380
4381         if (e_belt_nr == belt_nr)
4382         {
4383           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4384
4385           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4386         }
4387       }
4388     }
4389   }
4390 }
4391
4392 static void ToggleBeltSwitch(int x, int y)
4393 {
4394   static int belt_base_element[4] =
4395   {
4396     EL_CONVEYOR_BELT_1_LEFT,
4397     EL_CONVEYOR_BELT_2_LEFT,
4398     EL_CONVEYOR_BELT_3_LEFT,
4399     EL_CONVEYOR_BELT_4_LEFT
4400   };
4401   static int belt_base_active_element[4] =
4402   {
4403     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4404     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4405     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4406     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4407   };
4408   static int belt_base_switch_element[4] =
4409   {
4410     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4411     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4412     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4413     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4414   };
4415   static int belt_move_dir[4] =
4416   {
4417     MV_LEFT,
4418     MV_NONE,
4419     MV_RIGHT,
4420     MV_NONE,
4421   };
4422
4423   int element = Feld[x][y];
4424   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4425   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4426   int belt_dir = belt_move_dir[belt_dir_nr];
4427   int xx, yy, i;
4428
4429   if (!IS_BELT_SWITCH(element))
4430     return;
4431
4432   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4433   game.belt_dir[belt_nr] = belt_dir;
4434
4435   if (belt_dir_nr == 3)
4436     belt_dir_nr = 1;
4437
4438   /* set frame order for belt animation graphic according to belt direction */
4439   for (i = 0; i < NUM_BELT_PARTS; i++)
4440   {
4441     int element = belt_base_active_element[belt_nr] + i;
4442     int graphic = el2img(element);
4443
4444     if (belt_dir == MV_LEFT)
4445       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4446     else
4447       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4448   }
4449
4450   SCAN_PLAYFIELD(xx, yy)
4451   {
4452     int element = Feld[xx][yy];
4453
4454     if (IS_BELT_SWITCH(element))
4455     {
4456       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4457
4458       if (e_belt_nr == belt_nr)
4459       {
4460         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4461         DrawLevelField(xx, yy);
4462       }
4463     }
4464     else if (IS_BELT(element) && belt_dir != MV_NONE)
4465     {
4466       int e_belt_nr = getBeltNrFromBeltElement(element);
4467
4468       if (e_belt_nr == belt_nr)
4469       {
4470         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4471
4472         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4473         DrawLevelField(xx, yy);
4474       }
4475     }
4476     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4477     {
4478       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4479
4480       if (e_belt_nr == belt_nr)
4481       {
4482         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4483
4484         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4485         DrawLevelField(xx, yy);
4486       }
4487     }
4488   }
4489 }
4490
4491 static void ToggleSwitchgateSwitch(int x, int y)
4492 {
4493   int xx, yy;
4494
4495   game.switchgate_pos = !game.switchgate_pos;
4496
4497   SCAN_PLAYFIELD(xx, yy)
4498   {
4499     int element = Feld[xx][yy];
4500
4501 #if !USE_BOTH_SWITCHGATE_SWITCHES
4502     if (element == EL_SWITCHGATE_SWITCH_UP ||
4503         element == EL_SWITCHGATE_SWITCH_DOWN)
4504     {
4505       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4506       DrawLevelField(xx, yy);
4507     }
4508     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
4509              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4510     {
4511       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4512       DrawLevelField(xx, yy);
4513     }
4514 #else
4515     if (element == EL_SWITCHGATE_SWITCH_UP)
4516     {
4517       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4518       DrawLevelField(xx, yy);
4519     }
4520     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4521     {
4522       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4523       DrawLevelField(xx, yy);
4524     }
4525     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
4526     {
4527       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
4528       DrawLevelField(xx, yy);
4529     }
4530     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
4531     {
4532       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
4533       DrawLevelField(xx, yy);
4534     }
4535 #endif
4536     else if (element == EL_SWITCHGATE_OPEN ||
4537              element == EL_SWITCHGATE_OPENING)
4538     {
4539       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4540
4541       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4542     }
4543     else if (element == EL_SWITCHGATE_CLOSED ||
4544              element == EL_SWITCHGATE_CLOSING)
4545     {
4546       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4547
4548       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4549     }
4550   }
4551 }
4552
4553 static int getInvisibleActiveFromInvisibleElement(int element)
4554 {
4555   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4556           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
4557           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
4558           element);
4559 }
4560
4561 static int getInvisibleFromInvisibleActiveElement(int element)
4562 {
4563   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4564           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
4565           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
4566           element);
4567 }
4568
4569 static void RedrawAllLightSwitchesAndInvisibleElements()
4570 {
4571   int x, y;
4572
4573   SCAN_PLAYFIELD(x, y)
4574   {
4575     int element = Feld[x][y];
4576
4577     if (element == EL_LIGHT_SWITCH &&
4578         game.light_time_left > 0)
4579     {
4580       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4581       DrawLevelField(x, y);
4582     }
4583     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4584              game.light_time_left == 0)
4585     {
4586       Feld[x][y] = EL_LIGHT_SWITCH;
4587       DrawLevelField(x, y);
4588     }
4589     else if (element == EL_EMC_DRIPPER &&
4590              game.light_time_left > 0)
4591     {
4592       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4593       DrawLevelField(x, y);
4594     }
4595     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4596              game.light_time_left == 0)
4597     {
4598       Feld[x][y] = EL_EMC_DRIPPER;
4599       DrawLevelField(x, y);
4600     }
4601     else if (element == EL_INVISIBLE_STEELWALL ||
4602              element == EL_INVISIBLE_WALL ||
4603              element == EL_INVISIBLE_SAND)
4604     {
4605       if (game.light_time_left > 0)
4606         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4607
4608       DrawLevelField(x, y);
4609
4610       /* uncrumble neighbour fields, if needed */
4611       if (element == EL_INVISIBLE_SAND)
4612         DrawLevelFieldCrumbledSandNeighbours(x, y);
4613     }
4614     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4615              element == EL_INVISIBLE_WALL_ACTIVE ||
4616              element == EL_INVISIBLE_SAND_ACTIVE)
4617     {
4618       if (game.light_time_left == 0)
4619         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4620
4621       DrawLevelField(x, y);
4622
4623       /* re-crumble neighbour fields, if needed */
4624       if (element == EL_INVISIBLE_SAND)
4625         DrawLevelFieldCrumbledSandNeighbours(x, y);
4626     }
4627   }
4628 }
4629
4630 static void RedrawAllInvisibleElementsForLenses()
4631 {
4632   int x, y;
4633
4634   SCAN_PLAYFIELD(x, y)
4635   {
4636     int element = Feld[x][y];
4637
4638     if (element == EL_EMC_DRIPPER &&
4639         game.lenses_time_left > 0)
4640     {
4641       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4642       DrawLevelField(x, y);
4643     }
4644     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4645              game.lenses_time_left == 0)
4646     {
4647       Feld[x][y] = EL_EMC_DRIPPER;
4648       DrawLevelField(x, y);
4649     }
4650     else if (element == EL_INVISIBLE_STEELWALL ||
4651              element == EL_INVISIBLE_WALL ||
4652              element == EL_INVISIBLE_SAND)
4653     {
4654       if (game.lenses_time_left > 0)
4655         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4656
4657       DrawLevelField(x, y);
4658
4659       /* uncrumble neighbour fields, if needed */
4660       if (element == EL_INVISIBLE_SAND)
4661         DrawLevelFieldCrumbledSandNeighbours(x, y);
4662     }
4663     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4664              element == EL_INVISIBLE_WALL_ACTIVE ||
4665              element == EL_INVISIBLE_SAND_ACTIVE)
4666     {
4667       if (game.lenses_time_left == 0)
4668         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4669
4670       DrawLevelField(x, y);
4671
4672       /* re-crumble neighbour fields, if needed */
4673       if (element == EL_INVISIBLE_SAND)
4674         DrawLevelFieldCrumbledSandNeighbours(x, y);
4675     }
4676   }
4677 }
4678
4679 static void RedrawAllInvisibleElementsForMagnifier()
4680 {
4681   int x, y;
4682
4683   SCAN_PLAYFIELD(x, y)
4684   {
4685     int element = Feld[x][y];
4686
4687     if (element == EL_EMC_FAKE_GRASS &&
4688         game.magnify_time_left > 0)
4689     {
4690       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4691       DrawLevelField(x, y);
4692     }
4693     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4694              game.magnify_time_left == 0)
4695     {
4696       Feld[x][y] = EL_EMC_FAKE_GRASS;
4697       DrawLevelField(x, y);
4698     }
4699     else if (IS_GATE_GRAY(element) &&
4700              game.magnify_time_left > 0)
4701     {
4702       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4703                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4704                     IS_EM_GATE_GRAY(element) ?
4705                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4706                     IS_EMC_GATE_GRAY(element) ?
4707                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4708                     element);
4709       DrawLevelField(x, y);
4710     }
4711     else if (IS_GATE_GRAY_ACTIVE(element) &&
4712              game.magnify_time_left == 0)
4713     {
4714       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4715                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4716                     IS_EM_GATE_GRAY_ACTIVE(element) ?
4717                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4718                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
4719                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4720                     element);
4721       DrawLevelField(x, y);
4722     }
4723   }
4724 }
4725
4726 static void ToggleLightSwitch(int x, int y)
4727 {
4728   int element = Feld[x][y];
4729
4730   game.light_time_left =
4731     (element == EL_LIGHT_SWITCH ?
4732      level.time_light * FRAMES_PER_SECOND : 0);
4733
4734   RedrawAllLightSwitchesAndInvisibleElements();
4735 }
4736
4737 static void ActivateTimegateSwitch(int x, int y)
4738 {
4739   int xx, yy;
4740
4741   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4742
4743   SCAN_PLAYFIELD(xx, yy)
4744   {
4745     int element = Feld[xx][yy];
4746
4747     if (element == EL_TIMEGATE_CLOSED ||
4748         element == EL_TIMEGATE_CLOSING)
4749     {
4750       Feld[xx][yy] = EL_TIMEGATE_OPENING;
4751       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
4752     }
4753
4754     /*
4755     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4756     {
4757       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4758       DrawLevelField(xx, yy);
4759     }
4760     */
4761
4762   }
4763
4764 #if 1
4765   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
4766                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
4767 #else
4768   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4769 #endif
4770 }
4771
4772 void Impact(int x, int y)
4773 {
4774   boolean last_line = (y == lev_fieldy - 1);
4775   boolean object_hit = FALSE;
4776   boolean impact = (last_line || object_hit);
4777   int element = Feld[x][y];
4778   int smashed = EL_STEELWALL;
4779
4780   if (!last_line)       /* check if element below was hit */
4781   {
4782     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4783       return;
4784
4785     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4786                                          MovDir[x][y + 1] != MV_DOWN ||
4787                                          MovPos[x][y + 1] <= TILEY / 2));
4788
4789     /* do not smash moving elements that left the smashed field in time */
4790     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4791         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4792       object_hit = FALSE;
4793
4794 #if USE_QUICKSAND_IMPACT_BUGFIX
4795     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4796     {
4797       RemoveMovingField(x, y + 1);
4798       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4799       Feld[x][y + 2] = EL_ROCK;
4800       DrawLevelField(x, y + 2);
4801
4802       object_hit = TRUE;
4803     }
4804
4805     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
4806     {
4807       RemoveMovingField(x, y + 1);
4808       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
4809       Feld[x][y + 2] = EL_ROCK;
4810       DrawLevelField(x, y + 2);
4811
4812       object_hit = TRUE;
4813     }
4814 #endif
4815
4816     if (object_hit)
4817       smashed = MovingOrBlocked2Element(x, y + 1);
4818
4819     impact = (last_line || object_hit);
4820   }
4821
4822   if (!last_line && smashed == EL_ACID) /* element falls into acid */
4823   {
4824     SplashAcid(x, y + 1);
4825     return;
4826   }
4827
4828   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4829   /* only reset graphic animation if graphic really changes after impact */
4830   if (impact &&
4831       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4832   {
4833     ResetGfxAnimation(x, y);
4834     DrawLevelField(x, y);
4835   }
4836
4837   if (impact && CAN_EXPLODE_IMPACT(element))
4838   {
4839     Bang(x, y);
4840     return;
4841   }
4842   else if (impact && element == EL_PEARL &&
4843            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
4844   {
4845     ResetGfxAnimation(x, y);
4846
4847     Feld[x][y] = EL_PEARL_BREAKING;
4848     PlayLevelSound(x, y, SND_PEARL_BREAKING);
4849     return;
4850   }
4851   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4852   {
4853     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4854
4855     return;
4856   }
4857
4858   if (impact && element == EL_AMOEBA_DROP)
4859   {
4860     if (object_hit && IS_PLAYER(x, y + 1))
4861       KillPlayerUnlessEnemyProtected(x, y + 1);
4862     else if (object_hit && smashed == EL_PENGUIN)
4863       Bang(x, y + 1);
4864     else
4865     {
4866       Feld[x][y] = EL_AMOEBA_GROWING;
4867       Store[x][y] = EL_AMOEBA_WET;
4868
4869       ResetRandomAnimationValue(x, y);
4870     }
4871     return;
4872   }
4873
4874   if (object_hit)               /* check which object was hit */
4875   {
4876     if ((CAN_PASS_MAGIC_WALL(element) && 
4877          (smashed == EL_MAGIC_WALL ||
4878           smashed == EL_BD_MAGIC_WALL)) ||
4879         (CAN_PASS_DC_MAGIC_WALL(element) &&
4880          smashed == EL_DC_MAGIC_WALL))
4881     {
4882       int xx, yy;
4883       int activated_magic_wall =
4884         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4885          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
4886          EL_DC_MAGIC_WALL_ACTIVE);
4887
4888       /* activate magic wall / mill */
4889       SCAN_PLAYFIELD(xx, yy)
4890       {
4891         if (Feld[xx][yy] == smashed)
4892           Feld[xx][yy] = activated_magic_wall;
4893       }
4894
4895       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4896       game.magic_wall_active = TRUE;
4897
4898       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4899                             SND_MAGIC_WALL_ACTIVATING :
4900                             smashed == EL_BD_MAGIC_WALL ?
4901                             SND_BD_MAGIC_WALL_ACTIVATING :
4902                             SND_DC_MAGIC_WALL_ACTIVATING));
4903     }
4904
4905     if (IS_PLAYER(x, y + 1))
4906     {
4907       if (CAN_SMASH_PLAYER(element))
4908       {
4909         KillPlayerUnlessEnemyProtected(x, y + 1);
4910         return;
4911       }
4912     }
4913     else if (smashed == EL_PENGUIN)
4914     {
4915       if (CAN_SMASH_PLAYER(element))
4916       {
4917         Bang(x, y + 1);
4918         return;
4919       }
4920     }
4921     else if (element == EL_BD_DIAMOND)
4922     {
4923       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4924       {
4925         Bang(x, y + 1);
4926         return;
4927       }
4928     }
4929     else if (((element == EL_SP_INFOTRON ||
4930                element == EL_SP_ZONK) &&
4931               (smashed == EL_SP_SNIKSNAK ||
4932                smashed == EL_SP_ELECTRON ||
4933                smashed == EL_SP_DISK_ORANGE)) ||
4934              (element == EL_SP_INFOTRON &&
4935               smashed == EL_SP_DISK_YELLOW))
4936     {
4937       Bang(x, y + 1);
4938       return;
4939     }
4940     else if (CAN_SMASH_EVERYTHING(element))
4941     {
4942       if (IS_CLASSIC_ENEMY(smashed) ||
4943           CAN_EXPLODE_SMASHED(smashed))
4944       {
4945         Bang(x, y + 1);
4946         return;
4947       }
4948       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4949       {
4950         if (smashed == EL_LAMP ||
4951             smashed == EL_LAMP_ACTIVE)
4952         {
4953           Bang(x, y + 1);
4954           return;
4955         }
4956         else if (smashed == EL_NUT)
4957         {
4958           Feld[x][y + 1] = EL_NUT_BREAKING;
4959           PlayLevelSound(x, y, SND_NUT_BREAKING);
4960           RaiseScoreElement(EL_NUT);
4961           return;
4962         }
4963         else if (smashed == EL_PEARL)
4964         {
4965           ResetGfxAnimation(x, y);
4966
4967           Feld[x][y + 1] = EL_PEARL_BREAKING;
4968           PlayLevelSound(x, y, SND_PEARL_BREAKING);
4969           return;
4970         }
4971         else if (smashed == EL_DIAMOND)
4972         {
4973           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4974           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4975           return;
4976         }
4977         else if (IS_BELT_SWITCH(smashed))
4978         {
4979           ToggleBeltSwitch(x, y + 1);
4980         }
4981         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4982                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
4983                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
4984                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
4985         {
4986           ToggleSwitchgateSwitch(x, y + 1);
4987         }
4988         else if (smashed == EL_LIGHT_SWITCH ||
4989                  smashed == EL_LIGHT_SWITCH_ACTIVE)
4990         {
4991           ToggleLightSwitch(x, y + 1);
4992         }
4993         else
4994         {
4995 #if 0
4996           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4997 #endif
4998
4999           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5000
5001           CheckElementChangeBySide(x, y + 1, smashed, element,
5002                                    CE_SWITCHED, CH_SIDE_TOP);
5003           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5004                                             CH_SIDE_TOP);
5005         }
5006       }
5007       else
5008       {
5009         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5010       }
5011     }
5012   }
5013
5014   /* play sound of magic wall / mill */
5015   if (!last_line &&
5016       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5017        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
5018        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
5019   {
5020     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5021       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5022     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5023       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5024     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
5025       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
5026
5027     return;
5028   }
5029
5030   /* play sound of object that hits the ground */
5031   if (last_line || object_hit)
5032     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5033 }
5034
5035 inline static void TurnRoundExt(int x, int y)
5036 {
5037   static struct
5038   {
5039     int dx, dy;
5040   } move_xy[] =
5041   {
5042     {  0,  0 },
5043     { -1,  0 },
5044     { +1,  0 },
5045     {  0,  0 },
5046     {  0, -1 },
5047     {  0,  0 }, { 0, 0 }, { 0, 0 },
5048     {  0, +1 }
5049   };
5050   static struct
5051   {
5052     int left, right, back;
5053   } turn[] =
5054   {
5055     { 0,        0,              0        },
5056     { MV_DOWN,  MV_UP,          MV_RIGHT },
5057     { MV_UP,    MV_DOWN,        MV_LEFT  },
5058     { 0,        0,              0        },
5059     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5060     { 0,        0,              0        },
5061     { 0,        0,              0        },
5062     { 0,        0,              0        },
5063     { MV_RIGHT, MV_LEFT,        MV_UP    }
5064   };
5065
5066   int element = Feld[x][y];
5067   int move_pattern = element_info[element].move_pattern;
5068
5069   int old_move_dir = MovDir[x][y];
5070   int left_dir  = turn[old_move_dir].left;
5071   int right_dir = turn[old_move_dir].right;
5072   int back_dir  = turn[old_move_dir].back;
5073
5074   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5075   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5076   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5077   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5078
5079   int left_x  = x + left_dx,  left_y  = y + left_dy;
5080   int right_x = x + right_dx, right_y = y + right_dy;
5081   int move_x  = x + move_dx,  move_y  = y + move_dy;
5082
5083   int xx, yy;
5084
5085   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5086   {
5087     TestIfBadThingTouchesOtherBadThing(x, y);
5088
5089     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5090       MovDir[x][y] = right_dir;
5091     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5092       MovDir[x][y] = left_dir;
5093
5094     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5095       MovDelay[x][y] = 9;
5096     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5097       MovDelay[x][y] = 1;
5098   }
5099   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5100   {
5101     TestIfBadThingTouchesOtherBadThing(x, y);
5102
5103     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5104       MovDir[x][y] = left_dir;
5105     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5106       MovDir[x][y] = right_dir;
5107
5108     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5109       MovDelay[x][y] = 9;
5110     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5111       MovDelay[x][y] = 1;
5112   }
5113   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5114   {
5115     TestIfBadThingTouchesOtherBadThing(x, y);
5116
5117     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5118       MovDir[x][y] = left_dir;
5119     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5120       MovDir[x][y] = right_dir;
5121
5122     if (MovDir[x][y] != old_move_dir)
5123       MovDelay[x][y] = 9;
5124   }
5125   else if (element == EL_YAMYAM)
5126   {
5127     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5128     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5129
5130     if (can_turn_left && can_turn_right)
5131       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5132     else if (can_turn_left)
5133       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5134     else if (can_turn_right)
5135       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5136     else
5137       MovDir[x][y] = back_dir;
5138
5139     MovDelay[x][y] = 16 + 16 * RND(3);
5140   }
5141   else if (element == EL_DARK_YAMYAM)
5142   {
5143     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5144                                                          left_x, left_y);
5145     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5146                                                          right_x, right_y);
5147
5148     if (can_turn_left && can_turn_right)
5149       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5150     else if (can_turn_left)
5151       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5152     else if (can_turn_right)
5153       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5154     else
5155       MovDir[x][y] = back_dir;
5156
5157     MovDelay[x][y] = 16 + 16 * RND(3);
5158   }
5159   else if (element == EL_PACMAN)
5160   {
5161     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5162     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5163
5164     if (can_turn_left && can_turn_right)
5165       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5166     else if (can_turn_left)
5167       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5168     else if (can_turn_right)
5169       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5170     else
5171       MovDir[x][y] = back_dir;
5172
5173     MovDelay[x][y] = 6 + RND(40);
5174   }
5175   else if (element == EL_PIG)
5176   {
5177     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5178     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5179     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5180     boolean should_turn_left, should_turn_right, should_move_on;
5181     int rnd_value = 24;
5182     int rnd = RND(rnd_value);
5183
5184     should_turn_left = (can_turn_left &&
5185                         (!can_move_on ||
5186                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5187                                                    y + back_dy + left_dy)));
5188     should_turn_right = (can_turn_right &&
5189                          (!can_move_on ||
5190                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5191                                                     y + back_dy + right_dy)));
5192     should_move_on = (can_move_on &&
5193                       (!can_turn_left ||
5194                        !can_turn_right ||
5195                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5196                                                  y + move_dy + left_dy) ||
5197                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5198                                                  y + move_dy + right_dy)));
5199
5200     if (should_turn_left || should_turn_right || should_move_on)
5201     {
5202       if (should_turn_left && should_turn_right && should_move_on)
5203         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5204                         rnd < 2 * rnd_value / 3 ? right_dir :
5205                         old_move_dir);
5206       else if (should_turn_left && should_turn_right)
5207         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5208       else if (should_turn_left && should_move_on)
5209         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5210       else if (should_turn_right && should_move_on)
5211         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5212       else if (should_turn_left)
5213         MovDir[x][y] = left_dir;
5214       else if (should_turn_right)
5215         MovDir[x][y] = right_dir;
5216       else if (should_move_on)
5217         MovDir[x][y] = old_move_dir;
5218     }
5219     else if (can_move_on && rnd > rnd_value / 8)
5220       MovDir[x][y] = old_move_dir;
5221     else if (can_turn_left && can_turn_right)
5222       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5223     else if (can_turn_left && rnd > rnd_value / 8)
5224       MovDir[x][y] = left_dir;
5225     else if (can_turn_right && rnd > rnd_value/8)
5226       MovDir[x][y] = right_dir;
5227     else
5228       MovDir[x][y] = back_dir;
5229
5230     xx = x + move_xy[MovDir[x][y]].dx;
5231     yy = y + move_xy[MovDir[x][y]].dy;
5232
5233     if (!IN_LEV_FIELD(xx, yy) ||
5234         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5235       MovDir[x][y] = old_move_dir;
5236
5237     MovDelay[x][y] = 0;
5238   }
5239   else if (element == EL_DRAGON)
5240   {
5241     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5242     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5243     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5244     int rnd_value = 24;
5245     int rnd = RND(rnd_value);
5246
5247     if (can_move_on && rnd > rnd_value / 8)
5248       MovDir[x][y] = old_move_dir;
5249     else if (can_turn_left && can_turn_right)
5250       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5251     else if (can_turn_left && rnd > rnd_value / 8)
5252       MovDir[x][y] = left_dir;
5253     else if (can_turn_right && rnd > rnd_value / 8)
5254       MovDir[x][y] = right_dir;
5255     else
5256       MovDir[x][y] = back_dir;
5257
5258     xx = x + move_xy[MovDir[x][y]].dx;
5259     yy = y + move_xy[MovDir[x][y]].dy;
5260
5261     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5262       MovDir[x][y] = old_move_dir;
5263
5264     MovDelay[x][y] = 0;
5265   }
5266   else if (element == EL_MOLE)
5267   {
5268     boolean can_move_on =
5269       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5270                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5271                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5272     if (!can_move_on)
5273     {
5274       boolean can_turn_left =
5275         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5276                               IS_AMOEBOID(Feld[left_x][left_y])));
5277
5278       boolean can_turn_right =
5279         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5280                               IS_AMOEBOID(Feld[right_x][right_y])));
5281
5282       if (can_turn_left && can_turn_right)
5283         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5284       else if (can_turn_left)
5285         MovDir[x][y] = left_dir;
5286       else
5287         MovDir[x][y] = right_dir;
5288     }
5289
5290     if (MovDir[x][y] != old_move_dir)
5291       MovDelay[x][y] = 9;
5292   }
5293   else if (element == EL_BALLOON)
5294   {
5295     MovDir[x][y] = game.wind_direction;
5296     MovDelay[x][y] = 0;
5297   }
5298   else if (element == EL_SPRING)
5299   {
5300 #if USE_NEW_SPRING_BUMPER
5301     if (MovDir[x][y] & MV_HORIZONTAL)
5302     {
5303       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5304           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5305       {
5306         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5307         ResetGfxAnimation(move_x, move_y);
5308         DrawLevelField(move_x, move_y);
5309
5310         MovDir[x][y] = back_dir;
5311       }
5312       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5313                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5314         MovDir[x][y] = MV_NONE;
5315     }
5316 #else
5317     if (MovDir[x][y] & MV_HORIZONTAL &&
5318         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5319          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5320       MovDir[x][y] = MV_NONE;
5321 #endif
5322
5323     MovDelay[x][y] = 0;
5324   }
5325   else if (element == EL_ROBOT ||
5326            element == EL_SATELLITE ||
5327            element == EL_PENGUIN ||
5328            element == EL_EMC_ANDROID)
5329   {
5330     int attr_x = -1, attr_y = -1;
5331
5332     if (AllPlayersGone)
5333     {
5334       attr_x = ExitX;
5335       attr_y = ExitY;
5336     }
5337     else
5338     {
5339       int i;
5340
5341       for (i = 0; i < MAX_PLAYERS; i++)
5342       {
5343         struct PlayerInfo *player = &stored_player[i];
5344         int jx = player->jx, jy = player->jy;
5345
5346         if (!player->active)
5347           continue;
5348
5349         if (attr_x == -1 ||
5350             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5351         {
5352           attr_x = jx;
5353           attr_y = jy;
5354         }
5355       }
5356     }
5357
5358     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5359         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5360          game.engine_version < VERSION_IDENT(3,1,0,0)))
5361     {
5362       attr_x = ZX;
5363       attr_y = ZY;
5364     }
5365
5366     if (element == EL_PENGUIN)
5367     {
5368       int i;
5369       static int xy[4][2] =
5370       {
5371         { 0, -1 },
5372         { -1, 0 },
5373         { +1, 0 },
5374         { 0, +1 }
5375       };
5376
5377       for (i = 0; i < NUM_DIRECTIONS; i++)
5378       {
5379         int ex = x + xy[i][0];
5380         int ey = y + xy[i][1];
5381
5382         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
5383                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
5384                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
5385                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
5386         {
5387           attr_x = ex;
5388           attr_y = ey;
5389           break;
5390         }
5391       }
5392     }
5393
5394     MovDir[x][y] = MV_NONE;
5395     if (attr_x < x)
5396       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5397     else if (attr_x > x)
5398       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5399     if (attr_y < y)
5400       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5401     else if (attr_y > y)
5402       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5403
5404     if (element == EL_ROBOT)
5405     {
5406       int newx, newy;
5407
5408       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5409         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5410       Moving2Blocked(x, y, &newx, &newy);
5411
5412       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5413         MovDelay[x][y] = 8 + 8 * !RND(3);
5414       else
5415         MovDelay[x][y] = 16;
5416     }
5417     else if (element == EL_PENGUIN)
5418     {
5419       int newx, newy;
5420
5421       MovDelay[x][y] = 1;
5422
5423       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5424       {
5425         boolean first_horiz = RND(2);
5426         int new_move_dir = MovDir[x][y];
5427
5428         MovDir[x][y] =
5429           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5430         Moving2Blocked(x, y, &newx, &newy);
5431
5432         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5433           return;
5434
5435         MovDir[x][y] =
5436           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5437         Moving2Blocked(x, y, &newx, &newy);
5438
5439         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5440           return;
5441
5442         MovDir[x][y] = old_move_dir;
5443         return;
5444       }
5445     }
5446     else if (element == EL_SATELLITE)
5447     {
5448       int newx, newy;
5449
5450       MovDelay[x][y] = 1;
5451
5452       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5453       {
5454         boolean first_horiz = RND(2);
5455         int new_move_dir = MovDir[x][y];
5456
5457         MovDir[x][y] =
5458           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5459         Moving2Blocked(x, y, &newx, &newy);
5460
5461         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5462           return;
5463
5464         MovDir[x][y] =
5465           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5466         Moving2Blocked(x, y, &newx, &newy);
5467
5468         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5469           return;
5470
5471         MovDir[x][y] = old_move_dir;
5472         return;
5473       }
5474     }
5475     else if (element == EL_EMC_ANDROID)
5476     {
5477       static int check_pos[16] =
5478       {
5479         -1,             /*  0 => (invalid)          */
5480         7,              /*  1 => MV_LEFT            */
5481         3,              /*  2 => MV_RIGHT           */
5482         -1,             /*  3 => (invalid)          */
5483         1,              /*  4 =>            MV_UP   */
5484         0,              /*  5 => MV_LEFT  | MV_UP   */
5485         2,              /*  6 => MV_RIGHT | MV_UP   */
5486         -1,             /*  7 => (invalid)          */
5487         5,              /*  8 =>            MV_DOWN */
5488         6,              /*  9 => MV_LEFT  | MV_DOWN */
5489         4,              /* 10 => MV_RIGHT | MV_DOWN */
5490         -1,             /* 11 => (invalid)          */
5491         -1,             /* 12 => (invalid)          */
5492         -1,             /* 13 => (invalid)          */
5493         -1,             /* 14 => (invalid)          */
5494         -1,             /* 15 => (invalid)          */
5495       };
5496       static struct
5497       {
5498         int dx, dy;
5499         int dir;
5500       } check_xy[8] =
5501       {
5502         { -1, -1,       MV_LEFT  | MV_UP   },
5503         {  0, -1,                  MV_UP   },
5504         { +1, -1,       MV_RIGHT | MV_UP   },
5505         { +1,  0,       MV_RIGHT           },
5506         { +1, +1,       MV_RIGHT | MV_DOWN },
5507         {  0, +1,                  MV_DOWN },
5508         { -1, +1,       MV_LEFT  | MV_DOWN },
5509         { -1,  0,       MV_LEFT            },
5510       };
5511       int start_pos, check_order;
5512       boolean can_clone = FALSE;
5513       int i;
5514
5515       /* check if there is any free field around current position */
5516       for (i = 0; i < 8; i++)
5517       {
5518         int newx = x + check_xy[i].dx;
5519         int newy = y + check_xy[i].dy;
5520
5521         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5522         {
5523           can_clone = TRUE;
5524
5525           break;
5526         }
5527       }
5528
5529       if (can_clone)            /* randomly find an element to clone */
5530       {
5531         can_clone = FALSE;
5532
5533         start_pos = check_pos[RND(8)];
5534         check_order = (RND(2) ? -1 : +1);
5535
5536         for (i = 0; i < 8; i++)
5537         {
5538           int pos_raw = start_pos + i * check_order;
5539           int pos = (pos_raw + 8) % 8;
5540           int newx = x + check_xy[pos].dx;
5541           int newy = y + check_xy[pos].dy;
5542
5543           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5544           {
5545             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5546             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5547
5548             Store[x][y] = Feld[newx][newy];
5549
5550             can_clone = TRUE;
5551
5552             break;
5553           }
5554         }
5555       }
5556
5557       if (can_clone)            /* randomly find a direction to move */
5558       {
5559         can_clone = FALSE;
5560
5561         start_pos = check_pos[RND(8)];
5562         check_order = (RND(2) ? -1 : +1);
5563
5564         for (i = 0; i < 8; i++)
5565         {
5566           int pos_raw = start_pos + i * check_order;
5567           int pos = (pos_raw + 8) % 8;
5568           int newx = x + check_xy[pos].dx;
5569           int newy = y + check_xy[pos].dy;
5570           int new_move_dir = check_xy[pos].dir;
5571
5572           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5573           {
5574             MovDir[x][y] = new_move_dir;
5575             MovDelay[x][y] = level.android_clone_time * 8 + 1;
5576
5577             can_clone = TRUE;
5578
5579             break;
5580           }
5581         }
5582       }
5583
5584       if (can_clone)            /* cloning and moving successful */
5585         return;
5586
5587       /* cannot clone -- try to move towards player */
5588
5589       start_pos = check_pos[MovDir[x][y] & 0x0f];
5590       check_order = (RND(2) ? -1 : +1);
5591
5592       for (i = 0; i < 3; i++)
5593       {
5594         /* first check start_pos, then previous/next or (next/previous) pos */
5595         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5596         int pos = (pos_raw + 8) % 8;
5597         int newx = x + check_xy[pos].dx;
5598         int newy = y + check_xy[pos].dy;
5599         int new_move_dir = check_xy[pos].dir;
5600
5601         if (IS_PLAYER(newx, newy))
5602           break;
5603
5604         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5605         {
5606           MovDir[x][y] = new_move_dir;
5607           MovDelay[x][y] = level.android_move_time * 8 + 1;
5608
5609           break;
5610         }
5611       }
5612     }
5613   }
5614   else if (move_pattern == MV_TURNING_LEFT ||
5615            move_pattern == MV_TURNING_RIGHT ||
5616            move_pattern == MV_TURNING_LEFT_RIGHT ||
5617            move_pattern == MV_TURNING_RIGHT_LEFT ||
5618            move_pattern == MV_TURNING_RANDOM ||
5619            move_pattern == MV_ALL_DIRECTIONS)
5620   {
5621     boolean can_turn_left =
5622       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5623     boolean can_turn_right =
5624       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5625
5626     if (element_info[element].move_stepsize == 0)       /* "not moving" */
5627       return;
5628
5629     if (move_pattern == MV_TURNING_LEFT)
5630       MovDir[x][y] = left_dir;
5631     else if (move_pattern == MV_TURNING_RIGHT)
5632       MovDir[x][y] = right_dir;
5633     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5634       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5635     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5636       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5637     else if (move_pattern == MV_TURNING_RANDOM)
5638       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5639                       can_turn_right && !can_turn_left ? right_dir :
5640                       RND(2) ? left_dir : right_dir);
5641     else if (can_turn_left && can_turn_right)
5642       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5643     else if (can_turn_left)
5644       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5645     else if (can_turn_right)
5646       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5647     else
5648       MovDir[x][y] = back_dir;
5649
5650     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5651   }
5652   else if (move_pattern == MV_HORIZONTAL ||
5653            move_pattern == MV_VERTICAL)
5654   {
5655     if (move_pattern & old_move_dir)
5656       MovDir[x][y] = back_dir;
5657     else if (move_pattern == MV_HORIZONTAL)
5658       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5659     else if (move_pattern == MV_VERTICAL)
5660       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5661
5662     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5663   }
5664   else if (move_pattern & MV_ANY_DIRECTION)
5665   {
5666     MovDir[x][y] = move_pattern;
5667     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5668   }
5669   else if (move_pattern & MV_WIND_DIRECTION)
5670   {
5671     MovDir[x][y] = game.wind_direction;
5672     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5673   }
5674   else if (move_pattern == MV_ALONG_LEFT_SIDE)
5675   {
5676     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5677       MovDir[x][y] = left_dir;
5678     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5679       MovDir[x][y] = right_dir;
5680
5681     if (MovDir[x][y] != old_move_dir)
5682       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5683   }
5684   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5685   {
5686     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5687       MovDir[x][y] = right_dir;
5688     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5689       MovDir[x][y] = left_dir;
5690
5691     if (MovDir[x][y] != old_move_dir)
5692       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5693   }
5694   else if (move_pattern == MV_TOWARDS_PLAYER ||
5695            move_pattern == MV_AWAY_FROM_PLAYER)
5696   {
5697     int attr_x = -1, attr_y = -1;
5698     int newx, newy;
5699     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5700
5701     if (AllPlayersGone)
5702     {
5703       attr_x = ExitX;
5704       attr_y = ExitY;
5705     }
5706     else
5707     {
5708       int i;
5709
5710       for (i = 0; i < MAX_PLAYERS; i++)
5711       {
5712         struct PlayerInfo *player = &stored_player[i];
5713         int jx = player->jx, jy = player->jy;
5714
5715         if (!player->active)
5716           continue;
5717
5718         if (attr_x == -1 ||
5719             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5720         {
5721           attr_x = jx;
5722           attr_y = jy;
5723         }
5724       }
5725     }
5726
5727     MovDir[x][y] = MV_NONE;
5728     if (attr_x < x)
5729       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5730     else if (attr_x > x)
5731       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5732     if (attr_y < y)
5733       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5734     else if (attr_y > y)
5735       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5736
5737     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5738
5739     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5740     {
5741       boolean first_horiz = RND(2);
5742       int new_move_dir = MovDir[x][y];
5743
5744       if (element_info[element].move_stepsize == 0)     /* "not moving" */
5745       {
5746         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5747         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5748
5749         return;
5750       }
5751
5752       MovDir[x][y] =
5753         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5754       Moving2Blocked(x, y, &newx, &newy);
5755
5756       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5757         return;
5758
5759       MovDir[x][y] =
5760         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5761       Moving2Blocked(x, y, &newx, &newy);
5762
5763       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5764         return;
5765
5766       MovDir[x][y] = old_move_dir;
5767     }
5768   }
5769   else if (move_pattern == MV_WHEN_PUSHED ||
5770            move_pattern == MV_WHEN_DROPPED)
5771   {
5772     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5773       MovDir[x][y] = MV_NONE;
5774
5775     MovDelay[x][y] = 0;
5776   }
5777   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5778   {
5779     static int test_xy[7][2] =
5780     {
5781       { 0, -1 },
5782       { -1, 0 },
5783       { +1, 0 },
5784       { 0, +1 },
5785       { 0, -1 },
5786       { -1, 0 },
5787       { +1, 0 },
5788     };
5789     static int test_dir[7] =
5790     {
5791       MV_UP,
5792       MV_LEFT,
5793       MV_RIGHT,
5794       MV_DOWN,
5795       MV_UP,
5796       MV_LEFT,
5797       MV_RIGHT,
5798     };
5799     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5800     int move_preference = -1000000;     /* start with very low preference */
5801     int new_move_dir = MV_NONE;
5802     int start_test = RND(4);
5803     int i;
5804
5805     for (i = 0; i < NUM_DIRECTIONS; i++)
5806     {
5807       int move_dir = test_dir[start_test + i];
5808       int move_dir_preference;
5809
5810       xx = x + test_xy[start_test + i][0];
5811       yy = y + test_xy[start_test + i][1];
5812
5813       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5814           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5815       {
5816         new_move_dir = move_dir;
5817
5818         break;
5819       }
5820
5821       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5822         continue;
5823
5824       move_dir_preference = -1 * RunnerVisit[xx][yy];
5825       if (hunter_mode && PlayerVisit[xx][yy] > 0)
5826         move_dir_preference = PlayerVisit[xx][yy];
5827
5828       if (move_dir_preference > move_preference)
5829       {
5830         /* prefer field that has not been visited for the longest time */
5831         move_preference = move_dir_preference;
5832         new_move_dir = move_dir;
5833       }
5834       else if (move_dir_preference == move_preference &&
5835                move_dir == old_move_dir)
5836       {
5837         /* prefer last direction when all directions are preferred equally */
5838         move_preference = move_dir_preference;
5839         new_move_dir = move_dir;
5840       }
5841     }
5842
5843     MovDir[x][y] = new_move_dir;
5844     if (old_move_dir != new_move_dir)
5845       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5846   }
5847 }
5848
5849 static void TurnRound(int x, int y)
5850 {
5851   int direction = MovDir[x][y];
5852
5853   TurnRoundExt(x, y);
5854
5855   GfxDir[x][y] = MovDir[x][y];
5856
5857   if (direction != MovDir[x][y])
5858     GfxFrame[x][y] = 0;
5859
5860   if (MovDelay[x][y])
5861     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5862
5863   ResetGfxFrame(x, y, FALSE);
5864 }
5865
5866 static boolean JustBeingPushed(int x, int y)
5867 {
5868   int i;
5869
5870   for (i = 0; i < MAX_PLAYERS; i++)
5871   {
5872     struct PlayerInfo *player = &stored_player[i];
5873
5874     if (player->active && player->is_pushing && player->MovPos)
5875     {
5876       int next_jx = player->jx + (player->jx - player->last_jx);
5877       int next_jy = player->jy + (player->jy - player->last_jy);
5878
5879       if (x == next_jx && y == next_jy)
5880         return TRUE;
5881     }
5882   }
5883
5884   return FALSE;
5885 }
5886
5887 void StartMoving(int x, int y)
5888 {
5889   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
5890   int element = Feld[x][y];
5891
5892   if (Stop[x][y])
5893     return;
5894
5895   if (MovDelay[x][y] == 0)
5896     GfxAction[x][y] = ACTION_DEFAULT;
5897
5898   if (CAN_FALL(element) && y < lev_fieldy - 1)
5899   {
5900     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5901         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5902       if (JustBeingPushed(x, y))
5903         return;
5904
5905     if (element == EL_QUICKSAND_FULL)
5906     {
5907       if (IS_FREE(x, y + 1))
5908       {
5909         InitMovingField(x, y, MV_DOWN);
5910         started_moving = TRUE;
5911
5912         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5913 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5914         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5915           Store[x][y] = EL_ROCK;
5916 #else
5917         Store[x][y] = EL_ROCK;
5918 #endif
5919
5920         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5921       }
5922       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5923       {
5924         if (!MovDelay[x][y])
5925           MovDelay[x][y] = TILEY + 1;
5926
5927         if (MovDelay[x][y])
5928         {
5929           MovDelay[x][y]--;
5930           if (MovDelay[x][y])
5931             return;
5932         }
5933
5934         Feld[x][y] = EL_QUICKSAND_EMPTY;
5935         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5936         Store[x][y + 1] = Store[x][y];
5937         Store[x][y] = 0;
5938
5939         PlayLevelSoundAction(x, y, ACTION_FILLING);
5940       }
5941     }
5942     else if (element == EL_QUICKSAND_FAST_FULL)
5943     {
5944       if (IS_FREE(x, y + 1))
5945       {
5946         InitMovingField(x, y, MV_DOWN);
5947         started_moving = TRUE;
5948
5949         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
5950 #if USE_QUICKSAND_BD_ROCK_BUGFIX
5951         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
5952           Store[x][y] = EL_ROCK;
5953 #else
5954         Store[x][y] = EL_ROCK;
5955 #endif
5956
5957         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5958       }
5959       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5960       {
5961         if (!MovDelay[x][y])
5962           MovDelay[x][y] = TILEY + 1;
5963
5964         if (MovDelay[x][y])
5965         {
5966           MovDelay[x][y]--;
5967           if (MovDelay[x][y])
5968             return;
5969         }
5970
5971         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
5972         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
5973         Store[x][y + 1] = Store[x][y];
5974         Store[x][y] = 0;
5975
5976         PlayLevelSoundAction(x, y, ACTION_FILLING);
5977       }
5978     }
5979     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5980              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5981     {
5982       InitMovingField(x, y, MV_DOWN);
5983       started_moving = TRUE;
5984
5985       Feld[x][y] = EL_QUICKSAND_FILLING;
5986       Store[x][y] = element;
5987
5988       PlayLevelSoundAction(x, y, ACTION_FILLING);
5989     }
5990     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5991              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
5992     {
5993       InitMovingField(x, y, MV_DOWN);
5994       started_moving = TRUE;
5995
5996       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
5997       Store[x][y] = element;
5998
5999       PlayLevelSoundAction(x, y, ACTION_FILLING);
6000     }
6001     else if (element == EL_MAGIC_WALL_FULL)
6002     {
6003       if (IS_FREE(x, y + 1))
6004       {
6005         InitMovingField(x, y, MV_DOWN);
6006         started_moving = TRUE;
6007
6008         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6009         Store[x][y] = EL_CHANGED(Store[x][y]);
6010       }
6011       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6012       {
6013         if (!MovDelay[x][y])
6014           MovDelay[x][y] = TILEY/4 + 1;
6015
6016         if (MovDelay[x][y])
6017         {
6018           MovDelay[x][y]--;
6019           if (MovDelay[x][y])
6020             return;
6021         }
6022
6023         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6024         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6025         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6026         Store[x][y] = 0;
6027       }
6028     }
6029     else if (element == EL_BD_MAGIC_WALL_FULL)
6030     {
6031       if (IS_FREE(x, y + 1))
6032       {
6033         InitMovingField(x, y, MV_DOWN);
6034         started_moving = TRUE;
6035
6036         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6037         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
6038       }
6039       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6040       {
6041         if (!MovDelay[x][y])
6042           MovDelay[x][y] = TILEY/4 + 1;
6043
6044         if (MovDelay[x][y])
6045         {
6046           MovDelay[x][y]--;
6047           if (MovDelay[x][y])
6048             return;
6049         }
6050
6051         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6052         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6053         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
6054         Store[x][y] = 0;
6055       }
6056     }
6057     else if (element == EL_DC_MAGIC_WALL_FULL)
6058     {
6059       if (IS_FREE(x, y + 1))
6060       {
6061         InitMovingField(x, y, MV_DOWN);
6062         started_moving = TRUE;
6063
6064         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
6065         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
6066       }
6067       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6068       {
6069         if (!MovDelay[x][y])
6070           MovDelay[x][y] = TILEY/4 + 1;
6071
6072         if (MovDelay[x][y])
6073         {
6074           MovDelay[x][y]--;
6075           if (MovDelay[x][y])
6076             return;
6077         }
6078
6079         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
6080         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
6081         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
6082         Store[x][y] = 0;
6083       }
6084     }
6085     else if ((CAN_PASS_MAGIC_WALL(element) &&
6086               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6087                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
6088              (CAN_PASS_DC_MAGIC_WALL(element) &&
6089               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
6090
6091     {
6092       InitMovingField(x, y, MV_DOWN);
6093       started_moving = TRUE;
6094
6095       Feld[x][y] =
6096         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6097          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
6098          EL_DC_MAGIC_WALL_FILLING);
6099       Store[x][y] = element;
6100     }
6101     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6102     {
6103       SplashAcid(x, y + 1);
6104
6105       InitMovingField(x, y, MV_DOWN);
6106       started_moving = TRUE;
6107
6108       Store[x][y] = EL_ACID;
6109     }
6110     else if (
6111 #if USE_FIX_IMPACT_COLLISION
6112              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6113               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
6114 #else
6115              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6116               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6117 #endif
6118              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6119               CAN_FALL(element) && WasJustFalling[x][y] &&
6120               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6121
6122              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6123               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6124               (Feld[x][y + 1] == EL_BLOCKED)))
6125     {
6126       /* this is needed for a special case not covered by calling "Impact()"
6127          from "ContinueMoving()": if an element moves to a tile directly below
6128          another element which was just falling on that tile (which was empty
6129          in the previous frame), the falling element above would just stop
6130          instead of smashing the element below (in previous version, the above
6131          element was just checked for "moving" instead of "falling", resulting
6132          in incorrect smashes caused by horizontal movement of the above
6133          element; also, the case of the player being the element to smash was
6134          simply not covered here... :-/ ) */
6135
6136       CheckCollision[x][y] = 0;
6137       CheckImpact[x][y] = 0;
6138
6139       Impact(x, y);
6140     }
6141     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6142     {
6143       if (MovDir[x][y] == MV_NONE)
6144       {
6145         InitMovingField(x, y, MV_DOWN);
6146         started_moving = TRUE;
6147       }
6148     }
6149     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6150     {
6151       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6152         MovDir[x][y] = MV_DOWN;
6153
6154       InitMovingField(x, y, MV_DOWN);
6155       started_moving = TRUE;
6156     }
6157     else if (element == EL_AMOEBA_DROP)
6158     {
6159       Feld[x][y] = EL_AMOEBA_GROWING;
6160       Store[x][y] = EL_AMOEBA_WET;
6161     }
6162     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6163               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6164              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6165              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6166     {
6167       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6168                                 (IS_FREE(x - 1, y + 1) ||
6169                                  Feld[x - 1][y + 1] == EL_ACID));
6170       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6171                                 (IS_FREE(x + 1, y + 1) ||
6172                                  Feld[x + 1][y + 1] == EL_ACID));
6173       boolean can_fall_any  = (can_fall_left || can_fall_right);
6174       boolean can_fall_both = (can_fall_left && can_fall_right);
6175       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6176
6177 #if USE_NEW_ALL_SLIPPERY
6178       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6179       {
6180         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6181           can_fall_right = FALSE;
6182         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6183           can_fall_left = FALSE;
6184         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6185           can_fall_right = FALSE;
6186         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6187           can_fall_left = FALSE;
6188
6189         can_fall_any  = (can_fall_left || can_fall_right);
6190         can_fall_both = FALSE;
6191       }
6192 #else
6193       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6194       {
6195         if (slippery_type == SLIPPERY_ONLY_LEFT)
6196           can_fall_right = FALSE;
6197         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6198           can_fall_left = FALSE;
6199         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6200           can_fall_right = FALSE;
6201         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6202           can_fall_left = FALSE;
6203
6204         can_fall_any  = (can_fall_left || can_fall_right);
6205         can_fall_both = (can_fall_left && can_fall_right);
6206       }
6207 #endif
6208
6209 #if USE_NEW_ALL_SLIPPERY
6210 #else
6211 #if USE_NEW_SP_SLIPPERY
6212       /* !!! better use the same properties as for custom elements here !!! */
6213       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6214                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6215       {
6216         can_fall_right = FALSE;         /* slip down on left side */
6217         can_fall_both = FALSE;
6218       }
6219 #endif
6220 #endif
6221
6222 #if USE_NEW_ALL_SLIPPERY
6223       if (can_fall_both)
6224       {
6225         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6226           can_fall_right = FALSE;       /* slip down on left side */
6227         else
6228           can_fall_left = !(can_fall_right = RND(2));
6229
6230         can_fall_both = FALSE;
6231       }
6232 #else
6233       if (can_fall_both)
6234       {
6235         if (game.emulation == EMU_BOULDERDASH ||
6236             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6237           can_fall_right = FALSE;       /* slip down on left side */
6238         else
6239           can_fall_left = !(can_fall_right = RND(2));
6240
6241         can_fall_both = FALSE;
6242       }
6243 #endif
6244
6245       if (can_fall_any)
6246       {
6247         /* if not determined otherwise, prefer left side for slipping down */
6248         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6249         started_moving = TRUE;
6250       }
6251     }
6252 #if 0
6253     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6254 #else
6255     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6256 #endif
6257     {
6258       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6259       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6260       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6261       int belt_dir = game.belt_dir[belt_nr];
6262
6263       if ((belt_dir == MV_LEFT  && left_is_free) ||
6264           (belt_dir == MV_RIGHT && right_is_free))
6265       {
6266         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6267
6268         InitMovingField(x, y, belt_dir);
6269         started_moving = TRUE;
6270
6271         Pushed[x][y] = TRUE;
6272         Pushed[nextx][y] = TRUE;
6273
6274         GfxAction[x][y] = ACTION_DEFAULT;
6275       }
6276       else
6277       {
6278         MovDir[x][y] = 0;       /* if element was moving, stop it */
6279       }
6280     }
6281   }
6282
6283   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6284 #if 0
6285   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6286 #else
6287   if (CAN_MOVE(element) && !started_moving)
6288 #endif
6289   {
6290     int move_pattern = element_info[element].move_pattern;
6291     int newx, newy;
6292
6293 #if 0
6294 #if DEBUG
6295     if (MovDir[x][y] == MV_NONE)
6296     {
6297       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6298              x, y, element, element_info[element].token_name);
6299       printf("StartMoving(): This should never happen!\n");
6300     }
6301 #endif
6302 #endif
6303
6304     Moving2Blocked(x, y, &newx, &newy);
6305
6306     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6307       return;
6308
6309     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6310         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6311     {
6312       WasJustMoving[x][y] = 0;
6313       CheckCollision[x][y] = 0;
6314
6315       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6316
6317       if (Feld[x][y] != element)        /* element has changed */
6318         return;
6319     }
6320
6321     if (!MovDelay[x][y])        /* start new movement phase */
6322     {
6323       /* all objects that can change their move direction after each step
6324          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6325
6326       if (element != EL_YAMYAM &&
6327           element != EL_DARK_YAMYAM &&
6328           element != EL_PACMAN &&
6329           !(move_pattern & MV_ANY_DIRECTION) &&
6330           move_pattern != MV_TURNING_LEFT &&
6331           move_pattern != MV_TURNING_RIGHT &&
6332           move_pattern != MV_TURNING_LEFT_RIGHT &&
6333           move_pattern != MV_TURNING_RIGHT_LEFT &&
6334           move_pattern != MV_TURNING_RANDOM)
6335       {
6336         TurnRound(x, y);
6337
6338         if (MovDelay[x][y] && (element == EL_BUG ||
6339                                element == EL_SPACESHIP ||
6340                                element == EL_SP_SNIKSNAK ||
6341                                element == EL_SP_ELECTRON ||
6342                                element == EL_MOLE))
6343           DrawLevelField(x, y);
6344       }
6345     }
6346
6347     if (MovDelay[x][y])         /* wait some time before next movement */
6348     {
6349       MovDelay[x][y]--;
6350
6351       if (element == EL_ROBOT ||
6352           element == EL_YAMYAM ||
6353           element == EL_DARK_YAMYAM)
6354       {
6355         DrawLevelElementAnimationIfNeeded(x, y, element);
6356         PlayLevelSoundAction(x, y, ACTION_WAITING);
6357       }
6358       else if (element == EL_SP_ELECTRON)
6359         DrawLevelElementAnimationIfNeeded(x, y, element);
6360       else if (element == EL_DRAGON)
6361       {
6362         int i;
6363         int dir = MovDir[x][y];
6364         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6365         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6366         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6367                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6368                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6369                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6370         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6371
6372         GfxAction[x][y] = ACTION_ATTACKING;
6373
6374         if (IS_PLAYER(x, y))
6375           DrawPlayerField(x, y);
6376         else
6377           DrawLevelField(x, y);
6378
6379         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6380
6381         for (i = 1; i <= 3; i++)
6382         {
6383           int xx = x + i * dx;
6384           int yy = y + i * dy;
6385           int sx = SCREENX(xx);
6386           int sy = SCREENY(yy);
6387           int flame_graphic = graphic + (i - 1);
6388
6389           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6390             break;
6391
6392           if (MovDelay[x][y])
6393           {
6394             int flamed = MovingOrBlocked2Element(xx, yy);
6395
6396             /* !!! */
6397 #if 0
6398             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6399               Bang(xx, yy);
6400             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6401               RemoveMovingField(xx, yy);
6402             else
6403               RemoveField(xx, yy);
6404 #else
6405             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6406               Bang(xx, yy);
6407             else
6408               RemoveMovingField(xx, yy);
6409 #endif
6410
6411             ChangeDelay[xx][yy] = 0;
6412
6413             Feld[xx][yy] = EL_FLAMES;
6414
6415             if (IN_SCR_FIELD(sx, sy))
6416             {
6417               DrawLevelFieldCrumbledSand(xx, yy);
6418               DrawGraphic(sx, sy, flame_graphic, frame);
6419             }
6420           }
6421           else
6422           {
6423             if (Feld[xx][yy] == EL_FLAMES)
6424               Feld[xx][yy] = EL_EMPTY;
6425             DrawLevelField(xx, yy);
6426           }
6427         }
6428       }
6429
6430       if (MovDelay[x][y])       /* element still has to wait some time */
6431       {
6432         PlayLevelSoundAction(x, y, ACTION_WAITING);
6433
6434         return;
6435       }
6436     }
6437
6438     /* now make next step */
6439
6440     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6441
6442     if (DONT_COLLIDE_WITH(element) &&
6443         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6444         !PLAYER_ENEMY_PROTECTED(newx, newy))
6445     {
6446       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6447
6448       return;
6449     }
6450
6451     else if (CAN_MOVE_INTO_ACID(element) &&
6452              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6453              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6454              (MovDir[x][y] == MV_DOWN ||
6455               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6456     {
6457       SplashAcid(newx, newy);
6458       Store[x][y] = EL_ACID;
6459     }
6460     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6461     {
6462       if (Feld[newx][newy] == EL_EXIT_OPEN ||
6463           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
6464           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
6465           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
6466       {
6467         RemoveField(x, y);
6468         DrawLevelField(x, y);
6469
6470         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6471         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6472           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6473
6474         local_player->friends_still_needed--;
6475         if (!local_player->friends_still_needed &&
6476             !local_player->GameOver && AllPlayersGone)
6477           PlayerWins(local_player);
6478
6479         return;
6480       }
6481       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6482       {
6483         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6484           DrawLevelField(newx, newy);
6485         else
6486           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6487       }
6488       else if (!IS_FREE(newx, newy))
6489       {
6490         GfxAction[x][y] = ACTION_WAITING;
6491
6492         if (IS_PLAYER(x, y))
6493           DrawPlayerField(x, y);
6494         else
6495           DrawLevelField(x, y);
6496
6497         return;
6498       }
6499     }
6500     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6501     {
6502       if (IS_FOOD_PIG(Feld[newx][newy]))
6503       {
6504         if (IS_MOVING(newx, newy))
6505           RemoveMovingField(newx, newy);
6506         else
6507         {
6508           Feld[newx][newy] = EL_EMPTY;
6509           DrawLevelField(newx, newy);
6510         }
6511
6512         PlayLevelSound(x, y, SND_PIG_DIGGING);
6513       }
6514       else if (!IS_FREE(newx, newy))
6515       {
6516         if (IS_PLAYER(x, y))
6517           DrawPlayerField(x, y);
6518         else
6519           DrawLevelField(x, y);
6520
6521         return;
6522       }
6523     }
6524     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6525     {
6526       if (Store[x][y] != EL_EMPTY)
6527       {
6528         boolean can_clone = FALSE;
6529         int xx, yy;
6530
6531         /* check if element to clone is still there */
6532         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6533         {
6534           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6535           {
6536             can_clone = TRUE;
6537
6538             break;
6539           }
6540         }
6541
6542         /* cannot clone or target field not free anymore -- do not clone */
6543         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6544           Store[x][y] = EL_EMPTY;
6545       }
6546
6547       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6548       {
6549         if (IS_MV_DIAGONAL(MovDir[x][y]))
6550         {
6551           int diagonal_move_dir = MovDir[x][y];
6552           int stored = Store[x][y];
6553           int change_delay = 8;
6554           int graphic;
6555
6556           /* android is moving diagonally */
6557
6558           CreateField(x, y, EL_DIAGONAL_SHRINKING);
6559
6560           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6561           GfxElement[x][y] = EL_EMC_ANDROID;
6562           GfxAction[x][y] = ACTION_SHRINKING;
6563           GfxDir[x][y] = diagonal_move_dir;
6564           ChangeDelay[x][y] = change_delay;
6565
6566           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6567                                    GfxDir[x][y]);
6568
6569           DrawLevelGraphicAnimation(x, y, graphic);
6570           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6571
6572           if (Feld[newx][newy] == EL_ACID)
6573           {
6574             SplashAcid(newx, newy);
6575
6576             return;
6577           }
6578
6579           CreateField(newx, newy, EL_DIAGONAL_GROWING);
6580
6581           Store[newx][newy] = EL_EMC_ANDROID;
6582           GfxElement[newx][newy] = EL_EMC_ANDROID;
6583           GfxAction[newx][newy] = ACTION_GROWING;
6584           GfxDir[newx][newy] = diagonal_move_dir;
6585           ChangeDelay[newx][newy] = change_delay;
6586
6587           graphic = el_act_dir2img(GfxElement[newx][newy],
6588                                    GfxAction[newx][newy], GfxDir[newx][newy]);
6589
6590           DrawLevelGraphicAnimation(newx, newy, graphic);
6591           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6592
6593           return;
6594         }
6595         else
6596         {
6597           Feld[newx][newy] = EL_EMPTY;
6598           DrawLevelField(newx, newy);
6599
6600           PlayLevelSoundAction(x, y, ACTION_DIGGING);
6601         }
6602       }
6603       else if (!IS_FREE(newx, newy))
6604       {
6605 #if 0
6606         if (IS_PLAYER(x, y))
6607           DrawPlayerField(x, y);
6608         else
6609           DrawLevelField(x, y);
6610 #endif
6611
6612         return;
6613       }
6614     }
6615     else if (IS_CUSTOM_ELEMENT(element) &&
6616              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6617     {
6618       int new_element = Feld[newx][newy];
6619
6620       if (!IS_FREE(newx, newy))
6621       {
6622         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6623                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6624                       ACTION_BREAKING);
6625
6626         /* no element can dig solid indestructible elements */
6627         if (IS_INDESTRUCTIBLE(new_element) &&
6628             !IS_DIGGABLE(new_element) &&
6629             !IS_COLLECTIBLE(new_element))
6630           return;
6631
6632         if (AmoebaNr[newx][newy] &&
6633             (new_element == EL_AMOEBA_FULL ||
6634              new_element == EL_BD_AMOEBA ||
6635              new_element == EL_AMOEBA_GROWING))
6636         {
6637           AmoebaCnt[AmoebaNr[newx][newy]]--;
6638           AmoebaCnt2[AmoebaNr[newx][newy]]--;
6639         }
6640
6641         if (IS_MOVING(newx, newy))
6642           RemoveMovingField(newx, newy);
6643         else
6644         {
6645           RemoveField(newx, newy);
6646           DrawLevelField(newx, newy);
6647         }
6648
6649         /* if digged element was about to explode, prevent the explosion */
6650         ExplodeField[newx][newy] = EX_TYPE_NONE;
6651
6652         PlayLevelSoundAction(x, y, action);
6653       }
6654
6655       Store[newx][newy] = EL_EMPTY;
6656 #if 1
6657       /* this makes it possible to leave the removed element again */
6658       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6659         Store[newx][newy] = new_element;
6660 #else
6661       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6662       {
6663         int move_leave_element = element_info[element].move_leave_element;
6664
6665         /* this makes it possible to leave the removed element again */
6666         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6667                              new_element : move_leave_element);
6668       }
6669 #endif
6670
6671       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6672       {
6673         RunnerVisit[x][y] = FrameCounter;
6674         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6675       }
6676     }
6677     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6678     {
6679       if (!IS_FREE(newx, newy))
6680       {
6681         if (IS_PLAYER(x, y))
6682           DrawPlayerField(x, y);
6683         else
6684           DrawLevelField(x, y);
6685
6686         return;
6687       }
6688       else
6689       {
6690         boolean wanna_flame = !RND(10);
6691         int dx = newx - x, dy = newy - y;
6692         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6693         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6694         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6695                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6696         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6697                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6698
6699         if ((wanna_flame ||
6700              IS_CLASSIC_ENEMY(element1) ||
6701              IS_CLASSIC_ENEMY(element2)) &&
6702             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6703             element1 != EL_FLAMES && element2 != EL_FLAMES)
6704         {
6705           ResetGfxAnimation(x, y);
6706           GfxAction[x][y] = ACTION_ATTACKING;
6707
6708           if (IS_PLAYER(x, y))
6709             DrawPlayerField(x, y);
6710           else
6711             DrawLevelField(x, y);
6712
6713           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6714
6715           MovDelay[x][y] = 50;
6716
6717           /* !!! */
6718 #if 0
6719           RemoveField(newx, newy);
6720 #endif
6721           Feld[newx][newy] = EL_FLAMES;
6722           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6723           {
6724 #if 0
6725             RemoveField(newx1, newy1);
6726 #endif
6727             Feld[newx1][newy1] = EL_FLAMES;
6728           }
6729           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6730           {
6731 #if 0
6732             RemoveField(newx2, newy2);
6733 #endif
6734             Feld[newx2][newy2] = EL_FLAMES;
6735           }
6736
6737           return;
6738         }
6739       }
6740     }
6741     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6742              Feld[newx][newy] == EL_DIAMOND)
6743     {
6744       if (IS_MOVING(newx, newy))
6745         RemoveMovingField(newx, newy);
6746       else
6747       {
6748         Feld[newx][newy] = EL_EMPTY;
6749         DrawLevelField(newx, newy);
6750       }
6751
6752       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6753     }
6754     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6755              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6756     {
6757       if (AmoebaNr[newx][newy])
6758       {
6759         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6760         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6761             Feld[newx][newy] == EL_BD_AMOEBA)
6762           AmoebaCnt[AmoebaNr[newx][newy]]--;
6763       }
6764
6765 #if 0
6766       /* !!! test !!! */
6767       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6768       {
6769         RemoveMovingField(newx, newy);
6770       }
6771 #else
6772       if (IS_MOVING(newx, newy))
6773       {
6774         RemoveMovingField(newx, newy);
6775       }
6776 #endif
6777       else
6778       {
6779         Feld[newx][newy] = EL_EMPTY;
6780         DrawLevelField(newx, newy);
6781       }
6782
6783       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6784     }
6785     else if ((element == EL_PACMAN || element == EL_MOLE)
6786              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6787     {
6788       if (AmoebaNr[newx][newy])
6789       {
6790         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6791         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6792             Feld[newx][newy] == EL_BD_AMOEBA)
6793           AmoebaCnt[AmoebaNr[newx][newy]]--;
6794       }
6795
6796       if (element == EL_MOLE)
6797       {
6798         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6799         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6800
6801         ResetGfxAnimation(x, y);
6802         GfxAction[x][y] = ACTION_DIGGING;
6803         DrawLevelField(x, y);
6804
6805         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6806
6807         return;                         /* wait for shrinking amoeba */
6808       }
6809       else      /* element == EL_PACMAN */
6810       {
6811         Feld[newx][newy] = EL_EMPTY;
6812         DrawLevelField(newx, newy);
6813         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6814       }
6815     }
6816     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6817              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6818               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6819     {
6820       /* wait for shrinking amoeba to completely disappear */
6821       return;
6822     }
6823     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6824     {
6825       /* object was running against a wall */
6826
6827       TurnRound(x, y);
6828
6829 #if 0
6830       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6831       if (move_pattern & MV_ANY_DIRECTION &&
6832           move_pattern == MovDir[x][y])
6833       {
6834         int blocking_element =
6835           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6836
6837         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6838                                  MovDir[x][y]);
6839
6840         element = Feld[x][y];   /* element might have changed */
6841       }
6842 #endif
6843
6844       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
6845         DrawLevelElementAnimation(x, y, element);
6846
6847       if (DONT_TOUCH(element))
6848         TestIfBadThingTouchesPlayer(x, y);
6849
6850       return;
6851     }
6852
6853     InitMovingField(x, y, MovDir[x][y]);
6854
6855     PlayLevelSoundAction(x, y, ACTION_MOVING);
6856   }
6857
6858   if (MovDir[x][y])
6859     ContinueMoving(x, y);
6860 }
6861
6862 void ContinueMoving(int x, int y)
6863 {
6864   int element = Feld[x][y];
6865   struct ElementInfo *ei = &element_info[element];
6866   int direction = MovDir[x][y];
6867   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6868   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
6869   int newx = x + dx, newy = y + dy;
6870   int stored = Store[x][y];
6871   int stored_new = Store[newx][newy];
6872   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
6873   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6874   boolean last_line = (newy == lev_fieldy - 1);
6875
6876   MovPos[x][y] += getElementMoveStepsize(x, y);
6877
6878   if (pushed_by_player) /* special case: moving object pushed by player */
6879     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6880
6881   if (ABS(MovPos[x][y]) < TILEX)
6882   {
6883     DrawLevelField(x, y);
6884
6885     return;     /* element is still moving */
6886   }
6887
6888   /* element reached destination field */
6889
6890   Feld[x][y] = EL_EMPTY;
6891   Feld[newx][newy] = element;
6892   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6893
6894   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6895   {
6896     element = Feld[newx][newy] = EL_ACID;
6897   }
6898   else if (element == EL_MOLE)
6899   {
6900     Feld[x][y] = EL_SAND;
6901
6902     DrawLevelFieldCrumbledSandNeighbours(x, y);
6903   }
6904   else if (element == EL_QUICKSAND_FILLING)
6905   {
6906     element = Feld[newx][newy] = get_next_element(element);
6907     Store[newx][newy] = Store[x][y];
6908   }
6909   else if (element == EL_QUICKSAND_EMPTYING)
6910   {
6911     Feld[x][y] = get_next_element(element);
6912     element = Feld[newx][newy] = Store[x][y];
6913   }
6914   else if (element == EL_QUICKSAND_FAST_FILLING)
6915   {
6916     element = Feld[newx][newy] = get_next_element(element);
6917     Store[newx][newy] = Store[x][y];
6918   }
6919   else if (element == EL_QUICKSAND_FAST_EMPTYING)
6920   {
6921     Feld[x][y] = get_next_element(element);
6922     element = Feld[newx][newy] = Store[x][y];
6923   }
6924   else if (element == EL_MAGIC_WALL_FILLING)
6925   {
6926     element = Feld[newx][newy] = get_next_element(element);
6927     if (!game.magic_wall_active)
6928       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6929     Store[newx][newy] = Store[x][y];
6930   }
6931   else if (element == EL_MAGIC_WALL_EMPTYING)
6932   {
6933     Feld[x][y] = get_next_element(element);
6934     if (!game.magic_wall_active)
6935       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6936     element = Feld[newx][newy] = Store[x][y];
6937
6938 #if USE_NEW_CUSTOM_VALUE
6939     InitField(newx, newy, FALSE);
6940 #endif
6941   }
6942   else if (element == EL_BD_MAGIC_WALL_FILLING)
6943   {
6944     element = Feld[newx][newy] = get_next_element(element);
6945     if (!game.magic_wall_active)
6946       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6947     Store[newx][newy] = Store[x][y];
6948   }
6949   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6950   {
6951     Feld[x][y] = get_next_element(element);
6952     if (!game.magic_wall_active)
6953       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6954     element = Feld[newx][newy] = Store[x][y];
6955
6956 #if USE_NEW_CUSTOM_VALUE
6957     InitField(newx, newy, FALSE);
6958 #endif
6959   }
6960   else if (element == EL_DC_MAGIC_WALL_FILLING)
6961   {
6962     element = Feld[newx][newy] = get_next_element(element);
6963     if (!game.magic_wall_active)
6964       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
6965     Store[newx][newy] = Store[x][y];
6966   }
6967   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
6968   {
6969     Feld[x][y] = get_next_element(element);
6970     if (!game.magic_wall_active)
6971       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
6972     element = Feld[newx][newy] = Store[x][y];
6973
6974 #if USE_NEW_CUSTOM_VALUE
6975     InitField(newx, newy, FALSE);
6976 #endif
6977   }
6978   else if (element == EL_AMOEBA_DROPPING)
6979   {
6980     Feld[x][y] = get_next_element(element);
6981     element = Feld[newx][newy] = Store[x][y];
6982   }
6983   else if (element == EL_SOKOBAN_OBJECT)
6984   {
6985     if (Back[x][y])
6986       Feld[x][y] = Back[x][y];
6987
6988     if (Back[newx][newy])
6989       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6990
6991     Back[x][y] = Back[newx][newy] = 0;
6992   }
6993
6994   Store[x][y] = EL_EMPTY;
6995   MovPos[x][y] = 0;
6996   MovDir[x][y] = 0;
6997   MovDelay[x][y] = 0;
6998
6999   MovDelay[newx][newy] = 0;
7000
7001   if (CAN_CHANGE_OR_HAS_ACTION(element))
7002   {
7003     /* copy element change control values to new field */
7004     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7005     ChangePage[newx][newy]  = ChangePage[x][y];
7006     ChangeCount[newx][newy] = ChangeCount[x][y];
7007     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7008   }
7009
7010 #if USE_NEW_CUSTOM_VALUE
7011     CustomValue[newx][newy] = CustomValue[x][y];
7012 #endif
7013
7014   ChangeDelay[x][y] = 0;
7015   ChangePage[x][y] = -1;
7016   ChangeCount[x][y] = 0;
7017   ChangeEvent[x][y] = -1;
7018
7019 #if USE_NEW_CUSTOM_VALUE
7020   CustomValue[x][y] = 0;
7021 #endif
7022
7023   /* copy animation control values to new field */
7024   GfxFrame[newx][newy]  = GfxFrame[x][y];
7025   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7026   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7027   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7028
7029   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7030
7031   /* some elements can leave other elements behind after moving */
7032 #if 1
7033   if (ei->move_leave_element != EL_EMPTY &&
7034       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7035       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7036 #else
7037   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7038       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7039       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7040 #endif
7041   {
7042     int move_leave_element = ei->move_leave_element;
7043
7044 #if 1
7045 #if 1
7046     /* this makes it possible to leave the removed element again */
7047     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7048       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7049 #else
7050     /* this makes it possible to leave the removed element again */
7051     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7052       move_leave_element = stored;
7053 #endif
7054 #else
7055     /* this makes it possible to leave the removed element again */
7056     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7057         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7058       move_leave_element = stored;
7059 #endif
7060
7061     Feld[x][y] = move_leave_element;
7062
7063     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7064       MovDir[x][y] = direction;
7065
7066     InitField(x, y, FALSE);
7067
7068     if (GFX_CRUMBLED(Feld[x][y]))
7069       DrawLevelFieldCrumbledSandNeighbours(x, y);
7070
7071     if (ELEM_IS_PLAYER(move_leave_element))
7072       RelocatePlayer(x, y, move_leave_element);
7073   }
7074
7075   /* do this after checking for left-behind element */
7076   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7077
7078   if (!CAN_MOVE(element) ||
7079       (CAN_FALL(element) && direction == MV_DOWN &&
7080        (element == EL_SPRING ||
7081         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7082         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7083     GfxDir[x][y] = MovDir[newx][newy] = 0;
7084
7085   DrawLevelField(x, y);
7086   DrawLevelField(newx, newy);
7087
7088   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7089
7090   /* prevent pushed element from moving on in pushed direction */
7091   if (pushed_by_player && CAN_MOVE(element) &&
7092       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7093       !(element_info[element].move_pattern & direction))
7094     TurnRound(newx, newy);
7095
7096   /* prevent elements on conveyor belt from moving on in last direction */
7097   if (pushed_by_conveyor && CAN_FALL(element) &&
7098       direction & MV_HORIZONTAL)
7099     MovDir[newx][newy] = 0;
7100
7101   if (!pushed_by_player)
7102   {
7103     int nextx = newx + dx, nexty = newy + dy;
7104     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7105
7106     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7107
7108     if (CAN_FALL(element) && direction == MV_DOWN)
7109       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7110
7111     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7112       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7113
7114 #if USE_FIX_IMPACT_COLLISION
7115     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
7116       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
7117 #endif
7118   }
7119
7120   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7121   {
7122     TestIfBadThingTouchesPlayer(newx, newy);
7123     TestIfBadThingTouchesFriend(newx, newy);
7124
7125     if (!IS_CUSTOM_ELEMENT(element))
7126       TestIfBadThingTouchesOtherBadThing(newx, newy);
7127   }
7128   else if (element == EL_PENGUIN)
7129     TestIfFriendTouchesBadThing(newx, newy);
7130
7131   /* give the player one last chance (one more frame) to move away */
7132   if (CAN_FALL(element) && direction == MV_DOWN &&
7133       (last_line || (!IS_FREE(x, newy + 1) &&
7134                      (!IS_PLAYER(x, newy + 1) ||
7135                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7136     Impact(x, newy);
7137
7138   if (pushed_by_player && !game.use_change_when_pushing_bug)
7139   {
7140     int push_side = MV_DIR_OPPOSITE(direction);
7141     struct PlayerInfo *player = PLAYERINFO(x, y);
7142
7143     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7144                                player->index_bit, push_side);
7145     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7146                                         player->index_bit, push_side);
7147   }
7148
7149   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7150     MovDelay[newx][newy] = 1;
7151
7152   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7153
7154   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7155
7156 #if 0
7157   if (ChangePage[newx][newy] != -1)             /* delayed change */
7158   {
7159     int page = ChangePage[newx][newy];
7160     struct ElementChangeInfo *change = &ei->change_page[page];
7161
7162     ChangePage[newx][newy] = -1;
7163
7164     if (change->can_change)
7165     {
7166       if (ChangeElement(newx, newy, element, page))
7167       {
7168         if (change->post_change_function)
7169           change->post_change_function(newx, newy);
7170       }
7171     }
7172
7173     if (change->has_action)
7174       ExecuteCustomElementAction(newx, newy, element, page);
7175   }
7176 #endif
7177
7178   TestIfElementHitsCustomElement(newx, newy, direction);
7179   TestIfPlayerTouchesCustomElement(newx, newy);
7180   TestIfElementTouchesCustomElement(newx, newy);
7181
7182   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7183       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7184     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7185                              MV_DIR_OPPOSITE(direction));
7186 }
7187
7188 int AmoebeNachbarNr(int ax, int ay)
7189 {
7190   int i;
7191   int element = Feld[ax][ay];
7192   int group_nr = 0;
7193   static int xy[4][2] =
7194   {
7195     { 0, -1 },
7196     { -1, 0 },
7197     { +1, 0 },
7198     { 0, +1 }
7199   };
7200
7201   for (i = 0; i < NUM_DIRECTIONS; i++)
7202   {
7203     int x = ax + xy[i][0];
7204     int y = ay + xy[i][1];
7205
7206     if (!IN_LEV_FIELD(x, y))
7207       continue;
7208
7209     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7210       group_nr = AmoebaNr[x][y];
7211   }
7212
7213   return group_nr;
7214 }
7215
7216 void AmoebenVereinigen(int ax, int ay)
7217 {
7218   int i, x, y, xx, yy;
7219   int new_group_nr = AmoebaNr[ax][ay];
7220   static int xy[4][2] =
7221   {
7222     { 0, -1 },
7223     { -1, 0 },
7224     { +1, 0 },
7225     { 0, +1 }
7226   };
7227
7228   if (new_group_nr == 0)
7229     return;
7230
7231   for (i = 0; i < NUM_DIRECTIONS; i++)
7232   {
7233     x = ax + xy[i][0];
7234     y = ay + xy[i][1];
7235
7236     if (!IN_LEV_FIELD(x, y))
7237       continue;
7238
7239     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7240          Feld[x][y] == EL_BD_AMOEBA ||
7241          Feld[x][y] == EL_AMOEBA_DEAD) &&
7242         AmoebaNr[x][y] != new_group_nr)
7243     {
7244       int old_group_nr = AmoebaNr[x][y];
7245
7246       if (old_group_nr == 0)
7247         return;
7248
7249       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7250       AmoebaCnt[old_group_nr] = 0;
7251       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7252       AmoebaCnt2[old_group_nr] = 0;
7253
7254       SCAN_PLAYFIELD(xx, yy)
7255       {
7256         if (AmoebaNr[xx][yy] == old_group_nr)
7257           AmoebaNr[xx][yy] = new_group_nr;
7258       }
7259     }
7260   }
7261 }
7262
7263 void AmoebeUmwandeln(int ax, int ay)
7264 {
7265   int i, x, y;
7266
7267   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7268   {
7269     int group_nr = AmoebaNr[ax][ay];
7270
7271 #ifdef DEBUG
7272     if (group_nr == 0)
7273     {
7274       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7275       printf("AmoebeUmwandeln(): This should never happen!\n");
7276       return;
7277     }
7278 #endif
7279
7280     SCAN_PLAYFIELD(x, y)
7281     {
7282       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7283       {
7284         AmoebaNr[x][y] = 0;
7285         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7286       }
7287     }
7288
7289     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7290                             SND_AMOEBA_TURNING_TO_GEM :
7291                             SND_AMOEBA_TURNING_TO_ROCK));
7292     Bang(ax, ay);
7293   }
7294   else
7295   {
7296     static int xy[4][2] =
7297     {
7298       { 0, -1 },
7299       { -1, 0 },
7300       { +1, 0 },
7301       { 0, +1 }
7302     };
7303
7304     for (i = 0; i < NUM_DIRECTIONS; i++)
7305     {
7306       x = ax + xy[i][0];
7307       y = ay + xy[i][1];
7308
7309       if (!IN_LEV_FIELD(x, y))
7310         continue;
7311
7312       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7313       {
7314         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7315                               SND_AMOEBA_TURNING_TO_GEM :
7316                               SND_AMOEBA_TURNING_TO_ROCK));
7317         Bang(x, y);
7318       }
7319     }
7320   }
7321 }
7322
7323 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7324 {
7325   int x, y;
7326   int group_nr = AmoebaNr[ax][ay];
7327   boolean done = FALSE;
7328
7329 #ifdef DEBUG
7330   if (group_nr == 0)
7331   {
7332     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7333     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7334     return;
7335   }
7336 #endif
7337
7338   SCAN_PLAYFIELD(x, y)
7339   {
7340     if (AmoebaNr[x][y] == group_nr &&
7341         (Feld[x][y] == EL_AMOEBA_DEAD ||
7342          Feld[x][y] == EL_BD_AMOEBA ||
7343          Feld[x][y] == EL_AMOEBA_GROWING))
7344     {
7345       AmoebaNr[x][y] = 0;
7346       Feld[x][y] = new_element;
7347       InitField(x, y, FALSE);
7348       DrawLevelField(x, y);
7349       done = TRUE;
7350     }
7351   }
7352
7353   if (done)
7354     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7355                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7356                             SND_BD_AMOEBA_TURNING_TO_GEM));
7357 }
7358
7359 void AmoebeWaechst(int x, int y)
7360 {
7361   static unsigned long sound_delay = 0;
7362   static unsigned long sound_delay_value = 0;
7363
7364   if (!MovDelay[x][y])          /* start new growing cycle */
7365   {
7366     MovDelay[x][y] = 7;
7367
7368     if (DelayReached(&sound_delay, sound_delay_value))
7369     {
7370       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7371       sound_delay_value = 30;
7372     }
7373   }
7374
7375   if (MovDelay[x][y])           /* wait some time before growing bigger */
7376   {
7377     MovDelay[x][y]--;
7378     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7379     {
7380       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7381                                            6 - MovDelay[x][y]);
7382
7383       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7384     }
7385
7386     if (!MovDelay[x][y])
7387     {
7388       Feld[x][y] = Store[x][y];
7389       Store[x][y] = 0;
7390       DrawLevelField(x, y);
7391     }
7392   }
7393 }
7394
7395 void AmoebaDisappearing(int x, int y)
7396 {
7397   static unsigned long sound_delay = 0;
7398   static unsigned long sound_delay_value = 0;
7399
7400   if (!MovDelay[x][y])          /* start new shrinking cycle */
7401   {
7402     MovDelay[x][y] = 7;
7403
7404     if (DelayReached(&sound_delay, sound_delay_value))
7405       sound_delay_value = 30;
7406   }
7407
7408   if (MovDelay[x][y])           /* wait some time before shrinking */
7409   {
7410     MovDelay[x][y]--;
7411     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7412     {
7413       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7414                                            6 - MovDelay[x][y]);
7415
7416       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7417     }
7418
7419     if (!MovDelay[x][y])
7420     {
7421       Feld[x][y] = EL_EMPTY;
7422       DrawLevelField(x, y);
7423
7424       /* don't let mole enter this field in this cycle;
7425          (give priority to objects falling to this field from above) */
7426       Stop[x][y] = TRUE;
7427     }
7428   }
7429 }
7430
7431 void AmoebeAbleger(int ax, int ay)
7432 {
7433   int i;
7434   int element = Feld[ax][ay];
7435   int graphic = el2img(element);
7436   int newax = ax, neway = ay;
7437   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7438   static int xy[4][2] =
7439   {
7440     { 0, -1 },
7441     { -1, 0 },
7442     { +1, 0 },
7443     { 0, +1 }
7444   };
7445
7446   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7447   {
7448     Feld[ax][ay] = EL_AMOEBA_DEAD;
7449     DrawLevelField(ax, ay);
7450     return;
7451   }
7452
7453   if (IS_ANIMATED(graphic))
7454     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7455
7456   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7457     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7458
7459   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7460   {
7461     MovDelay[ax][ay]--;
7462     if (MovDelay[ax][ay])
7463       return;
7464   }
7465
7466   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7467   {
7468     int start = RND(4);
7469     int x = ax + xy[start][0];
7470     int y = ay + xy[start][1];
7471
7472     if (!IN_LEV_FIELD(x, y))
7473       return;
7474
7475     if (IS_FREE(x, y) ||
7476         CAN_GROW_INTO(Feld[x][y]) ||
7477         Feld[x][y] == EL_QUICKSAND_EMPTY ||
7478         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7479     {
7480       newax = x;
7481       neway = y;
7482     }
7483
7484     if (newax == ax && neway == ay)
7485       return;
7486   }
7487   else                          /* normal or "filled" (BD style) amoeba */
7488   {
7489     int start = RND(4);
7490     boolean waiting_for_player = FALSE;
7491
7492     for (i = 0; i < NUM_DIRECTIONS; i++)
7493     {
7494       int j = (start + i) % 4;
7495       int x = ax + xy[j][0];
7496       int y = ay + xy[j][1];
7497
7498       if (!IN_LEV_FIELD(x, y))
7499         continue;
7500
7501       if (IS_FREE(x, y) ||
7502           CAN_GROW_INTO(Feld[x][y]) ||
7503           Feld[x][y] == EL_QUICKSAND_EMPTY ||
7504           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
7505       {
7506         newax = x;
7507         neway = y;
7508         break;
7509       }
7510       else if (IS_PLAYER(x, y))
7511         waiting_for_player = TRUE;
7512     }
7513
7514     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7515     {
7516       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7517       {
7518         Feld[ax][ay] = EL_AMOEBA_DEAD;
7519         DrawLevelField(ax, ay);
7520         AmoebaCnt[AmoebaNr[ax][ay]]--;
7521
7522         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7523         {
7524           if (element == EL_AMOEBA_FULL)
7525             AmoebeUmwandeln(ax, ay);
7526           else if (element == EL_BD_AMOEBA)
7527             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7528         }
7529       }
7530       return;
7531     }
7532     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7533     {
7534       /* amoeba gets larger by growing in some direction */
7535
7536       int new_group_nr = AmoebaNr[ax][ay];
7537
7538 #ifdef DEBUG
7539   if (new_group_nr == 0)
7540   {
7541     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7542     printf("AmoebeAbleger(): This should never happen!\n");
7543     return;
7544   }
7545 #endif
7546
7547       AmoebaNr[newax][neway] = new_group_nr;
7548       AmoebaCnt[new_group_nr]++;
7549       AmoebaCnt2[new_group_nr]++;
7550
7551       /* if amoeba touches other amoeba(s) after growing, unify them */
7552       AmoebenVereinigen(newax, neway);
7553
7554       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7555       {
7556         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7557         return;
7558       }
7559     }
7560   }
7561
7562   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7563       (neway == lev_fieldy - 1 && newax != ax))
7564   {
7565     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7566     Store[newax][neway] = element;
7567   }
7568   else if (neway == ay || element == EL_EMC_DRIPPER)
7569   {
7570     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7571
7572     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7573   }
7574   else
7575   {
7576     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7577     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7578     Store[ax][ay] = EL_AMOEBA_DROP;
7579     ContinueMoving(ax, ay);
7580     return;
7581   }
7582
7583   DrawLevelField(newax, neway);
7584 }
7585
7586 void Life(int ax, int ay)
7587 {
7588   int x1, y1, x2, y2;
7589   int life_time = 40;
7590   int element = Feld[ax][ay];
7591   int graphic = el2img(element);
7592   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7593                          level.biomaze);
7594   boolean changed = FALSE;
7595
7596   if (IS_ANIMATED(graphic))
7597     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7598
7599   if (Stop[ax][ay])
7600     return;
7601
7602   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7603     MovDelay[ax][ay] = life_time;
7604
7605   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7606   {
7607     MovDelay[ax][ay]--;
7608     if (MovDelay[ax][ay])
7609       return;
7610   }
7611
7612   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7613   {
7614     int xx = ax+x1, yy = ay+y1;
7615     int nachbarn = 0;
7616
7617     if (!IN_LEV_FIELD(xx, yy))
7618       continue;
7619
7620     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7621     {
7622       int x = xx+x2, y = yy+y2;
7623
7624       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7625         continue;
7626
7627       if (((Feld[x][y] == element ||
7628             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7629            !Stop[x][y]) ||
7630           (IS_FREE(x, y) && Stop[x][y]))
7631         nachbarn++;
7632     }
7633
7634     if (xx == ax && yy == ay)           /* field in the middle */
7635     {
7636       if (nachbarn < life_parameter[0] ||
7637           nachbarn > life_parameter[1])
7638       {
7639         Feld[xx][yy] = EL_EMPTY;
7640         if (!Stop[xx][yy])
7641           DrawLevelField(xx, yy);
7642         Stop[xx][yy] = TRUE;
7643         changed = TRUE;
7644       }
7645     }
7646     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7647     {                                   /* free border field */
7648       if (nachbarn >= life_parameter[2] &&
7649           nachbarn <= life_parameter[3])
7650       {
7651         Feld[xx][yy] = element;
7652         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7653         if (!Stop[xx][yy])
7654           DrawLevelField(xx, yy);
7655         Stop[xx][yy] = TRUE;
7656         changed = TRUE;
7657       }
7658     }
7659   }
7660
7661   if (changed)
7662     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7663                    SND_GAME_OF_LIFE_GROWING);
7664 }
7665
7666 static void InitRobotWheel(int x, int y)
7667 {
7668   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7669 }
7670
7671 static void RunRobotWheel(int x, int y)
7672 {
7673   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7674 }
7675
7676 static void StopRobotWheel(int x, int y)
7677 {
7678   if (ZX == x && ZY == y)
7679     ZX = ZY = -1;
7680 }
7681
7682 static void InitTimegateWheel(int x, int y)
7683 {
7684   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7685 }
7686
7687 static void RunTimegateWheel(int x, int y)
7688 {
7689   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
7690 }
7691
7692 static void InitMagicBallDelay(int x, int y)
7693 {
7694 #if 1
7695   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7696 #else
7697   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7698 #endif
7699 }
7700
7701 static void ActivateMagicBall(int bx, int by)
7702 {
7703   int x, y;
7704
7705   if (level.ball_random)
7706   {
7707     int pos_border = RND(8);    /* select one of the eight border elements */
7708     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7709     int xx = pos_content % 3;
7710     int yy = pos_content / 3;
7711
7712     x = bx - 1 + xx;
7713     y = by - 1 + yy;
7714
7715     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7716       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7717   }
7718   else
7719   {
7720     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7721     {
7722       int xx = x - bx + 1;
7723       int yy = y - by + 1;
7724
7725       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7726         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7727     }
7728   }
7729
7730   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7731 }
7732
7733 void CheckExit(int x, int y)
7734 {
7735   if (local_player->gems_still_needed > 0 ||
7736       local_player->sokobanfields_still_needed > 0 ||
7737       local_player->lights_still_needed > 0)
7738   {
7739     int element = Feld[x][y];
7740     int graphic = el2img(element);
7741
7742     if (IS_ANIMATED(graphic))
7743       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7744
7745     return;
7746   }
7747
7748   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7749     return;
7750
7751   Feld[x][y] = EL_EXIT_OPENING;
7752
7753   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7754 }
7755
7756 void CheckExitEM(int x, int y)
7757 {
7758   if (local_player->gems_still_needed > 0 ||
7759       local_player->sokobanfields_still_needed > 0 ||
7760       local_player->lights_still_needed > 0)
7761   {
7762     int element = Feld[x][y];
7763     int graphic = el2img(element);
7764
7765     if (IS_ANIMATED(graphic))
7766       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7767
7768     return;
7769   }
7770
7771   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7772     return;
7773
7774   Feld[x][y] = EL_EM_EXIT_OPENING;
7775
7776   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
7777 }
7778
7779 void CheckExitSteel(int x, int y)
7780 {
7781   if (local_player->gems_still_needed > 0 ||
7782       local_player->sokobanfields_still_needed > 0 ||
7783       local_player->lights_still_needed > 0)
7784   {
7785     int element = Feld[x][y];
7786     int graphic = el2img(element);
7787
7788     if (IS_ANIMATED(graphic))
7789       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7790
7791     return;
7792   }
7793
7794   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7795     return;
7796
7797   Feld[x][y] = EL_STEEL_EXIT_OPENING;
7798
7799   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
7800 }
7801
7802 void CheckExitSteelEM(int x, int y)
7803 {
7804   if (local_player->gems_still_needed > 0 ||
7805       local_player->sokobanfields_still_needed > 0 ||
7806       local_player->lights_still_needed > 0)
7807   {
7808     int element = Feld[x][y];
7809     int graphic = el2img(element);
7810
7811     if (IS_ANIMATED(graphic))
7812       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7813
7814     return;
7815   }
7816
7817   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7818     return;
7819
7820   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
7821
7822   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
7823 }
7824
7825 void CheckExitSP(int x, int y)
7826 {
7827   if (local_player->gems_still_needed > 0)
7828   {
7829     int element = Feld[x][y];
7830     int graphic = el2img(element);
7831
7832     if (IS_ANIMATED(graphic))
7833       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7834
7835     return;
7836   }
7837
7838   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7839     return;
7840
7841   Feld[x][y] = EL_SP_EXIT_OPENING;
7842
7843   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7844 }
7845
7846 static void CloseAllOpenTimegates()
7847 {
7848   int x, y;
7849
7850   SCAN_PLAYFIELD(x, y)
7851   {
7852     int element = Feld[x][y];
7853
7854     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7855     {
7856       Feld[x][y] = EL_TIMEGATE_CLOSING;
7857
7858       PlayLevelSoundAction(x, y, ACTION_CLOSING);
7859     }
7860   }
7861 }
7862
7863 void DrawTwinkleOnField(int x, int y)
7864 {
7865   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7866     return;
7867
7868   if (Feld[x][y] == EL_BD_DIAMOND)
7869     return;
7870
7871   if (MovDelay[x][y] == 0)      /* next animation frame */
7872     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
7873
7874   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7875   {
7876     MovDelay[x][y]--;
7877
7878     if (setup.direct_draw && MovDelay[x][y])
7879       SetDrawtoField(DRAW_BUFFERED);
7880
7881     DrawLevelElementAnimation(x, y, Feld[x][y]);
7882
7883     if (MovDelay[x][y] != 0)
7884     {
7885       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7886                                            10 - MovDelay[x][y]);
7887
7888       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7889
7890       if (setup.direct_draw)
7891       {
7892         int dest_x, dest_y;
7893
7894         dest_x = FX + SCREENX(x) * TILEX;
7895         dest_y = FY + SCREENY(y) * TILEY;
7896
7897         BlitBitmap(drawto_field, window,
7898                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7899         SetDrawtoField(DRAW_DIRECT);
7900       }
7901     }
7902   }
7903 }
7904
7905 void MauerWaechst(int x, int y)
7906 {
7907   int delay = 6;
7908
7909   if (!MovDelay[x][y])          /* next animation frame */
7910     MovDelay[x][y] = 3 * delay;
7911
7912   if (MovDelay[x][y])           /* wait some time before next frame */
7913   {
7914     MovDelay[x][y]--;
7915
7916     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7917     {
7918       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7919       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7920
7921       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7922     }
7923
7924     if (!MovDelay[x][y])
7925     {
7926       if (MovDir[x][y] == MV_LEFT)
7927       {
7928         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7929           DrawLevelField(x - 1, y);
7930       }
7931       else if (MovDir[x][y] == MV_RIGHT)
7932       {
7933         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7934           DrawLevelField(x + 1, y);
7935       }
7936       else if (MovDir[x][y] == MV_UP)
7937       {
7938         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7939           DrawLevelField(x, y - 1);
7940       }
7941       else
7942       {
7943         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7944           DrawLevelField(x, y + 1);
7945       }
7946
7947       Feld[x][y] = Store[x][y];
7948       Store[x][y] = 0;
7949       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7950       DrawLevelField(x, y);
7951     }
7952   }
7953 }
7954
7955 void MauerAbleger(int ax, int ay)
7956 {
7957   int element = Feld[ax][ay];
7958   int graphic = el2img(element);
7959   boolean oben_frei = FALSE, unten_frei = FALSE;
7960   boolean links_frei = FALSE, rechts_frei = FALSE;
7961   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7962   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7963   boolean new_wall = FALSE;
7964
7965   if (IS_ANIMATED(graphic))
7966     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7967
7968   if (!MovDelay[ax][ay])        /* start building new wall */
7969     MovDelay[ax][ay] = 6;
7970
7971   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7972   {
7973     MovDelay[ax][ay]--;
7974     if (MovDelay[ax][ay])
7975       return;
7976   }
7977
7978   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7979     oben_frei = TRUE;
7980   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7981     unten_frei = TRUE;
7982   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7983     links_frei = TRUE;
7984   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7985     rechts_frei = TRUE;
7986
7987   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7988       element == EL_EXPANDABLE_WALL_ANY)
7989   {
7990     if (oben_frei)
7991     {
7992       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7993       Store[ax][ay-1] = element;
7994       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7995       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7996         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7997                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7998       new_wall = TRUE;
7999     }
8000     if (unten_frei)
8001     {
8002       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8003       Store[ax][ay+1] = element;
8004       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8005       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8006         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8007                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8008       new_wall = TRUE;
8009     }
8010   }
8011
8012   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8013       element == EL_EXPANDABLE_WALL_ANY ||
8014       element == EL_EXPANDABLE_WALL ||
8015       element == EL_BD_EXPANDABLE_WALL)
8016   {
8017     if (links_frei)
8018     {
8019       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8020       Store[ax-1][ay] = element;
8021       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8022       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8023         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8024                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8025       new_wall = TRUE;
8026     }
8027
8028     if (rechts_frei)
8029     {
8030       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8031       Store[ax+1][ay] = element;
8032       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8033       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8034         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8035                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8036       new_wall = TRUE;
8037     }
8038   }
8039
8040   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8041     DrawLevelField(ax, ay);
8042
8043   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8044     oben_massiv = TRUE;
8045   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8046     unten_massiv = TRUE;
8047   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8048     links_massiv = TRUE;
8049   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8050     rechts_massiv = TRUE;
8051
8052   if (((oben_massiv && unten_massiv) ||
8053        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8054        element == EL_EXPANDABLE_WALL) &&
8055       ((links_massiv && rechts_massiv) ||
8056        element == EL_EXPANDABLE_WALL_VERTICAL))
8057     Feld[ax][ay] = EL_WALL;
8058
8059   if (new_wall)
8060     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8061 }
8062
8063 void MauerAblegerStahl(int ax, int ay)
8064 {
8065   int element = Feld[ax][ay];
8066   int graphic = el2img(element);
8067   boolean oben_frei = FALSE, unten_frei = FALSE;
8068   boolean links_frei = FALSE, rechts_frei = FALSE;
8069   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8070   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8071   boolean new_wall = FALSE;
8072
8073   if (IS_ANIMATED(graphic))
8074     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8075
8076   if (!MovDelay[ax][ay])        /* start building new wall */
8077     MovDelay[ax][ay] = 6;
8078
8079   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8080   {
8081     MovDelay[ax][ay]--;
8082     if (MovDelay[ax][ay])
8083       return;
8084   }
8085
8086   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8087     oben_frei = TRUE;
8088   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8089     unten_frei = TRUE;
8090   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8091     links_frei = TRUE;
8092   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8093     rechts_frei = TRUE;
8094
8095   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
8096       element == EL_EXPANDABLE_STEELWALL_ANY)
8097   {
8098     if (oben_frei)
8099     {
8100       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
8101       Store[ax][ay-1] = element;
8102       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8103       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8104         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8105                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
8106       new_wall = TRUE;
8107     }
8108     if (unten_frei)
8109     {
8110       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
8111       Store[ax][ay+1] = element;
8112       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8113       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8114         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8115                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
8116       new_wall = TRUE;
8117     }
8118   }
8119
8120   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
8121       element == EL_EXPANDABLE_STEELWALL_ANY)
8122   {
8123     if (links_frei)
8124     {
8125       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8126       Store[ax-1][ay] = element;
8127       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8128       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8129         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8130                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
8131       new_wall = TRUE;
8132     }
8133
8134     if (rechts_frei)
8135     {
8136       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
8137       Store[ax+1][ay] = element;
8138       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8139       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8140         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8141                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
8142       new_wall = TRUE;
8143     }
8144   }
8145
8146   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8147     oben_massiv = TRUE;
8148   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8149     unten_massiv = TRUE;
8150   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8151     links_massiv = TRUE;
8152   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8153     rechts_massiv = TRUE;
8154
8155   if (((oben_massiv && unten_massiv) ||
8156        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
8157       ((links_massiv && rechts_massiv) ||
8158        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
8159     Feld[ax][ay] = EL_WALL;
8160
8161   if (new_wall)
8162     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8163 }
8164
8165 void CheckForDragon(int x, int y)
8166 {
8167   int i, j;
8168   boolean dragon_found = FALSE;
8169   static int xy[4][2] =
8170   {
8171     { 0, -1 },
8172     { -1, 0 },
8173     { +1, 0 },
8174     { 0, +1 }
8175   };
8176
8177   for (i = 0; i < NUM_DIRECTIONS; i++)
8178   {
8179     for (j = 0; j < 4; j++)
8180     {
8181       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8182
8183       if (IN_LEV_FIELD(xx, yy) &&
8184           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8185       {
8186         if (Feld[xx][yy] == EL_DRAGON)
8187           dragon_found = TRUE;
8188       }
8189       else
8190         break;
8191     }
8192   }
8193
8194   if (!dragon_found)
8195   {
8196     for (i = 0; i < NUM_DIRECTIONS; i++)
8197     {
8198       for (j = 0; j < 3; j++)
8199       {
8200         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8201   
8202         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8203         {
8204           Feld[xx][yy] = EL_EMPTY;
8205           DrawLevelField(xx, yy);
8206         }
8207         else
8208           break;
8209       }
8210     }
8211   }
8212 }
8213
8214 static void InitBuggyBase(int x, int y)
8215 {
8216   int element = Feld[x][y];
8217   int activating_delay = FRAMES_PER_SECOND / 4;
8218
8219   ChangeDelay[x][y] =
8220     (element == EL_SP_BUGGY_BASE ?
8221      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8222      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8223      activating_delay :
8224      element == EL_SP_BUGGY_BASE_ACTIVE ?
8225      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8226 }
8227
8228 static void WarnBuggyBase(int x, int y)
8229 {
8230   int i;
8231   static int xy[4][2] =
8232   {
8233     { 0, -1 },
8234     { -1, 0 },
8235     { +1, 0 },
8236     { 0, +1 }
8237   };
8238
8239   for (i = 0; i < NUM_DIRECTIONS; i++)
8240   {
8241     int xx = x + xy[i][0];
8242     int yy = y + xy[i][1];
8243
8244     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8245     {
8246       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8247
8248       break;
8249     }
8250   }
8251 }
8252
8253 static void InitTrap(int x, int y)
8254 {
8255   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8256 }
8257
8258 static void ActivateTrap(int x, int y)
8259 {
8260   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8261 }
8262
8263 static void ChangeActiveTrap(int x, int y)
8264 {
8265   int graphic = IMG_TRAP_ACTIVE;
8266
8267   /* if new animation frame was drawn, correct crumbled sand border */
8268   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8269     DrawLevelFieldCrumbledSand(x, y);
8270 }
8271
8272 static int getSpecialActionElement(int element, int number, int base_element)
8273 {
8274   return (element != EL_EMPTY ? element :
8275           number != -1 ? base_element + number - 1 :
8276           EL_EMPTY);
8277 }
8278
8279 static int getModifiedActionNumber(int value_old, int operator, int operand,
8280                                    int value_min, int value_max)
8281 {
8282   int value_new = (operator == CA_MODE_SET      ? operand :
8283                    operator == CA_MODE_ADD      ? value_old + operand :
8284                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8285                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8286                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8287                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8288                    value_old);
8289
8290   return (value_new < value_min ? value_min :
8291           value_new > value_max ? value_max :
8292           value_new);
8293 }
8294
8295 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8296 {
8297   struct ElementInfo *ei = &element_info[element];
8298   struct ElementChangeInfo *change = &ei->change_page[page];
8299   int target_element = change->target_element;
8300   int action_type = change->action_type;
8301   int action_mode = change->action_mode;
8302   int action_arg = change->action_arg;
8303   int i;
8304
8305   if (!change->has_action)
8306     return;
8307
8308   /* ---------- determine action paramater values -------------------------- */
8309
8310   int level_time_value =
8311     (level.time > 0 ? TimeLeft :
8312      TimePlayed);
8313
8314   int action_arg_element =
8315     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8316      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8317      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8318      EL_EMPTY);
8319
8320   int action_arg_direction =
8321     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8322      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8323      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8324      change->actual_trigger_side :
8325      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8326      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8327      MV_NONE);
8328
8329   int action_arg_number_min =
8330     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8331      CA_ARG_MIN);
8332
8333   int action_arg_number_max =
8334     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8335      action_type == CA_SET_LEVEL_GEMS ? 999 :
8336      action_type == CA_SET_LEVEL_TIME ? 9999 :
8337      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8338      action_type == CA_SET_CE_VALUE ? 9999 :
8339      action_type == CA_SET_CE_SCORE ? 9999 :
8340      CA_ARG_MAX);
8341
8342   int action_arg_number_reset =
8343     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8344      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8345      action_type == CA_SET_LEVEL_TIME ? level.time :
8346      action_type == CA_SET_LEVEL_SCORE ? 0 :
8347 #if USE_NEW_CUSTOM_VALUE
8348      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8349 #else
8350      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8351 #endif
8352      action_type == CA_SET_CE_SCORE ? 0 :
8353      0);
8354
8355   int action_arg_number =
8356     (action_arg <= CA_ARG_MAX ? action_arg :
8357      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8358      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8359      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8360      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8361      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8362      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8363 #if USE_NEW_CUSTOM_VALUE
8364      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8365 #else
8366      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8367 #endif
8368      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8369      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8370      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8371      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8372      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8373      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8374      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8375      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8376      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8377      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8378      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8379      -1);
8380
8381   int action_arg_number_old =
8382     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8383      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8384      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8385      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8386      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8387      0);
8388
8389   int action_arg_number_new =
8390     getModifiedActionNumber(action_arg_number_old,
8391                             action_mode, action_arg_number,
8392                             action_arg_number_min, action_arg_number_max);
8393
8394   int trigger_player_bits =
8395     (change->actual_trigger_player >= EL_PLAYER_1 &&
8396      change->actual_trigger_player <= EL_PLAYER_4 ?
8397      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8398      PLAYER_BITS_ANY);
8399
8400   int action_arg_player_bits =
8401     (action_arg >= CA_ARG_PLAYER_1 &&
8402      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8403      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8404      PLAYER_BITS_ANY);
8405
8406   /* ---------- execute action  -------------------------------------------- */
8407
8408   switch (action_type)
8409   {
8410     case CA_NO_ACTION:
8411     {
8412       return;
8413     }
8414
8415     /* ---------- level actions  ------------------------------------------- */
8416
8417     case CA_RESTART_LEVEL:
8418     {
8419       game.restart_level = TRUE;
8420
8421       break;
8422     }
8423
8424     case CA_SHOW_ENVELOPE:
8425     {
8426       int element = getSpecialActionElement(action_arg_element,
8427                                             action_arg_number, EL_ENVELOPE_1);
8428
8429       if (IS_ENVELOPE(element))
8430         local_player->show_envelope = element;
8431
8432       break;
8433     }
8434
8435     case CA_SET_LEVEL_TIME:
8436     {
8437       if (level.time > 0)       /* only modify limited time value */
8438       {
8439         TimeLeft = action_arg_number_new;
8440
8441         DrawGameValue_Time(TimeLeft);
8442
8443         if (!TimeLeft && setup.time_limit)
8444           for (i = 0; i < MAX_PLAYERS; i++)
8445             KillPlayer(&stored_player[i]);
8446       }
8447
8448       break;
8449     }
8450
8451     case CA_SET_LEVEL_SCORE:
8452     {
8453       local_player->score = action_arg_number_new;
8454
8455       DrawGameValue_Score(local_player->score);
8456
8457       break;
8458     }
8459
8460     case CA_SET_LEVEL_GEMS:
8461     {
8462       local_player->gems_still_needed = action_arg_number_new;
8463
8464       DrawGameValue_Emeralds(local_player->gems_still_needed);
8465
8466       break;
8467     }
8468
8469 #if !USE_PLAYER_GRAVITY
8470     case CA_SET_LEVEL_GRAVITY:
8471     {
8472       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
8473                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
8474                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8475                       game.gravity);
8476       break;
8477     }
8478 #endif
8479
8480     case CA_SET_LEVEL_WIND:
8481     {
8482       game.wind_direction = action_arg_direction;
8483
8484       break;
8485     }
8486
8487     /* ---------- player actions  ------------------------------------------ */
8488
8489     case CA_MOVE_PLAYER:
8490     {
8491       /* automatically move to the next field in specified direction */
8492       for (i = 0; i < MAX_PLAYERS; i++)
8493         if (trigger_player_bits & (1 << i))
8494           stored_player[i].programmed_action = action_arg_direction;
8495
8496       break;
8497     }
8498
8499     case CA_EXIT_PLAYER:
8500     {
8501       for (i = 0; i < MAX_PLAYERS; i++)
8502         if (action_arg_player_bits & (1 << i))
8503           PlayerWins(&stored_player[i]);
8504
8505       break;
8506     }
8507
8508     case CA_KILL_PLAYER:
8509     {
8510       for (i = 0; i < MAX_PLAYERS; i++)
8511         if (action_arg_player_bits & (1 << i))
8512           KillPlayer(&stored_player[i]);
8513
8514       break;
8515     }
8516
8517     case CA_SET_PLAYER_KEYS:
8518     {
8519       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8520       int element = getSpecialActionElement(action_arg_element,
8521                                             action_arg_number, EL_KEY_1);
8522
8523       if (IS_KEY(element))
8524       {
8525         for (i = 0; i < MAX_PLAYERS; i++)
8526         {
8527           if (trigger_player_bits & (1 << i))
8528           {
8529             stored_player[i].key[KEY_NR(element)] = key_state;
8530
8531             DrawGameDoorValues();
8532           }
8533         }
8534       }
8535
8536       break;
8537     }
8538
8539     case CA_SET_PLAYER_SPEED:
8540     {
8541       for (i = 0; i < MAX_PLAYERS; i++)
8542       {
8543         if (trigger_player_bits & (1 << i))
8544         {
8545           int move_stepsize = TILEX / stored_player[i].move_delay_value;
8546
8547           if (action_arg == CA_ARG_SPEED_FASTER &&
8548               stored_player[i].cannot_move)
8549           {
8550             action_arg_number = STEPSIZE_VERY_SLOW;
8551           }
8552           else if (action_arg == CA_ARG_SPEED_SLOWER ||
8553                    action_arg == CA_ARG_SPEED_FASTER)
8554           {
8555             action_arg_number = 2;
8556             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8557                            CA_MODE_MULTIPLY);
8558           }
8559           else if (action_arg == CA_ARG_NUMBER_RESET)
8560           {
8561             action_arg_number = level.initial_player_stepsize[i];
8562           }
8563
8564           move_stepsize =
8565             getModifiedActionNumber(move_stepsize,
8566                                     action_mode,
8567                                     action_arg_number,
8568                                     action_arg_number_min,
8569                                     action_arg_number_max);
8570
8571           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8572         }
8573       }
8574
8575       break;
8576     }
8577
8578     case CA_SET_PLAYER_SHIELD:
8579     {
8580       for (i = 0; i < MAX_PLAYERS; i++)
8581       {
8582         if (trigger_player_bits & (1 << i))
8583         {
8584           if (action_arg == CA_ARG_SHIELD_OFF)
8585           {
8586             stored_player[i].shield_normal_time_left = 0;
8587             stored_player[i].shield_deadly_time_left = 0;
8588           }
8589           else if (action_arg == CA_ARG_SHIELD_NORMAL)
8590           {
8591             stored_player[i].shield_normal_time_left = 999999;
8592           }
8593           else if (action_arg == CA_ARG_SHIELD_DEADLY)
8594           {
8595             stored_player[i].shield_normal_time_left = 999999;
8596             stored_player[i].shield_deadly_time_left = 999999;
8597           }
8598         }
8599       }
8600
8601       break;
8602     }
8603
8604 #if USE_PLAYER_GRAVITY
8605     case CA_SET_PLAYER_GRAVITY:
8606     {
8607       for (i = 0; i < MAX_PLAYERS; i++)
8608       {
8609         if (trigger_player_bits & (1 << i))
8610         {
8611           stored_player[i].gravity =
8612             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
8613              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
8614              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8615              stored_player[i].gravity);
8616         }
8617       }
8618
8619       break;
8620     }
8621 #endif
8622
8623     case CA_SET_PLAYER_ARTWORK:
8624     {
8625       for (i = 0; i < MAX_PLAYERS; i++)
8626       {
8627         if (trigger_player_bits & (1 << i))
8628         {
8629           int artwork_element = action_arg_element;
8630
8631           if (action_arg == CA_ARG_ELEMENT_RESET)
8632             artwork_element =
8633               (level.use_artwork_element[i] ? level.artwork_element[i] :
8634                stored_player[i].element_nr);
8635
8636 #if USE_GFX_RESET_PLAYER_ARTWORK
8637           if (stored_player[i].artwork_element != artwork_element)
8638             stored_player[i].Frame = 0;
8639 #endif
8640
8641           stored_player[i].artwork_element = artwork_element;
8642
8643           SetPlayerWaiting(&stored_player[i], FALSE);
8644
8645           /* set number of special actions for bored and sleeping animation */
8646           stored_player[i].num_special_action_bored =
8647             get_num_special_action(artwork_element,
8648                                    ACTION_BORING_1, ACTION_BORING_LAST);
8649           stored_player[i].num_special_action_sleeping =
8650             get_num_special_action(artwork_element,
8651                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8652         }
8653       }
8654
8655       break;
8656     }
8657
8658     /* ---------- CE actions  ---------------------------------------------- */
8659
8660     case CA_SET_CE_VALUE:
8661     {
8662 #if USE_NEW_CUSTOM_VALUE
8663       int last_ce_value = CustomValue[x][y];
8664
8665       CustomValue[x][y] = action_arg_number_new;
8666
8667       if (CustomValue[x][y] != last_ce_value)
8668       {
8669         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8670         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8671
8672         if (CustomValue[x][y] == 0)
8673         {
8674           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8675           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8676         }
8677       }
8678 #endif
8679
8680       break;
8681     }
8682
8683     case CA_SET_CE_SCORE:
8684     {
8685 #if USE_NEW_CUSTOM_VALUE
8686       int last_ce_score = ei->collect_score;
8687
8688       ei->collect_score = action_arg_number_new;
8689
8690       if (ei->collect_score != last_ce_score)
8691       {
8692         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8693         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8694
8695         if (ei->collect_score == 0)
8696         {
8697           int xx, yy;
8698
8699           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8700           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8701
8702           /*
8703             This is a very special case that seems to be a mixture between
8704             CheckElementChange() and CheckTriggeredElementChange(): while
8705             the first one only affects single elements that are triggered
8706             directly, the second one affects multiple elements in the playfield
8707             that are triggered indirectly by another element. This is a third
8708             case: Changing the CE score always affects multiple identical CEs,
8709             so every affected CE must be checked, not only the single CE for
8710             which the CE score was changed in the first place (as every instance
8711             of that CE shares the same CE score, and therefore also can change)!
8712           */
8713           SCAN_PLAYFIELD(xx, yy)
8714           {
8715             if (Feld[xx][yy] == element)
8716               CheckElementChange(xx, yy, element, EL_UNDEFINED,
8717                                  CE_SCORE_GETS_ZERO);
8718           }
8719         }
8720       }
8721 #endif
8722
8723       break;
8724     }
8725
8726     /* ---------- engine actions  ------------------------------------------ */
8727
8728     case CA_SET_ENGINE_SCAN_MODE:
8729     {
8730       InitPlayfieldScanMode(action_arg);
8731
8732       break;
8733     }
8734
8735     default:
8736       break;
8737   }
8738 }
8739
8740 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8741 {
8742   int old_element = Feld[x][y];
8743   int new_element = get_element_from_group_element(element);
8744   int previous_move_direction = MovDir[x][y];
8745 #if USE_NEW_CUSTOM_VALUE
8746   int last_ce_value = CustomValue[x][y];
8747 #endif
8748   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
8749   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8750   boolean add_player_onto_element = (new_element_is_player &&
8751 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8752                                      /* this breaks SnakeBite when a snake is
8753                                         halfway through a door that closes */
8754                                      /* NOW FIXED AT LEVEL INIT IN files.c */
8755                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
8756 #endif
8757                                      IS_WALKABLE(old_element));
8758
8759 #if 0
8760   /* check if element under the player changes from accessible to unaccessible
8761      (needed for special case of dropping element which then changes) */
8762   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8763       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8764   {
8765     Bang(x, y);
8766
8767     return;
8768   }
8769 #endif
8770
8771   if (!add_player_onto_element)
8772   {
8773     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8774       RemoveMovingField(x, y);
8775     else
8776       RemoveField(x, y);
8777
8778     Feld[x][y] = new_element;
8779
8780 #if !USE_GFX_RESET_GFX_ANIMATION
8781     ResetGfxAnimation(x, y);
8782     ResetRandomAnimationValue(x, y);
8783 #endif
8784
8785     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8786       MovDir[x][y] = previous_move_direction;
8787
8788 #if USE_NEW_CUSTOM_VALUE
8789     if (element_info[new_element].use_last_ce_value)
8790       CustomValue[x][y] = last_ce_value;
8791 #endif
8792
8793     InitField_WithBug1(x, y, FALSE);
8794
8795     new_element = Feld[x][y];   /* element may have changed */
8796
8797 #if USE_GFX_RESET_GFX_ANIMATION
8798     ResetGfxAnimation(x, y);
8799     ResetRandomAnimationValue(x, y);
8800 #endif
8801
8802     DrawLevelField(x, y);
8803
8804     if (GFX_CRUMBLED(new_element))
8805       DrawLevelFieldCrumbledSandNeighbours(x, y);
8806   }
8807
8808 #if 1
8809   /* check if element under the player changes from accessible to unaccessible
8810      (needed for special case of dropping element which then changes) */
8811   /* (must be checked after creating new element for walkable group elements) */
8812 #if USE_FIX_KILLED_BY_NON_WALKABLE
8813   if (IS_PLAYER(x, y) && !player_explosion_protected &&
8814       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8815   {
8816     Bang(x, y);
8817
8818     return;
8819   }
8820 #else
8821   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8822       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8823   {
8824     Bang(x, y);
8825
8826     return;
8827   }
8828 #endif
8829 #endif
8830
8831   /* "ChangeCount" not set yet to allow "entered by player" change one time */
8832   if (new_element_is_player)
8833     RelocatePlayer(x, y, new_element);
8834
8835   if (is_change)
8836     ChangeCount[x][y]++;        /* count number of changes in the same frame */
8837
8838   TestIfBadThingTouchesPlayer(x, y);
8839   TestIfPlayerTouchesCustomElement(x, y);
8840   TestIfElementTouchesCustomElement(x, y);
8841 }
8842
8843 static void CreateField(int x, int y, int element)
8844 {
8845   CreateFieldExt(x, y, element, FALSE);
8846 }
8847
8848 static void CreateElementFromChange(int x, int y, int element)
8849 {
8850   element = GET_VALID_RUNTIME_ELEMENT(element);
8851
8852 #if USE_STOP_CHANGED_ELEMENTS
8853   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8854   {
8855     int old_element = Feld[x][y];
8856
8857     /* prevent changed element from moving in same engine frame
8858        unless both old and new element can either fall or move */
8859     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8860         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8861       Stop[x][y] = TRUE;
8862   }
8863 #endif
8864
8865   CreateFieldExt(x, y, element, TRUE);
8866 }
8867
8868 static boolean ChangeElement(int x, int y, int element, int page)
8869 {
8870   struct ElementInfo *ei = &element_info[element];
8871   struct ElementChangeInfo *change = &ei->change_page[page];
8872   int ce_value = CustomValue[x][y];
8873   int ce_score = ei->collect_score;
8874   int target_element;
8875   int old_element = Feld[x][y];
8876
8877   /* always use default change event to prevent running into a loop */
8878   if (ChangeEvent[x][y] == -1)
8879     ChangeEvent[x][y] = CE_DELAY;
8880
8881   if (ChangeEvent[x][y] == CE_DELAY)
8882   {
8883     /* reset actual trigger element, trigger player and action element */
8884     change->actual_trigger_element = EL_EMPTY;
8885     change->actual_trigger_player = EL_PLAYER_1;
8886     change->actual_trigger_side = CH_SIDE_NONE;
8887     change->actual_trigger_ce_value = 0;
8888     change->actual_trigger_ce_score = 0;
8889   }
8890
8891   /* do not change elements more than a specified maximum number of changes */
8892   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8893     return FALSE;
8894
8895   ChangeCount[x][y]++;          /* count number of changes in the same frame */
8896
8897   if (change->explode)
8898   {
8899     Bang(x, y);
8900
8901     return TRUE;
8902   }
8903
8904   if (change->use_target_content)
8905   {
8906     boolean complete_replace = TRUE;
8907     boolean can_replace[3][3];
8908     int xx, yy;
8909
8910     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8911     {
8912       boolean is_empty;
8913       boolean is_walkable;
8914       boolean is_diggable;
8915       boolean is_collectible;
8916       boolean is_removable;
8917       boolean is_destructible;
8918       int ex = x + xx - 1;
8919       int ey = y + yy - 1;
8920       int content_element = change->target_content.e[xx][yy];
8921       int e;
8922
8923       can_replace[xx][yy] = TRUE;
8924
8925       if (ex == x && ey == y)   /* do not check changing element itself */
8926         continue;
8927
8928       if (content_element == EL_EMPTY_SPACE)
8929       {
8930         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8931
8932         continue;
8933       }
8934
8935       if (!IN_LEV_FIELD(ex, ey))
8936       {
8937         can_replace[xx][yy] = FALSE;
8938         complete_replace = FALSE;
8939
8940         continue;
8941       }
8942
8943       e = Feld[ex][ey];
8944
8945       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8946         e = MovingOrBlocked2Element(ex, ey);
8947
8948       is_empty = (IS_FREE(ex, ey) ||
8949                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8950
8951       is_walkable     = (is_empty || IS_WALKABLE(e));
8952       is_diggable     = (is_empty || IS_DIGGABLE(e));
8953       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8954       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8955       is_removable    = (is_diggable || is_collectible);
8956
8957       can_replace[xx][yy] =
8958         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
8959           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
8960           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
8961           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
8962           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
8963           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8964          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8965
8966       if (!can_replace[xx][yy])
8967         complete_replace = FALSE;
8968     }
8969
8970     if (!change->only_if_complete || complete_replace)
8971     {
8972       boolean something_has_changed = FALSE;
8973
8974       if (change->only_if_complete && change->use_random_replace &&
8975           RND(100) < change->random_percentage)
8976         return FALSE;
8977
8978       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8979       {
8980         int ex = x + xx - 1;
8981         int ey = y + yy - 1;
8982         int content_element;
8983
8984         if (can_replace[xx][yy] && (!change->use_random_replace ||
8985                                     RND(100) < change->random_percentage))
8986         {
8987           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8988             RemoveMovingField(ex, ey);
8989
8990           ChangeEvent[ex][ey] = ChangeEvent[x][y];
8991
8992           content_element = change->target_content.e[xx][yy];
8993           target_element = GET_TARGET_ELEMENT(element, content_element, change,
8994                                               ce_value, ce_score);
8995
8996           CreateElementFromChange(ex, ey, target_element);
8997
8998           something_has_changed = TRUE;
8999
9000           /* for symmetry reasons, freeze newly created border elements */
9001           if (ex != x || ey != y)
9002             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9003         }
9004       }
9005
9006       if (something_has_changed)
9007       {
9008         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9009         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9010       }
9011     }
9012   }
9013   else
9014   {
9015     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9016                                         ce_value, ce_score);
9017
9018     if (element == EL_DIAGONAL_GROWING ||
9019         element == EL_DIAGONAL_SHRINKING)
9020     {
9021       target_element = Store[x][y];
9022
9023       Store[x][y] = EL_EMPTY;
9024     }
9025
9026     CreateElementFromChange(x, y, target_element);
9027
9028     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9029     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9030   }
9031
9032   /* this uses direct change before indirect change */
9033   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9034
9035   return TRUE;
9036 }
9037
9038 #if USE_NEW_DELAYED_ACTION
9039
9040 static void HandleElementChange(int x, int y, int page)
9041 {
9042   int element = MovingOrBlocked2Element(x, y);
9043   struct ElementInfo *ei = &element_info[element];
9044   struct ElementChangeInfo *change = &ei->change_page[page];
9045
9046 #ifdef DEBUG
9047   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9048       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9049   {
9050     printf("\n\n");
9051     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9052            x, y, element, element_info[element].token_name);
9053     printf("HandleElementChange(): This should never happen!\n");
9054     printf("\n\n");
9055   }
9056 #endif
9057
9058   /* this can happen with classic bombs on walkable, changing elements */
9059   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9060   {
9061 #if 0
9062     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9063       ChangeDelay[x][y] = 0;
9064 #endif
9065
9066     return;
9067   }
9068
9069   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9070   {
9071     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9072
9073     if (change->can_change)
9074     {
9075       ResetGfxAnimation(x, y);
9076       ResetRandomAnimationValue(x, y);
9077
9078       if (change->pre_change_function)
9079         change->pre_change_function(x, y);
9080     }
9081   }
9082
9083   ChangeDelay[x][y]--;
9084
9085   if (ChangeDelay[x][y] != 0)           /* continue element change */
9086   {
9087     if (change->can_change)
9088     {
9089       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9090
9091       if (IS_ANIMATED(graphic))
9092         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9093
9094       if (change->change_function)
9095         change->change_function(x, y);
9096     }
9097   }
9098   else                                  /* finish element change */
9099   {
9100     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9101     {
9102       page = ChangePage[x][y];
9103       ChangePage[x][y] = -1;
9104
9105       change = &ei->change_page[page];
9106     }
9107
9108     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9109     {
9110       ChangeDelay[x][y] = 1;            /* try change after next move step */
9111       ChangePage[x][y] = page;          /* remember page to use for change */
9112
9113       return;
9114     }
9115
9116     if (change->can_change)
9117     {
9118       if (ChangeElement(x, y, element, page))
9119       {
9120         if (change->post_change_function)
9121           change->post_change_function(x, y);
9122       }
9123     }
9124
9125     if (change->has_action)
9126       ExecuteCustomElementAction(x, y, element, page);
9127   }
9128 }
9129
9130 #else
9131
9132 static void HandleElementChange(int x, int y, int page)
9133 {
9134   int element = MovingOrBlocked2Element(x, y);
9135   struct ElementInfo *ei = &element_info[element];
9136   struct ElementChangeInfo *change = &ei->change_page[page];
9137
9138 #ifdef DEBUG
9139   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9140   {
9141     printf("\n\n");
9142     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9143            x, y, element, element_info[element].token_name);
9144     printf("HandleElementChange(): This should never happen!\n");
9145     printf("\n\n");
9146   }
9147 #endif
9148
9149   /* this can happen with classic bombs on walkable, changing elements */
9150   if (!CAN_CHANGE(element))
9151   {
9152 #if 0
9153     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9154       ChangeDelay[x][y] = 0;
9155 #endif
9156
9157     return;
9158   }
9159
9160   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9161   {
9162     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9163
9164     ResetGfxAnimation(x, y);
9165     ResetRandomAnimationValue(x, y);
9166
9167     if (change->pre_change_function)
9168       change->pre_change_function(x, y);
9169   }
9170
9171   ChangeDelay[x][y]--;
9172
9173   if (ChangeDelay[x][y] != 0)           /* continue element change */
9174   {
9175     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9176
9177     if (IS_ANIMATED(graphic))
9178       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9179
9180     if (change->change_function)
9181       change->change_function(x, y);
9182   }
9183   else                                  /* finish element change */
9184   {
9185     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9186     {
9187       page = ChangePage[x][y];
9188       ChangePage[x][y] = -1;
9189
9190       change = &ei->change_page[page];
9191     }
9192
9193     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9194     {
9195       ChangeDelay[x][y] = 1;            /* try change after next move step */
9196       ChangePage[x][y] = page;          /* remember page to use for change */
9197
9198       return;
9199     }
9200
9201     if (ChangeElement(x, y, element, page))
9202     {
9203       if (change->post_change_function)
9204         change->post_change_function(x, y);
9205     }
9206   }
9207 }
9208
9209 #endif
9210
9211 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9212                                               int trigger_element,
9213                                               int trigger_event,
9214                                               int trigger_player,
9215                                               int trigger_side,
9216                                               int trigger_page)
9217 {
9218   boolean change_done_any = FALSE;
9219   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9220   int i;
9221
9222   if (!(trigger_events[trigger_element][trigger_event]))
9223     return FALSE;
9224
9225 #if 0
9226   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9227          trigger_event, recursion_loop_depth, recursion_loop_detected,
9228          recursion_loop_element, EL_NAME(recursion_loop_element));
9229 #endif
9230
9231   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9232
9233   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9234   {
9235     int element = EL_CUSTOM_START + i;
9236     boolean change_done = FALSE;
9237     int p;
9238
9239     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9240         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9241       continue;
9242
9243     for (p = 0; p < element_info[element].num_change_pages; p++)
9244     {
9245       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9246
9247       if (change->can_change_or_has_action &&
9248           change->has_event[trigger_event] &&
9249           change->trigger_side & trigger_side &&
9250           change->trigger_player & trigger_player &&
9251           change->trigger_page & trigger_page_bits &&
9252           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9253       {
9254         change->actual_trigger_element = trigger_element;
9255         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9256         change->actual_trigger_side = trigger_side;
9257         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9258         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9259
9260         if ((change->can_change && !change_done) || change->has_action)
9261         {
9262           int x, y;
9263
9264           SCAN_PLAYFIELD(x, y)
9265           {
9266             if (Feld[x][y] == element)
9267             {
9268               if (change->can_change && !change_done)
9269               {
9270                 ChangeDelay[x][y] = 1;
9271                 ChangeEvent[x][y] = trigger_event;
9272
9273                 HandleElementChange(x, y, p);
9274               }
9275 #if USE_NEW_DELAYED_ACTION
9276               else if (change->has_action)
9277               {
9278                 ExecuteCustomElementAction(x, y, element, p);
9279                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9280               }
9281 #else
9282               if (change->has_action)
9283               {
9284                 ExecuteCustomElementAction(x, y, element, p);
9285                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9286               }
9287 #endif
9288             }
9289           }
9290
9291           if (change->can_change)
9292           {
9293             change_done = TRUE;
9294             change_done_any = TRUE;
9295           }
9296         }
9297       }
9298     }
9299   }
9300
9301   RECURSION_LOOP_DETECTION_END();
9302
9303   return change_done_any;
9304 }
9305
9306 static boolean CheckElementChangeExt(int x, int y,
9307                                      int element,
9308                                      int trigger_element,
9309                                      int trigger_event,
9310                                      int trigger_player,
9311                                      int trigger_side)
9312 {
9313   boolean change_done = FALSE;
9314   int p;
9315
9316   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9317       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9318     return FALSE;
9319
9320   if (Feld[x][y] == EL_BLOCKED)
9321   {
9322     Blocked2Moving(x, y, &x, &y);
9323     element = Feld[x][y];
9324   }
9325
9326 #if 0
9327   /* check if element has already changed */
9328   if (Feld[x][y] != element)
9329     return FALSE;
9330 #else
9331   /* check if element has already changed or is about to change after moving */
9332   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9333        Feld[x][y] != element) ||
9334
9335       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9336        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9337         ChangePage[x][y] != -1)))
9338     return FALSE;
9339 #endif
9340
9341 #if 0
9342   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
9343          trigger_event, recursion_loop_depth, recursion_loop_detected,
9344          recursion_loop_element, EL_NAME(recursion_loop_element));
9345 #endif
9346
9347   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
9348
9349   for (p = 0; p < element_info[element].num_change_pages; p++)
9350   {
9351     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9352
9353     /* check trigger element for all events where the element that is checked
9354        for changing interacts with a directly adjacent element -- this is
9355        different to element changes that affect other elements to change on the
9356        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
9357     boolean check_trigger_element =
9358       (trigger_event == CE_TOUCHING_X ||
9359        trigger_event == CE_HITTING_X ||
9360        trigger_event == CE_HIT_BY_X ||
9361 #if 1
9362        /* this one was forgotten until 3.2.3 */
9363        trigger_event == CE_DIGGING_X);
9364 #endif
9365
9366     if (change->can_change_or_has_action &&
9367         change->has_event[trigger_event] &&
9368         change->trigger_side & trigger_side &&
9369         change->trigger_player & trigger_player &&
9370         (!check_trigger_element ||
9371          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9372     {
9373       change->actual_trigger_element = trigger_element;
9374       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9375       change->actual_trigger_side = trigger_side;
9376       change->actual_trigger_ce_value = CustomValue[x][y];
9377       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9378
9379       /* special case: trigger element not at (x,y) position for some events */
9380       if (check_trigger_element)
9381       {
9382         static struct
9383         {
9384           int dx, dy;
9385         } move_xy[] =
9386           {
9387             {  0,  0 },
9388             { -1,  0 },
9389             { +1,  0 },
9390             {  0,  0 },
9391             {  0, -1 },
9392             {  0,  0 }, { 0, 0 }, { 0, 0 },
9393             {  0, +1 }
9394           };
9395
9396         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9397         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9398
9399         change->actual_trigger_ce_value = CustomValue[xx][yy];
9400         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9401       }
9402
9403       if (change->can_change && !change_done)
9404       {
9405         ChangeDelay[x][y] = 1;
9406         ChangeEvent[x][y] = trigger_event;
9407
9408         HandleElementChange(x, y, p);
9409
9410         change_done = TRUE;
9411       }
9412 #if USE_NEW_DELAYED_ACTION
9413       else if (change->has_action)
9414       {
9415         ExecuteCustomElementAction(x, y, element, p);
9416         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9417       }
9418 #else
9419       if (change->has_action)
9420       {
9421         ExecuteCustomElementAction(x, y, element, p);
9422         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9423       }
9424 #endif
9425     }
9426   }
9427
9428   RECURSION_LOOP_DETECTION_END();
9429
9430   return change_done;
9431 }
9432
9433 static void PlayPlayerSound(struct PlayerInfo *player)
9434 {
9435   int jx = player->jx, jy = player->jy;
9436   int sound_element = player->artwork_element;
9437   int last_action = player->last_action_waiting;
9438   int action = player->action_waiting;
9439
9440   if (player->is_waiting)
9441   {
9442     if (action != last_action)
9443       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9444     else
9445       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9446   }
9447   else
9448   {
9449     if (action != last_action)
9450       StopSound(element_info[sound_element].sound[last_action]);
9451
9452     if (last_action == ACTION_SLEEPING)
9453       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9454   }
9455 }
9456
9457 static void PlayAllPlayersSound()
9458 {
9459   int i;
9460
9461   for (i = 0; i < MAX_PLAYERS; i++)
9462     if (stored_player[i].active)
9463       PlayPlayerSound(&stored_player[i]);
9464 }
9465
9466 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9467 {
9468   boolean last_waiting = player->is_waiting;
9469   int move_dir = player->MovDir;
9470
9471   player->dir_waiting = move_dir;
9472   player->last_action_waiting = player->action_waiting;
9473
9474   if (is_waiting)
9475   {
9476     if (!last_waiting)          /* not waiting -> waiting */
9477     {
9478       player->is_waiting = TRUE;
9479
9480       player->frame_counter_bored =
9481         FrameCounter +
9482         game.player_boring_delay_fixed +
9483         GetSimpleRandom(game.player_boring_delay_random);
9484       player->frame_counter_sleeping =
9485         FrameCounter +
9486         game.player_sleeping_delay_fixed +
9487         GetSimpleRandom(game.player_sleeping_delay_random);
9488
9489       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9490     }
9491
9492     if (game.player_sleeping_delay_fixed +
9493         game.player_sleeping_delay_random > 0 &&
9494         player->anim_delay_counter == 0 &&
9495         player->post_delay_counter == 0 &&
9496         FrameCounter >= player->frame_counter_sleeping)
9497       player->is_sleeping = TRUE;
9498     else if (game.player_boring_delay_fixed +
9499              game.player_boring_delay_random > 0 &&
9500              FrameCounter >= player->frame_counter_bored)
9501       player->is_bored = TRUE;
9502
9503     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9504                               player->is_bored ? ACTION_BORING :
9505                               ACTION_WAITING);
9506
9507     if (player->is_sleeping && player->use_murphy)
9508     {
9509       /* special case for sleeping Murphy when leaning against non-free tile */
9510
9511       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9512           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9513            !IS_MOVING(player->jx - 1, player->jy)))
9514         move_dir = MV_LEFT;
9515       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9516                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9517                 !IS_MOVING(player->jx + 1, player->jy)))
9518         move_dir = MV_RIGHT;
9519       else
9520         player->is_sleeping = FALSE;
9521
9522       player->dir_waiting = move_dir;
9523     }
9524
9525     if (player->is_sleeping)
9526     {
9527       if (player->num_special_action_sleeping > 0)
9528       {
9529         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9530         {
9531           int last_special_action = player->special_action_sleeping;
9532           int num_special_action = player->num_special_action_sleeping;
9533           int special_action =
9534             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9535              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9536              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9537              last_special_action + 1 : ACTION_SLEEPING);
9538           int special_graphic =
9539             el_act_dir2img(player->artwork_element, special_action, move_dir);
9540
9541           player->anim_delay_counter =
9542             graphic_info[special_graphic].anim_delay_fixed +
9543             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9544           player->post_delay_counter =
9545             graphic_info[special_graphic].post_delay_fixed +
9546             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9547
9548           player->special_action_sleeping = special_action;
9549         }
9550
9551         if (player->anim_delay_counter > 0)
9552         {
9553           player->action_waiting = player->special_action_sleeping;
9554           player->anim_delay_counter--;
9555         }
9556         else if (player->post_delay_counter > 0)
9557         {
9558           player->post_delay_counter--;
9559         }
9560       }
9561     }
9562     else if (player->is_bored)
9563     {
9564       if (player->num_special_action_bored > 0)
9565       {
9566         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9567         {
9568           int special_action =
9569             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
9570           int special_graphic =
9571             el_act_dir2img(player->artwork_element, special_action, move_dir);
9572
9573           player->anim_delay_counter =
9574             graphic_info[special_graphic].anim_delay_fixed +
9575             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
9576           player->post_delay_counter =
9577             graphic_info[special_graphic].post_delay_fixed +
9578             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
9579
9580           player->special_action_bored = special_action;
9581         }
9582
9583         if (player->anim_delay_counter > 0)
9584         {
9585           player->action_waiting = player->special_action_bored;
9586           player->anim_delay_counter--;
9587         }
9588         else if (player->post_delay_counter > 0)
9589         {
9590           player->post_delay_counter--;
9591         }
9592       }
9593     }
9594   }
9595   else if (last_waiting)        /* waiting -> not waiting */
9596   {
9597     player->is_waiting = FALSE;
9598     player->is_bored = FALSE;
9599     player->is_sleeping = FALSE;
9600
9601     player->frame_counter_bored = -1;
9602     player->frame_counter_sleeping = -1;
9603
9604     player->anim_delay_counter = 0;
9605     player->post_delay_counter = 0;
9606
9607     player->dir_waiting = player->MovDir;
9608     player->action_waiting = ACTION_DEFAULT;
9609
9610     player->special_action_bored = ACTION_DEFAULT;
9611     player->special_action_sleeping = ACTION_DEFAULT;
9612   }
9613 }
9614
9615 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9616 {
9617   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9618   int left      = player_action & JOY_LEFT;
9619   int right     = player_action & JOY_RIGHT;
9620   int up        = player_action & JOY_UP;
9621   int down      = player_action & JOY_DOWN;
9622   int button1   = player_action & JOY_BUTTON_1;
9623   int button2   = player_action & JOY_BUTTON_2;
9624   int dx        = (left ? -1 : right ? 1 : 0);
9625   int dy        = (up   ? -1 : down  ? 1 : 0);
9626
9627   if (!player->active || tape.pausing)
9628     return 0;
9629
9630   if (player_action)
9631   {
9632     if (button1)
9633       snapped = SnapField(player, dx, dy);
9634     else
9635     {
9636       if (button2)
9637         dropped = DropElement(player);
9638
9639       moved = MovePlayer(player, dx, dy);
9640     }
9641
9642     if (tape.single_step && tape.recording && !tape.pausing)
9643     {
9644       if (button1 || (dropped && !moved))
9645       {
9646         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9647         SnapField(player, 0, 0);                /* stop snapping */
9648       }
9649     }
9650
9651     SetPlayerWaiting(player, FALSE);
9652
9653     return player_action;
9654   }
9655   else
9656   {
9657     /* no actions for this player (no input at player's configured device) */
9658
9659     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9660     SnapField(player, 0, 0);
9661     CheckGravityMovementWhenNotMoving(player);
9662
9663     if (player->MovPos == 0)
9664       SetPlayerWaiting(player, TRUE);
9665
9666     if (player->MovPos == 0)    /* needed for tape.playing */
9667       player->is_moving = FALSE;
9668
9669     player->is_dropping = FALSE;
9670     player->is_dropping_pressed = FALSE;
9671     player->drop_pressed_delay = 0;
9672
9673     return 0;
9674   }
9675 }
9676
9677 static void CheckLevelTime()
9678 {
9679   int i;
9680
9681   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9682   {
9683     if (level.native_em_level->lev->home == 0)  /* all players at home */
9684     {
9685       PlayerWins(local_player);
9686
9687       AllPlayersGone = TRUE;
9688
9689       level.native_em_level->lev->home = -1;
9690     }
9691
9692     if (level.native_em_level->ply[0]->alive == 0 &&
9693         level.native_em_level->ply[1]->alive == 0 &&
9694         level.native_em_level->ply[2]->alive == 0 &&
9695         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9696       AllPlayersGone = TRUE;
9697   }
9698
9699   if (TimeFrames >= FRAMES_PER_SECOND)
9700   {
9701     TimeFrames = 0;
9702     TapeTime++;
9703
9704     for (i = 0; i < MAX_PLAYERS; i++)
9705     {
9706       struct PlayerInfo *player = &stored_player[i];
9707
9708       if (SHIELD_ON(player))
9709       {
9710         player->shield_normal_time_left--;
9711
9712         if (player->shield_deadly_time_left > 0)
9713           player->shield_deadly_time_left--;
9714       }
9715     }
9716
9717     if (!local_player->LevelSolved && !level.use_step_counter)
9718     {
9719       TimePlayed++;
9720
9721       if (TimeLeft > 0)
9722       {
9723         TimeLeft--;
9724
9725         if (TimeLeft <= 10 && setup.time_limit)
9726           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
9727
9728         DrawGameValue_Time(TimeLeft);
9729
9730         if (!TimeLeft && setup.time_limit)
9731         {
9732           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9733             level.native_em_level->lev->killed_out_of_time = TRUE;
9734           else
9735             for (i = 0; i < MAX_PLAYERS; i++)
9736               KillPlayer(&stored_player[i]);
9737         }
9738       }
9739       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9740         DrawGameValue_Time(TimePlayed);
9741
9742       level.native_em_level->lev->time =
9743         (level.time == 0 ? TimePlayed : TimeLeft);
9744     }
9745
9746     if (tape.recording || tape.playing)
9747       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9748   }
9749 }
9750
9751 void AdvanceFrameAndPlayerCounters(int player_nr)
9752 {
9753   int i;
9754
9755   /* advance frame counters (global frame counter and time frame counter) */
9756   FrameCounter++;
9757   TimeFrames++;
9758
9759   /* advance player counters (counters for move delay, move animation etc.) */
9760   for (i = 0; i < MAX_PLAYERS; i++)
9761   {
9762     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9763     int move_delay_value = stored_player[i].move_delay_value;
9764     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9765
9766     if (!advance_player_counters)       /* not all players may be affected */
9767       continue;
9768
9769 #if USE_NEW_PLAYER_ANIM
9770     if (move_frames == 0)       /* less than one move per game frame */
9771     {
9772       int stepsize = TILEX / move_delay_value;
9773       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9774       int count = (stored_player[i].is_moving ?
9775                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9776
9777       if (count % delay == 0)
9778         move_frames = 1;
9779     }
9780 #endif
9781
9782     stored_player[i].Frame += move_frames;
9783
9784     if (stored_player[i].MovPos != 0)
9785       stored_player[i].StepFrame += move_frames;
9786
9787     if (stored_player[i].move_delay > 0)
9788       stored_player[i].move_delay--;
9789
9790     /* due to bugs in previous versions, counter must count up, not down */
9791     if (stored_player[i].push_delay != -1)
9792       stored_player[i].push_delay++;
9793
9794     if (stored_player[i].drop_delay > 0)
9795       stored_player[i].drop_delay--;
9796
9797     if (stored_player[i].is_dropping_pressed)
9798       stored_player[i].drop_pressed_delay++;
9799   }
9800 }
9801
9802 void StartGameActions(boolean init_network_game, boolean record_tape,
9803                       long random_seed)
9804 {
9805   unsigned long new_random_seed = InitRND(random_seed);
9806
9807   if (record_tape)
9808     TapeStartRecording(new_random_seed);
9809
9810 #if defined(NETWORK_AVALIABLE)
9811   if (init_network_game)
9812   {
9813     SendToServer_StartPlaying();
9814
9815     return;
9816   }
9817 #endif
9818
9819   InitGame();
9820 }
9821
9822 void GameActions()
9823 {
9824   static unsigned long game_frame_delay = 0;
9825   unsigned long game_frame_delay_value;
9826   byte *recorded_player_action;
9827   byte summarized_player_action = 0;
9828   byte tape_action[MAX_PLAYERS];
9829   int i;
9830
9831   /* detect endless loops, caused by custom element programming */
9832   if (recursion_loop_detected && recursion_loop_depth == 0)
9833   {
9834     char *message = getStringCat3("Internal Error ! Element ",
9835                                   EL_NAME(recursion_loop_element),
9836                                   " caused endless loop ! Quit the game ?");
9837
9838     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
9839           EL_NAME(recursion_loop_element));
9840
9841     RequestQuitGameExt(FALSE, level_editor_test_game, message);
9842
9843     recursion_loop_detected = FALSE;    /* if game should be continued */
9844
9845     free(message);
9846
9847     return;
9848   }
9849
9850   if (game.restart_level)
9851     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9852
9853   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9854   {
9855     if (level.native_em_level->lev->home == 0)  /* all players at home */
9856     {
9857       PlayerWins(local_player);
9858
9859       AllPlayersGone = TRUE;
9860
9861       level.native_em_level->lev->home = -1;
9862     }
9863
9864     if (level.native_em_level->ply[0]->alive == 0 &&
9865         level.native_em_level->ply[1]->alive == 0 &&
9866         level.native_em_level->ply[2]->alive == 0 &&
9867         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9868       AllPlayersGone = TRUE;
9869   }
9870
9871   if (local_player->LevelSolved)
9872     GameWon();
9873
9874   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9875     TapeStop();
9876
9877   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9878     return;
9879
9880   game_frame_delay_value =
9881     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9882
9883   if (tape.playing && tape.warp_forward && !tape.pausing)
9884     game_frame_delay_value = 0;
9885
9886   /* ---------- main game synchronization point ---------- */
9887
9888   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9889
9890   if (network_playing && !network_player_action_received)
9891   {
9892     /* try to get network player actions in time */
9893
9894 #if defined(NETWORK_AVALIABLE)
9895     /* last chance to get network player actions without main loop delay */
9896     HandleNetworking();
9897 #endif
9898
9899     /* game was quit by network peer */
9900     if (game_status != GAME_MODE_PLAYING)
9901       return;
9902
9903     if (!network_player_action_received)
9904       return;           /* failed to get network player actions in time */
9905
9906     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9907   }
9908
9909   if (tape.pausing)
9910     return;
9911
9912   /* at this point we know that we really continue executing the game */
9913
9914   network_player_action_received = FALSE;
9915
9916   /* when playing tape, read previously recorded player input from tape data */
9917   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9918
9919 #if 1
9920   /* TapePlayAction() may return NULL when toggling to "pause before death" */
9921   if (tape.pausing)
9922     return;
9923 #endif
9924
9925   if (tape.set_centered_player)
9926   {
9927     game.centered_player_nr_next = tape.centered_player_nr_next;
9928     game.set_centered_player = TRUE;
9929   }
9930
9931   for (i = 0; i < MAX_PLAYERS; i++)
9932   {
9933     summarized_player_action |= stored_player[i].action;
9934
9935     if (!network_playing)
9936       stored_player[i].effective_action = stored_player[i].action;
9937   }
9938
9939 #if defined(NETWORK_AVALIABLE)
9940   if (network_playing)
9941     SendToServer_MovePlayer(summarized_player_action);
9942 #endif
9943
9944   if (!options.network && !setup.team_mode)
9945     local_player->effective_action = summarized_player_action;
9946
9947   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9948   {
9949     for (i = 0; i < MAX_PLAYERS; i++)
9950       stored_player[i].effective_action =
9951         (i == game.centered_player_nr ? summarized_player_action : 0);
9952   }
9953
9954   if (recorded_player_action != NULL)
9955     for (i = 0; i < MAX_PLAYERS; i++)
9956       stored_player[i].effective_action = recorded_player_action[i];
9957
9958   for (i = 0; i < MAX_PLAYERS; i++)
9959   {
9960     tape_action[i] = stored_player[i].effective_action;
9961
9962     /* (this can only happen in the R'n'D game engine) */
9963     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9964       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9965   }
9966
9967   /* only record actions from input devices, but not programmed actions */
9968   if (tape.recording)
9969     TapeRecordAction(tape_action);
9970
9971   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9972   {
9973     GameActions_EM_Main();
9974   }
9975   else
9976   {
9977     GameActions_RND();
9978   }
9979 }
9980
9981 void GameActions_EM_Main()
9982 {
9983   byte effective_action[MAX_PLAYERS];
9984   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9985   int i;
9986
9987   for (i = 0; i < MAX_PLAYERS; i++)
9988     effective_action[i] = stored_player[i].effective_action;
9989
9990   GameActions_EM(effective_action, warp_mode);
9991
9992   CheckLevelTime();
9993
9994   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9995 }
9996
9997 void GameActions_RND()
9998 {
9999   int magic_wall_x = 0, magic_wall_y = 0;
10000   int i, x, y, element, graphic;
10001
10002   InitPlayfieldScanModeVars();
10003
10004 #if USE_ONE_MORE_CHANGE_PER_FRAME
10005   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10006   {
10007     SCAN_PLAYFIELD(x, y)
10008     {
10009       ChangeCount[x][y] = 0;
10010       ChangeEvent[x][y] = -1;
10011     }
10012   }
10013 #endif
10014
10015   if (game.set_centered_player)
10016   {
10017     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10018
10019     /* switching to "all players" only possible if all players fit to screen */
10020     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10021     {
10022       game.centered_player_nr_next = game.centered_player_nr;
10023       game.set_centered_player = FALSE;
10024     }
10025
10026     /* do not switch focus to non-existing (or non-active) player */
10027     if (game.centered_player_nr_next >= 0 &&
10028         !stored_player[game.centered_player_nr_next].active)
10029     {
10030       game.centered_player_nr_next = game.centered_player_nr;
10031       game.set_centered_player = FALSE;
10032     }
10033   }
10034
10035   if (game.set_centered_player &&
10036       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10037   {
10038     int sx, sy;
10039
10040     if (game.centered_player_nr_next == -1)
10041     {
10042       setScreenCenteredToAllPlayers(&sx, &sy);
10043     }
10044     else
10045     {
10046       sx = stored_player[game.centered_player_nr_next].jx;
10047       sy = stored_player[game.centered_player_nr_next].jy;
10048     }
10049
10050     game.centered_player_nr = game.centered_player_nr_next;
10051     game.set_centered_player = FALSE;
10052
10053     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
10054     DrawGameDoorValues();
10055   }
10056
10057   for (i = 0; i < MAX_PLAYERS; i++)
10058   {
10059     int actual_player_action = stored_player[i].effective_action;
10060
10061 #if 1
10062     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10063        - rnd_equinox_tetrachloride 048
10064        - rnd_equinox_tetrachloride_ii 096
10065        - rnd_emanuel_schmieg 002
10066        - doctor_sloan_ww 001, 020
10067     */
10068     if (stored_player[i].MovPos == 0)
10069       CheckGravityMovement(&stored_player[i]);
10070 #endif
10071
10072     /* overwrite programmed action with tape action */
10073     if (stored_player[i].programmed_action)
10074       actual_player_action = stored_player[i].programmed_action;
10075
10076     PlayerActions(&stored_player[i], actual_player_action);
10077
10078     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10079   }
10080
10081   ScrollScreen(NULL, SCROLL_GO_ON);
10082
10083   /* for backwards compatibility, the following code emulates a fixed bug that
10084      occured when pushing elements (causing elements that just made their last
10085      pushing step to already (if possible) make their first falling step in the
10086      same game frame, which is bad); this code is also needed to use the famous
10087      "spring push bug" which is used in older levels and might be wanted to be
10088      used also in newer levels, but in this case the buggy pushing code is only
10089      affecting the "spring" element and no other elements */
10090
10091   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10092   {
10093     for (i = 0; i < MAX_PLAYERS; i++)
10094     {
10095       struct PlayerInfo *player = &stored_player[i];
10096       int x = player->jx;
10097       int y = player->jy;
10098
10099       if (player->active && player->is_pushing && player->is_moving &&
10100           IS_MOVING(x, y) &&
10101           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10102            Feld[x][y] == EL_SPRING))
10103       {
10104         ContinueMoving(x, y);
10105
10106         /* continue moving after pushing (this is actually a bug) */
10107         if (!IS_MOVING(x, y))
10108         {
10109           Stop[x][y] = FALSE;
10110         }
10111       }
10112     }
10113   }
10114
10115   SCAN_PLAYFIELD(x, y)
10116   {
10117     ChangeCount[x][y] = 0;
10118     ChangeEvent[x][y] = -1;
10119
10120     /* this must be handled before main playfield loop */
10121     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10122     {
10123       MovDelay[x][y]--;
10124       if (MovDelay[x][y] <= 0)
10125         RemoveField(x, y);
10126     }
10127
10128 #if USE_NEW_SNAP_DELAY
10129     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10130     {
10131       MovDelay[x][y]--;
10132       if (MovDelay[x][y] <= 0)
10133       {
10134         RemoveField(x, y);
10135         DrawLevelField(x, y);
10136
10137         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10138       }
10139     }
10140 #endif
10141
10142 #if DEBUG
10143     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10144     {
10145       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10146       printf("GameActions(): This should never happen!\n");
10147
10148       ChangePage[x][y] = -1;
10149     }
10150 #endif
10151
10152     Stop[x][y] = FALSE;
10153     if (WasJustMoving[x][y] > 0)
10154       WasJustMoving[x][y]--;
10155     if (WasJustFalling[x][y] > 0)
10156       WasJustFalling[x][y]--;
10157     if (CheckCollision[x][y] > 0)
10158       CheckCollision[x][y]--;
10159     if (CheckImpact[x][y] > 0)
10160       CheckImpact[x][y]--;
10161
10162     GfxFrame[x][y]++;
10163
10164     /* reset finished pushing action (not done in ContinueMoving() to allow
10165        continuous pushing animation for elements with zero push delay) */
10166     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10167     {
10168       ResetGfxAnimation(x, y);
10169       DrawLevelField(x, y);
10170     }
10171
10172 #if DEBUG
10173     if (IS_BLOCKED(x, y))
10174     {
10175       int oldx, oldy;
10176
10177       Blocked2Moving(x, y, &oldx, &oldy);
10178       if (!IS_MOVING(oldx, oldy))
10179       {
10180         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10181         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10182         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10183         printf("GameActions(): This should never happen!\n");
10184       }
10185     }
10186 #endif
10187   }
10188
10189   SCAN_PLAYFIELD(x, y)
10190   {
10191     element = Feld[x][y];
10192     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10193
10194     ResetGfxFrame(x, y, TRUE);
10195
10196     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10197         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10198       ResetRandomAnimationValue(x, y);
10199
10200     SetRandomAnimationValue(x, y);
10201
10202     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10203
10204     if (IS_INACTIVE(element))
10205     {
10206       if (IS_ANIMATED(graphic))
10207         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10208
10209       continue;
10210     }
10211
10212     /* this may take place after moving, so 'element' may have changed */
10213     if (IS_CHANGING(x, y) &&
10214         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10215     {
10216       int page = element_info[element].event_page_nr[CE_DELAY];
10217
10218 #if 1
10219       HandleElementChange(x, y, page);
10220 #else
10221       if (CAN_CHANGE(element))
10222         HandleElementChange(x, y, page);
10223
10224       if (HAS_ACTION(element))
10225         ExecuteCustomElementAction(x, y, element, page);
10226 #endif
10227
10228       element = Feld[x][y];
10229       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10230     }
10231
10232     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10233     {
10234       StartMoving(x, y);
10235
10236       element = Feld[x][y];
10237       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10238
10239       if (IS_ANIMATED(graphic) &&
10240           !IS_MOVING(x, y) &&
10241           !Stop[x][y])
10242         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10243
10244       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10245         DrawTwinkleOnField(x, y);
10246     }
10247     else if ((element == EL_ACID ||
10248               element == EL_EXIT_OPEN ||
10249               element == EL_EM_EXIT_OPEN ||
10250               element == EL_SP_EXIT_OPEN ||
10251               element == EL_STEEL_EXIT_OPEN ||
10252               element == EL_EM_STEEL_EXIT_OPEN ||
10253               element == EL_SP_TERMINAL ||
10254               element == EL_SP_TERMINAL_ACTIVE ||
10255               element == EL_EXTRA_TIME ||
10256               element == EL_SHIELD_NORMAL ||
10257               element == EL_SHIELD_DEADLY) &&
10258              IS_ANIMATED(graphic))
10259       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10260     else if (IS_MOVING(x, y))
10261       ContinueMoving(x, y);
10262     else if (IS_ACTIVE_BOMB(element))
10263       CheckDynamite(x, y);
10264     else if (element == EL_AMOEBA_GROWING)
10265       AmoebeWaechst(x, y);
10266     else if (element == EL_AMOEBA_SHRINKING)
10267       AmoebaDisappearing(x, y);
10268
10269 #if !USE_NEW_AMOEBA_CODE
10270     else if (IS_AMOEBALIVE(element))
10271       AmoebeAbleger(x, y);
10272 #endif
10273
10274     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10275       Life(x, y);
10276     else if (element == EL_EXIT_CLOSED)
10277       CheckExit(x, y);
10278     else if (element == EL_EM_EXIT_CLOSED)
10279       CheckExitEM(x, y);
10280     else if (element == EL_STEEL_EXIT_CLOSED)
10281       CheckExitSteel(x, y);
10282     else if (element == EL_EM_STEEL_EXIT_CLOSED)
10283       CheckExitSteelEM(x, y);
10284     else if (element == EL_SP_EXIT_CLOSED)
10285       CheckExitSP(x, y);
10286     else if (element == EL_EXPANDABLE_WALL_GROWING ||
10287              element == EL_EXPANDABLE_STEELWALL_GROWING)
10288       MauerWaechst(x, y);
10289     else if (element == EL_EXPANDABLE_WALL ||
10290              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10291              element == EL_EXPANDABLE_WALL_VERTICAL ||
10292              element == EL_EXPANDABLE_WALL_ANY ||
10293              element == EL_BD_EXPANDABLE_WALL)
10294       MauerAbleger(x, y);
10295     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10296              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10297              element == EL_EXPANDABLE_STEELWALL_ANY)
10298       MauerAblegerStahl(x, y);
10299     else if (element == EL_FLAMES)
10300       CheckForDragon(x, y);
10301     else if (element == EL_EXPLOSION)
10302       ; /* drawing of correct explosion animation is handled separately */
10303     else if (element == EL_ELEMENT_SNAPPING ||
10304              element == EL_DIAGONAL_SHRINKING ||
10305              element == EL_DIAGONAL_GROWING)
10306     {
10307       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10308
10309       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10310     }
10311     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10312       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10313
10314     if (IS_BELT_ACTIVE(element))
10315       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10316
10317     if (game.magic_wall_active)
10318     {
10319       int jx = local_player->jx, jy = local_player->jy;
10320
10321       /* play the element sound at the position nearest to the player */
10322       if ((element == EL_MAGIC_WALL_FULL ||
10323            element == EL_MAGIC_WALL_ACTIVE ||
10324            element == EL_MAGIC_WALL_EMPTYING ||
10325            element == EL_BD_MAGIC_WALL_FULL ||
10326            element == EL_BD_MAGIC_WALL_ACTIVE ||
10327            element == EL_BD_MAGIC_WALL_EMPTYING ||
10328            element == EL_DC_MAGIC_WALL_FULL ||
10329            element == EL_DC_MAGIC_WALL_ACTIVE ||
10330            element == EL_DC_MAGIC_WALL_EMPTYING) &&
10331           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10332       {
10333         magic_wall_x = x;
10334         magic_wall_y = y;
10335       }
10336     }
10337   }
10338
10339 #if USE_NEW_AMOEBA_CODE
10340   /* new experimental amoeba growth stuff */
10341   if (!(FrameCounter % 8))
10342   {
10343     static unsigned long random = 1684108901;
10344
10345     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10346     {
10347       x = RND(lev_fieldx);
10348       y = RND(lev_fieldy);
10349       element = Feld[x][y];
10350
10351       if (!IS_PLAYER(x,y) &&
10352           (element == EL_EMPTY ||
10353            CAN_GROW_INTO(element) ||
10354            element == EL_QUICKSAND_EMPTY ||
10355            element == EL_QUICKSAND_FAST_EMPTY ||
10356            element == EL_ACID_SPLASH_LEFT ||
10357            element == EL_ACID_SPLASH_RIGHT))
10358       {
10359         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10360             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10361             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10362             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10363           Feld[x][y] = EL_AMOEBA_DROP;
10364       }
10365
10366       random = random * 129 + 1;
10367     }
10368   }
10369 #endif
10370
10371 #if 0
10372   if (game.explosions_delayed)
10373 #endif
10374   {
10375     game.explosions_delayed = FALSE;
10376
10377     SCAN_PLAYFIELD(x, y)
10378     {
10379       element = Feld[x][y];
10380
10381       if (ExplodeField[x][y])
10382         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10383       else if (element == EL_EXPLOSION)
10384         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10385
10386       ExplodeField[x][y] = EX_TYPE_NONE;
10387     }
10388
10389     game.explosions_delayed = TRUE;
10390   }
10391
10392   if (game.magic_wall_active)
10393   {
10394     if (!(game.magic_wall_time_left % 4))
10395     {
10396       int element = Feld[magic_wall_x][magic_wall_y];
10397
10398       if (element == EL_BD_MAGIC_WALL_FULL ||
10399           element == EL_BD_MAGIC_WALL_ACTIVE ||
10400           element == EL_BD_MAGIC_WALL_EMPTYING)
10401         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10402       else if (element == EL_DC_MAGIC_WALL_FULL ||
10403                element == EL_DC_MAGIC_WALL_ACTIVE ||
10404                element == EL_DC_MAGIC_WALL_EMPTYING)
10405         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
10406       else
10407         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10408     }
10409
10410     if (game.magic_wall_time_left > 0)
10411     {
10412       game.magic_wall_time_left--;
10413       if (!game.magic_wall_time_left)
10414       {
10415         SCAN_PLAYFIELD(x, y)
10416         {
10417           element = Feld[x][y];
10418
10419           if (element == EL_MAGIC_WALL_ACTIVE ||
10420               element == EL_MAGIC_WALL_FULL)
10421           {
10422             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10423             DrawLevelField(x, y);
10424           }
10425           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10426                    element == EL_BD_MAGIC_WALL_FULL)
10427           {
10428             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10429             DrawLevelField(x, y);
10430           }
10431           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
10432                    element == EL_DC_MAGIC_WALL_FULL)
10433           {
10434             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
10435             DrawLevelField(x, y);
10436           }
10437         }
10438
10439         game.magic_wall_active = FALSE;
10440       }
10441     }
10442   }
10443
10444   if (game.light_time_left > 0)
10445   {
10446     game.light_time_left--;
10447
10448     if (game.light_time_left == 0)
10449       RedrawAllLightSwitchesAndInvisibleElements();
10450   }
10451
10452   if (game.timegate_time_left > 0)
10453   {
10454     game.timegate_time_left--;
10455
10456     if (game.timegate_time_left == 0)
10457       CloseAllOpenTimegates();
10458   }
10459
10460   if (game.lenses_time_left > 0)
10461   {
10462     game.lenses_time_left--;
10463
10464     if (game.lenses_time_left == 0)
10465       RedrawAllInvisibleElementsForLenses();
10466   }
10467
10468   if (game.magnify_time_left > 0)
10469   {
10470     game.magnify_time_left--;
10471
10472     if (game.magnify_time_left == 0)
10473       RedrawAllInvisibleElementsForMagnifier();
10474   }
10475
10476   for (i = 0; i < MAX_PLAYERS; i++)
10477   {
10478     struct PlayerInfo *player = &stored_player[i];
10479
10480     if (SHIELD_ON(player))
10481     {
10482       if (player->shield_deadly_time_left)
10483         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10484       else if (player->shield_normal_time_left)
10485         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10486     }
10487   }
10488
10489   CheckLevelTime();
10490
10491   DrawAllPlayers();
10492   PlayAllPlayersSound();
10493
10494   if (options.debug)                    /* calculate frames per second */
10495   {
10496     static unsigned long fps_counter = 0;
10497     static int fps_frames = 0;
10498     unsigned long fps_delay_ms = Counter() - fps_counter;
10499
10500     fps_frames++;
10501
10502     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10503     {
10504       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10505
10506       fps_frames = 0;
10507       fps_counter = Counter();
10508     }
10509
10510     redraw_mask |= REDRAW_FPS;
10511   }
10512
10513   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10514
10515   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10516   {
10517     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10518
10519     local_player->show_envelope = 0;
10520   }
10521
10522   /* use random number generator in every frame to make it less predictable */
10523   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10524     RND(1);
10525 }
10526
10527 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10528 {
10529   int min_x = x, min_y = y, max_x = x, max_y = y;
10530   int i;
10531
10532   for (i = 0; i < MAX_PLAYERS; i++)
10533   {
10534     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10535
10536     if (!stored_player[i].active || &stored_player[i] == player)
10537       continue;
10538
10539     min_x = MIN(min_x, jx);
10540     min_y = MIN(min_y, jy);
10541     max_x = MAX(max_x, jx);
10542     max_y = MAX(max_y, jy);
10543   }
10544
10545   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10546 }
10547
10548 static boolean AllPlayersInVisibleScreen()
10549 {
10550   int i;
10551
10552   for (i = 0; i < MAX_PLAYERS; i++)
10553   {
10554     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10555
10556     if (!stored_player[i].active)
10557       continue;
10558
10559     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10560       return FALSE;
10561   }
10562
10563   return TRUE;
10564 }
10565
10566 void ScrollLevel(int dx, int dy)
10567 {
10568   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10569   int x, y;
10570
10571   BlitBitmap(drawto_field, drawto_field,
10572              FX + TILEX * (dx == -1) - softscroll_offset,
10573              FY + TILEY * (dy == -1) - softscroll_offset,
10574              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10575              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10576              FX + TILEX * (dx == 1) - softscroll_offset,
10577              FY + TILEY * (dy == 1) - softscroll_offset);
10578
10579   if (dx)
10580   {
10581     x = (dx == 1 ? BX1 : BX2);
10582     for (y = BY1; y <= BY2; y++)
10583       DrawScreenField(x, y);
10584   }
10585
10586   if (dy)
10587   {
10588     y = (dy == 1 ? BY1 : BY2);
10589     for (x = BX1; x <= BX2; x++)
10590       DrawScreenField(x, y);
10591   }
10592
10593   redraw_mask |= REDRAW_FIELD;
10594 }
10595
10596 static boolean canFallDown(struct PlayerInfo *player)
10597 {
10598   int jx = player->jx, jy = player->jy;
10599
10600   return (IN_LEV_FIELD(jx, jy + 1) &&
10601           (IS_FREE(jx, jy + 1) ||
10602            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10603           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10604           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10605 }
10606
10607 static boolean canPassField(int x, int y, int move_dir)
10608 {
10609   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10610   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10611   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10612   int nextx = x + dx;
10613   int nexty = y + dy;
10614   int element = Feld[x][y];
10615
10616   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10617           !CAN_MOVE(element) &&
10618           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10619           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10620           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10621 }
10622
10623 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10624 {
10625   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10626   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10627   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10628   int newx = x + dx;
10629   int newy = y + dy;
10630
10631   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10632           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10633           (IS_DIGGABLE(Feld[newx][newy]) ||
10634            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10635            canPassField(newx, newy, move_dir)));
10636 }
10637
10638 static void CheckGravityMovement(struct PlayerInfo *player)
10639 {
10640 #if USE_PLAYER_GRAVITY
10641   if (player->gravity && !player->programmed_action)
10642 #else
10643   if (game.gravity && !player->programmed_action)
10644 #endif
10645   {
10646     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10647     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10648     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10649     int jx = player->jx, jy = player->jy;
10650     boolean player_is_moving_to_valid_field =
10651       (!player_is_snapping &&
10652        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10653         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10654     boolean player_can_fall_down = canFallDown(player);
10655
10656     if (player_can_fall_down &&
10657         !player_is_moving_to_valid_field)
10658       player->programmed_action = MV_DOWN;
10659   }
10660 }
10661
10662 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10663 {
10664   return CheckGravityMovement(player);
10665
10666 #if USE_PLAYER_GRAVITY
10667   if (player->gravity && !player->programmed_action)
10668 #else
10669   if (game.gravity && !player->programmed_action)
10670 #endif
10671   {
10672     int jx = player->jx, jy = player->jy;
10673     boolean field_under_player_is_free =
10674       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10675     boolean player_is_standing_on_valid_field =
10676       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10677        (IS_WALKABLE(Feld[jx][jy]) &&
10678         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10679
10680     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10681       player->programmed_action = MV_DOWN;
10682   }
10683 }
10684
10685 /*
10686   MovePlayerOneStep()
10687   -----------------------------------------------------------------------------
10688   dx, dy:               direction (non-diagonal) to try to move the player to
10689   real_dx, real_dy:     direction as read from input device (can be diagonal)
10690 */
10691
10692 boolean MovePlayerOneStep(struct PlayerInfo *player,
10693                           int dx, int dy, int real_dx, int real_dy)
10694 {
10695   int jx = player->jx, jy = player->jy;
10696   int new_jx = jx + dx, new_jy = jy + dy;
10697 #if !USE_FIXED_DONT_RUN_INTO
10698   int element;
10699 #endif
10700   int can_move;
10701   boolean player_can_move = !player->cannot_move;
10702
10703   if (!player->active || (!dx && !dy))
10704     return MP_NO_ACTION;
10705
10706   player->MovDir = (dx < 0 ? MV_LEFT :
10707                     dx > 0 ? MV_RIGHT :
10708                     dy < 0 ? MV_UP :
10709                     dy > 0 ? MV_DOWN :  MV_NONE);
10710
10711   if (!IN_LEV_FIELD(new_jx, new_jy))
10712     return MP_NO_ACTION;
10713
10714   if (!player_can_move)
10715   {
10716     if (player->MovPos == 0)
10717     {
10718       player->is_moving = FALSE;
10719       player->is_digging = FALSE;
10720       player->is_collecting = FALSE;
10721       player->is_snapping = FALSE;
10722       player->is_pushing = FALSE;
10723     }
10724   }
10725
10726 #if 1
10727   if (!options.network && game.centered_player_nr == -1 &&
10728       !AllPlayersInSight(player, new_jx, new_jy))
10729     return MP_NO_ACTION;
10730 #else
10731   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10732     return MP_NO_ACTION;
10733 #endif
10734
10735 #if !USE_FIXED_DONT_RUN_INTO
10736   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10737
10738   /* (moved to DigField()) */
10739   if (player_can_move && DONT_RUN_INTO(element))
10740   {
10741     if (element == EL_ACID && dx == 0 && dy == 1)
10742     {
10743       SplashAcid(new_jx, new_jy);
10744       Feld[jx][jy] = EL_PLAYER_1;
10745       InitMovingField(jx, jy, MV_DOWN);
10746       Store[jx][jy] = EL_ACID;
10747       ContinueMoving(jx, jy);
10748       BuryPlayer(player);
10749     }
10750     else
10751       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10752
10753     return MP_MOVING;
10754   }
10755 #endif
10756
10757   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10758   if (can_move != MP_MOVING)
10759     return can_move;
10760
10761   /* check if DigField() has caused relocation of the player */
10762   if (player->jx != jx || player->jy != jy)
10763     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10764
10765   StorePlayer[jx][jy] = 0;
10766   player->last_jx = jx;
10767   player->last_jy = jy;
10768   player->jx = new_jx;
10769   player->jy = new_jy;
10770   StorePlayer[new_jx][new_jy] = player->element_nr;
10771
10772   if (player->move_delay_value_next != -1)
10773   {
10774     player->move_delay_value = player->move_delay_value_next;
10775     player->move_delay_value_next = -1;
10776   }
10777
10778   player->MovPos =
10779     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10780
10781   player->step_counter++;
10782
10783   PlayerVisit[jx][jy] = FrameCounter;
10784
10785 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10786   player->is_moving = TRUE;
10787 #endif
10788
10789 #if 1
10790   /* should better be called in MovePlayer(), but this breaks some tapes */
10791   ScrollPlayer(player, SCROLL_INIT);
10792 #endif
10793
10794   return MP_MOVING;
10795 }
10796
10797 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10798 {
10799   int jx = player->jx, jy = player->jy;
10800   int old_jx = jx, old_jy = jy;
10801   int moved = MP_NO_ACTION;
10802
10803   if (!player->active)
10804     return FALSE;
10805
10806   if (!dx && !dy)
10807   {
10808     if (player->MovPos == 0)
10809     {
10810       player->is_moving = FALSE;
10811       player->is_digging = FALSE;
10812       player->is_collecting = FALSE;
10813       player->is_snapping = FALSE;
10814       player->is_pushing = FALSE;
10815     }
10816
10817     return FALSE;
10818   }
10819
10820   if (player->move_delay > 0)
10821     return FALSE;
10822
10823   player->move_delay = -1;              /* set to "uninitialized" value */
10824
10825   /* store if player is automatically moved to next field */
10826   player->is_auto_moving = (player->programmed_action != MV_NONE);
10827
10828   /* remove the last programmed player action */
10829   player->programmed_action = 0;
10830
10831   if (player->MovPos)
10832   {
10833     /* should only happen if pre-1.2 tape recordings are played */
10834     /* this is only for backward compatibility */
10835
10836     int original_move_delay_value = player->move_delay_value;
10837
10838 #if DEBUG
10839     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10840            tape.counter);
10841 #endif
10842
10843     /* scroll remaining steps with finest movement resolution */
10844     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10845
10846     while (player->MovPos)
10847     {
10848       ScrollPlayer(player, SCROLL_GO_ON);
10849       ScrollScreen(NULL, SCROLL_GO_ON);
10850
10851       AdvanceFrameAndPlayerCounters(player->index_nr);
10852
10853       DrawAllPlayers();
10854       BackToFront();
10855     }
10856
10857     player->move_delay_value = original_move_delay_value;
10858   }
10859
10860   player->is_active = FALSE;
10861
10862   if (player->last_move_dir & MV_HORIZONTAL)
10863   {
10864     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10865       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10866   }
10867   else
10868   {
10869     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10870       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10871   }
10872
10873 #if USE_FIXED_BORDER_RUNNING_GFX
10874   if (!moved && !player->is_active)
10875   {
10876     player->is_moving = FALSE;
10877     player->is_digging = FALSE;
10878     player->is_collecting = FALSE;
10879     player->is_snapping = FALSE;
10880     player->is_pushing = FALSE;
10881   }
10882 #endif
10883
10884   jx = player->jx;
10885   jy = player->jy;
10886
10887 #if 1
10888   if (moved & MP_MOVING && !ScreenMovPos &&
10889       (player->index_nr == game.centered_player_nr ||
10890        game.centered_player_nr == -1))
10891 #else
10892   if (moved & MP_MOVING && !ScreenMovPos &&
10893       (player == local_player || !options.network))
10894 #endif
10895   {
10896     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10897     int offset = (setup.scroll_delay ? 3 : 0);
10898
10899     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10900     {
10901       /* actual player has left the screen -- scroll in that direction */
10902       if (jx != old_jx)         /* player has moved horizontally */
10903         scroll_x += (jx - old_jx);
10904       else                      /* player has moved vertically */
10905         scroll_y += (jy - old_jy);
10906     }
10907     else
10908     {
10909       if (jx != old_jx)         /* player has moved horizontally */
10910       {
10911         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10912             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10913           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10914
10915         /* don't scroll over playfield boundaries */
10916         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10917           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10918
10919         /* don't scroll more than one field at a time */
10920         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10921
10922         /* don't scroll against the player's moving direction */
10923         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10924             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10925           scroll_x = old_scroll_x;
10926       }
10927       else                      /* player has moved vertically */
10928       {
10929         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10930             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10931           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10932
10933         /* don't scroll over playfield boundaries */
10934         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10935           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10936
10937         /* don't scroll more than one field at a time */
10938         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10939
10940         /* don't scroll against the player's moving direction */
10941         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10942             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10943           scroll_y = old_scroll_y;
10944       }
10945     }
10946
10947     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10948     {
10949 #if 1
10950       if (!options.network && game.centered_player_nr == -1 &&
10951           !AllPlayersInVisibleScreen())
10952       {
10953         scroll_x = old_scroll_x;
10954         scroll_y = old_scroll_y;
10955       }
10956       else
10957 #else
10958       if (!options.network && !AllPlayersInVisibleScreen())
10959       {
10960         scroll_x = old_scroll_x;
10961         scroll_y = old_scroll_y;
10962       }
10963       else
10964 #endif
10965       {
10966         ScrollScreen(player, SCROLL_INIT);
10967         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10968       }
10969     }
10970   }
10971
10972   player->StepFrame = 0;
10973
10974   if (moved & MP_MOVING)
10975   {
10976     if (old_jx != jx && old_jy == jy)
10977       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10978     else if (old_jx == jx && old_jy != jy)
10979       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10980
10981     DrawLevelField(jx, jy);     /* for "crumbled sand" */
10982
10983     player->last_move_dir = player->MovDir;
10984     player->is_moving = TRUE;
10985     player->is_snapping = FALSE;
10986     player->is_switching = FALSE;
10987     player->is_dropping = FALSE;
10988     player->is_dropping_pressed = FALSE;
10989     player->drop_pressed_delay = 0;
10990
10991 #if 0
10992     /* should better be called here than above, but this breaks some tapes */
10993     ScrollPlayer(player, SCROLL_INIT);
10994 #endif
10995   }
10996   else
10997   {
10998     CheckGravityMovementWhenNotMoving(player);
10999
11000     player->is_moving = FALSE;
11001
11002     /* at this point, the player is allowed to move, but cannot move right now
11003        (e.g. because of something blocking the way) -- ensure that the player
11004        is also allowed to move in the next frame (in old versions before 3.1.1,
11005        the player was forced to wait again for eight frames before next try) */
11006
11007     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11008       player->move_delay = 0;   /* allow direct movement in the next frame */
11009   }
11010
11011   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11012     player->move_delay = player->move_delay_value;
11013
11014   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11015   {
11016     TestIfPlayerTouchesBadThing(jx, jy);
11017     TestIfPlayerTouchesCustomElement(jx, jy);
11018   }
11019
11020   if (!player->active)
11021     RemovePlayer(player);
11022
11023   return moved;
11024 }
11025
11026 void ScrollPlayer(struct PlayerInfo *player, int mode)
11027 {
11028   int jx = player->jx, jy = player->jy;
11029   int last_jx = player->last_jx, last_jy = player->last_jy;
11030   int move_stepsize = TILEX / player->move_delay_value;
11031
11032 #if USE_NEW_PLAYER_SPEED
11033   if (!player->active)
11034     return;
11035
11036   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11037     return;
11038 #else
11039   if (!player->active || player->MovPos == 0)
11040     return;
11041 #endif
11042
11043   if (mode == SCROLL_INIT)
11044   {
11045     player->actual_frame_counter = FrameCounter;
11046     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11047
11048     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11049         Feld[last_jx][last_jy] == EL_EMPTY)
11050     {
11051       int last_field_block_delay = 0;   /* start with no blocking at all */
11052       int block_delay_adjustment = player->block_delay_adjustment;
11053
11054       /* if player blocks last field, add delay for exactly one move */
11055       if (player->block_last_field)
11056       {
11057         last_field_block_delay += player->move_delay_value;
11058
11059         /* when blocking enabled, prevent moving up despite gravity */
11060 #if USE_PLAYER_GRAVITY
11061         if (player->gravity && player->MovDir == MV_UP)
11062           block_delay_adjustment = -1;
11063 #else
11064         if (game.gravity && player->MovDir == MV_UP)
11065           block_delay_adjustment = -1;
11066 #endif
11067       }
11068
11069       /* add block delay adjustment (also possible when not blocking) */
11070       last_field_block_delay += block_delay_adjustment;
11071
11072       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11073       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11074     }
11075
11076 #if USE_NEW_PLAYER_SPEED
11077     if (player->MovPos != 0)    /* player has not yet reached destination */
11078       return;
11079 #else
11080     return;
11081 #endif
11082   }
11083   else if (!FrameReached(&player->actual_frame_counter, 1))
11084     return;
11085
11086 #if USE_NEW_PLAYER_SPEED
11087   if (player->MovPos != 0)
11088   {
11089     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11090     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11091
11092     /* before DrawPlayer() to draw correct player graphic for this case */
11093     if (player->MovPos == 0)
11094       CheckGravityMovement(player);
11095   }
11096 #else
11097   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11098   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11099
11100   /* before DrawPlayer() to draw correct player graphic for this case */
11101   if (player->MovPos == 0)
11102     CheckGravityMovement(player);
11103 #endif
11104
11105   if (player->MovPos == 0)      /* player reached destination field */
11106   {
11107     if (player->move_delay_reset_counter > 0)
11108     {
11109       player->move_delay_reset_counter--;
11110
11111       if (player->move_delay_reset_counter == 0)
11112       {
11113         /* continue with normal speed after quickly moving through gate */
11114         HALVE_PLAYER_SPEED(player);
11115
11116         /* be able to make the next move without delay */
11117         player->move_delay = 0;
11118       }
11119     }
11120
11121     player->last_jx = jx;
11122     player->last_jy = jy;
11123
11124     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11125         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
11126         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
11127         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
11128         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11129         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11130     {
11131       DrawPlayer(player);       /* needed here only to cleanup last field */
11132       RemovePlayer(player);
11133
11134       if (local_player->friends_still_needed == 0 ||
11135           IS_SP_ELEMENT(Feld[jx][jy]))
11136         PlayerWins(player);
11137     }
11138
11139     /* this breaks one level: "machine", level 000 */
11140     {
11141       int move_direction = player->MovDir;
11142       int enter_side = MV_DIR_OPPOSITE(move_direction);
11143       int leave_side = move_direction;
11144       int old_jx = last_jx;
11145       int old_jy = last_jy;
11146       int old_element = Feld[old_jx][old_jy];
11147       int new_element = Feld[jx][jy];
11148
11149       if (IS_CUSTOM_ELEMENT(old_element))
11150         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11151                                    CE_LEFT_BY_PLAYER,
11152                                    player->index_bit, leave_side);
11153
11154       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11155                                           CE_PLAYER_LEAVES_X,
11156                                           player->index_bit, leave_side);
11157
11158       if (IS_CUSTOM_ELEMENT(new_element))
11159         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11160                                    player->index_bit, enter_side);
11161
11162       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11163                                           CE_PLAYER_ENTERS_X,
11164                                           player->index_bit, enter_side);
11165
11166       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11167                                         CE_MOVE_OF_X, move_direction);
11168     }
11169
11170     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11171     {
11172       TestIfPlayerTouchesBadThing(jx, jy);
11173       TestIfPlayerTouchesCustomElement(jx, jy);
11174
11175       /* needed because pushed element has not yet reached its destination,
11176          so it would trigger a change event at its previous field location */
11177       if (!player->is_pushing)
11178         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
11179
11180       if (!player->active)
11181         RemovePlayer(player);
11182     }
11183
11184     if (!local_player->LevelSolved && level.use_step_counter)
11185     {
11186       int i;
11187
11188       TimePlayed++;
11189
11190       if (TimeLeft > 0)
11191       {
11192         TimeLeft--;
11193
11194         if (TimeLeft <= 10 && setup.time_limit)
11195           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11196
11197         DrawGameValue_Time(TimeLeft);
11198
11199         if (!TimeLeft && setup.time_limit)
11200           for (i = 0; i < MAX_PLAYERS; i++)
11201             KillPlayer(&stored_player[i]);
11202       }
11203       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11204         DrawGameValue_Time(TimePlayed);
11205     }
11206
11207     if (tape.single_step && tape.recording && !tape.pausing &&
11208         !player->programmed_action)
11209       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11210   }
11211 }
11212
11213 void ScrollScreen(struct PlayerInfo *player, int mode)
11214 {
11215   static unsigned long screen_frame_counter = 0;
11216
11217   if (mode == SCROLL_INIT)
11218   {
11219     /* set scrolling step size according to actual player's moving speed */
11220     ScrollStepSize = TILEX / player->move_delay_value;
11221
11222     screen_frame_counter = FrameCounter;
11223     ScreenMovDir = player->MovDir;
11224     ScreenMovPos = player->MovPos;
11225     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11226     return;
11227   }
11228   else if (!FrameReached(&screen_frame_counter, 1))
11229     return;
11230
11231   if (ScreenMovPos)
11232   {
11233     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11234     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11235     redraw_mask |= REDRAW_FIELD;
11236   }
11237   else
11238     ScreenMovDir = MV_NONE;
11239 }
11240
11241 void TestIfPlayerTouchesCustomElement(int x, int y)
11242 {
11243   static int xy[4][2] =
11244   {
11245     { 0, -1 },
11246     { -1, 0 },
11247     { +1, 0 },
11248     { 0, +1 }
11249   };
11250   static int trigger_sides[4][2] =
11251   {
11252     /* center side       border side */
11253     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11254     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11255     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11256     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11257   };
11258   static int touch_dir[4] =
11259   {
11260     MV_LEFT | MV_RIGHT,
11261     MV_UP   | MV_DOWN,
11262     MV_UP   | MV_DOWN,
11263     MV_LEFT | MV_RIGHT
11264   };
11265   int center_element = Feld[x][y];      /* should always be non-moving! */
11266   int i;
11267
11268   for (i = 0; i < NUM_DIRECTIONS; i++)
11269   {
11270     int xx = x + xy[i][0];
11271     int yy = y + xy[i][1];
11272     int center_side = trigger_sides[i][0];
11273     int border_side = trigger_sides[i][1];
11274     int border_element;
11275
11276     if (!IN_LEV_FIELD(xx, yy))
11277       continue;
11278
11279     if (IS_PLAYER(x, y))
11280     {
11281       struct PlayerInfo *player = PLAYERINFO(x, y);
11282
11283       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11284         border_element = Feld[xx][yy];          /* may be moving! */
11285       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11286         border_element = Feld[xx][yy];
11287       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11288         border_element = MovingOrBlocked2Element(xx, yy);
11289       else
11290         continue;               /* center and border element do not touch */
11291
11292       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11293                                  player->index_bit, border_side);
11294       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11295                                           CE_PLAYER_TOUCHES_X,
11296                                           player->index_bit, border_side);
11297     }
11298     else if (IS_PLAYER(xx, yy))
11299     {
11300       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11301
11302       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11303       {
11304         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11305           continue;             /* center and border element do not touch */
11306       }
11307
11308       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11309                                  player->index_bit, center_side);
11310       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11311                                           CE_PLAYER_TOUCHES_X,
11312                                           player->index_bit, center_side);
11313       break;
11314     }
11315   }
11316 }
11317
11318 #if USE_ELEMENT_TOUCHING_BUGFIX
11319
11320 void TestIfElementTouchesCustomElement(int x, int y)
11321 {
11322   static int xy[4][2] =
11323   {
11324     { 0, -1 },
11325     { -1, 0 },
11326     { +1, 0 },
11327     { 0, +1 }
11328   };
11329   static int trigger_sides[4][2] =
11330   {
11331     /* center side      border side */
11332     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11333     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11334     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11335     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11336   };
11337   static int touch_dir[4] =
11338   {
11339     MV_LEFT | MV_RIGHT,
11340     MV_UP   | MV_DOWN,
11341     MV_UP   | MV_DOWN,
11342     MV_LEFT | MV_RIGHT
11343   };
11344   boolean change_center_element = FALSE;
11345   int center_element = Feld[x][y];      /* should always be non-moving! */
11346   int border_element_old[NUM_DIRECTIONS];
11347   int i;
11348
11349   for (i = 0; i < NUM_DIRECTIONS; i++)
11350   {
11351     int xx = x + xy[i][0];
11352     int yy = y + xy[i][1];
11353     int border_element;
11354
11355     border_element_old[i] = -1;
11356
11357     if (!IN_LEV_FIELD(xx, yy))
11358       continue;
11359
11360     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11361       border_element = Feld[xx][yy];    /* may be moving! */
11362     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11363       border_element = Feld[xx][yy];
11364     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11365       border_element = MovingOrBlocked2Element(xx, yy);
11366     else
11367       continue;                 /* center and border element do not touch */
11368
11369     border_element_old[i] = border_element;
11370   }
11371
11372   for (i = 0; i < NUM_DIRECTIONS; i++)
11373   {
11374     int xx = x + xy[i][0];
11375     int yy = y + xy[i][1];
11376     int center_side = trigger_sides[i][0];
11377     int border_element = border_element_old[i];
11378
11379     if (border_element == -1)
11380       continue;
11381
11382     /* check for change of border element */
11383     CheckElementChangeBySide(xx, yy, border_element, center_element,
11384                              CE_TOUCHING_X, center_side);
11385   }
11386
11387   for (i = 0; i < NUM_DIRECTIONS; i++)
11388   {
11389     int border_side = trigger_sides[i][1];
11390     int border_element = border_element_old[i];
11391
11392     if (border_element == -1)
11393       continue;
11394
11395     /* check for change of center element (but change it only once) */
11396     if (!change_center_element)
11397       change_center_element =
11398         CheckElementChangeBySide(x, y, center_element, border_element,
11399                                  CE_TOUCHING_X, border_side);
11400   }
11401 }
11402
11403 #else
11404
11405 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11406 {
11407   static int xy[4][2] =
11408   {
11409     { 0, -1 },
11410     { -1, 0 },
11411     { +1, 0 },
11412     { 0, +1 }
11413   };
11414   static int trigger_sides[4][2] =
11415   {
11416     /* center side      border side */
11417     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11418     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11419     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11420     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11421   };
11422   static int touch_dir[4] =
11423   {
11424     MV_LEFT | MV_RIGHT,
11425     MV_UP   | MV_DOWN,
11426     MV_UP   | MV_DOWN,
11427     MV_LEFT | MV_RIGHT
11428   };
11429   boolean change_center_element = FALSE;
11430   int center_element = Feld[x][y];      /* should always be non-moving! */
11431   int i;
11432
11433   for (i = 0; i < NUM_DIRECTIONS; i++)
11434   {
11435     int xx = x + xy[i][0];
11436     int yy = y + xy[i][1];
11437     int center_side = trigger_sides[i][0];
11438     int border_side = trigger_sides[i][1];
11439     int border_element;
11440
11441     if (!IN_LEV_FIELD(xx, yy))
11442       continue;
11443
11444     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11445       border_element = Feld[xx][yy];    /* may be moving! */
11446     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11447       border_element = Feld[xx][yy];
11448     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11449       border_element = MovingOrBlocked2Element(xx, yy);
11450     else
11451       continue;                 /* center and border element do not touch */
11452
11453     /* check for change of center element (but change it only once) */
11454     if (!change_center_element)
11455       change_center_element =
11456         CheckElementChangeBySide(x, y, center_element, border_element,
11457                                  CE_TOUCHING_X, border_side);
11458
11459     /* check for change of border element */
11460     CheckElementChangeBySide(xx, yy, border_element, center_element,
11461                              CE_TOUCHING_X, center_side);
11462   }
11463 }
11464
11465 #endif
11466
11467 void TestIfElementHitsCustomElement(int x, int y, int direction)
11468 {
11469   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11470   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11471   int hitx = x + dx, hity = y + dy;
11472   int hitting_element = Feld[x][y];
11473   int touched_element;
11474
11475   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11476     return;
11477
11478   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11479                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11480
11481   if (IN_LEV_FIELD(hitx, hity))
11482   {
11483     int opposite_direction = MV_DIR_OPPOSITE(direction);
11484     int hitting_side = direction;
11485     int touched_side = opposite_direction;
11486     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11487                           MovDir[hitx][hity] != direction ||
11488                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11489
11490     object_hit = TRUE;
11491
11492     if (object_hit)
11493     {
11494       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11495                                CE_HITTING_X, touched_side);
11496
11497       CheckElementChangeBySide(hitx, hity, touched_element,
11498                                hitting_element, CE_HIT_BY_X, hitting_side);
11499
11500       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11501                                CE_HIT_BY_SOMETHING, opposite_direction);
11502     }
11503   }
11504
11505   /* "hitting something" is also true when hitting the playfield border */
11506   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11507                            CE_HITTING_SOMETHING, direction);
11508 }
11509
11510 #if 0
11511 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11512 {
11513   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11514   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11515   int hitx = x + dx, hity = y + dy;
11516   int hitting_element = Feld[x][y];
11517   int touched_element;
11518 #if 0
11519   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11520                         !IS_FREE(hitx, hity) &&
11521                         (!IS_MOVING(hitx, hity) ||
11522                          MovDir[hitx][hity] != direction ||
11523                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11524 #endif
11525
11526   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11527     return;
11528
11529 #if 0
11530   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11531     return;
11532 #endif
11533
11534   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11535                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11536
11537   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11538                            EP_CAN_SMASH_EVERYTHING, direction);
11539
11540   if (IN_LEV_FIELD(hitx, hity))
11541   {
11542     int opposite_direction = MV_DIR_OPPOSITE(direction);
11543     int hitting_side = direction;
11544     int touched_side = opposite_direction;
11545 #if 0
11546     int touched_element = MovingOrBlocked2Element(hitx, hity);
11547 #endif
11548 #if 1
11549     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11550                           MovDir[hitx][hity] != direction ||
11551                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11552
11553     object_hit = TRUE;
11554 #endif
11555
11556     if (object_hit)
11557     {
11558       int i;
11559
11560       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11561                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11562
11563       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11564                                CE_OTHER_IS_SMASHING, touched_side);
11565
11566       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11567                                CE_OTHER_GETS_SMASHED, hitting_side);
11568     }
11569   }
11570 }
11571 #endif
11572
11573 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11574 {
11575   int i, kill_x = -1, kill_y = -1;
11576
11577   int bad_element = -1;
11578   static int test_xy[4][2] =
11579   {
11580     { 0, -1 },
11581     { -1, 0 },
11582     { +1, 0 },
11583     { 0, +1 }
11584   };
11585   static int test_dir[4] =
11586   {
11587     MV_UP,
11588     MV_LEFT,
11589     MV_RIGHT,
11590     MV_DOWN
11591   };
11592
11593   for (i = 0; i < NUM_DIRECTIONS; i++)
11594   {
11595     int test_x, test_y, test_move_dir, test_element;
11596
11597     test_x = good_x + test_xy[i][0];
11598     test_y = good_y + test_xy[i][1];
11599
11600     if (!IN_LEV_FIELD(test_x, test_y))
11601       continue;
11602
11603     test_move_dir =
11604       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11605
11606     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11607
11608     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11609        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11610     */
11611     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11612         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11613     {
11614       kill_x = test_x;
11615       kill_y = test_y;
11616       bad_element = test_element;
11617
11618       break;
11619     }
11620   }
11621
11622   if (kill_x != -1 || kill_y != -1)
11623   {
11624     if (IS_PLAYER(good_x, good_y))
11625     {
11626       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11627
11628       if (player->shield_deadly_time_left > 0 &&
11629           !IS_INDESTRUCTIBLE(bad_element))
11630         Bang(kill_x, kill_y);
11631       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11632         KillPlayer(player);
11633     }
11634     else
11635       Bang(good_x, good_y);
11636   }
11637 }
11638
11639 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11640 {
11641   int i, kill_x = -1, kill_y = -1;
11642   int bad_element = Feld[bad_x][bad_y];
11643   static int test_xy[4][2] =
11644   {
11645     { 0, -1 },
11646     { -1, 0 },
11647     { +1, 0 },
11648     { 0, +1 }
11649   };
11650   static int touch_dir[4] =
11651   {
11652     MV_LEFT | MV_RIGHT,
11653     MV_UP   | MV_DOWN,
11654     MV_UP   | MV_DOWN,
11655     MV_LEFT | MV_RIGHT
11656   };
11657   static int test_dir[4] =
11658   {
11659     MV_UP,
11660     MV_LEFT,
11661     MV_RIGHT,
11662     MV_DOWN
11663   };
11664
11665   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11666     return;
11667
11668   for (i = 0; i < NUM_DIRECTIONS; i++)
11669   {
11670     int test_x, test_y, test_move_dir, test_element;
11671
11672     test_x = bad_x + test_xy[i][0];
11673     test_y = bad_y + test_xy[i][1];
11674     if (!IN_LEV_FIELD(test_x, test_y))
11675       continue;
11676
11677     test_move_dir =
11678       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11679
11680     test_element = Feld[test_x][test_y];
11681
11682     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11683        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11684     */
11685     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11686         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11687     {
11688       /* good thing is player or penguin that does not move away */
11689       if (IS_PLAYER(test_x, test_y))
11690       {
11691         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11692
11693         if (bad_element == EL_ROBOT && player->is_moving)
11694           continue;     /* robot does not kill player if he is moving */
11695
11696         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11697         {
11698           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11699             continue;           /* center and border element do not touch */
11700         }
11701
11702         kill_x = test_x;
11703         kill_y = test_y;
11704         break;
11705       }
11706       else if (test_element == EL_PENGUIN)
11707       {
11708         kill_x = test_x;
11709         kill_y = test_y;
11710         break;
11711       }
11712     }
11713   }
11714
11715   if (kill_x != -1 || kill_y != -1)
11716   {
11717     if (IS_PLAYER(kill_x, kill_y))
11718     {
11719       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11720
11721       if (player->shield_deadly_time_left > 0 &&
11722           !IS_INDESTRUCTIBLE(bad_element))
11723         Bang(bad_x, bad_y);
11724       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11725         KillPlayer(player);
11726     }
11727     else
11728       Bang(kill_x, kill_y);
11729   }
11730 }
11731
11732 void TestIfPlayerTouchesBadThing(int x, int y)
11733 {
11734   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11735 }
11736
11737 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11738 {
11739   TestIfGoodThingHitsBadThing(x, y, move_dir);
11740 }
11741
11742 void TestIfBadThingTouchesPlayer(int x, int y)
11743 {
11744   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11745 }
11746
11747 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11748 {
11749   TestIfBadThingHitsGoodThing(x, y, move_dir);
11750 }
11751
11752 void TestIfFriendTouchesBadThing(int x, int y)
11753 {
11754   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11755 }
11756
11757 void TestIfBadThingTouchesFriend(int x, int y)
11758 {
11759   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11760 }
11761
11762 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11763 {
11764   int i, kill_x = bad_x, kill_y = bad_y;
11765   static int xy[4][2] =
11766   {
11767     { 0, -1 },
11768     { -1, 0 },
11769     { +1, 0 },
11770     { 0, +1 }
11771   };
11772
11773   for (i = 0; i < NUM_DIRECTIONS; i++)
11774   {
11775     int x, y, element;
11776
11777     x = bad_x + xy[i][0];
11778     y = bad_y + xy[i][1];
11779     if (!IN_LEV_FIELD(x, y))
11780       continue;
11781
11782     element = Feld[x][y];
11783     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11784         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11785     {
11786       kill_x = x;
11787       kill_y = y;
11788       break;
11789     }
11790   }
11791
11792   if (kill_x != bad_x || kill_y != bad_y)
11793     Bang(bad_x, bad_y);
11794 }
11795
11796 void KillPlayer(struct PlayerInfo *player)
11797 {
11798   int jx = player->jx, jy = player->jy;
11799
11800   if (!player->active)
11801     return;
11802
11803   /* the following code was introduced to prevent an infinite loop when calling
11804      -> Bang()
11805      -> CheckTriggeredElementChangeExt()
11806      -> ExecuteCustomElementAction()
11807      -> KillPlayer()
11808      -> (infinitely repeating the above sequence of function calls)
11809      which occurs when killing the player while having a CE with the setting
11810      "kill player X when explosion of <player X>"; the solution using a new
11811      field "player->killed" was chosen for backwards compatibility, although
11812      clever use of the fields "player->active" etc. would probably also work */
11813 #if 1
11814   if (player->killed)
11815     return;
11816 #endif
11817
11818   player->killed = TRUE;
11819
11820   /* remove accessible field at the player's position */
11821   Feld[jx][jy] = EL_EMPTY;
11822
11823   /* deactivate shield (else Bang()/Explode() would not work right) */
11824   player->shield_normal_time_left = 0;
11825   player->shield_deadly_time_left = 0;
11826
11827   Bang(jx, jy);
11828   BuryPlayer(player);
11829 }
11830
11831 static void KillPlayerUnlessEnemyProtected(int x, int y)
11832 {
11833   if (!PLAYER_ENEMY_PROTECTED(x, y))
11834     KillPlayer(PLAYERINFO(x, y));
11835 }
11836
11837 static void KillPlayerUnlessExplosionProtected(int x, int y)
11838 {
11839   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11840     KillPlayer(PLAYERINFO(x, y));
11841 }
11842
11843 void BuryPlayer(struct PlayerInfo *player)
11844 {
11845   int jx = player->jx, jy = player->jy;
11846
11847   if (!player->active)
11848     return;
11849
11850   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11851   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11852
11853   player->GameOver = TRUE;
11854   RemovePlayer(player);
11855 }
11856
11857 void RemovePlayer(struct PlayerInfo *player)
11858 {
11859   int jx = player->jx, jy = player->jy;
11860   int i, found = FALSE;
11861
11862   player->present = FALSE;
11863   player->active = FALSE;
11864
11865   if (!ExplodeField[jx][jy])
11866     StorePlayer[jx][jy] = 0;
11867
11868   if (player->is_moving)
11869     DrawLevelField(player->last_jx, player->last_jy);
11870
11871   for (i = 0; i < MAX_PLAYERS; i++)
11872     if (stored_player[i].active)
11873       found = TRUE;
11874
11875   if (!found)
11876     AllPlayersGone = TRUE;
11877
11878   ExitX = ZX = jx;
11879   ExitY = ZY = jy;
11880 }
11881
11882 #if USE_NEW_SNAP_DELAY
11883 static void setFieldForSnapping(int x, int y, int element, int direction)
11884 {
11885   struct ElementInfo *ei = &element_info[element];
11886   int direction_bit = MV_DIR_TO_BIT(direction);
11887   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11888   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11889                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11890
11891   Feld[x][y] = EL_ELEMENT_SNAPPING;
11892   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11893
11894   ResetGfxAnimation(x, y);
11895
11896   GfxElement[x][y] = element;
11897   GfxAction[x][y] = action;
11898   GfxDir[x][y] = direction;
11899   GfxFrame[x][y] = -1;
11900 }
11901 #endif
11902
11903 /*
11904   =============================================================================
11905   checkDiagonalPushing()
11906   -----------------------------------------------------------------------------
11907   check if diagonal input device direction results in pushing of object
11908   (by checking if the alternative direction is walkable, diggable, ...)
11909   =============================================================================
11910 */
11911
11912 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11913                                     int x, int y, int real_dx, int real_dy)
11914 {
11915   int jx, jy, dx, dy, xx, yy;
11916
11917   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11918     return TRUE;
11919
11920   /* diagonal direction: check alternative direction */
11921   jx = player->jx;
11922   jy = player->jy;
11923   dx = x - jx;
11924   dy = y - jy;
11925   xx = jx + (dx == 0 ? real_dx : 0);
11926   yy = jy + (dy == 0 ? real_dy : 0);
11927
11928   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11929 }
11930
11931 /*
11932   =============================================================================
11933   DigField()
11934   -----------------------------------------------------------------------------
11935   x, y:                 field next to player (non-diagonal) to try to dig to
11936   real_dx, real_dy:     direction as read from input device (can be diagonal)
11937   =============================================================================
11938 */
11939
11940 int DigField(struct PlayerInfo *player,
11941              int oldx, int oldy, int x, int y,
11942              int real_dx, int real_dy, int mode)
11943 {
11944   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11945   boolean player_was_pushing = player->is_pushing;
11946   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11947   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11948   int jx = oldx, jy = oldy;
11949   int dx = x - jx, dy = y - jy;
11950   int nextx = x + dx, nexty = y + dy;
11951   int move_direction = (dx == -1 ? MV_LEFT  :
11952                         dx == +1 ? MV_RIGHT :
11953                         dy == -1 ? MV_UP    :
11954                         dy == +1 ? MV_DOWN  : MV_NONE);
11955   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11956   int dig_side = MV_DIR_OPPOSITE(move_direction);
11957   int old_element = Feld[jx][jy];
11958 #if USE_FIXED_DONT_RUN_INTO
11959   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11960 #else
11961   int element;
11962 #endif
11963   int collect_count;
11964
11965   if (is_player)                /* function can also be called by EL_PENGUIN */
11966   {
11967     if (player->MovPos == 0)
11968     {
11969       player->is_digging = FALSE;
11970       player->is_collecting = FALSE;
11971     }
11972
11973     if (player->MovPos == 0)    /* last pushing move finished */
11974       player->is_pushing = FALSE;
11975
11976     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11977     {
11978       player->is_switching = FALSE;
11979       player->push_delay = -1;
11980
11981       return MP_NO_ACTION;
11982     }
11983   }
11984
11985 #if !USE_FIXED_DONT_RUN_INTO
11986   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11987     return MP_NO_ACTION;
11988 #endif
11989
11990   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11991     old_element = Back[jx][jy];
11992
11993   /* in case of element dropped at player position, check background */
11994   else if (Back[jx][jy] != EL_EMPTY &&
11995            game.engine_version >= VERSION_IDENT(2,2,0,0))
11996     old_element = Back[jx][jy];
11997
11998   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11999     return MP_NO_ACTION;        /* field has no opening in this direction */
12000
12001   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12002     return MP_NO_ACTION;        /* field has no opening in this direction */
12003
12004 #if USE_FIXED_DONT_RUN_INTO
12005   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12006   {
12007     SplashAcid(x, y);
12008
12009     Feld[jx][jy] = player->artwork_element;
12010     InitMovingField(jx, jy, MV_DOWN);
12011     Store[jx][jy] = EL_ACID;
12012     ContinueMoving(jx, jy);
12013     BuryPlayer(player);
12014
12015     return MP_DONT_RUN_INTO;
12016   }
12017 #endif
12018
12019 #if USE_FIXED_DONT_RUN_INTO
12020   if (player_can_move && DONT_RUN_INTO(element))
12021   {
12022     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12023
12024     return MP_DONT_RUN_INTO;
12025   }
12026 #endif
12027
12028 #if USE_FIXED_DONT_RUN_INTO
12029   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12030     return MP_NO_ACTION;
12031 #endif
12032
12033 #if !USE_FIXED_DONT_RUN_INTO
12034   element = Feld[x][y];
12035 #endif
12036
12037   collect_count = element_info[element].collect_count_initial;
12038
12039   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12040     return MP_NO_ACTION;
12041
12042   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12043     player_can_move = player_can_move_or_snap;
12044
12045   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12046       game.engine_version >= VERSION_IDENT(2,2,0,0))
12047   {
12048     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12049                                player->index_bit, dig_side);
12050     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12051                                         player->index_bit, dig_side);
12052
12053     if (element == EL_DC_LANDMINE)
12054       Bang(x, y);
12055
12056     if (Feld[x][y] != element)          /* field changed by snapping */
12057       return MP_ACTION;
12058
12059     return MP_NO_ACTION;
12060   }
12061
12062 #if USE_PLAYER_GRAVITY
12063   if (player->gravity && is_player && !player->is_auto_moving &&
12064       canFallDown(player) && move_direction != MV_DOWN &&
12065       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12066     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12067 #else
12068   if (game.gravity && is_player && !player->is_auto_moving &&
12069       canFallDown(player) && move_direction != MV_DOWN &&
12070       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12071     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12072 #endif
12073
12074   if (player_can_move &&
12075       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12076   {
12077     int sound_element = SND_ELEMENT(element);
12078     int sound_action = ACTION_WALKING;
12079
12080     if (IS_RND_GATE(element))
12081     {
12082       if (!player->key[RND_GATE_NR(element)])
12083         return MP_NO_ACTION;
12084     }
12085     else if (IS_RND_GATE_GRAY(element))
12086     {
12087       if (!player->key[RND_GATE_GRAY_NR(element)])
12088         return MP_NO_ACTION;
12089     }
12090     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12091     {
12092       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12093         return MP_NO_ACTION;
12094     }
12095     else if (element == EL_EXIT_OPEN ||
12096              element == EL_EM_EXIT_OPEN ||
12097              element == EL_STEEL_EXIT_OPEN ||
12098              element == EL_EM_STEEL_EXIT_OPEN ||
12099              element == EL_SP_EXIT_OPEN ||
12100              element == EL_SP_EXIT_OPENING)
12101     {
12102       sound_action = ACTION_PASSING;    /* player is passing exit */
12103     }
12104     else if (element == EL_EMPTY)
12105     {
12106       sound_action = ACTION_MOVING;             /* nothing to walk on */
12107     }
12108
12109     /* play sound from background or player, whatever is available */
12110     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12111       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12112     else
12113       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12114   }
12115   else if (player_can_move &&
12116            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12117   {
12118     if (!ACCESS_FROM(element, opposite_direction))
12119       return MP_NO_ACTION;      /* field not accessible from this direction */
12120
12121     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12122       return MP_NO_ACTION;
12123
12124     if (IS_EM_GATE(element))
12125     {
12126       if (!player->key[EM_GATE_NR(element)])
12127         return MP_NO_ACTION;
12128     }
12129     else if (IS_EM_GATE_GRAY(element))
12130     {
12131       if (!player->key[EM_GATE_GRAY_NR(element)])
12132         return MP_NO_ACTION;
12133     }
12134     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12135     {
12136       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12137         return MP_NO_ACTION;
12138     }
12139     else if (IS_EMC_GATE(element))
12140     {
12141       if (!player->key[EMC_GATE_NR(element)])
12142         return MP_NO_ACTION;
12143     }
12144     else if (IS_EMC_GATE_GRAY(element))
12145     {
12146       if (!player->key[EMC_GATE_GRAY_NR(element)])
12147         return MP_NO_ACTION;
12148     }
12149     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12150     {
12151       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12152         return MP_NO_ACTION;
12153     }
12154     else if (element == EL_DC_GATE_WHITE ||
12155              element == EL_DC_GATE_WHITE_GRAY ||
12156              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
12157     {
12158       if (player->num_white_keys == 0)
12159         return MP_NO_ACTION;
12160
12161       player->num_white_keys--;
12162     }
12163     else if (IS_SP_PORT(element))
12164     {
12165       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12166           element == EL_SP_GRAVITY_PORT_RIGHT ||
12167           element == EL_SP_GRAVITY_PORT_UP ||
12168           element == EL_SP_GRAVITY_PORT_DOWN)
12169 #if USE_PLAYER_GRAVITY
12170         player->gravity = !player->gravity;
12171 #else
12172         game.gravity = !game.gravity;
12173 #endif
12174       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12175                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12176                element == EL_SP_GRAVITY_ON_PORT_UP ||
12177                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12178 #if USE_PLAYER_GRAVITY
12179         player->gravity = TRUE;
12180 #else
12181         game.gravity = TRUE;
12182 #endif
12183       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12184                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12185                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12186                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12187 #if USE_PLAYER_GRAVITY
12188         player->gravity = FALSE;
12189 #else
12190         game.gravity = FALSE;
12191 #endif
12192     }
12193
12194     /* automatically move to the next field with double speed */
12195     player->programmed_action = move_direction;
12196
12197     if (player->move_delay_reset_counter == 0)
12198     {
12199       player->move_delay_reset_counter = 2;     /* two double speed steps */
12200
12201       DOUBLE_PLAYER_SPEED(player);
12202     }
12203
12204     PlayLevelSoundAction(x, y, ACTION_PASSING);
12205   }
12206   else if (player_can_move_or_snap && IS_DIGGABLE(element))
12207   {
12208     RemoveField(x, y);
12209
12210     if (mode != DF_SNAP)
12211     {
12212       GfxElement[x][y] = GFX_ELEMENT(element);
12213       player->is_digging = TRUE;
12214     }
12215
12216     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12217
12218     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12219                                         player->index_bit, dig_side);
12220
12221     if (mode == DF_SNAP)
12222     {
12223 #if USE_NEW_SNAP_DELAY
12224       if (level.block_snap_field)
12225         setFieldForSnapping(x, y, element, move_direction);
12226       else
12227         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12228 #else
12229       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12230 #endif
12231
12232       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12233                                           player->index_bit, dig_side);
12234     }
12235   }
12236   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12237   {
12238     RemoveField(x, y);
12239
12240     if (is_player && mode != DF_SNAP)
12241     {
12242       GfxElement[x][y] = element;
12243       player->is_collecting = TRUE;
12244     }
12245
12246     if (element == EL_SPEED_PILL)
12247     {
12248       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12249     }
12250     else if (element == EL_EXTRA_TIME && level.time > 0)
12251     {
12252       TimeLeft += level.extra_time;
12253       DrawGameValue_Time(TimeLeft);
12254     }
12255     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12256     {
12257       player->shield_normal_time_left += level.shield_normal_time;
12258       if (element == EL_SHIELD_DEADLY)
12259         player->shield_deadly_time_left += level.shield_deadly_time;
12260     }
12261     else if (element == EL_DYNAMITE ||
12262              element == EL_EM_DYNAMITE ||
12263              element == EL_SP_DISK_RED)
12264     {
12265       if (player->inventory_size < MAX_INVENTORY_SIZE)
12266         player->inventory_element[player->inventory_size++] = element;
12267
12268       DrawGameDoorValues();
12269     }
12270     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12271     {
12272       player->dynabomb_count++;
12273       player->dynabombs_left++;
12274     }
12275     else if (element == EL_DYNABOMB_INCREASE_SIZE)
12276     {
12277       player->dynabomb_size++;
12278     }
12279     else if (element == EL_DYNABOMB_INCREASE_POWER)
12280     {
12281       player->dynabomb_xl = TRUE;
12282     }
12283     else if (IS_KEY(element))
12284     {
12285       player->key[KEY_NR(element)] = TRUE;
12286
12287       DrawGameDoorValues();
12288     }
12289     else if (element == EL_DC_KEY_WHITE)
12290     {
12291       player->num_white_keys++;
12292
12293       /* display white keys? */
12294       /* DrawGameDoorValues(); */
12295     }
12296     else if (IS_ENVELOPE(element))
12297     {
12298       player->show_envelope = element;
12299     }
12300     else if (element == EL_EMC_LENSES)
12301     {
12302       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12303
12304       RedrawAllInvisibleElementsForLenses();
12305     }
12306     else if (element == EL_EMC_MAGNIFIER)
12307     {
12308       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12309
12310       RedrawAllInvisibleElementsForMagnifier();
12311     }
12312     else if (IS_DROPPABLE(element) ||
12313              IS_THROWABLE(element))     /* can be collected and dropped */
12314     {
12315       int i;
12316
12317       if (collect_count == 0)
12318         player->inventory_infinite_element = element;
12319       else
12320         for (i = 0; i < collect_count; i++)
12321           if (player->inventory_size < MAX_INVENTORY_SIZE)
12322             player->inventory_element[player->inventory_size++] = element;
12323
12324       DrawGameDoorValues();
12325     }
12326     else if (collect_count > 0)
12327     {
12328       local_player->gems_still_needed -= collect_count;
12329       if (local_player->gems_still_needed < 0)
12330         local_player->gems_still_needed = 0;
12331
12332       DrawGameValue_Emeralds(local_player->gems_still_needed);
12333     }
12334
12335     RaiseScoreElement(element);
12336     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12337
12338     if (is_player)
12339       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12340                                           player->index_bit, dig_side);
12341
12342     if (mode == DF_SNAP)
12343     {
12344 #if USE_NEW_SNAP_DELAY
12345       if (level.block_snap_field)
12346         setFieldForSnapping(x, y, element, move_direction);
12347       else
12348         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12349 #else
12350       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12351 #endif
12352
12353       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12354                                           player->index_bit, dig_side);
12355     }
12356   }
12357   else if (player_can_move_or_snap && IS_PUSHABLE(element))
12358   {
12359     if (mode == DF_SNAP && element != EL_BD_ROCK)
12360       return MP_NO_ACTION;
12361
12362     if (CAN_FALL(element) && dy)
12363       return MP_NO_ACTION;
12364
12365     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12366         !(element == EL_SPRING && level.use_spring_bug))
12367       return MP_NO_ACTION;
12368
12369     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12370         ((move_direction & MV_VERTICAL &&
12371           ((element_info[element].move_pattern & MV_LEFT &&
12372             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12373            (element_info[element].move_pattern & MV_RIGHT &&
12374             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12375          (move_direction & MV_HORIZONTAL &&
12376           ((element_info[element].move_pattern & MV_UP &&
12377             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12378            (element_info[element].move_pattern & MV_DOWN &&
12379             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12380       return MP_NO_ACTION;
12381
12382     /* do not push elements already moving away faster than player */
12383     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12384         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12385       return MP_NO_ACTION;
12386
12387     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12388     {
12389       if (player->push_delay_value == -1 || !player_was_pushing)
12390         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12391     }
12392     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12393     {
12394       if (player->push_delay_value == -1)
12395         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12396     }
12397     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12398     {
12399       if (!player->is_pushing)
12400         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12401     }
12402
12403     player->is_pushing = TRUE;
12404     player->is_active = TRUE;
12405
12406     if (!(IN_LEV_FIELD(nextx, nexty) &&
12407           (IS_FREE(nextx, nexty) ||
12408            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12409             IS_SB_ELEMENT(element)))))
12410       return MP_NO_ACTION;
12411
12412     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12413       return MP_NO_ACTION;
12414
12415     if (player->push_delay == -1)       /* new pushing; restart delay */
12416       player->push_delay = 0;
12417
12418     if (player->push_delay < player->push_delay_value &&
12419         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12420         element != EL_SPRING && element != EL_BALLOON)
12421     {
12422       /* make sure that there is no move delay before next try to push */
12423       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12424         player->move_delay = 0;
12425
12426       return MP_NO_ACTION;
12427     }
12428
12429     if (IS_SB_ELEMENT(element))
12430     {
12431       if (element == EL_SOKOBAN_FIELD_FULL)
12432       {
12433         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12434         local_player->sokobanfields_still_needed++;
12435       }
12436
12437       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12438       {
12439         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12440         local_player->sokobanfields_still_needed--;
12441       }
12442
12443       Feld[x][y] = EL_SOKOBAN_OBJECT;
12444
12445       if (Back[x][y] == Back[nextx][nexty])
12446         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12447       else if (Back[x][y] != 0)
12448         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12449                                     ACTION_EMPTYING);
12450       else
12451         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12452                                     ACTION_FILLING);
12453
12454       if (local_player->sokobanfields_still_needed == 0 &&
12455           game.emulation == EMU_SOKOBAN)
12456       {
12457         PlayerWins(player);
12458
12459         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12460       }
12461     }
12462     else
12463       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12464
12465     InitMovingField(x, y, move_direction);
12466     GfxAction[x][y] = ACTION_PUSHING;
12467
12468     if (mode == DF_SNAP)
12469       ContinueMoving(x, y);
12470     else
12471       MovPos[x][y] = (dx != 0 ? dx : dy);
12472
12473     Pushed[x][y] = TRUE;
12474     Pushed[nextx][nexty] = TRUE;
12475
12476     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12477       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12478     else
12479       player->push_delay_value = -1;    /* get new value later */
12480
12481     /* check for element change _after_ element has been pushed */
12482     if (game.use_change_when_pushing_bug)
12483     {
12484       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12485                                  player->index_bit, dig_side);
12486       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12487                                           player->index_bit, dig_side);
12488     }
12489   }
12490   else if (IS_SWITCHABLE(element))
12491   {
12492     if (PLAYER_SWITCHING(player, x, y))
12493     {
12494       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12495                                           player->index_bit, dig_side);
12496
12497       return MP_ACTION;
12498     }
12499
12500     player->is_switching = TRUE;
12501     player->switch_x = x;
12502     player->switch_y = y;
12503
12504     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12505
12506     if (element == EL_ROBOT_WHEEL)
12507     {
12508       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12509       ZX = x;
12510       ZY = y;
12511
12512       DrawLevelField(x, y);
12513     }
12514     else if (element == EL_SP_TERMINAL)
12515     {
12516       int xx, yy;
12517
12518       SCAN_PLAYFIELD(xx, yy)
12519       {
12520         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12521           Bang(xx, yy);
12522         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12523           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12524       }
12525     }
12526     else if (IS_BELT_SWITCH(element))
12527     {
12528       ToggleBeltSwitch(x, y);
12529     }
12530     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12531              element == EL_SWITCHGATE_SWITCH_DOWN ||
12532              element == EL_DC_SWITCHGATE_SWITCH_UP ||
12533              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
12534     {
12535       ToggleSwitchgateSwitch(x, y);
12536     }
12537     else if (element == EL_LIGHT_SWITCH ||
12538              element == EL_LIGHT_SWITCH_ACTIVE)
12539     {
12540       ToggleLightSwitch(x, y);
12541     }
12542     else if (element == EL_TIMEGATE_SWITCH ||
12543              element == EL_DC_TIMEGATE_SWITCH)
12544     {
12545       ActivateTimegateSwitch(x, y);
12546     }
12547     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12548              element == EL_BALLOON_SWITCH_RIGHT ||
12549              element == EL_BALLOON_SWITCH_UP    ||
12550              element == EL_BALLOON_SWITCH_DOWN  ||
12551              element == EL_BALLOON_SWITCH_NONE  ||
12552              element == EL_BALLOON_SWITCH_ANY)
12553     {
12554       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12555                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12556                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12557                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12558                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12559                              move_direction);
12560     }
12561     else if (element == EL_LAMP)
12562     {
12563       Feld[x][y] = EL_LAMP_ACTIVE;
12564       local_player->lights_still_needed--;
12565
12566       ResetGfxAnimation(x, y);
12567       DrawLevelField(x, y);
12568     }
12569     else if (element == EL_TIME_ORB_FULL)
12570     {
12571       Feld[x][y] = EL_TIME_ORB_EMPTY;
12572
12573       if (level.time > 0 || level.use_time_orb_bug)
12574       {
12575         TimeLeft += level.time_orb_time;
12576         DrawGameValue_Time(TimeLeft);
12577       }
12578
12579       ResetGfxAnimation(x, y);
12580       DrawLevelField(x, y);
12581     }
12582     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12583              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12584     {
12585       int xx, yy;
12586
12587       game.ball_state = !game.ball_state;
12588
12589       SCAN_PLAYFIELD(xx, yy)
12590       {
12591         int e = Feld[xx][yy];
12592
12593         if (game.ball_state)
12594         {
12595           if (e == EL_EMC_MAGIC_BALL)
12596             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12597           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12598             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12599         }
12600         else
12601         {
12602           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12603             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12604           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12605             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12606         }
12607       }
12608     }
12609
12610     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12611                                         player->index_bit, dig_side);
12612
12613     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12614                                         player->index_bit, dig_side);
12615
12616     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12617                                         player->index_bit, dig_side);
12618
12619     return MP_ACTION;
12620   }
12621   else
12622   {
12623     if (!PLAYER_SWITCHING(player, x, y))
12624     {
12625       player->is_switching = TRUE;
12626       player->switch_x = x;
12627       player->switch_y = y;
12628
12629       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12630                                  player->index_bit, dig_side);
12631       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12632                                           player->index_bit, dig_side);
12633
12634       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12635                                  player->index_bit, dig_side);
12636       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12637                                           player->index_bit, dig_side);
12638     }
12639
12640     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12641                                player->index_bit, dig_side);
12642     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12643                                         player->index_bit, dig_side);
12644
12645     return MP_NO_ACTION;
12646   }
12647
12648   player->push_delay = -1;
12649
12650   if (is_player)                /* function can also be called by EL_PENGUIN */
12651   {
12652     if (Feld[x][y] != element)          /* really digged/collected something */
12653     {
12654       player->is_collecting = !player->is_digging;
12655       player->is_active = TRUE;
12656     }
12657   }
12658
12659   return MP_MOVING;
12660 }
12661
12662 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12663 {
12664   int jx = player->jx, jy = player->jy;
12665   int x = jx + dx, y = jy + dy;
12666   int snap_direction = (dx == -1 ? MV_LEFT  :
12667                         dx == +1 ? MV_RIGHT :
12668                         dy == -1 ? MV_UP    :
12669                         dy == +1 ? MV_DOWN  : MV_NONE);
12670   boolean can_continue_snapping = (level.continuous_snapping &&
12671                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12672
12673   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12674     return FALSE;
12675
12676   if (!player->active || !IN_LEV_FIELD(x, y))
12677     return FALSE;
12678
12679   if (dx && dy)
12680     return FALSE;
12681
12682   if (!dx && !dy)
12683   {
12684     if (player->MovPos == 0)
12685       player->is_pushing = FALSE;
12686
12687     player->is_snapping = FALSE;
12688
12689     if (player->MovPos == 0)
12690     {
12691       player->is_moving = FALSE;
12692       player->is_digging = FALSE;
12693       player->is_collecting = FALSE;
12694     }
12695
12696     return FALSE;
12697   }
12698
12699 #if USE_NEW_CONTINUOUS_SNAPPING
12700   /* prevent snapping with already pressed snap key when not allowed */
12701   if (player->is_snapping && !can_continue_snapping)
12702     return FALSE;
12703 #else
12704   if (player->is_snapping)
12705     return FALSE;
12706 #endif
12707
12708   player->MovDir = snap_direction;
12709
12710   if (player->MovPos == 0)
12711   {
12712     player->is_moving = FALSE;
12713     player->is_digging = FALSE;
12714     player->is_collecting = FALSE;
12715   }
12716
12717   player->is_dropping = FALSE;
12718   player->is_dropping_pressed = FALSE;
12719   player->drop_pressed_delay = 0;
12720
12721   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12722     return FALSE;
12723
12724   player->is_snapping = TRUE;
12725   player->is_active = TRUE;
12726
12727   if (player->MovPos == 0)
12728   {
12729     player->is_moving = FALSE;
12730     player->is_digging = FALSE;
12731     player->is_collecting = FALSE;
12732   }
12733
12734   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12735     DrawLevelField(player->last_jx, player->last_jy);
12736
12737   DrawLevelField(x, y);
12738
12739   return TRUE;
12740 }
12741
12742 boolean DropElement(struct PlayerInfo *player)
12743 {
12744   int old_element, new_element;
12745   int dropx = player->jx, dropy = player->jy;
12746   int drop_direction = player->MovDir;
12747   int drop_side = drop_direction;
12748   int drop_element = (player->inventory_size > 0 ?
12749                       player->inventory_element[player->inventory_size - 1] :
12750                       player->inventory_infinite_element != EL_UNDEFINED ?
12751                       player->inventory_infinite_element :
12752                       player->dynabombs_left > 0 ?
12753                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12754                       EL_UNDEFINED);
12755
12756   player->is_dropping_pressed = TRUE;
12757
12758   /* do not drop an element on top of another element; when holding drop key
12759      pressed without moving, dropped element must move away before the next
12760      element can be dropped (this is especially important if the next element
12761      is dynamite, which can be placed on background for historical reasons) */
12762   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12763     return MP_ACTION;
12764
12765   if (IS_THROWABLE(drop_element))
12766   {
12767     dropx += GET_DX_FROM_DIR(drop_direction);
12768     dropy += GET_DY_FROM_DIR(drop_direction);
12769
12770     if (!IN_LEV_FIELD(dropx, dropy))
12771       return FALSE;
12772   }
12773
12774   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12775   new_element = drop_element;           /* default: no change when dropping */
12776
12777   /* check if player is active, not moving and ready to drop */
12778   if (!player->active || player->MovPos || player->drop_delay > 0)
12779     return FALSE;
12780
12781   /* check if player has anything that can be dropped */
12782   if (new_element == EL_UNDEFINED)
12783     return FALSE;
12784
12785   /* check if drop key was pressed long enough for EM style dynamite */
12786   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12787     return FALSE;
12788
12789   /* check if anything can be dropped at the current position */
12790   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12791     return FALSE;
12792
12793   /* collected custom elements can only be dropped on empty fields */
12794   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12795     return FALSE;
12796
12797   if (old_element != EL_EMPTY)
12798     Back[dropx][dropy] = old_element;   /* store old element on this field */
12799
12800   ResetGfxAnimation(dropx, dropy);
12801   ResetRandomAnimationValue(dropx, dropy);
12802
12803   if (player->inventory_size > 0 ||
12804       player->inventory_infinite_element != EL_UNDEFINED)
12805   {
12806     if (player->inventory_size > 0)
12807     {
12808       player->inventory_size--;
12809
12810       DrawGameDoorValues();
12811
12812       if (new_element == EL_DYNAMITE)
12813         new_element = EL_DYNAMITE_ACTIVE;
12814       else if (new_element == EL_EM_DYNAMITE)
12815         new_element = EL_EM_DYNAMITE_ACTIVE;
12816       else if (new_element == EL_SP_DISK_RED)
12817         new_element = EL_SP_DISK_RED_ACTIVE;
12818     }
12819
12820     Feld[dropx][dropy] = new_element;
12821
12822     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12823       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12824                           el2img(Feld[dropx][dropy]), 0);
12825
12826     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12827
12828     /* needed if previous element just changed to "empty" in the last frame */
12829     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12830
12831     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12832                                player->index_bit, drop_side);
12833     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12834                                         CE_PLAYER_DROPS_X,
12835                                         player->index_bit, drop_side);
12836
12837     TestIfElementTouchesCustomElement(dropx, dropy);
12838   }
12839   else          /* player is dropping a dyna bomb */
12840   {
12841     player->dynabombs_left--;
12842
12843     Feld[dropx][dropy] = new_element;
12844
12845     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12846       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12847                           el2img(Feld[dropx][dropy]), 0);
12848
12849     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12850   }
12851
12852   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12853     InitField_WithBug1(dropx, dropy, FALSE);
12854
12855   new_element = Feld[dropx][dropy];     /* element might have changed */
12856
12857   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12858       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12859   {
12860     int move_direction, nextx, nexty;
12861
12862     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12863       MovDir[dropx][dropy] = drop_direction;
12864
12865     move_direction = MovDir[dropx][dropy];
12866     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12867     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12868
12869     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12870
12871 #if USE_FIX_IMPACT_COLLISION
12872     /* do not cause impact style collision by dropping elements that can fall */
12873     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12874 #else
12875     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12876 #endif
12877   }
12878
12879   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12880   player->is_dropping = TRUE;
12881
12882   player->drop_pressed_delay = 0;
12883   player->is_dropping_pressed = FALSE;
12884
12885   player->drop_x = dropx;
12886   player->drop_y = dropy;
12887
12888   return TRUE;
12889 }
12890
12891 /* ------------------------------------------------------------------------- */
12892 /* game sound playing functions                                              */
12893 /* ------------------------------------------------------------------------- */
12894
12895 static int *loop_sound_frame = NULL;
12896 static int *loop_sound_volume = NULL;
12897
12898 void InitPlayLevelSound()
12899 {
12900   int num_sounds = getSoundListSize();
12901
12902   checked_free(loop_sound_frame);
12903   checked_free(loop_sound_volume);
12904
12905   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12906   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12907 }
12908
12909 static void PlayLevelSound(int x, int y, int nr)
12910 {
12911   int sx = SCREENX(x), sy = SCREENY(y);
12912   int volume, stereo_position;
12913   int max_distance = 8;
12914   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12915
12916   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12917       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12918     return;
12919
12920   if (!IN_LEV_FIELD(x, y) ||
12921       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12922       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12923     return;
12924
12925   volume = SOUND_MAX_VOLUME;
12926
12927   if (!IN_SCR_FIELD(sx, sy))
12928   {
12929     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12930     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12931
12932     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12933   }
12934
12935   stereo_position = (SOUND_MAX_LEFT +
12936                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12937                      (SCR_FIELDX + 2 * max_distance));
12938
12939   if (IS_LOOP_SOUND(nr))
12940   {
12941     /* This assures that quieter loop sounds do not overwrite louder ones,
12942        while restarting sound volume comparison with each new game frame. */
12943
12944     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12945       return;
12946
12947     loop_sound_volume[nr] = volume;
12948     loop_sound_frame[nr] = FrameCounter;
12949   }
12950
12951   PlaySoundExt(nr, volume, stereo_position, type);
12952 }
12953
12954 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12955 {
12956   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12957                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12958                  y < LEVELY(BY1) ? LEVELY(BY1) :
12959                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12960                  sound_action);
12961 }
12962
12963 static void PlayLevelSoundAction(int x, int y, int action)
12964 {
12965   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12966 }
12967
12968 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12969 {
12970   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12971
12972   if (sound_effect != SND_UNDEFINED)
12973     PlayLevelSound(x, y, sound_effect);
12974 }
12975
12976 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12977                                               int action)
12978 {
12979   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12980
12981   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12982     PlayLevelSound(x, y, sound_effect);
12983 }
12984
12985 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12986 {
12987   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12988
12989   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12990     PlayLevelSound(x, y, sound_effect);
12991 }
12992
12993 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12994 {
12995   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12996
12997   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12998     StopSound(sound_effect);
12999 }
13000
13001 static void PlayLevelMusic()
13002 {
13003   if (levelset.music[level_nr] != MUS_UNDEFINED)
13004     PlayMusic(levelset.music[level_nr]);        /* from config file */
13005   else
13006     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13007 }
13008
13009 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
13010 {
13011   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13012   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
13013   int x = xx - 1 - offset;
13014   int y = yy - 1 - offset;
13015
13016   switch (sample)
13017   {
13018     case SAMPLE_blank:
13019       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13020       break;
13021
13022     case SAMPLE_roll:
13023       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13024       break;
13025
13026     case SAMPLE_stone:
13027       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13028       break;
13029
13030     case SAMPLE_nut:
13031       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13032       break;
13033
13034     case SAMPLE_crack:
13035       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13036       break;
13037
13038     case SAMPLE_bug:
13039       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13040       break;
13041
13042     case SAMPLE_tank:
13043       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13044       break;
13045
13046     case SAMPLE_android_clone:
13047       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13048       break;
13049
13050     case SAMPLE_android_move:
13051       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13052       break;
13053
13054     case SAMPLE_spring:
13055       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13056       break;
13057
13058     case SAMPLE_slurp:
13059       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13060       break;
13061
13062     case SAMPLE_eater:
13063       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13064       break;
13065
13066     case SAMPLE_eater_eat:
13067       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13068       break;
13069
13070     case SAMPLE_alien:
13071       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13072       break;
13073
13074     case SAMPLE_collect:
13075       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13076       break;
13077
13078     case SAMPLE_diamond:
13079       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13080       break;
13081
13082     case SAMPLE_squash:
13083       /* !!! CHECK THIS !!! */
13084 #if 1
13085       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13086 #else
13087       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13088 #endif
13089       break;
13090
13091     case SAMPLE_wonderfall:
13092       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13093       break;
13094
13095     case SAMPLE_drip:
13096       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13097       break;
13098
13099     case SAMPLE_push:
13100       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13101       break;
13102
13103     case SAMPLE_dirt:
13104       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13105       break;
13106
13107     case SAMPLE_acid:
13108       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13109       break;
13110
13111     case SAMPLE_ball:
13112       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13113       break;
13114
13115     case SAMPLE_grow:
13116       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13117       break;
13118
13119     case SAMPLE_wonder:
13120       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13121       break;
13122
13123     case SAMPLE_door:
13124       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13125       break;
13126
13127     case SAMPLE_exit_open:
13128       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13129       break;
13130
13131     case SAMPLE_exit_leave:
13132       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13133       break;
13134
13135     case SAMPLE_dynamite:
13136       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13137       break;
13138
13139     case SAMPLE_tick:
13140       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13141       break;
13142
13143     case SAMPLE_press:
13144       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13145       break;
13146
13147     case SAMPLE_wheel:
13148       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13149       break;
13150
13151     case SAMPLE_boom:
13152       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13153       break;
13154
13155     case SAMPLE_die:
13156       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13157       break;
13158
13159     case SAMPLE_time:
13160       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13161       break;
13162
13163     default:
13164       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13165       break;
13166   }
13167 }
13168
13169 #if 0
13170 void ChangeTime(int value)
13171 {
13172   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
13173
13174   *time += value;
13175
13176   /* EMC game engine uses value from time counter of RND game engine */
13177   level.native_em_level->lev->time = *time;
13178
13179   DrawGameValue_Time(*time);
13180 }
13181
13182 void RaiseScore(int value)
13183 {
13184   /* EMC game engine and RND game engine have separate score counters */
13185   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
13186                 &level.native_em_level->lev->score : &local_player->score);
13187
13188   *score += value;
13189
13190   DrawGameValue_Score(*score);
13191 }
13192 #endif
13193
13194 void RaiseScore(int value)
13195 {
13196   local_player->score += value;
13197
13198   DrawGameValue_Score(local_player->score);
13199 }
13200
13201 void RaiseScoreElement(int element)
13202 {
13203   switch (element)
13204   {
13205     case EL_EMERALD:
13206     case EL_BD_DIAMOND:
13207     case EL_EMERALD_YELLOW:
13208     case EL_EMERALD_RED:
13209     case EL_EMERALD_PURPLE:
13210     case EL_SP_INFOTRON:
13211       RaiseScore(level.score[SC_EMERALD]);
13212       break;
13213     case EL_DIAMOND:
13214       RaiseScore(level.score[SC_DIAMOND]);
13215       break;
13216     case EL_CRYSTAL:
13217       RaiseScore(level.score[SC_CRYSTAL]);
13218       break;
13219     case EL_PEARL:
13220       RaiseScore(level.score[SC_PEARL]);
13221       break;
13222     case EL_BUG:
13223     case EL_BD_BUTTERFLY:
13224     case EL_SP_ELECTRON:
13225       RaiseScore(level.score[SC_BUG]);
13226       break;
13227     case EL_SPACESHIP:
13228     case EL_BD_FIREFLY:
13229     case EL_SP_SNIKSNAK:
13230       RaiseScore(level.score[SC_SPACESHIP]);
13231       break;
13232     case EL_YAMYAM:
13233     case EL_DARK_YAMYAM:
13234       RaiseScore(level.score[SC_YAMYAM]);
13235       break;
13236     case EL_ROBOT:
13237       RaiseScore(level.score[SC_ROBOT]);
13238       break;
13239     case EL_PACMAN:
13240       RaiseScore(level.score[SC_PACMAN]);
13241       break;
13242     case EL_NUT:
13243       RaiseScore(level.score[SC_NUT]);
13244       break;
13245     case EL_DYNAMITE:
13246     case EL_EM_DYNAMITE:
13247     case EL_SP_DISK_RED:
13248     case EL_DYNABOMB_INCREASE_NUMBER:
13249     case EL_DYNABOMB_INCREASE_SIZE:
13250     case EL_DYNABOMB_INCREASE_POWER:
13251       RaiseScore(level.score[SC_DYNAMITE]);
13252       break;
13253     case EL_SHIELD_NORMAL:
13254     case EL_SHIELD_DEADLY:
13255       RaiseScore(level.score[SC_SHIELD]);
13256       break;
13257     case EL_EXTRA_TIME:
13258       RaiseScore(level.extra_time_score);
13259       break;
13260     case EL_KEY_1:
13261     case EL_KEY_2:
13262     case EL_KEY_3:
13263     case EL_KEY_4:
13264     case EL_EM_KEY_1:
13265     case EL_EM_KEY_2:
13266     case EL_EM_KEY_3:
13267     case EL_EM_KEY_4:
13268     case EL_EMC_KEY_5:
13269     case EL_EMC_KEY_6:
13270     case EL_EMC_KEY_7:
13271     case EL_EMC_KEY_8:
13272     case EL_DC_KEY_WHITE:
13273       RaiseScore(level.score[SC_KEY]);
13274       break;
13275     default:
13276       RaiseScore(element_info[element].collect_score);
13277       break;
13278   }
13279 }
13280
13281 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
13282 {
13283   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
13284   {
13285 #if defined(NETWORK_AVALIABLE)
13286     if (options.network)
13287       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13288     else
13289 #endif
13290     {
13291       if (quick_quit)
13292       {
13293         game_status = GAME_MODE_MAIN;
13294
13295         DrawMainMenu();
13296       }
13297       else
13298       {
13299         FadeOut(REDRAW_FIELD);
13300
13301         game_status = GAME_MODE_MAIN;
13302
13303         DrawAndFadeInMainMenu(REDRAW_FIELD);
13304       }
13305     }
13306   }
13307   else          /* continue playing the game */
13308   {
13309     if (tape.playing && tape.deactivate_display)
13310       TapeDeactivateDisplayOff(TRUE);
13311
13312     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13313
13314     if (tape.playing && tape.deactivate_display)
13315       TapeDeactivateDisplayOn();
13316   }
13317 }
13318
13319 void RequestQuitGame(boolean ask_if_really_quit)
13320 {
13321   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
13322   boolean skip_request = AllPlayersGone || quick_quit;
13323
13324   RequestQuitGameExt(skip_request, quick_quit,
13325                      "Do you really want to quit the game ?");
13326 }
13327
13328
13329 /* ------------------------------------------------------------------------- */
13330 /* random generator functions                                                */
13331 /* ------------------------------------------------------------------------- */
13332
13333 unsigned int InitEngineRandom_RND(long seed)
13334 {
13335   game.num_random_calls = 0;
13336
13337 #if 0
13338   unsigned int rnd_seed = InitEngineRandom(seed);
13339
13340   printf("::: START RND: %d\n", rnd_seed);
13341
13342   return rnd_seed;
13343 #else
13344
13345   return InitEngineRandom(seed);
13346
13347 #endif
13348
13349 }
13350
13351 unsigned int RND(int max)
13352 {
13353   if (max > 0)
13354   {
13355     game.num_random_calls++;
13356
13357     return GetEngineRandom(max);
13358   }
13359
13360   return 0;
13361 }
13362
13363
13364 /* ------------------------------------------------------------------------- */
13365 /* game engine snapshot handling functions                                   */
13366 /* ------------------------------------------------------------------------- */
13367
13368 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
13369
13370 struct EngineSnapshotInfo
13371 {
13372   /* runtime values for custom element collect score */
13373   int collect_score[NUM_CUSTOM_ELEMENTS];
13374
13375   /* runtime values for group element choice position */
13376   int choice_pos[NUM_GROUP_ELEMENTS];
13377
13378   /* runtime values for belt position animations */
13379   int belt_graphic[4 * NUM_BELT_PARTS];
13380   int belt_anim_mode[4 * NUM_BELT_PARTS];
13381 };
13382
13383 struct EngineSnapshotNodeInfo
13384 {
13385   void *buffer_orig;
13386   void *buffer_copy;
13387   int size;
13388 };
13389
13390 static struct EngineSnapshotInfo engine_snapshot_rnd;
13391 static ListNode *engine_snapshot_list = NULL;
13392 static char *snapshot_level_identifier = NULL;
13393 static int snapshot_level_nr = -1;
13394
13395 void FreeEngineSnapshot()
13396 {
13397   while (engine_snapshot_list != NULL)
13398     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
13399                        checked_free);
13400
13401   setString(&snapshot_level_identifier, NULL);
13402   snapshot_level_nr = -1;
13403 }
13404
13405 static void SaveEngineSnapshotValues_RND()
13406 {
13407   static int belt_base_active_element[4] =
13408   {
13409     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
13410     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
13411     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
13412     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
13413   };
13414   int i, j;
13415
13416   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13417   {
13418     int element = EL_CUSTOM_START + i;
13419
13420     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
13421   }
13422
13423   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13424   {
13425     int element = EL_GROUP_START + i;
13426
13427     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
13428   }
13429
13430   for (i = 0; i < 4; i++)
13431   {
13432     for (j = 0; j < NUM_BELT_PARTS; j++)
13433     {
13434       int element = belt_base_active_element[i] + j;
13435       int graphic = el2img(element);
13436       int anim_mode = graphic_info[graphic].anim_mode;
13437
13438       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
13439       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
13440     }
13441   }
13442 }
13443
13444 static void LoadEngineSnapshotValues_RND()
13445 {
13446   unsigned long num_random_calls = game.num_random_calls;
13447   int i, j;
13448
13449   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
13450   {
13451     int element = EL_CUSTOM_START + i;
13452
13453     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
13454   }
13455
13456   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
13457   {
13458     int element = EL_GROUP_START + i;
13459
13460     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
13461   }
13462
13463   for (i = 0; i < 4; i++)
13464   {
13465     for (j = 0; j < NUM_BELT_PARTS; j++)
13466     {
13467       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
13468       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
13469
13470       graphic_info[graphic].anim_mode = anim_mode;
13471     }
13472   }
13473
13474   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
13475   {
13476     InitRND(tape.random_seed);
13477     for (i = 0; i < num_random_calls; i++)
13478       RND(1);
13479   }
13480
13481   if (game.num_random_calls != num_random_calls)
13482   {
13483     Error(ERR_RETURN, "number of random calls out of sync");
13484     Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
13485     Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
13486     Error(ERR_EXIT, "this should not happen -- please debug");
13487   }
13488 }
13489
13490 static void SaveEngineSnapshotBuffer(void *buffer, int size)
13491 {
13492   struct EngineSnapshotNodeInfo *bi =
13493     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
13494
13495   bi->buffer_orig = buffer;
13496   bi->buffer_copy = checked_malloc(size);
13497   bi->size = size;
13498
13499   memcpy(bi->buffer_copy, buffer, size);
13500
13501   addNodeToList(&engine_snapshot_list, NULL, bi);
13502 }
13503
13504 void SaveEngineSnapshot()
13505 {
13506   FreeEngineSnapshot();         /* free previous snapshot, if needed */
13507
13508   if (level_editor_test_game)   /* do not save snapshots from editor */
13509     return;
13510
13511   /* copy some special values to a structure better suited for the snapshot */
13512
13513   SaveEngineSnapshotValues_RND();
13514   SaveEngineSnapshotValues_EM();
13515
13516   /* save values stored in special snapshot structure */
13517
13518   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
13519   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
13520
13521   /* save further RND engine values */
13522
13523   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
13524   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
13525   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
13526
13527   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
13528   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
13529   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
13530   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
13531
13532   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
13533   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
13534   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
13535   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
13536   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
13537
13538   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
13539   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
13540   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
13541
13542   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
13543
13544   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
13545
13546   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
13547   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
13548
13549   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
13550   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
13551   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
13552   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
13553   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
13554   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
13555   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
13556   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
13557   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
13558   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
13559   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
13560   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
13561   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
13562   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
13563   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
13564   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
13565   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
13566   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
13567
13568   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
13569   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
13570
13571   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
13572   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
13573   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
13574
13575   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
13576   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
13577
13578   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
13579   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
13580   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
13581   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
13582   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
13583
13584   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
13585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
13586
13587   /* save level identification information */
13588
13589   setString(&snapshot_level_identifier, leveldir_current->identifier);
13590   snapshot_level_nr = level_nr;
13591
13592 #if 0
13593   ListNode *node = engine_snapshot_list;
13594   int num_bytes = 0;
13595
13596   while (node != NULL)
13597   {
13598     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
13599
13600     node = node->next;
13601   }
13602
13603   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
13604 #endif
13605 }
13606
13607 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
13608 {
13609   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
13610 }
13611
13612 void LoadEngineSnapshot()
13613 {
13614   ListNode *node = engine_snapshot_list;
13615
13616   if (engine_snapshot_list == NULL)
13617     return;
13618
13619   while (node != NULL)
13620   {
13621     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
13622
13623     node = node->next;
13624   }
13625
13626   /* restore special values from snapshot structure */
13627
13628   LoadEngineSnapshotValues_RND();
13629   LoadEngineSnapshotValues_EM();
13630 }
13631
13632 boolean CheckEngineSnapshot()
13633 {
13634   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
13635           snapshot_level_nr == level_nr);
13636 }
13637
13638
13639 /* ---------- new game button stuff ---------------------------------------- */
13640
13641 /* graphic position values for game buttons */
13642 #define GAME_BUTTON_XSIZE       30
13643 #define GAME_BUTTON_YSIZE       30
13644 #define GAME_BUTTON_XPOS        5
13645 #define GAME_BUTTON_YPOS        215
13646 #define SOUND_BUTTON_XPOS       5
13647 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13648
13649 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13650 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13651 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13652 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13653 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13654 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13655
13656 static struct
13657 {
13658   int x, y;
13659   int gadget_id;
13660   char *infotext;
13661 } gamebutton_info[NUM_GAME_BUTTONS] =
13662 {
13663   {
13664     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13665     GAME_CTRL_ID_STOP,
13666     "stop game"
13667   },
13668   {
13669     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13670     GAME_CTRL_ID_PAUSE,
13671     "pause game"
13672   },
13673   {
13674     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13675     GAME_CTRL_ID_PLAY,
13676     "play game"
13677   },
13678   {
13679     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13680     SOUND_CTRL_ID_MUSIC,
13681     "background music on/off"
13682   },
13683   {
13684     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13685     SOUND_CTRL_ID_LOOPS,
13686     "sound loops on/off"
13687   },
13688   {
13689     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13690     SOUND_CTRL_ID_SIMPLE,
13691     "normal sounds on/off"
13692   }
13693 };
13694
13695 void CreateGameButtons()
13696 {
13697   int i;
13698
13699   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13700   {
13701     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13702     struct GadgetInfo *gi;
13703     int button_type;
13704     boolean checked;
13705     unsigned long event_mask;
13706     int gd_xoffset, gd_yoffset;
13707     int gd_x1, gd_x2, gd_y1, gd_y2;
13708     int id = i;
13709
13710     gd_xoffset = gamebutton_info[i].x;
13711     gd_yoffset = gamebutton_info[i].y;
13712     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13713     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13714
13715     if (id == GAME_CTRL_ID_STOP ||
13716         id == GAME_CTRL_ID_PAUSE ||
13717         id == GAME_CTRL_ID_PLAY)
13718     {
13719       button_type = GD_TYPE_NORMAL_BUTTON;
13720       checked = FALSE;
13721       event_mask = GD_EVENT_RELEASED;
13722       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13723       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13724     }
13725     else
13726     {
13727       button_type = GD_TYPE_CHECK_BUTTON;
13728       checked =
13729         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13730          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13731          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13732       event_mask = GD_EVENT_PRESSED;
13733       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
13734       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13735     }
13736
13737     gi = CreateGadget(GDI_CUSTOM_ID, id,
13738                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
13739                       GDI_X, DX + gd_xoffset,
13740                       GDI_Y, DY + gd_yoffset,
13741                       GDI_WIDTH, GAME_BUTTON_XSIZE,
13742                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
13743                       GDI_TYPE, button_type,
13744                       GDI_STATE, GD_BUTTON_UNPRESSED,
13745                       GDI_CHECKED, checked,
13746                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13747                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13748                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13749                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13750                       GDI_EVENT_MASK, event_mask,
13751                       GDI_CALLBACK_ACTION, HandleGameButtons,
13752                       GDI_END);
13753
13754     if (gi == NULL)
13755       Error(ERR_EXIT, "cannot create gadget");
13756
13757     game_gadget[id] = gi;
13758   }
13759 }
13760
13761 void FreeGameButtons()
13762 {
13763   int i;
13764
13765   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13766     FreeGadget(game_gadget[i]);
13767 }
13768
13769 static void MapGameButtons()
13770 {
13771   int i;
13772
13773   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13774     MapGadget(game_gadget[i]);
13775 }
13776
13777 void UnmapGameButtons()
13778 {
13779   int i;
13780
13781   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13782     UnmapGadget(game_gadget[i]);
13783 }
13784
13785 static void HandleGameButtons(struct GadgetInfo *gi)
13786 {
13787   int id = gi->custom_id;
13788
13789   if (game_status != GAME_MODE_PLAYING)
13790     return;
13791
13792   switch (id)
13793   {
13794     case GAME_CTRL_ID_STOP:
13795       if (tape.playing)
13796         TapeStop();
13797       else
13798         RequestQuitGame(TRUE);
13799       break;
13800
13801     case GAME_CTRL_ID_PAUSE:
13802       if (options.network)
13803       {
13804 #if defined(NETWORK_AVALIABLE)
13805         if (tape.pausing)
13806           SendToServer_ContinuePlaying();
13807         else
13808           SendToServer_PausePlaying();
13809 #endif
13810       }
13811       else
13812         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13813       break;
13814
13815     case GAME_CTRL_ID_PLAY:
13816       if (tape.pausing)
13817       {
13818 #if defined(NETWORK_AVALIABLE)
13819         if (options.network)
13820           SendToServer_ContinuePlaying();
13821         else
13822 #endif
13823         {
13824           tape.pausing = FALSE;
13825           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13826         }
13827       }
13828       break;
13829
13830     case SOUND_CTRL_ID_MUSIC:
13831       if (setup.sound_music)
13832       { 
13833         setup.sound_music = FALSE;
13834         FadeMusic();
13835       }
13836       else if (audio.music_available)
13837       { 
13838         setup.sound = setup.sound_music = TRUE;
13839
13840         SetAudioMode(setup.sound);
13841
13842         PlayLevelMusic();
13843       }
13844       break;
13845
13846     case SOUND_CTRL_ID_LOOPS:
13847       if (setup.sound_loops)
13848         setup.sound_loops = FALSE;
13849       else if (audio.loops_available)
13850       {
13851         setup.sound = setup.sound_loops = TRUE;
13852         SetAudioMode(setup.sound);
13853       }
13854       break;
13855
13856     case SOUND_CTRL_ID_SIMPLE:
13857       if (setup.sound_simple)
13858         setup.sound_simple = FALSE;
13859       else if (audio.sound_available)
13860       {
13861         setup.sound = setup.sound_simple = TRUE;
13862         SetAudioMode(setup.sound);
13863       }
13864       break;
13865
13866     default:
13867       break;
13868   }
13869 }