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