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