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