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