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