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