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