rnd-20060818-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55 #define USE_NEW_GAME_WON                (USE_NEW_STUFF          * 1)
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #if 1
84
85 #define PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
86
87 /* special positions in the game control window (relative to control window) */
88 #define XX_LEVEL1               (game.panel.level.x)
89 #define XX_LEVEL2               (game.panel.level.x - 1)
90 #define YY_LEVEL                (game.panel.level.y)
91 #define XX_EMERALDS             (game.panel.gems.x)
92 #define YY_EMERALDS             (game.panel.gems.y)
93 #define XX_DYNAMITE             (game.panel.inventory.x)
94 #define YY_DYNAMITE             (game.panel.inventory.y)
95 #define XX_KEYS                 (game.panel.keys.x)
96 #define YY_KEYS                 (game.panel.keys.y)
97 #define XX_SCORE                (game.panel.score.x)
98 #define YY_SCORE                (game.panel.score.y)
99 #define XX_TIME1                (game.panel.time.x)
100 #define XX_TIME2                (game.panel.time.x + 1)
101 #define YY_TIME                 (game.panel.time.y)
102
103 #else
104
105 /* special positions in the game control window (relative to control window) */
106 #define XX_LEVEL                37
107 #define YY_LEVEL                20
108 #define XX_EMERALDS             29
109 #define YY_EMERALDS             54
110 #define XX_DYNAMITE             29
111 #define YY_DYNAMITE             89
112 #define XX_KEYS                 18
113 #define YY_KEYS                 123
114 #define XX_SCORE                15
115 #define YY_SCORE                159
116 #define XX_TIME1                29
117 #define XX_TIME2                30
118 #define YY_TIME                 194
119
120 #endif
121
122 /* special positions in the game control window (relative to main window) */
123 #define DX_LEVEL1               (DX + XX_LEVEL1)
124 #define DX_LEVEL2               (DX + XX_LEVEL2)
125 #define DY_LEVEL                (DY + YY_LEVEL)
126 #define DX_EMERALDS             (DX + XX_EMERALDS)
127 #define DY_EMERALDS             (DY + YY_EMERALDS)
128 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
129 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
130 #define DX_KEYS                 (DX + XX_KEYS)
131 #define DY_KEYS                 (DY + YY_KEYS)
132 #define DX_SCORE                (DX + XX_SCORE)
133 #define DY_SCORE                (DY + YY_SCORE)
134 #define DX_TIME1                (DX + XX_TIME1)
135 #define DX_TIME2                (DX + XX_TIME2)
136 #define DY_TIME                 (DY + YY_TIME)
137
138 /* values for delayed check of falling and moving elements and for collision */
139 #define CHECK_DELAY_MOVING      3
140 #define CHECK_DELAY_FALLING     3
141 #define CHECK_DELAY_COLLISION   2
142
143 /* values for initial player move delay (initial delay counter value) */
144 #define INITIAL_MOVE_DELAY_OFF  -1
145 #define INITIAL_MOVE_DELAY_ON   0
146
147 /* values for player movement speed (which is in fact a delay value) */
148 #define MOVE_DELAY_MIN_SPEED    32
149 #define MOVE_DELAY_NORMAL_SPEED 8
150 #define MOVE_DELAY_HIGH_SPEED   4
151 #define MOVE_DELAY_MAX_SPEED    1
152
153 #if 0
154 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
155 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
156 #else
157 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
158 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
159 #endif
160 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
161 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
162
163 /* values for other actions */
164 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
165 #define MOVE_STEPSIZE_MIN       (1)
166 #define MOVE_STEPSIZE_MAX       (TILEX)
167
168 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
169 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
170
171 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
172
173 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
174                                  RND(element_info[e].push_delay_random))
175 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
176                                  RND(element_info[e].drop_delay_random))
177 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
178                                  RND(element_info[e].move_delay_random))
179 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
180                                     (element_info[e].move_delay_random))
181 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
182                                  RND(element_info[e].ce_value_random_initial))
183 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
184 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
185                                  RND((c)->delay_random * (c)->delay_frames))
186 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
187                                  RND((c)->delay_random))
188
189
190 #if 1
191 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
192          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
193 #else
194 #define GET_VALID_FILE_ELEMENT(e)                                       \
195         ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
196 #endif
197
198 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
199         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
200          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
201          (be) + (e) - EL_SELF)
202
203 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
204         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
205          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
206          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
207          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
208          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
209          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
210          (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
211          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
212          (e))
213
214 #define CAN_GROW_INTO(e)                                                \
215         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
216
217 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
218                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
219                                         (condition)))
220
221 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
222                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
223                                         (CAN_MOVE_INTO_ACID(e) &&       \
224                                          Feld[x][y] == EL_ACID) ||      \
225                                         (condition)))
226
227 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
228                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
229                                         (CAN_MOVE_INTO_ACID(e) &&       \
230                                          Feld[x][y] == EL_ACID) ||      \
231                                         (condition)))
232
233 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
234                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
235                                         (condition) ||                  \
236                                         (CAN_MOVE_INTO_ACID(e) &&       \
237                                          Feld[x][y] == EL_ACID) ||      \
238                                         (DONT_COLLIDE_WITH(e) &&        \
239                                          IS_PLAYER(x, y) &&             \
240                                          !PLAYER_ENEMY_PROTECTED(x, y))))
241
242 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
243         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
244
245 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
246         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
247
248 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
249         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
250
251 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
252         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
253                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
254
255 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
256         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
257
258 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
259         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
260
261 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
262         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
263
264 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
265         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
266
267 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
268         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
269
270 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
271         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
272                                                  IS_FOOD_PENGUIN(Feld[x][y])))
273 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
274         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
275
276 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
277         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
278
279 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
280         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
281
282 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
283         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
284                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
285
286 #if 0
287 #define GROUP_NR(e)             ((e) - EL_GROUP_START)
288 #define IS_IN_GROUP(e, nr)      (element_info[e].in_group[nr] == TRUE)
289 #define IS_IN_GROUP_EL(e, ge)   (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
290
291 #define IS_EQUAL_OR_IN_GROUP(e, ge)                                     \
292         (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
293 #endif
294
295 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
296
297 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
298                 (!IS_PLAYER(x, y) &&                                    \
299                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
300
301 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
302         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
303
304 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
305 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
306
307 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
308 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
309 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
310 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
311
312 /* game button identifiers */
313 #define GAME_CTRL_ID_STOP               0
314 #define GAME_CTRL_ID_PAUSE              1
315 #define GAME_CTRL_ID_PLAY               2
316 #define SOUND_CTRL_ID_MUSIC             3
317 #define SOUND_CTRL_ID_LOOPS             4
318 #define SOUND_CTRL_ID_SIMPLE            5
319
320 #define NUM_GAME_BUTTONS                6
321
322
323 /* forward declaration for internal use */
324
325 static void CreateField(int, int, int);
326
327 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
328 static void AdvanceFrameAndPlayerCounters(int);
329
330 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
331 static boolean MovePlayer(struct PlayerInfo *, int, int);
332 static void ScrollPlayer(struct PlayerInfo *, int);
333 static void ScrollScreen(struct PlayerInfo *, int);
334
335 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
336
337 static void InitBeltMovement(void);
338 static void CloseAllOpenTimegates(void);
339 static void CheckGravityMovement(struct PlayerInfo *);
340 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
341 static void KillPlayerUnlessEnemyProtected(int, int);
342 static void KillPlayerUnlessExplosionProtected(int, int);
343
344 static void TestIfPlayerTouchesCustomElement(int, int);
345 static void TestIfElementTouchesCustomElement(int, int);
346 static void TestIfElementHitsCustomElement(int, int, int);
347 #if 0
348 static void TestIfElementSmashesCustomElement(int, int, int);
349 #endif
350
351 static void HandleElementChange(int, int, int);
352 static void ExecuteCustomElementAction(int, int, int, int);
353 static boolean ChangeElement(int, int, int, int);
354
355 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
356 #define CheckTriggeredElementChange(x, y, e, ev)                        \
357         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
358 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
359         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
360 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
361         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
362 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
363         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
364
365 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
366 #define CheckElementChange(x, y, e, te, ev)                             \
367         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
368 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
369         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
370 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
371         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
372
373 static void PlayLevelSound(int, int, int);
374 static void PlayLevelSoundNearest(int, int, int);
375 static void PlayLevelSoundAction(int, int, int);
376 static void PlayLevelSoundElementAction(int, int, int, int);
377 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
378 static void PlayLevelSoundActionIfLoop(int, int, int);
379 static void StopLevelSoundActionIfLoop(int, int, int);
380 static void PlayLevelMusic();
381
382 static void MapGameButtons();
383 static void HandleGameButtons(struct GadgetInfo *);
384
385 int AmoebeNachbarNr(int, int);
386 void AmoebeUmwandeln(int, int);
387 void ContinueMoving(int, int);
388 void Bang(int, int);
389 void InitMovDir(int, int);
390 void InitAmoebaNr(int, int);
391 int NewHiScore(void);
392
393 void TestIfGoodThingHitsBadThing(int, int, int);
394 void TestIfBadThingHitsGoodThing(int, int, int);
395 void TestIfPlayerTouchesBadThing(int, int);
396 void TestIfPlayerRunsIntoBadThing(int, int, int);
397 void TestIfBadThingTouchesPlayer(int, int);
398 void TestIfBadThingRunsIntoPlayer(int, int, int);
399 void TestIfFriendTouchesBadThing(int, int);
400 void TestIfBadThingTouchesFriend(int, int);
401 void TestIfBadThingTouchesOtherBadThing(int, int);
402
403 void KillPlayer(struct PlayerInfo *);
404 void BuryPlayer(struct PlayerInfo *);
405 void RemovePlayer(struct PlayerInfo *);
406
407 boolean SnapField(struct PlayerInfo *, int, int);
408 boolean DropElement(struct PlayerInfo *);
409
410 static int getInvisibleActiveFromInvisibleElement(int);
411 static int getInvisibleFromInvisibleActiveElement(int);
412
413 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
414
415
416 /* ------------------------------------------------------------------------- */
417 /* definition of elements that automatically change to other elements after  */
418 /* a specified time, eventually calling a function when changing             */
419 /* ------------------------------------------------------------------------- */
420
421 /* forward declaration for changer functions */
422 static void InitBuggyBase(int, int);
423 static void WarnBuggyBase(int, int);
424
425 static void InitTrap(int, int);
426 static void ActivateTrap(int, int);
427 static void ChangeActiveTrap(int, int);
428
429 static void InitRobotWheel(int, int);
430 static void RunRobotWheel(int, int);
431 static void StopRobotWheel(int, int);
432
433 static void InitTimegateWheel(int, int);
434 static void RunTimegateWheel(int, int);
435
436 static void InitMagicBallDelay(int, int);
437 static void ActivateMagicBall(int, int);
438
439 static void InitDiagonalMovingElement(int, int);
440
441 struct ChangingElementInfo
442 {
443   int element;
444   int target_element;
445   int change_delay;
446   void (*pre_change_function)(int x, int y);
447   void (*change_function)(int x, int y);
448   void (*post_change_function)(int x, int y);
449 };
450
451 static struct ChangingElementInfo change_delay_list[] =
452 {
453   {
454     EL_NUT_BREAKING,
455     EL_EMERALD,
456     6,
457     NULL,
458     NULL,
459     NULL
460   },
461   {
462     EL_PEARL_BREAKING,
463     EL_EMPTY,
464     8,
465     NULL,
466     NULL,
467     NULL
468   },
469   {
470     EL_EXIT_OPENING,
471     EL_EXIT_OPEN,
472     29,
473     NULL,
474     NULL,
475     NULL
476   },
477   {
478     EL_EXIT_CLOSING,
479     EL_EXIT_CLOSED,
480     29,
481     NULL,
482     NULL,
483     NULL
484   },
485   {
486     EL_SP_EXIT_OPENING,
487     EL_SP_EXIT_OPEN,
488     29,
489     NULL,
490     NULL,
491     NULL
492   },
493   {
494     EL_SP_EXIT_CLOSING,
495     EL_SP_EXIT_CLOSED,
496     29,
497     NULL,
498     NULL,
499     NULL
500   },
501   {
502     EL_SWITCHGATE_OPENING,
503     EL_SWITCHGATE_OPEN,
504     29,
505     NULL,
506     NULL,
507     NULL
508   },
509   {
510     EL_SWITCHGATE_CLOSING,
511     EL_SWITCHGATE_CLOSED,
512     29,
513     NULL,
514     NULL,
515     NULL
516   },
517   {
518     EL_TIMEGATE_OPENING,
519     EL_TIMEGATE_OPEN,
520     29,
521     NULL,
522     NULL,
523     NULL
524   },
525   {
526     EL_TIMEGATE_CLOSING,
527     EL_TIMEGATE_CLOSED,
528     29,
529     NULL,
530     NULL,
531     NULL
532   },
533
534   {
535     EL_ACID_SPLASH_LEFT,
536     EL_EMPTY,
537     8,
538     NULL,
539     NULL,
540     NULL
541   },
542   {
543     EL_ACID_SPLASH_RIGHT,
544     EL_EMPTY,
545     8,
546     NULL,
547     NULL,
548     NULL
549   },
550   {
551     EL_SP_BUGGY_BASE,
552     EL_SP_BUGGY_BASE_ACTIVATING,
553     0,
554     InitBuggyBase,
555     NULL,
556     NULL
557   },
558   {
559     EL_SP_BUGGY_BASE_ACTIVATING,
560     EL_SP_BUGGY_BASE_ACTIVE,
561     0,
562     InitBuggyBase,
563     NULL,
564     NULL
565   },
566   {
567     EL_SP_BUGGY_BASE_ACTIVE,
568     EL_SP_BUGGY_BASE,
569     0,
570     InitBuggyBase,
571     WarnBuggyBase,
572     NULL
573   },
574   {
575     EL_TRAP,
576     EL_TRAP_ACTIVE,
577     0,
578     InitTrap,
579     NULL,
580     ActivateTrap
581   },
582   {
583     EL_TRAP_ACTIVE,
584     EL_TRAP,
585     31,
586     NULL,
587     ChangeActiveTrap,
588     NULL
589   },
590   {
591     EL_ROBOT_WHEEL_ACTIVE,
592     EL_ROBOT_WHEEL,
593     0,
594     InitRobotWheel,
595     RunRobotWheel,
596     StopRobotWheel
597   },
598   {
599     EL_TIMEGATE_SWITCH_ACTIVE,
600     EL_TIMEGATE_SWITCH,
601     0,
602     InitTimegateWheel,
603     RunTimegateWheel,
604     NULL
605   },
606   {
607     EL_EMC_MAGIC_BALL_ACTIVE,
608     EL_EMC_MAGIC_BALL_ACTIVE,
609     0,
610     InitMagicBallDelay,
611     NULL,
612     ActivateMagicBall
613   },
614   {
615     EL_EMC_SPRING_BUMPER_ACTIVE,
616     EL_EMC_SPRING_BUMPER,
617     8,
618     NULL,
619     NULL,
620     NULL
621   },
622   {
623     EL_DIAGONAL_SHRINKING,
624     EL_UNDEFINED,
625     0,
626     NULL,
627     NULL,
628     NULL
629   },
630   {
631     EL_DIAGONAL_GROWING,
632     EL_UNDEFINED,
633     0,
634     NULL,
635     NULL,
636     InitDiagonalMovingElement
637   },
638
639   {
640     EL_UNDEFINED,
641     EL_UNDEFINED,
642     -1,
643     NULL,
644     NULL,
645     NULL
646   }
647 };
648
649 struct
650 {
651   int element;
652   int push_delay_fixed, push_delay_random;
653 }
654 push_delay_list[] =
655 {
656   { EL_SPRING,                  0, 0 },
657   { EL_BALLOON,                 0, 0 },
658
659   { EL_SOKOBAN_OBJECT,          2, 0 },
660   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
661   { EL_SATELLITE,               2, 0 },
662   { EL_SP_DISK_YELLOW,          2, 0 },
663
664   { EL_UNDEFINED,               0, 0 },
665 };
666
667 struct
668 {
669   int element;
670   int move_stepsize;
671 }
672 move_stepsize_list[] =
673 {
674   { EL_AMOEBA_DROP,             2 },
675   { EL_AMOEBA_DROPPING,         2 },
676   { EL_QUICKSAND_FILLING,       1 },
677   { EL_QUICKSAND_EMPTYING,      1 },
678   { EL_MAGIC_WALL_FILLING,      2 },
679   { EL_BD_MAGIC_WALL_FILLING,   2 },
680   { EL_MAGIC_WALL_EMPTYING,     2 },
681   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
682
683   { EL_UNDEFINED,               0 },
684 };
685
686 struct
687 {
688   int element;
689   int count;
690 }
691 collect_count_list[] =
692 {
693   { EL_EMERALD,                 1 },
694   { EL_BD_DIAMOND,              1 },
695   { EL_EMERALD_YELLOW,          1 },
696   { EL_EMERALD_RED,             1 },
697   { EL_EMERALD_PURPLE,          1 },
698   { EL_DIAMOND,                 3 },
699   { EL_SP_INFOTRON,             1 },
700   { EL_PEARL,                   5 },
701   { EL_CRYSTAL,                 8 },
702
703   { EL_UNDEFINED,               0 },
704 };
705
706 struct
707 {
708   int element;
709   int direction;
710 }
711 access_direction_list[] =
712 {
713   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
714   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
715   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
716   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
717   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
718   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
719   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
720   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
721   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
722   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
723   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
724
725   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
726   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
727   { EL_SP_PORT_UP,                                                   MV_DOWN },
728   { EL_SP_PORT_DOWN,                                         MV_UP           },
729   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
730   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
731   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
732   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
733   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
734   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
735   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
736   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
737   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
738   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
739   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
740   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
741   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
742   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
743   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
744
745   { EL_UNDEFINED,                       MV_NONE                              }
746 };
747
748 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
749
750 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
751 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
752 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
753                                  IS_JUST_CHANGING(x, y))
754
755 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
756
757 /* static variables for playfield scan mode (scanning forward or backward) */
758 static int playfield_scan_start_x = 0;
759 static int playfield_scan_start_y = 0;
760 static int playfield_scan_delta_x = 1;
761 static int playfield_scan_delta_y = 1;
762
763 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
764                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
765                                      (y) += playfield_scan_delta_y)     \
766                                 for ((x) = playfield_scan_start_x;      \
767                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
768                                      (x) += playfield_scan_delta_x)     \
769
770 #ifdef DEBUG
771 void DEBUG_SetMaximumDynamite()
772 {
773   int i;
774
775   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
776     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
777       local_player->inventory_element[local_player->inventory_size++] =
778         EL_DYNAMITE;
779 }
780 #endif
781
782 static void InitPlayfieldScanModeVars()
783 {
784   if (game.use_reverse_scan_direction)
785   {
786     playfield_scan_start_x = lev_fieldx - 1;
787     playfield_scan_start_y = lev_fieldy - 1;
788
789     playfield_scan_delta_x = -1;
790     playfield_scan_delta_y = -1;
791   }
792   else
793   {
794     playfield_scan_start_x = 0;
795     playfield_scan_start_y = 0;
796
797     playfield_scan_delta_x = 1;
798     playfield_scan_delta_y = 1;
799   }
800 }
801
802 static void InitPlayfieldScanMode(int mode)
803 {
804   game.use_reverse_scan_direction =
805     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
806
807   InitPlayfieldScanModeVars();
808 }
809
810 static int get_move_delay_from_stepsize(int move_stepsize)
811 {
812   move_stepsize =
813     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
814
815   /* make sure that stepsize value is always a power of 2 */
816   move_stepsize = (1 << log_2(move_stepsize));
817
818   return TILEX / move_stepsize;
819 }
820
821 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
822                                boolean init_game)
823 {
824   int player_nr = player->index_nr;
825   int move_delay = get_move_delay_from_stepsize(move_stepsize);
826   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
827
828   /* do no immediately change move delay -- the player might just be moving */
829   player->move_delay_value_next = move_delay;
830
831   /* information if player can move must be set separately */
832   player->cannot_move = cannot_move;
833
834   if (init_game)
835   {
836     player->move_delay       = game.initial_move_delay[player_nr];
837     player->move_delay_value = game.initial_move_delay_value[player_nr];
838
839     player->move_delay_value_next = -1;
840
841     player->move_delay_reset_counter = 0;
842   }
843 }
844
845 void GetPlayerConfig()
846 {
847   if (!audio.sound_available)
848     setup.sound_simple = FALSE;
849
850   if (!audio.loops_available)
851     setup.sound_loops = FALSE;
852
853   if (!audio.music_available)
854     setup.sound_music = FALSE;
855
856   if (!video.fullscreen_available)
857     setup.fullscreen = FALSE;
858
859   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
860
861   SetAudioMode(setup.sound);
862   InitJoysticks();
863 }
864
865 static int getBeltNrFromBeltElement(int element)
866 {
867   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
868           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
869           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
870 }
871
872 static int getBeltNrFromBeltActiveElement(int element)
873 {
874   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
875           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
876           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
877 }
878
879 static int getBeltNrFromBeltSwitchElement(int element)
880 {
881   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
882           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
883           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
884 }
885
886 static int getBeltDirNrFromBeltSwitchElement(int element)
887 {
888   static int belt_base_element[4] =
889   {
890     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
891     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
892     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
893     EL_CONVEYOR_BELT_4_SWITCH_LEFT
894   };
895
896   int belt_nr = getBeltNrFromBeltSwitchElement(element);
897   int belt_dir_nr = element - belt_base_element[belt_nr];
898
899   return (belt_dir_nr % 3);
900 }
901
902 static int getBeltDirFromBeltSwitchElement(int element)
903 {
904   static int belt_move_dir[3] =
905   {
906     MV_LEFT,
907     MV_NONE,
908     MV_RIGHT
909   };
910
911   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
912
913   return belt_move_dir[belt_dir_nr];
914 }
915
916 static int get_element_from_group_element(int element)
917 {
918   if (IS_GROUP_ELEMENT(element))
919   {
920     struct ElementGroupInfo *group = element_info[element].group;
921     int last_anim_random_frame = gfx.anim_random_frame;
922     int element_pos;
923
924     if (group->choice_mode == ANIM_RANDOM)
925       gfx.anim_random_frame = RND(group->num_elements_resolved);
926
927     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
928                                     group->choice_mode, 0,
929                                     group->choice_pos);
930
931     if (group->choice_mode == ANIM_RANDOM)
932       gfx.anim_random_frame = last_anim_random_frame;
933
934     group->choice_pos++;
935
936     element = group->element_resolved[element_pos];
937   }
938
939   return element;
940 }
941
942 static void InitPlayerField(int x, int y, int element, boolean init_game)
943 {
944   if (element == EL_SP_MURPHY)
945   {
946     if (init_game)
947     {
948       if (stored_player[0].present)
949       {
950         Feld[x][y] = EL_SP_MURPHY_CLONE;
951
952         return;
953       }
954       else
955       {
956         stored_player[0].use_murphy = TRUE;
957
958         if (!level.use_artwork_element[0])
959           stored_player[0].artwork_element = EL_SP_MURPHY;
960       }
961
962       Feld[x][y] = EL_PLAYER_1;
963     }
964   }
965
966   if (init_game)
967   {
968     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
969     int jx = player->jx, jy = player->jy;
970
971     player->present = TRUE;
972
973     player->block_last_field = (element == EL_SP_MURPHY ?
974                                 level.sp_block_last_field :
975                                 level.block_last_field);
976
977     /* ---------- initialize player's last field block delay --------------- */
978
979     /* always start with reliable default value (no adjustment needed) */
980     player->block_delay_adjustment = 0;
981
982     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
983     if (player->block_last_field && element == EL_SP_MURPHY)
984       player->block_delay_adjustment = 1;
985
986     /* special case 2: in game engines before 3.1.1, blocking was different */
987     if (game.use_block_last_field_bug)
988       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
989
990     if (!options.network || player->connected)
991     {
992       player->active = TRUE;
993
994       /* remove potentially duplicate players */
995       if (StorePlayer[jx][jy] == Feld[x][y])
996         StorePlayer[jx][jy] = 0;
997
998       StorePlayer[x][y] = Feld[x][y];
999
1000       if (options.debug)
1001       {
1002         printf("Player %d activated.\n", player->element_nr);
1003         printf("[Local player is %d and currently %s.]\n",
1004                local_player->element_nr,
1005                local_player->active ? "active" : "not active");
1006       }
1007     }
1008
1009     Feld[x][y] = EL_EMPTY;
1010
1011     player->jx = player->last_jx = x;
1012     player->jy = player->last_jy = y;
1013   }
1014 }
1015
1016 static void InitField(int x, int y, boolean init_game)
1017 {
1018   int element = Feld[x][y];
1019
1020   switch (element)
1021   {
1022     case EL_SP_MURPHY:
1023     case EL_PLAYER_1:
1024     case EL_PLAYER_2:
1025     case EL_PLAYER_3:
1026     case EL_PLAYER_4:
1027       InitPlayerField(x, y, element, init_game);
1028       break;
1029
1030     case EL_SOKOBAN_FIELD_PLAYER:
1031       element = Feld[x][y] = EL_PLAYER_1;
1032       InitField(x, y, init_game);
1033
1034       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1035       InitField(x, y, init_game);
1036       break;
1037
1038     case EL_SOKOBAN_FIELD_EMPTY:
1039       local_player->sokobanfields_still_needed++;
1040       break;
1041
1042     case EL_STONEBLOCK:
1043       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1044         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1045       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1046         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1047       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1048         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1049       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1050         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1051       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1052         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1053       break;
1054
1055     case EL_BUG:
1056     case EL_BUG_RIGHT:
1057     case EL_BUG_UP:
1058     case EL_BUG_LEFT:
1059     case EL_BUG_DOWN:
1060     case EL_SPACESHIP:
1061     case EL_SPACESHIP_RIGHT:
1062     case EL_SPACESHIP_UP:
1063     case EL_SPACESHIP_LEFT:
1064     case EL_SPACESHIP_DOWN:
1065     case EL_BD_BUTTERFLY:
1066     case EL_BD_BUTTERFLY_RIGHT:
1067     case EL_BD_BUTTERFLY_UP:
1068     case EL_BD_BUTTERFLY_LEFT:
1069     case EL_BD_BUTTERFLY_DOWN:
1070     case EL_BD_FIREFLY:
1071     case EL_BD_FIREFLY_RIGHT:
1072     case EL_BD_FIREFLY_UP:
1073     case EL_BD_FIREFLY_LEFT:
1074     case EL_BD_FIREFLY_DOWN:
1075     case EL_PACMAN_RIGHT:
1076     case EL_PACMAN_UP:
1077     case EL_PACMAN_LEFT:
1078     case EL_PACMAN_DOWN:
1079     case EL_YAMYAM:
1080     case EL_YAMYAM_LEFT:
1081     case EL_YAMYAM_RIGHT:
1082     case EL_YAMYAM_UP:
1083     case EL_YAMYAM_DOWN:
1084     case EL_DARK_YAMYAM:
1085     case EL_ROBOT:
1086     case EL_PACMAN:
1087     case EL_SP_SNIKSNAK:
1088     case EL_SP_ELECTRON:
1089     case EL_MOLE:
1090     case EL_MOLE_LEFT:
1091     case EL_MOLE_RIGHT:
1092     case EL_MOLE_UP:
1093     case EL_MOLE_DOWN:
1094       InitMovDir(x, y);
1095       break;
1096
1097     case EL_AMOEBA_FULL:
1098     case EL_BD_AMOEBA:
1099       InitAmoebaNr(x, y);
1100       break;
1101
1102     case EL_AMOEBA_DROP:
1103       if (y == lev_fieldy - 1)
1104       {
1105         Feld[x][y] = EL_AMOEBA_GROWING;
1106         Store[x][y] = EL_AMOEBA_WET;
1107       }
1108       break;
1109
1110     case EL_DYNAMITE_ACTIVE:
1111     case EL_SP_DISK_RED_ACTIVE:
1112     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1113     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1114     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1115     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1116       MovDelay[x][y] = 96;
1117       break;
1118
1119     case EL_EM_DYNAMITE_ACTIVE:
1120       MovDelay[x][y] = 32;
1121       break;
1122
1123     case EL_LAMP:
1124       local_player->lights_still_needed++;
1125       break;
1126
1127     case EL_PENGUIN:
1128       local_player->friends_still_needed++;
1129       break;
1130
1131     case EL_PIG:
1132     case EL_DRAGON:
1133       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1134       break;
1135
1136     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1137     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1138     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1139     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1140     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1141     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1142     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1143     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1144     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1145     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1146     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1147     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1148       if (init_game)
1149       {
1150         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1151         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1152         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1153
1154         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1155         {
1156           game.belt_dir[belt_nr] = belt_dir;
1157           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1158         }
1159         else    /* more than one switch -- set it like the first switch */
1160         {
1161           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1162         }
1163       }
1164       break;
1165
1166 #if !USE_BOTH_SWITCHGATE_SWITCHES
1167     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1168       if (init_game)
1169         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1170       break;
1171 #endif
1172
1173     case EL_LIGHT_SWITCH_ACTIVE:
1174       if (init_game)
1175         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1176       break;
1177
1178     case EL_INVISIBLE_STEELWALL:
1179     case EL_INVISIBLE_WALL:
1180     case EL_INVISIBLE_SAND:
1181       if (game.light_time_left > 0 ||
1182           game.lenses_time_left > 0)
1183         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1184       break;
1185
1186     case EL_EMC_MAGIC_BALL:
1187       if (game.ball_state)
1188         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1189       break;
1190
1191     case EL_EMC_MAGIC_BALL_SWITCH:
1192       if (game.ball_state)
1193         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1194       break;
1195
1196     default:
1197 #if 1
1198       if (IS_CUSTOM_ELEMENT(element))
1199       {
1200         if (CAN_MOVE(element))
1201           InitMovDir(x, y);
1202
1203 #if USE_NEW_CUSTOM_VALUE
1204         if (!element_info[element].use_last_ce_value || init_game)
1205           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1206 #endif
1207       }
1208 #else
1209       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1210         InitMovDir(x, y);
1211 #endif
1212       else if (IS_GROUP_ELEMENT(element))
1213       {
1214 #if 1
1215         Feld[x][y] = get_element_from_group_element(element);
1216
1217         InitField(x, y, init_game);
1218 #else
1219         struct ElementGroupInfo *group = element_info[element].group;
1220         int last_anim_random_frame = gfx.anim_random_frame;
1221         int element_pos;
1222
1223         if (group->choice_mode == ANIM_RANDOM)
1224           gfx.anim_random_frame = RND(group->num_elements_resolved);
1225
1226         element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1227                                         group->choice_mode, 0,
1228                                         group->choice_pos);
1229
1230         if (group->choice_mode == ANIM_RANDOM)
1231           gfx.anim_random_frame = last_anim_random_frame;
1232
1233         group->choice_pos++;
1234
1235         Feld[x][y] = group->element_resolved[element_pos];
1236
1237         InitField(x, y, init_game);
1238 #endif
1239       }
1240
1241       break;
1242   }
1243
1244 #if 1
1245   if (!init_game)
1246     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1247 #endif
1248
1249 #if 0
1250
1251 #if USE_NEW_CUSTOM_VALUE
1252
1253 #if 1
1254   CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1255 #else
1256   CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1257 #endif
1258
1259 #endif
1260
1261 #endif
1262 }
1263
1264 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1265 {
1266   InitField(x, y, init_game);
1267
1268   /* not needed to call InitMovDir() -- already done by InitField()! */
1269   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1270       CAN_MOVE(Feld[x][y]))
1271     InitMovDir(x, y);
1272 }
1273
1274 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1275 {
1276   int old_element = Feld[x][y];
1277
1278   InitField(x, y, init_game);
1279
1280   /* not needed to call InitMovDir() -- already done by InitField()! */
1281   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1282       CAN_MOVE(old_element) &&
1283       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1284     InitMovDir(x, y);
1285
1286   /* this case is in fact a combination of not less than three bugs:
1287      first, it calls InitMovDir() for elements that can move, although this is
1288      already done by InitField(); then, it checks the element that was at this
1289      field _before_ the call to InitField() (which can change it); lastly, it
1290      was not called for "mole with direction" elements, which were treated as
1291      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1292   */
1293 }
1294
1295 inline void DrawGameValue_Emeralds(int value)
1296 {
1297   int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1298
1299   if (PANEL_DEACTIVATED(game.panel.gems))
1300     return;
1301
1302   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1303 }
1304
1305 inline void DrawGameValue_Dynamite(int value)
1306 {
1307   int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1308
1309   if (PANEL_DEACTIVATED(game.panel.inventory))
1310     return;
1311
1312   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1313 }
1314
1315 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1316 {
1317   int base_key_graphic = EL_KEY_1;
1318   int i;
1319
1320   if (PANEL_DEACTIVATED(game.panel.keys))
1321     return;
1322
1323   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1324     base_key_graphic = EL_EM_KEY_1;
1325
1326   /* currently only 4 of 8 possible keys are displayed */
1327   for (i = 0; i < STD_NUM_KEYS; i++)
1328   {
1329     int x = XX_KEYS + i * MINI_TILEX;
1330     int y = YY_KEYS;
1331
1332     if (key[i])
1333       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1334     else
1335       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1336                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1337   }
1338 }
1339
1340 inline void DrawGameValue_Score(int value)
1341 {
1342   int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1343
1344   if (PANEL_DEACTIVATED(game.panel.score))
1345     return;
1346
1347   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1348 }
1349
1350 inline void DrawGameValue_Time(int value)
1351 {
1352   int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1353   int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1354
1355   if (PANEL_DEACTIVATED(game.panel.time))
1356     return;
1357
1358   /* clear background if value just changed its size */
1359   if (value == 999 || value == 1000)
1360     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1361
1362   if (value < 1000)
1363     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1364   else
1365     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1366 }
1367
1368 inline void DrawGameValue_Level(int value)
1369 {
1370   if (PANEL_DEACTIVATED(game.panel.level))
1371     return;
1372
1373   if (level_nr < 100)
1374     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1375   else
1376 #if 1
1377     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1378 #else
1379   {
1380     /* misuse area for displaying emeralds to draw bigger level number */
1381     DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1382                 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1383
1384     /* now copy it to the area for displaying level number */
1385     BlitBitmap(drawto, drawto,
1386                DX_EMERALDS, DY_EMERALDS + 1,
1387                getFontWidth(FONT_LEVEL_NUMBER) * 3,
1388                getFontHeight(FONT_LEVEL_NUMBER) - 1,
1389                DX_LEVEL - 1, DY_LEVEL + 1);
1390
1391     /* restore the area for displaying emeralds */
1392     DrawGameValue_Emeralds(local_player->gems_still_needed);
1393
1394     /* yes, this is all really ugly :-) */
1395   }
1396 #endif
1397 }
1398
1399 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1400                        int key_bits)
1401 {
1402   int key[MAX_NUM_KEYS];
1403   int i;
1404
1405   for (i = 0; i < MAX_NUM_KEYS; i++)
1406     key[i] = key_bits & (1 << i);
1407
1408   DrawGameValue_Level(level_nr);
1409
1410   DrawGameValue_Emeralds(emeralds);
1411   DrawGameValue_Dynamite(dynamite);
1412   DrawGameValue_Score(score);
1413   DrawGameValue_Time(time);
1414
1415   DrawGameValue_Keys(key);
1416 }
1417
1418 void DrawGameDoorValues()
1419 {
1420   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1421   int dynamite_state = 0;
1422   int key_bits = 0;
1423   int i, j;
1424
1425   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1426   {
1427     DrawGameDoorValues_EM();
1428
1429     return;
1430   }
1431
1432 #if 0
1433   DrawGameValue_Level(level_nr);
1434
1435   DrawGameValue_Emeralds(local_player->gems_still_needed);
1436   DrawGameValue_Dynamite(local_player->inventory_size);
1437   DrawGameValue_Score(local_player->score);
1438   DrawGameValue_Time(TimeLeft);
1439
1440 #else
1441
1442   if (game.centered_player_nr == -1)
1443   {
1444     for (i = 0; i < MAX_PLAYERS; i++)
1445     {
1446       for (j = 0; j < MAX_NUM_KEYS; j++)
1447         if (stored_player[i].key[j])
1448           key_bits |= (1 << j);
1449
1450       dynamite_state += stored_player[i].inventory_size;
1451     }
1452
1453 #if 0
1454     DrawGameValue_Keys(stored_player[i].key);
1455 #endif
1456   }
1457   else
1458   {
1459     int player_nr = game.centered_player_nr;
1460
1461     for (i = 0; i < MAX_NUM_KEYS; i++)
1462       if (stored_player[player_nr].key[i])
1463         key_bits |= (1 << i);
1464
1465     dynamite_state = stored_player[player_nr].inventory_size;
1466   }
1467
1468   DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1469                     local_player->score, time_value, key_bits);
1470 #endif
1471 }
1472
1473 #if 0
1474 static void resolve_group_element(int group_element, int recursion_depth)
1475 {
1476   static int group_nr;
1477   static struct ElementGroupInfo *group;
1478   struct ElementGroupInfo *actual_group = element_info[group_element].group;
1479   int i;
1480
1481   if (recursion_depth > NUM_GROUP_ELEMENTS)     /* recursion too deep */
1482   {
1483     Error(ERR_WARN, "recursion too deep when resolving group element %d",
1484           group_element - EL_GROUP_START + 1);
1485
1486     /* replace element which caused too deep recursion by question mark */
1487     group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1488
1489     return;
1490   }
1491
1492   if (recursion_depth == 0)                     /* initialization */
1493   {
1494     group = element_info[group_element].group;
1495     group_nr = group_element - EL_GROUP_START;
1496
1497     group->num_elements_resolved = 0;
1498     group->choice_pos = 0;
1499   }
1500
1501   for (i = 0; i < actual_group->num_elements; i++)
1502   {
1503     int element = actual_group->element[i];
1504
1505     if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1506       break;
1507
1508     if (IS_GROUP_ELEMENT(element))
1509       resolve_group_element(element, recursion_depth + 1);
1510     else
1511     {
1512       group->element_resolved[group->num_elements_resolved++] = element;
1513       element_info[element].in_group[group_nr] = TRUE;
1514     }
1515   }
1516 }
1517 #endif
1518
1519 #if 0
1520 static void replace_reference_element(int base_element, int *element)
1521 {
1522   if (*element >= EL_LAST_CE_8 && *element <= EL_NEXT_CE_8)
1523   {
1524     *element = base_element + *element - EL_SELF;
1525     *element = (*element < EL_CUSTOM_START ? EL_CUSTOM_START :
1526                 *element > EL_CUSTOM_END   ? EL_CUSTOM_END   : *element);
1527   }
1528 }
1529 #endif
1530
1531 /*
1532   =============================================================================
1533   InitGameEngine()
1534   -----------------------------------------------------------------------------
1535   initialize game engine due to level / tape version number
1536   =============================================================================
1537 */
1538
1539 static void InitGameEngine()
1540 {
1541   int i, j, k, l, x, y;
1542
1543   /* set game engine from tape file when re-playing, else from level file */
1544   game.engine_version = (tape.playing ? tape.engine_version :
1545                          level.game_version);
1546
1547   /* ---------------------------------------------------------------------- */
1548   /* set flags for bugs and changes according to active game engine version */
1549   /* ---------------------------------------------------------------------- */
1550
1551   /*
1552     Summary of bugfix/change:
1553     Fixed handling for custom elements that change when pushed by the player.
1554
1555     Fixed/changed in version:
1556     3.1.0
1557
1558     Description:
1559     Before 3.1.0, custom elements that "change when pushing" changed directly
1560     after the player started pushing them (until then handled in "DigField()").
1561     Since 3.1.0, these custom elements are not changed until the "pushing"
1562     move of the element is finished (now handled in "ContinueMoving()").
1563
1564     Affected levels/tapes:
1565     The first condition is generally needed for all levels/tapes before version
1566     3.1.0, which might use the old behaviour before it was changed; known tapes
1567     that are affected are some tapes from the level set "Walpurgis Gardens" by
1568     Jamie Cullen.
1569     The second condition is an exception from the above case and is needed for
1570     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1571     above (including some development versions of 3.1.0), but before it was
1572     known that this change would break tapes like the above and was fixed in
1573     3.1.1, so that the changed behaviour was active although the engine version
1574     while recording maybe was before 3.1.0. There is at least one tape that is
1575     affected by this exception, which is the tape for the one-level set "Bug
1576     Machine" by Juergen Bonhagen.
1577   */
1578
1579   game.use_change_when_pushing_bug =
1580     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1581      !(tape.playing &&
1582        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1583        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1584
1585   /*
1586     Summary of bugfix/change:
1587     Fixed handling for blocking the field the player leaves when moving.
1588
1589     Fixed/changed in version:
1590     3.1.1
1591
1592     Description:
1593     Before 3.1.1, when "block last field when moving" was enabled, the field
1594     the player is leaving when moving was blocked for the time of the move,
1595     and was directly unblocked afterwards. This resulted in the last field
1596     being blocked for exactly one less than the number of frames of one player
1597     move. Additionally, even when blocking was disabled, the last field was
1598     blocked for exactly one frame.
1599     Since 3.1.1, due to changes in player movement handling, the last field
1600     is not blocked at all when blocking is disabled. When blocking is enabled,
1601     the last field is blocked for exactly the number of frames of one player
1602     move. Additionally, if the player is Murphy, the hero of Supaplex, the
1603     last field is blocked for exactly one more than the number of frames of
1604     one player move.
1605
1606     Affected levels/tapes:
1607     (!!! yet to be determined -- probably many !!!)
1608   */
1609
1610   game.use_block_last_field_bug =
1611     (game.engine_version < VERSION_IDENT(3,1,1,0));
1612
1613   /*
1614     Summary of bugfix/change:
1615     Changed behaviour of CE changes with multiple changes per single frame.
1616
1617     Fixed/changed in version:
1618     3.2.0-6
1619
1620     Description:
1621     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1622     This resulted in race conditions where CEs seem to behave strange in some
1623     situations (where triggered CE changes were just skipped because there was
1624     already a CE change on that tile in the playfield in that engine frame).
1625     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1626     (The number of changes per frame must be limited in any case, because else
1627     it is easily possible to define CE changes that would result in an infinite
1628     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1629     should be set large enough so that it would only be reached in cases where
1630     the corresponding CE change conditions run into a loop. Therefore, it seems
1631     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1632     maximal number of change pages for custom elements.)
1633
1634     Affected levels/tapes:
1635     Probably many.
1636   */
1637
1638 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1639   game.max_num_changes_per_frame = 1;
1640 #else
1641   game.max_num_changes_per_frame =
1642     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1643 #endif
1644
1645   /* ---------------------------------------------------------------------- */
1646
1647   /* default scan direction: scan playfield from top/left to bottom/right */
1648   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1649
1650   /* dynamically adjust element properties according to game engine version */
1651   InitElementPropertiesEngine(game.engine_version);
1652
1653 #if 0
1654   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1655   printf("          tape version == %06d [%s] [file: %06d]\n",
1656          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1657          tape.file_version);
1658   printf("       => game.engine_version == %06d\n", game.engine_version);
1659 #endif
1660
1661 #if 0
1662   /* ---------- recursively resolve group elements ------------------------- */
1663
1664   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1665     for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1666       element_info[i].in_group[j] = FALSE;
1667
1668   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1669     resolve_group_element(EL_GROUP_START + i, 0);
1670 #endif
1671
1672   /* ---------- initialize player's initial move delay --------------------- */
1673
1674 #if 1
1675   /* dynamically adjust player properties according to level information */
1676   for (i = 0; i < MAX_PLAYERS; i++)
1677     game.initial_move_delay_value[i] =
1678       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1679 #else
1680   /* dynamically adjust player properties according to level information */
1681   game.initial_move_delay_value =
1682     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1683 #endif
1684
1685   /* dynamically adjust player properties according to game engine version */
1686   for (i = 0; i < MAX_PLAYERS; i++)
1687     game.initial_move_delay[i] =
1688       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1689        game.initial_move_delay_value[i] : 0);
1690
1691   /* ---------- initialize player's initial push delay --------------------- */
1692
1693   /* dynamically adjust player properties according to game engine version */
1694   game.initial_push_delay_value =
1695     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1696
1697   /* ---------- initialize changing elements ------------------------------- */
1698
1699   /* initialize changing elements information */
1700   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1701   {
1702     struct ElementInfo *ei = &element_info[i];
1703
1704     /* this pointer might have been changed in the level editor */
1705     ei->change = &ei->change_page[0];
1706
1707     if (!IS_CUSTOM_ELEMENT(i))
1708     {
1709       ei->change->target_element = EL_EMPTY_SPACE;
1710       ei->change->delay_fixed = 0;
1711       ei->change->delay_random = 0;
1712       ei->change->delay_frames = 1;
1713     }
1714
1715     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1716     {
1717       ei->has_change_event[j] = FALSE;
1718
1719       ei->event_page_nr[j] = 0;
1720       ei->event_page[j] = &ei->change_page[0];
1721     }
1722   }
1723
1724   /* add changing elements from pre-defined list */
1725   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1726   {
1727     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1728     struct ElementInfo *ei = &element_info[ch_delay->element];
1729
1730     ei->change->target_element       = ch_delay->target_element;
1731     ei->change->delay_fixed          = ch_delay->change_delay;
1732
1733     ei->change->pre_change_function  = ch_delay->pre_change_function;
1734     ei->change->change_function      = ch_delay->change_function;
1735     ei->change->post_change_function = ch_delay->post_change_function;
1736
1737     ei->change->can_change = TRUE;
1738     ei->change->can_change_or_has_action = TRUE;
1739
1740     ei->has_change_event[CE_DELAY] = TRUE;
1741
1742     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1743     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1744   }
1745
1746   /* ---------- initialize internal run-time variables ------------- */
1747
1748   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1749   {
1750     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1751
1752     for (j = 0; j < ei->num_change_pages; j++)
1753     {
1754       ei->change_page[j].can_change_or_has_action =
1755         (ei->change_page[j].can_change |
1756          ei->change_page[j].has_action);
1757     }
1758   }
1759
1760   /* add change events from custom element configuration */
1761   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1762   {
1763     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1764
1765     for (j = 0; j < ei->num_change_pages; j++)
1766     {
1767       if (!ei->change_page[j].can_change_or_has_action)
1768         continue;
1769
1770       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1771       {
1772         /* only add event page for the first page found with this event */
1773         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1774         {
1775           ei->has_change_event[k] = TRUE;
1776
1777           ei->event_page_nr[k] = j;
1778           ei->event_page[k] = &ei->change_page[j];
1779         }
1780       }
1781     }
1782   }
1783
1784   /* ---------- initialize run-time trigger player and element ------------- */
1785
1786   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1787   {
1788     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1789
1790     for (j = 0; j < ei->num_change_pages; j++)
1791     {
1792       ei->change_page[j].actual_trigger_element = EL_EMPTY;
1793       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1794       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1795       ei->change_page[j].actual_trigger_ce_value = 0;
1796       ei->change_page[j].actual_trigger_ce_score = 0;
1797     }
1798   }
1799
1800   /* ---------- initialize trigger events ---------------------------------- */
1801
1802   /* initialize trigger events information */
1803   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1804     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1805       trigger_events[i][j] = FALSE;
1806
1807   /* add trigger events from element change event properties */
1808   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1809   {
1810     struct ElementInfo *ei = &element_info[i];
1811
1812     for (j = 0; j < ei->num_change_pages; j++)
1813     {
1814       if (!ei->change_page[j].can_change_or_has_action)
1815         continue;
1816
1817       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1818       {
1819         int trigger_element = ei->change_page[j].trigger_element;
1820
1821         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1822         {
1823           if (ei->change_page[j].has_event[k])
1824           {
1825             if (IS_GROUP_ELEMENT(trigger_element))
1826             {
1827               struct ElementGroupInfo *group =
1828                 element_info[trigger_element].group;
1829
1830               for (l = 0; l < group->num_elements_resolved; l++)
1831                 trigger_events[group->element_resolved[l]][k] = TRUE;
1832             }
1833 #if 1
1834             else if (trigger_element == EL_ANY_ELEMENT)
1835               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1836                 trigger_events[l][k] = TRUE;
1837 #endif
1838             else
1839               trigger_events[trigger_element][k] = TRUE;
1840           }
1841         }
1842       }
1843     }
1844   }
1845
1846   /* ---------- initialize push delay -------------------------------------- */
1847
1848   /* initialize push delay values to default */
1849   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1850   {
1851     if (!IS_CUSTOM_ELEMENT(i))
1852     {
1853 #if 1
1854       /* set default push delay values (corrected since version 3.0.7-1) */
1855       if (game.engine_version < VERSION_IDENT(3,0,7,1))
1856       {
1857         element_info[i].push_delay_fixed = 2;
1858         element_info[i].push_delay_random = 8;
1859       }
1860       else
1861       {
1862         element_info[i].push_delay_fixed = 8;
1863         element_info[i].push_delay_random = 8;
1864       }
1865 #else
1866       element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
1867       element_info[i].push_delay_random = game.default_push_delay_random;
1868 #endif
1869     }
1870   }
1871
1872   /* set push delay value for certain elements from pre-defined list */
1873   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1874   {
1875     int e = push_delay_list[i].element;
1876
1877     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1878     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1879   }
1880
1881   /* set push delay value for Supaplex elements for newer engine versions */
1882   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1883   {
1884     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1885     {
1886       if (IS_SP_ELEMENT(i))
1887       {
1888         /* set SP push delay to just enough to push under a falling zonk */
1889         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1890
1891         element_info[i].push_delay_fixed  = delay;
1892         element_info[i].push_delay_random = 0;
1893       }
1894     }
1895   }
1896
1897   /* ---------- initialize move stepsize ----------------------------------- */
1898
1899   /* initialize move stepsize values to default */
1900   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1901     if (!IS_CUSTOM_ELEMENT(i))
1902       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1903
1904   /* set move stepsize value for certain elements from pre-defined list */
1905   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1906   {
1907     int e = move_stepsize_list[i].element;
1908
1909     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1910   }
1911
1912   /* ---------- initialize collect score ----------------------------------- */
1913
1914   /* initialize collect score values for custom elements from initial value */
1915   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1916     if (IS_CUSTOM_ELEMENT(i))
1917       element_info[i].collect_score = element_info[i].collect_score_initial;
1918
1919   /* ---------- initialize collect count ----------------------------------- */
1920
1921   /* initialize collect count values for non-custom elements */
1922   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1923     if (!IS_CUSTOM_ELEMENT(i))
1924       element_info[i].collect_count_initial = 0;
1925
1926   /* add collect count values for all elements from pre-defined list */
1927   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1928     element_info[collect_count_list[i].element].collect_count_initial =
1929       collect_count_list[i].count;
1930
1931   /* ---------- initialize access direction -------------------------------- */
1932
1933   /* initialize access direction values to default (access from every side) */
1934   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1935     if (!IS_CUSTOM_ELEMENT(i))
1936       element_info[i].access_direction = MV_ALL_DIRECTIONS;
1937
1938   /* set access direction value for certain elements from pre-defined list */
1939   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1940     element_info[access_direction_list[i].element].access_direction =
1941       access_direction_list[i].direction;
1942
1943   /* ---------- initialize explosion content ------------------------------- */
1944   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1945   {
1946     if (IS_CUSTOM_ELEMENT(i))
1947       continue;
1948
1949     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1950     {
1951       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1952
1953       element_info[i].content.e[x][y] =
1954         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1955          i == EL_PLAYER_2 ? EL_EMERALD_RED :
1956          i == EL_PLAYER_3 ? EL_EMERALD :
1957          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1958          i == EL_MOLE ? EL_EMERALD_RED :
1959          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1960          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1961          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1962          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1963          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1964          i == EL_WALL_EMERALD ? EL_EMERALD :
1965          i == EL_WALL_DIAMOND ? EL_DIAMOND :
1966          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1967          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1968          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1969          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1970          i == EL_WALL_PEARL ? EL_PEARL :
1971          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1972          EL_EMPTY);
1973     }
1974   }
1975
1976 #if 0
1977   /* ---------- initialize reference elements ------------------------------- */
1978   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1979   {
1980     int element = EL_CUSTOM_START + i;
1981     struct ElementInfo *ei = &element_info[element];
1982
1983     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1984       replace_reference_element(element, &ei->content.e[x][y]);
1985
1986     for (j = 0; j < ei->num_change_pages; j++)
1987     {
1988       struct ElementChangeInfo *change = &ei->change_page[j];
1989
1990       replace_reference_element(element, &change->target_element);
1991       replace_reference_element(element, &change->trigger_element);
1992
1993       for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1994         replace_reference_element(element, &change->target_content.e[x][y]);
1995     }
1996   }
1997 #endif
1998 }
1999
2000 int get_num_special_action(int element, int action_first, int action_last)
2001 {
2002   int num_special_action = 0;
2003   int i, j;
2004
2005   for (i = action_first; i <= action_last; i++)
2006   {
2007     boolean found = FALSE;
2008
2009     for (j = 0; j < NUM_DIRECTIONS; j++)
2010       if (el_act_dir2img(element, i, j) !=
2011           el_act_dir2img(element, ACTION_DEFAULT, j))
2012         found = TRUE;
2013
2014     if (found)
2015       num_special_action++;
2016     else
2017       break;
2018   }
2019
2020 #if 0
2021   printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
2022 #endif
2023
2024   return num_special_action;
2025 }
2026
2027 /*
2028   =============================================================================
2029   InitGame()
2030   -----------------------------------------------------------------------------
2031   initialize and start new game
2032   =============================================================================
2033 */
2034
2035 void InitGame()
2036 {
2037   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2038   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2039   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2040   boolean do_fading = (game_status == GAME_MODE_MAIN);
2041   int i, j, x, y;
2042
2043   game_status = GAME_MODE_PLAYING;
2044
2045   InitGameEngine();
2046
2047   /* don't play tapes over network */
2048   network_playing = (options.network && !tape.playing);
2049
2050   for (i = 0; i < MAX_PLAYERS; i++)
2051   {
2052     struct PlayerInfo *player = &stored_player[i];
2053
2054     player->index_nr = i;
2055     player->index_bit = (1 << i);
2056     player->element_nr = EL_PLAYER_1 + i;
2057
2058     player->present = FALSE;
2059     player->active = FALSE;
2060
2061     player->action = 0;
2062     player->effective_action = 0;
2063     player->programmed_action = 0;
2064
2065     player->score = 0;
2066     player->gems_still_needed = level.gems_needed;
2067     player->sokobanfields_still_needed = 0;
2068     player->lights_still_needed = 0;
2069     player->friends_still_needed = 0;
2070
2071     for (j = 0; j < MAX_NUM_KEYS; j++)
2072       player->key[j] = FALSE;
2073
2074     player->dynabomb_count = 0;
2075     player->dynabomb_size = 1;
2076     player->dynabombs_left = 0;
2077     player->dynabomb_xl = FALSE;
2078
2079     player->MovDir = MV_NONE;
2080     player->MovPos = 0;
2081     player->GfxPos = 0;
2082     player->GfxDir = MV_NONE;
2083     player->GfxAction = ACTION_DEFAULT;
2084     player->Frame = 0;
2085     player->StepFrame = 0;
2086
2087     player->use_murphy = FALSE;
2088     player->artwork_element =
2089       (level.use_artwork_element[i] ? level.artwork_element[i] :
2090        player->element_nr);
2091
2092     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2093     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2094
2095     player->gravity = level.initial_player_gravity[i];
2096
2097     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2098
2099     player->actual_frame_counter = 0;
2100
2101     player->step_counter = 0;
2102
2103     player->last_move_dir = MV_NONE;
2104
2105     player->is_active = FALSE;
2106
2107     player->is_waiting = FALSE;
2108     player->is_moving = FALSE;
2109     player->is_auto_moving = FALSE;
2110     player->is_digging = FALSE;
2111     player->is_snapping = FALSE;
2112     player->is_collecting = FALSE;
2113     player->is_pushing = FALSE;
2114     player->is_switching = FALSE;
2115     player->is_dropping = FALSE;
2116     player->is_dropping_pressed = FALSE;
2117
2118     player->is_bored = FALSE;
2119     player->is_sleeping = FALSE;
2120
2121     player->frame_counter_bored = -1;
2122     player->frame_counter_sleeping = -1;
2123
2124     player->anim_delay_counter = 0;
2125     player->post_delay_counter = 0;
2126
2127     player->dir_waiting = MV_NONE;
2128     player->action_waiting = ACTION_DEFAULT;
2129     player->last_action_waiting = ACTION_DEFAULT;
2130     player->special_action_bored = ACTION_DEFAULT;
2131     player->special_action_sleeping = ACTION_DEFAULT;
2132
2133 #if 1
2134     /* cannot be set here -- could be modified in Init[Player]Field() below */
2135 #else
2136     /* set number of special actions for bored and sleeping animation */
2137     player->num_special_action_bored =
2138       get_num_special_action(player->artwork_element,
2139                              ACTION_BORING_1, ACTION_BORING_LAST);
2140     player->num_special_action_sleeping =
2141       get_num_special_action(player->artwork_element,
2142                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2143 #endif
2144
2145     player->switch_x = -1;
2146     player->switch_y = -1;
2147
2148     player->drop_x = -1;
2149     player->drop_y = -1;
2150
2151     player->show_envelope = 0;
2152
2153 #if 1
2154     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2155 #else
2156     player->move_delay       = game.initial_move_delay;
2157     player->move_delay_value = game.initial_move_delay_value;
2158
2159     player->move_delay_value_next = -1;
2160
2161     player->move_delay_reset_counter = 0;
2162
2163     player->cannot_move = FALSE;
2164 #endif
2165
2166     player->push_delay       = -1;      /* initialized when pushing starts */
2167     player->push_delay_value = game.initial_push_delay_value;
2168
2169     player->drop_delay = 0;
2170     player->drop_pressed_delay = 0;
2171
2172     player->last_jx = player->last_jy = 0;
2173     player->jx = player->jy = 0;
2174
2175     player->shield_normal_time_left = 0;
2176     player->shield_deadly_time_left = 0;
2177
2178     player->inventory_infinite_element = EL_UNDEFINED;
2179     player->inventory_size = 0;
2180
2181     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2182     SnapField(player, 0, 0);
2183
2184     player->LevelSolved = FALSE;
2185     player->GameOver = FALSE;
2186
2187     player->LevelSolved_GameEnd = FALSE;
2188     player->LevelSolved_SaveTape = FALSE;
2189     player->LevelSolved_SaveScore = FALSE;
2190   }
2191
2192   network_player_action_received = FALSE;
2193
2194 #if defined(NETWORK_AVALIABLE)
2195   /* initial null action */
2196   if (network_playing)
2197     SendToServer_MovePlayer(MV_NONE);
2198 #endif
2199
2200   ZX = ZY = -1;
2201   ExitX = ExitY = -1;
2202
2203   FrameCounter = 0;
2204   TimeFrames = 0;
2205   TimePlayed = 0;
2206   TimeLeft = level.time;
2207   TapeTime = 0;
2208
2209   ScreenMovDir = MV_NONE;
2210   ScreenMovPos = 0;
2211   ScreenGfxPos = 0;
2212
2213   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2214
2215   AllPlayersGone = FALSE;
2216
2217   game.yamyam_content_nr = 0;
2218   game.magic_wall_active = FALSE;
2219   game.magic_wall_time_left = 0;
2220   game.light_time_left = 0;
2221   game.timegate_time_left = 0;
2222   game.switchgate_pos = 0;
2223   game.wind_direction = level.wind_direction_initial;
2224
2225 #if !USE_PLAYER_GRAVITY
2226 #if 1
2227   game.gravity = FALSE;
2228 #else
2229   game.gravity = level.initial_gravity;
2230 #endif
2231   game.explosions_delayed = TRUE;
2232 #endif
2233
2234   game.lenses_time_left = 0;
2235   game.magnify_time_left = 0;
2236
2237   game.ball_state = level.ball_state_initial;
2238   game.ball_content_nr = 0;
2239
2240   game.envelope_active = FALSE;
2241
2242   /* set focus to local player for network games, else to all players */
2243   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2244   game.centered_player_nr_next = game.centered_player_nr;
2245   game.set_centered_player = FALSE;
2246
2247   if (network_playing && tape.recording)
2248   {
2249     /* store client dependent player focus when recording network games */
2250     tape.centered_player_nr_next = game.centered_player_nr_next;
2251     tape.set_centered_player = TRUE;
2252   }
2253
2254 #if 0
2255   printf("::: focus set to player %d [%d]\n",
2256          game.centered_player_nr, local_player->index_nr);
2257 #endif
2258
2259   for (i = 0; i < NUM_BELTS; i++)
2260   {
2261     game.belt_dir[i] = MV_NONE;
2262     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2263   }
2264
2265   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2266     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2267
2268 #if 1
2269   SCAN_PLAYFIELD(x, y)
2270 #else
2271   for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2272 #endif
2273   {
2274     Feld[x][y] = level.field[x][y];
2275     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2276     ChangeDelay[x][y] = 0;
2277     ChangePage[x][y] = -1;
2278 #if USE_NEW_CUSTOM_VALUE
2279     CustomValue[x][y] = 0;              /* initialized in InitField() */
2280 #endif
2281     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2282     AmoebaNr[x][y] = 0;
2283     WasJustMoving[x][y] = 0;
2284     WasJustFalling[x][y] = 0;
2285     CheckCollision[x][y] = 0;
2286     Stop[x][y] = FALSE;
2287     Pushed[x][y] = FALSE;
2288
2289     ChangeCount[x][y] = 0;
2290     ChangeEvent[x][y] = -1;
2291
2292     ExplodePhase[x][y] = 0;
2293     ExplodeDelay[x][y] = 0;
2294     ExplodeField[x][y] = EX_TYPE_NONE;
2295
2296     RunnerVisit[x][y] = 0;
2297     PlayerVisit[x][y] = 0;
2298
2299     GfxFrame[x][y] = 0;
2300     GfxRandom[x][y] = INIT_GFX_RANDOM();
2301     GfxElement[x][y] = EL_UNDEFINED;
2302     GfxAction[x][y] = ACTION_DEFAULT;
2303     GfxDir[x][y] = MV_NONE;
2304   }
2305
2306 #if 1
2307   SCAN_PLAYFIELD(x, y)
2308 #else
2309   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2310 #endif
2311   {
2312     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2313       emulate_bd = FALSE;
2314     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2315       emulate_sb = FALSE;
2316     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2317       emulate_sp = FALSE;
2318
2319     InitField(x, y, TRUE);
2320   }
2321
2322   InitBeltMovement();
2323
2324   for (i = 0; i < MAX_PLAYERS; i++)
2325   {
2326     struct PlayerInfo *player = &stored_player[i];
2327
2328 #if 1
2329     /* set number of special actions for bored and sleeping animation */
2330     player->num_special_action_bored =
2331       get_num_special_action(player->artwork_element,
2332                              ACTION_BORING_1, ACTION_BORING_LAST);
2333     player->num_special_action_sleeping =
2334       get_num_special_action(player->artwork_element,
2335                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2336 #endif
2337
2338   }
2339
2340   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2341                     emulate_sb ? EMU_SOKOBAN :
2342                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2343
2344 #if USE_NEW_ALL_SLIPPERY
2345   /* initialize type of slippery elements */
2346   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2347   {
2348     if (!IS_CUSTOM_ELEMENT(i))
2349     {
2350       /* default: elements slip down either to the left or right randomly */
2351       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2352
2353       /* SP style elements prefer to slip down on the left side */
2354       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2355         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2356
2357       /* BD style elements prefer to slip down on the left side */
2358       if (game.emulation == EMU_BOULDERDASH)
2359         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2360     }
2361   }
2362 #endif
2363
2364   /* initialize explosion and ignition delay */
2365   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2366   {
2367     if (!IS_CUSTOM_ELEMENT(i))
2368     {
2369       int num_phase = 8;
2370       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2371                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2372                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2373       int last_phase = (num_phase + 1) * delay;
2374       int half_phase = (num_phase / 2) * delay;
2375
2376       element_info[i].explosion_delay = last_phase - 1;
2377       element_info[i].ignition_delay = half_phase;
2378
2379       if (i == EL_BLACK_ORB)
2380         element_info[i].ignition_delay = 1;
2381     }
2382
2383 #if 0
2384     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2385       element_info[i].explosion_delay = 1;
2386
2387     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2388       element_info[i].ignition_delay = 1;
2389 #endif
2390   }
2391
2392   /* correct non-moving belts to start moving left */
2393   for (i = 0; i < NUM_BELTS; i++)
2394     if (game.belt_dir[i] == MV_NONE)
2395       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2396
2397   /* check if any connected player was not found in playfield */
2398   for (i = 0; i < MAX_PLAYERS; i++)
2399   {
2400     struct PlayerInfo *player = &stored_player[i];
2401
2402     if (player->connected && !player->present)
2403     {
2404       for (j = 0; j < MAX_PLAYERS; j++)
2405       {
2406         struct PlayerInfo *some_player = &stored_player[j];
2407         int jx = some_player->jx, jy = some_player->jy;
2408
2409         /* assign first free player found that is present in the playfield */
2410         if (some_player->present && !some_player->connected)
2411         {
2412           player->present = TRUE;
2413           player->active = TRUE;
2414
2415           some_player->present = FALSE;
2416           some_player->active = FALSE;
2417
2418 #if 0
2419           player->element_nr = some_player->element_nr;
2420 #endif
2421
2422           player->artwork_element = some_player->artwork_element;
2423
2424           player->block_last_field       = some_player->block_last_field;
2425           player->block_delay_adjustment = some_player->block_delay_adjustment;
2426
2427           StorePlayer[jx][jy] = player->element_nr;
2428           player->jx = player->last_jx = jx;
2429           player->jy = player->last_jy = jy;
2430
2431           break;
2432         }
2433       }
2434     }
2435   }
2436
2437   if (tape.playing)
2438   {
2439     /* when playing a tape, eliminate all players who do not participate */
2440
2441     for (i = 0; i < MAX_PLAYERS; i++)
2442     {
2443       if (stored_player[i].active && !tape.player_participates[i])
2444       {
2445         struct PlayerInfo *player = &stored_player[i];
2446         int jx = player->jx, jy = player->jy;
2447
2448         player->active = FALSE;
2449         StorePlayer[jx][jy] = 0;
2450         Feld[jx][jy] = EL_EMPTY;
2451       }
2452     }
2453   }
2454   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2455   {
2456     /* when in single player mode, eliminate all but the first active player */
2457
2458     for (i = 0; i < MAX_PLAYERS; i++)
2459     {
2460       if (stored_player[i].active)
2461       {
2462         for (j = i + 1; j < MAX_PLAYERS; j++)
2463         {
2464           if (stored_player[j].active)
2465           {
2466             struct PlayerInfo *player = &stored_player[j];
2467             int jx = player->jx, jy = player->jy;
2468
2469             player->active = FALSE;
2470             player->present = FALSE;
2471
2472             StorePlayer[jx][jy] = 0;
2473             Feld[jx][jy] = EL_EMPTY;
2474           }
2475         }
2476       }
2477     }
2478   }
2479
2480   /* when recording the game, store which players take part in the game */
2481   if (tape.recording)
2482   {
2483     for (i = 0; i < MAX_PLAYERS; i++)
2484       if (stored_player[i].active)
2485         tape.player_participates[i] = TRUE;
2486   }
2487
2488   if (options.debug)
2489   {
2490     for (i = 0; i < MAX_PLAYERS; i++)
2491     {
2492       struct PlayerInfo *player = &stored_player[i];
2493
2494       printf("Player %d: present == %d, connected == %d, active == %d.\n",
2495              i+1,
2496              player->present,
2497              player->connected,
2498              player->active);
2499       if (local_player == player)
2500         printf("Player  %d is local player.\n", i+1);
2501     }
2502   }
2503
2504   if (BorderElement == EL_EMPTY)
2505   {
2506     SBX_Left = 0;
2507     SBX_Right = lev_fieldx - SCR_FIELDX;
2508     SBY_Upper = 0;
2509     SBY_Lower = lev_fieldy - SCR_FIELDY;
2510   }
2511   else
2512   {
2513     SBX_Left = -1;
2514     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2515     SBY_Upper = -1;
2516     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2517   }
2518
2519   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2520     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2521
2522   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2523     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2524
2525   /* if local player not found, look for custom element that might create
2526      the player (make some assumptions about the right custom element) */
2527   if (!local_player->present)
2528   {
2529     int start_x = 0, start_y = 0;
2530     int found_rating = 0;
2531     int found_element = EL_UNDEFINED;
2532     int player_nr = local_player->index_nr;
2533
2534 #if 1
2535     SCAN_PLAYFIELD(x, y)
2536 #else
2537     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2538 #endif
2539     {
2540       int element = Feld[x][y];
2541       int content;
2542       int xx, yy;
2543       boolean is_player;
2544
2545       if (level.use_start_element[player_nr] &&
2546           level.start_element[player_nr] == element &&
2547           found_rating < 4)
2548       {
2549         start_x = x;
2550         start_y = y;
2551
2552         found_rating = 4;
2553         found_element = element;
2554       }
2555
2556       if (!IS_CUSTOM_ELEMENT(element))
2557         continue;
2558
2559       if (CAN_CHANGE(element))
2560       {
2561         for (i = 0; i < element_info[element].num_change_pages; i++)
2562         {
2563           /* check for player created from custom element as single target */
2564           content = element_info[element].change_page[i].target_element;
2565           is_player = ELEM_IS_PLAYER(content);
2566
2567           if (is_player && (found_rating < 3 || element < found_element))
2568           {
2569             start_x = x;
2570             start_y = y;
2571
2572             found_rating = 3;
2573             found_element = element;
2574           }
2575         }
2576       }
2577
2578       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2579       {
2580         /* check for player created from custom element as explosion content */
2581         content = element_info[element].content.e[xx][yy];
2582         is_player = ELEM_IS_PLAYER(content);
2583
2584         if (is_player && (found_rating < 2 || element < found_element))
2585         {
2586           start_x = x + xx - 1;
2587           start_y = y + yy - 1;
2588
2589           found_rating = 2;
2590           found_element = element;
2591         }
2592
2593         if (!CAN_CHANGE(element))
2594           continue;
2595
2596         for (i = 0; i < element_info[element].num_change_pages; i++)
2597         {
2598           /* check for player created from custom element as extended target */
2599           content =
2600             element_info[element].change_page[i].target_content.e[xx][yy];
2601
2602           is_player = ELEM_IS_PLAYER(content);
2603
2604           if (is_player && (found_rating < 1 || element < found_element))
2605           {
2606             start_x = x + xx - 1;
2607             start_y = y + yy - 1;
2608
2609             found_rating = 1;
2610             found_element = element;
2611           }
2612         }
2613       }
2614     }
2615
2616     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
2617                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2618                 start_x - MIDPOSX);
2619
2620     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2621                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2622                 start_y - MIDPOSY);
2623   }
2624   else
2625   {
2626     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2627                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2628                 local_player->jx - MIDPOSX);
2629
2630     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2631                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2632                 local_player->jy - MIDPOSY);
2633   }
2634
2635   StopAnimation();
2636
2637   if (!game.restart_level)
2638     CloseDoor(DOOR_CLOSE_1);
2639
2640   if (do_fading)
2641     FadeOut(REDRAW_FIELD);
2642
2643   /* !!! FIX THIS (START) !!! */
2644   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2645   {
2646     InitGameEngine_EM();
2647   }
2648   else
2649   {
2650     DrawLevel();
2651     DrawAllPlayers();
2652
2653     /* after drawing the level, correct some elements */
2654     if (game.timegate_time_left == 0)
2655       CloseAllOpenTimegates();
2656
2657     if (setup.soft_scrolling)
2658       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2659
2660     redraw_mask |= REDRAW_FROM_BACKBUFFER;
2661
2662 #if 0
2663     FadeToFront();
2664 #endif
2665   }
2666   /* !!! FIX THIS (END) !!! */
2667
2668   if (do_fading)
2669     FadeIn(REDRAW_FIELD);
2670
2671   if (!game.restart_level)
2672   {
2673     /* copy default game door content to main double buffer */
2674     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2675                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2676   }
2677
2678 #if 1
2679   SetPanelBackground();
2680   SetDrawBackgroundMask(REDRAW_DOOR_1);
2681 #endif
2682
2683   DrawGameDoorValues();
2684
2685   if (!game.restart_level)
2686   {
2687     UnmapGameButtons();
2688     UnmapTapeButtons();
2689     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2690     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2691     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2692     MapGameButtons();
2693     MapTapeButtons();
2694
2695     /* copy actual game door content to door double buffer for OpenDoor() */
2696     BlitBitmap(drawto, bitmap_db_door,
2697                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2698
2699     OpenDoor(DOOR_OPEN_ALL);
2700
2701     PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2702
2703     if (setup.sound_music)
2704       PlayLevelMusic();
2705
2706     KeyboardAutoRepeatOffUnlessAutoplay();
2707
2708     if (options.debug)
2709     {
2710       for (i = 0; i < MAX_PLAYERS; i++)
2711         printf("Player %d %sactive.\n",
2712                i + 1, (stored_player[i].active ? "" : "not "));
2713     }
2714   }
2715
2716   game.restart_level = FALSE;
2717 }
2718
2719 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2720 {
2721   /* this is used for non-R'n'D game engines to update certain engine values */
2722
2723   /* needed to determine if sounds are played within the visible screen area */
2724   scroll_x = actual_scroll_x;
2725   scroll_y = actual_scroll_y;
2726 }
2727
2728 void InitMovDir(int x, int y)
2729 {
2730   int i, element = Feld[x][y];
2731   static int xy[4][2] =
2732   {
2733     {  0, +1 },
2734     { +1,  0 },
2735     {  0, -1 },
2736     { -1,  0 }
2737   };
2738   static int direction[3][4] =
2739   {
2740     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
2741     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
2742     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
2743   };
2744
2745   switch(element)
2746   {
2747     case EL_BUG_RIGHT:
2748     case EL_BUG_UP:
2749     case EL_BUG_LEFT:
2750     case EL_BUG_DOWN:
2751       Feld[x][y] = EL_BUG;
2752       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2753       break;
2754
2755     case EL_SPACESHIP_RIGHT:
2756     case EL_SPACESHIP_UP:
2757     case EL_SPACESHIP_LEFT:
2758     case EL_SPACESHIP_DOWN:
2759       Feld[x][y] = EL_SPACESHIP;
2760       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2761       break;
2762
2763     case EL_BD_BUTTERFLY_RIGHT:
2764     case EL_BD_BUTTERFLY_UP:
2765     case EL_BD_BUTTERFLY_LEFT:
2766     case EL_BD_BUTTERFLY_DOWN:
2767       Feld[x][y] = EL_BD_BUTTERFLY;
2768       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2769       break;
2770
2771     case EL_BD_FIREFLY_RIGHT:
2772     case EL_BD_FIREFLY_UP:
2773     case EL_BD_FIREFLY_LEFT:
2774     case EL_BD_FIREFLY_DOWN:
2775       Feld[x][y] = EL_BD_FIREFLY;
2776       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2777       break;
2778
2779     case EL_PACMAN_RIGHT:
2780     case EL_PACMAN_UP:
2781     case EL_PACMAN_LEFT:
2782     case EL_PACMAN_DOWN:
2783       Feld[x][y] = EL_PACMAN;
2784       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2785       break;
2786
2787     case EL_YAMYAM_LEFT:
2788     case EL_YAMYAM_RIGHT:
2789     case EL_YAMYAM_UP:
2790     case EL_YAMYAM_DOWN:
2791       Feld[x][y] = EL_YAMYAM;
2792       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2793       break;
2794
2795     case EL_SP_SNIKSNAK:
2796       MovDir[x][y] = MV_UP;
2797       break;
2798
2799     case EL_SP_ELECTRON:
2800       MovDir[x][y] = MV_LEFT;
2801       break;
2802
2803     case EL_MOLE_LEFT:
2804     case EL_MOLE_RIGHT:
2805     case EL_MOLE_UP:
2806     case EL_MOLE_DOWN:
2807       Feld[x][y] = EL_MOLE;
2808       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2809       break;
2810
2811     default:
2812       if (IS_CUSTOM_ELEMENT(element))
2813       {
2814         struct ElementInfo *ei = &element_info[element];
2815         int move_direction_initial = ei->move_direction_initial;
2816         int move_pattern = ei->move_pattern;
2817
2818         if (move_direction_initial == MV_START_PREVIOUS)
2819         {
2820           if (MovDir[x][y] != MV_NONE)
2821             return;
2822
2823           move_direction_initial = MV_START_AUTOMATIC;
2824         }
2825
2826         if (move_direction_initial == MV_START_RANDOM)
2827           MovDir[x][y] = 1 << RND(4);
2828         else if (move_direction_initial & MV_ANY_DIRECTION)
2829           MovDir[x][y] = move_direction_initial;
2830         else if (move_pattern == MV_ALL_DIRECTIONS ||
2831                  move_pattern == MV_TURNING_LEFT ||
2832                  move_pattern == MV_TURNING_RIGHT ||
2833                  move_pattern == MV_TURNING_LEFT_RIGHT ||
2834                  move_pattern == MV_TURNING_RIGHT_LEFT ||
2835                  move_pattern == MV_TURNING_RANDOM)
2836           MovDir[x][y] = 1 << RND(4);
2837         else if (move_pattern == MV_HORIZONTAL)
2838           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2839         else if (move_pattern == MV_VERTICAL)
2840           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2841         else if (move_pattern & MV_ANY_DIRECTION)
2842           MovDir[x][y] = element_info[element].move_pattern;
2843         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2844                  move_pattern == MV_ALONG_RIGHT_SIDE)
2845         {
2846           /* use random direction as default start direction */
2847           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2848             MovDir[x][y] = 1 << RND(4);
2849
2850           for (i = 0; i < NUM_DIRECTIONS; i++)
2851           {
2852             int x1 = x + xy[i][0];
2853             int y1 = y + xy[i][1];
2854
2855             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2856             {
2857               if (move_pattern == MV_ALONG_RIGHT_SIDE)
2858                 MovDir[x][y] = direction[0][i];
2859               else
2860                 MovDir[x][y] = direction[1][i];
2861
2862               break;
2863             }
2864           }
2865         }                
2866       }
2867       else
2868       {
2869         MovDir[x][y] = 1 << RND(4);
2870
2871         if (element != EL_BUG &&
2872             element != EL_SPACESHIP &&
2873             element != EL_BD_BUTTERFLY &&
2874             element != EL_BD_FIREFLY)
2875           break;
2876
2877         for (i = 0; i < NUM_DIRECTIONS; i++)
2878         {
2879           int x1 = x + xy[i][0];
2880           int y1 = y + xy[i][1];
2881
2882           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2883           {
2884             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2885             {
2886               MovDir[x][y] = direction[0][i];
2887               break;
2888             }
2889             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2890                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2891             {
2892               MovDir[x][y] = direction[1][i];
2893               break;
2894             }
2895           }
2896         }
2897       }
2898       break;
2899   }
2900
2901   GfxDir[x][y] = MovDir[x][y];
2902 }
2903
2904 void InitAmoebaNr(int x, int y)
2905 {
2906   int i;
2907   int group_nr = AmoebeNachbarNr(x, y);
2908
2909   if (group_nr == 0)
2910   {
2911     for (i = 1; i < MAX_NUM_AMOEBA; i++)
2912     {
2913       if (AmoebaCnt[i] == 0)
2914       {
2915         group_nr = i;
2916         break;
2917       }
2918     }
2919   }
2920
2921   AmoebaNr[x][y] = group_nr;
2922   AmoebaCnt[group_nr]++;
2923   AmoebaCnt2[group_nr]++;
2924 }
2925
2926 #if USE_NEW_GAME_WON
2927
2928 void GameWon()
2929 {
2930   static boolean score_done = FALSE;
2931   static boolean player_done = FALSE;
2932   static int game_over_delay = 0;
2933   int game_over_delay_value = 50;
2934
2935   /* do not start end game actions before the player stops moving (to exit) */
2936   if (local_player->MovPos)
2937     return;
2938
2939   if (tape.auto_play)           /* tape might already be stopped here */
2940     tape.auto_play_level_solved = TRUE;
2941
2942   if (!local_player->LevelSolved_GameEnd)
2943   {
2944     local_player->LevelSolved_GameEnd = TRUE;
2945     local_player->LevelSolved_SaveTape = tape.recording;
2946     local_player->LevelSolved_SaveScore = !tape.playing;
2947
2948     score_done = FALSE;
2949     player_done = FALSE;
2950     game_over_delay = 0;
2951   }
2952
2953   PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2954
2955   if (TimeLeft > 0)
2956   {
2957     if (!tape.playing)
2958     {
2959       if (setup.sound_loops)
2960         PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2961                      SND_CTRL_PLAY_LOOP);
2962       else
2963         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2964     }
2965
2966     if (TimeLeft > 100 && TimeLeft % 10 == 0)
2967     {
2968       TimeLeft -= 10;
2969       RaiseScore(level.score[SC_TIME_BONUS] * 10);
2970     }
2971     else
2972     {
2973       TimeLeft--;
2974       RaiseScore(level.score[SC_TIME_BONUS]);
2975     }
2976
2977     DrawGameValue_Time(TimeLeft);
2978
2979 #if 0
2980     if (!tape.playing)
2981       Delay(10);
2982 #endif
2983
2984     if (TimeLeft <= 0 && !tape.playing && setup.sound_loops)
2985       StopSound(SND_GAME_LEVELTIME_BONUS);
2986   }
2987   else if (level.time == 0 && TimePlayed < 999) /* level without time limit */
2988   {
2989     if (!tape.playing)
2990     {
2991       if (setup.sound_loops)
2992         PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2993                      SND_CTRL_PLAY_LOOP);
2994       else
2995         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2996     }
2997
2998     if (TimePlayed < 900 && TimePlayed % 10 == 0)
2999     {
3000       TimePlayed += 10;
3001       RaiseScore(level.score[SC_TIME_BONUS] * 10);
3002     }
3003     else
3004     {
3005       TimePlayed++;
3006       RaiseScore(level.score[SC_TIME_BONUS]);
3007     }
3008
3009     DrawGameValue_Time(TimePlayed);
3010
3011     if (TimePlayed >= 999 && !tape.playing && setup.sound_loops)
3012       StopSound(SND_GAME_LEVELTIME_BONUS);
3013   }
3014   else
3015   {
3016     score_done = TRUE;
3017   }
3018
3019   /* close exit door after last player */
3020   if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
3021       (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3022        Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
3023   {
3024     int element = Feld[ExitX][ExitY];
3025
3026     Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3027                           EL_SP_EXIT_CLOSING);
3028
3029     PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3030   }
3031
3032   /* player disappears */
3033   if (ExitX >= 0 && ExitY >= 0 && !player_done)
3034   {
3035     DrawLevelField(ExitX, ExitY);
3036
3037     player_done = TRUE;
3038   }
3039
3040   game_over_delay++;
3041
3042   if (game_over_delay < game_over_delay_value || !score_done)
3043     return;
3044 }
3045
3046 void GameEnd()
3047 {
3048   int hi_pos;
3049   boolean raise_level = FALSE;
3050
3051   CloseDoor(DOOR_CLOSE_1);
3052
3053   if (local_player->LevelSolved_SaveTape)
3054   {
3055     TapeStop();
3056
3057     SaveTape(tape.level_nr);            /* Ask to save tape */
3058   }
3059
3060   if (!local_player->LevelSolved_SaveScore)
3061   {
3062     FadeOut(REDRAW_FIELD);
3063
3064     game_status = GAME_MODE_MAIN;
3065
3066     DrawAndFadeInMainMenu(REDRAW_FIELD);
3067
3068     return;
3069   }
3070
3071   if (level_nr == leveldir_current->handicap_level)
3072   {
3073     leveldir_current->handicap_level++;
3074     SaveLevelSetup_SeriesInfo();
3075   }
3076
3077   if (level_editor_test_game)
3078     local_player->score = -1;   /* no highscore when playing from editor */
3079   else if (level_nr < leveldir_current->last_level)
3080     raise_level = TRUE;         /* advance to next level */
3081
3082   if ((hi_pos = NewHiScore()) >= 0) 
3083   {
3084     game_status = GAME_MODE_SCORES;
3085
3086     DrawHallOfFame(hi_pos);
3087
3088     if (raise_level)
3089     {
3090       level_nr++;
3091       TapeErase();
3092     }
3093   }
3094   else
3095   {
3096     FadeOut(REDRAW_FIELD);
3097
3098     game_status = GAME_MODE_MAIN;
3099
3100     if (raise_level)
3101     {
3102       level_nr++;
3103       TapeErase();
3104     }
3105
3106     DrawAndFadeInMainMenu(REDRAW_FIELD);
3107   }
3108
3109   local_player->LevelSolved_SaveScore = FALSE;
3110 }
3111
3112 #else
3113
3114 void GameWon()
3115 {
3116   int hi_pos;
3117   boolean raise_level = FALSE;
3118
3119   if (local_player->MovPos)
3120     return;
3121
3122   if (tape.auto_play)           /* tape might already be stopped here */
3123     tape.auto_play_level_solved = TRUE;
3124
3125   local_player->LevelSolved = FALSE;
3126
3127   PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
3128
3129   if (TimeLeft)
3130   {
3131     if (!tape.playing && setup.sound_loops)
3132       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
3133                    SND_CTRL_PLAY_LOOP);
3134
3135     while (TimeLeft > 0)
3136     {
3137       if (!tape.playing && !setup.sound_loops)
3138         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
3139
3140       if (TimeLeft > 100 && TimeLeft % 10 == 0)
3141       {
3142         TimeLeft -= 10;
3143         RaiseScore(level.score[SC_TIME_BONUS] * 10);
3144       }
3145       else
3146       {
3147         TimeLeft--;
3148         RaiseScore(level.score[SC_TIME_BONUS]);
3149       }
3150
3151       DrawGameValue_Time(TimeLeft);
3152
3153       BackToFront();
3154
3155       if (!tape.playing)
3156         Delay(10);
3157     }
3158
3159     if (!tape.playing && setup.sound_loops)
3160       StopSound(SND_GAME_LEVELTIME_BONUS);
3161   }
3162   else if (level.time == 0)             /* level without time limit */
3163   {
3164     if (!tape.playing && setup.sound_loops)
3165       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
3166                    SND_CTRL_PLAY_LOOP);
3167
3168     while (TimePlayed < 999)
3169     {
3170       if (!tape.playing && !setup.sound_loops)
3171         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
3172
3173       if (TimePlayed < 900 && TimePlayed % 10 == 0)
3174       {
3175         TimePlayed += 10;
3176         RaiseScore(level.score[SC_TIME_BONUS] * 10);
3177       }
3178       else
3179       {
3180         TimePlayed++;
3181         RaiseScore(level.score[SC_TIME_BONUS]);
3182       }
3183
3184       DrawGameValue_Time(TimePlayed);
3185
3186       BackToFront();
3187
3188       if (!tape.playing)
3189         Delay(10);
3190     }
3191
3192     if (!tape.playing && setup.sound_loops)
3193       StopSound(SND_GAME_LEVELTIME_BONUS);
3194   }
3195
3196   /* close exit door after last player */
3197   if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
3198       (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3199        Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
3200   {
3201     int element = Feld[ExitX][ExitY];
3202
3203     Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3204                           EL_SP_EXIT_CLOSING);
3205
3206     PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3207   }
3208
3209   /* player disappears */
3210   if (ExitX >= 0 && ExitY >= 0)
3211     DrawLevelField(ExitX, ExitY);
3212
3213   BackToFront();
3214
3215 #if 0
3216   if (tape.playing)
3217     printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
3218   else
3219     printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
3220 #endif
3221
3222   if (tape.playing)
3223     return;
3224
3225   CloseDoor(DOOR_CLOSE_1);
3226
3227   if (tape.recording)
3228   {
3229     TapeStop();
3230     SaveTape(tape.level_nr);            /* Ask to save tape */
3231   }
3232
3233   if (level_nr == leveldir_current->handicap_level)
3234   {
3235     leveldir_current->handicap_level++;
3236     SaveLevelSetup_SeriesInfo();
3237   }
3238
3239   if (level_editor_test_game)
3240     local_player->score = -1;   /* no highscore when playing from editor */
3241   else if (level_nr < leveldir_current->last_level)
3242     raise_level = TRUE;         /* advance to next level */
3243
3244   if ((hi_pos = NewHiScore()) >= 0) 
3245   {
3246     game_status = GAME_MODE_SCORES;
3247     DrawHallOfFame(hi_pos);
3248     if (raise_level)
3249     {
3250       level_nr++;
3251       TapeErase();
3252     }
3253   }
3254   else
3255   {
3256     game_status = GAME_MODE_MAIN;
3257     if (raise_level)
3258     {
3259       level_nr++;
3260       TapeErase();
3261     }
3262     DrawMainMenu();
3263   }
3264
3265   BackToFront();
3266 }
3267
3268 #endif
3269
3270 int NewHiScore()
3271 {
3272   int k, l;
3273   int position = -1;
3274
3275   LoadScore(level_nr);
3276
3277   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3278       local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3279     return -1;
3280
3281   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3282   {
3283     if (local_player->score > highscore[k].Score)
3284     {
3285       /* player has made it to the hall of fame */
3286
3287       if (k < MAX_SCORE_ENTRIES - 1)
3288       {
3289         int m = MAX_SCORE_ENTRIES - 1;
3290
3291 #ifdef ONE_PER_NAME
3292         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3293           if (strEqual(setup.player_name, highscore[l].Name))
3294             m = l;
3295         if (m == k)     /* player's new highscore overwrites his old one */
3296           goto put_into_list;
3297 #endif
3298
3299         for (l = m; l > k; l--)
3300         {
3301           strcpy(highscore[l].Name, highscore[l - 1].Name);
3302           highscore[l].Score = highscore[l - 1].Score;
3303         }
3304       }
3305
3306 #ifdef ONE_PER_NAME
3307       put_into_list:
3308 #endif
3309       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3310       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3311       highscore[k].Score = local_player->score; 
3312       position = k;
3313       break;
3314     }
3315
3316 #ifdef ONE_PER_NAME
3317     else if (!strncmp(setup.player_name, highscore[k].Name,
3318                       MAX_PLAYER_NAME_LEN))
3319       break;    /* player already there with a higher score */
3320 #endif
3321
3322   }
3323
3324   if (position >= 0) 
3325     SaveScore(level_nr);
3326
3327   return position;
3328 }
3329
3330 inline static int getElementMoveStepsize(int x, int y)
3331 {
3332   int element = Feld[x][y];
3333   int direction = MovDir[x][y];
3334   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3335   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3336   int horiz_move = (dx != 0);
3337   int sign = (horiz_move ? dx : dy);
3338   int step = sign * element_info[element].move_stepsize;
3339
3340   /* special values for move stepsize for spring and things on conveyor belt */
3341   if (horiz_move)
3342   {
3343 #if 0
3344     if (element == EL_SPRING)
3345       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3346     else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3347              y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3348       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3349 #else
3350     if (CAN_FALL(element) &&
3351         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3352       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3353     else if (element == EL_SPRING)
3354       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3355 #endif
3356   }
3357
3358   return step;
3359 }
3360
3361 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3362 {
3363   if (player->GfxAction != action || player->GfxDir != dir)
3364   {
3365 #if 0
3366     printf("Player frame reset! (%d => %d, %d => %d)\n",
3367            player->GfxAction, action, player->GfxDir, dir);
3368 #endif
3369
3370     player->GfxAction = action;
3371     player->GfxDir = dir;
3372     player->Frame = 0;
3373     player->StepFrame = 0;
3374   }
3375 }
3376
3377 #if USE_GFX_RESET_GFX_ANIMATION
3378 static void ResetGfxFrame(int x, int y, boolean redraw)
3379 {
3380   int element = Feld[x][y];
3381   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3382   int last_gfx_frame = GfxFrame[x][y];
3383
3384   if (graphic_info[graphic].anim_global_sync)
3385     GfxFrame[x][y] = FrameCounter;
3386   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3387     GfxFrame[x][y] = CustomValue[x][y];
3388   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3389     GfxFrame[x][y] = element_info[element].collect_score;
3390   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3391     GfxFrame[x][y] = ChangeDelay[x][y];
3392
3393   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3394     DrawLevelGraphicAnimation(x, y, graphic);
3395 }
3396 #endif
3397
3398 static void ResetGfxAnimation(int x, int y)
3399 {
3400 #if 0
3401   int element, graphic;
3402 #endif
3403
3404   GfxAction[x][y] = ACTION_DEFAULT;
3405   GfxDir[x][y] = MovDir[x][y];
3406   GfxFrame[x][y] = 0;
3407
3408 #if 0
3409   element = Feld[x][y];
3410   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3411
3412   if (graphic_info[graphic].anim_global_sync)
3413     GfxFrame[x][y] = FrameCounter;
3414   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3415     GfxFrame[x][y] = CustomValue[x][y];
3416   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3417     GfxFrame[x][y] = element_info[element].collect_score;
3418   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3419     GfxFrame[x][y] = ChangeDelay[x][y];
3420 #endif
3421
3422 #if USE_GFX_RESET_GFX_ANIMATION
3423   ResetGfxFrame(x, y, FALSE);
3424 #endif
3425 }
3426
3427 static void ResetRandomAnimationValue(int x, int y)
3428 {
3429   GfxRandom[x][y] = INIT_GFX_RANDOM();
3430 }
3431
3432 void InitMovingField(int x, int y, int direction)
3433 {
3434   int element = Feld[x][y];
3435 #if 0
3436   int graphic;
3437 #endif
3438   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3439   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3440   int newx = x + dx;
3441   int newy = y + dy;
3442
3443   if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3444     ResetGfxAnimation(x, y);
3445
3446   MovDir[x][y] = direction;
3447   GfxDir[x][y] = direction;
3448   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3449                      ACTION_FALLING : ACTION_MOVING);
3450
3451 #if 0
3452   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3453
3454   if (graphic_info[graphic].anim_global_sync)
3455     GfxFrame[x][y] = FrameCounter;
3456   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3457     GfxFrame[x][y] = CustomValue[x][y];
3458   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3459     GfxFrame[x][y] = element_info[element].collect_score;
3460   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3461     GfxFrame[x][y] = ChangeDelay[x][y];
3462 #endif
3463
3464   /* this is needed for CEs with property "can move" / "not moving" */
3465
3466   if (getElementMoveStepsize(x, y) != 0)        /* moving or being moved */
3467   {
3468     if (Feld[newx][newy] == EL_EMPTY)
3469       Feld[newx][newy] = EL_BLOCKED;
3470
3471     MovDir[newx][newy] = MovDir[x][y];
3472
3473 #if USE_NEW_CUSTOM_VALUE
3474     CustomValue[newx][newy] = CustomValue[x][y];
3475 #endif
3476
3477     GfxFrame[newx][newy] = GfxFrame[x][y];
3478     GfxRandom[newx][newy] = GfxRandom[x][y];
3479     GfxAction[newx][newy] = GfxAction[x][y];
3480     GfxDir[newx][newy] = GfxDir[x][y];
3481   }
3482 }
3483
3484 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3485 {
3486   int direction = MovDir[x][y];
3487 #if 1
3488   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3489   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3490 #else
3491   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3492   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3493 #endif
3494
3495   *goes_to_x = newx;
3496   *goes_to_y = newy;
3497 }
3498
3499 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3500 {
3501   int oldx = x, oldy = y;
3502   int direction = MovDir[x][y];
3503
3504   if (direction == MV_LEFT)
3505     oldx++;
3506   else if (direction == MV_RIGHT)
3507     oldx--;
3508   else if (direction == MV_UP)
3509     oldy++;
3510   else if (direction == MV_DOWN)
3511     oldy--;
3512
3513   *comes_from_x = oldx;
3514   *comes_from_y = oldy;
3515 }
3516
3517 int MovingOrBlocked2Element(int x, int y)
3518 {
3519   int element = Feld[x][y];
3520
3521   if (element == EL_BLOCKED)
3522   {
3523     int oldx, oldy;
3524
3525     Blocked2Moving(x, y, &oldx, &oldy);
3526     return Feld[oldx][oldy];
3527   }
3528   else
3529     return element;
3530 }
3531
3532 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3533 {
3534   /* like MovingOrBlocked2Element(), but if element is moving
3535      and (x,y) is the field the moving element is just leaving,
3536      return EL_BLOCKED instead of the element value */
3537   int element = Feld[x][y];
3538
3539   if (IS_MOVING(x, y))
3540   {
3541     if (element == EL_BLOCKED)
3542     {
3543       int oldx, oldy;
3544
3545       Blocked2Moving(x, y, &oldx, &oldy);
3546       return Feld[oldx][oldy];
3547     }
3548     else
3549       return EL_BLOCKED;
3550   }
3551   else
3552     return element;
3553 }
3554
3555 static void RemoveField(int x, int y)
3556 {
3557   Feld[x][y] = EL_EMPTY;
3558
3559   MovPos[x][y] = 0;
3560   MovDir[x][y] = 0;
3561   MovDelay[x][y] = 0;
3562
3563 #if USE_NEW_CUSTOM_VALUE
3564   CustomValue[x][y] = 0;
3565 #endif
3566
3567   AmoebaNr[x][y] = 0;
3568   ChangeDelay[x][y] = 0;
3569   ChangePage[x][y] = -1;
3570   Pushed[x][y] = FALSE;
3571
3572 #if 0
3573   ExplodeField[x][y] = EX_TYPE_NONE;
3574 #endif
3575
3576   GfxElement[x][y] = EL_UNDEFINED;
3577   GfxAction[x][y] = ACTION_DEFAULT;
3578   GfxDir[x][y] = MV_NONE;
3579 }
3580
3581 void RemoveMovingField(int x, int y)
3582 {
3583   int oldx = x, oldy = y, newx = x, newy = y;
3584   int element = Feld[x][y];
3585   int next_element = EL_UNDEFINED;
3586
3587   if (element != EL_BLOCKED && !IS_MOVING(x, y))
3588     return;
3589
3590   if (IS_MOVING(x, y))
3591   {
3592     Moving2Blocked(x, y, &newx, &newy);
3593
3594     if (Feld[newx][newy] != EL_BLOCKED)
3595     {
3596       /* element is moving, but target field is not free (blocked), but
3597          already occupied by something different (example: acid pool);
3598          in this case, only remove the moving field, but not the target */
3599
3600       RemoveField(oldx, oldy);
3601
3602       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3603
3604       DrawLevelField(oldx, oldy);
3605
3606       return;
3607     }
3608   }
3609   else if (element == EL_BLOCKED)
3610   {
3611     Blocked2Moving(x, y, &oldx, &oldy);
3612     if (!IS_MOVING(oldx, oldy))
3613       return;
3614   }
3615
3616   if (element == EL_BLOCKED &&
3617       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3618        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3619        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3620        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3621     next_element = get_next_element(Feld[oldx][oldy]);
3622
3623   RemoveField(oldx, oldy);
3624   RemoveField(newx, newy);
3625
3626   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3627
3628   if (next_element != EL_UNDEFINED)
3629     Feld[oldx][oldy] = next_element;
3630
3631   DrawLevelField(oldx, oldy);
3632   DrawLevelField(newx, newy);
3633 }
3634
3635 void DrawDynamite(int x, int y)
3636 {
3637   int sx = SCREENX(x), sy = SCREENY(y);
3638   int graphic = el2img(Feld[x][y]);
3639   int frame;
3640
3641   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3642     return;
3643
3644   if (IS_WALKABLE_INSIDE(Back[x][y]))
3645     return;
3646
3647   if (Back[x][y])
3648     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3649   else if (Store[x][y])
3650     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3651
3652   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3653
3654   if (Back[x][y] || Store[x][y])
3655     DrawGraphicThruMask(sx, sy, graphic, frame);
3656   else
3657     DrawGraphic(sx, sy, graphic, frame);
3658 }
3659
3660 void CheckDynamite(int x, int y)
3661 {
3662   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
3663   {
3664     MovDelay[x][y]--;
3665
3666     if (MovDelay[x][y] != 0)
3667     {
3668       DrawDynamite(x, y);
3669       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3670
3671       return;
3672     }
3673   }
3674
3675   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3676
3677   Bang(x, y);
3678 }
3679
3680 #if 1
3681
3682 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3683 {
3684   boolean num_checked_players = 0;
3685   int i;
3686
3687   for (i = 0; i < MAX_PLAYERS; i++)
3688   {
3689     if (stored_player[i].active)
3690     {
3691       int sx = stored_player[i].jx;
3692       int sy = stored_player[i].jy;
3693
3694       if (num_checked_players == 0)
3695       {
3696         *sx1 = *sx2 = sx;
3697         *sy1 = *sy2 = sy;
3698       }
3699       else
3700       {
3701         *sx1 = MIN(*sx1, sx);
3702         *sy1 = MIN(*sy1, sy);
3703         *sx2 = MAX(*sx2, sx);
3704         *sy2 = MAX(*sy2, sy);
3705       }
3706
3707       num_checked_players++;
3708     }
3709   }
3710 }
3711
3712 static boolean checkIfAllPlayersFitToScreen_RND()
3713 {
3714   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3715
3716   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3717
3718   return (sx2 - sx1 < SCR_FIELDX &&
3719           sy2 - sy1 < SCR_FIELDY);
3720 }
3721
3722 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3723 {
3724   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3725
3726   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3727
3728   *sx = (sx1 + sx2) / 2;
3729   *sy = (sy1 + sy2) / 2;
3730 }
3731
3732 #if 0
3733 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3734                                               int center_x, int center_y)
3735 {
3736   int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3737
3738   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3739
3740   *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3741   *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3742 }
3743
3744 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3745 {
3746   int max_dx, max_dy;
3747
3748   setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3749
3750   return (max_dx <= SCR_FIELDX / 2 &&
3751           max_dy <= SCR_FIELDY / 2);
3752 }
3753 #endif
3754
3755 #endif
3756
3757 #if 1
3758
3759 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3760                         boolean quick_relocation)
3761 {
3762   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3763   boolean no_delay = (tape.warp_forward);
3764   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3765   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3766
3767   if (quick_relocation)
3768   {
3769     int offset = (setup.scroll_delay ? 3 : 0);
3770
3771 #if 0
3772     if (center_screen)
3773       offset = 0;
3774 #endif
3775
3776     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3777     {
3778       scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3779                   x > SBX_Right + MIDPOSX ? SBX_Right :
3780                   x - MIDPOSX);
3781
3782       scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3783                   y > SBY_Lower + MIDPOSY ? SBY_Lower :
3784                   y - MIDPOSY);
3785     }
3786     else
3787     {
3788       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
3789           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3790         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3791
3792       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
3793           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3794         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3795
3796       /* don't scroll over playfield boundaries */
3797       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3798         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3799
3800       /* don't scroll over playfield boundaries */
3801       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3802         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3803     }
3804
3805     RedrawPlayfield(TRUE, 0,0,0,0);
3806   }
3807   else
3808   {
3809     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3810                      x > SBX_Right + MIDPOSX ? SBX_Right :
3811                      x - MIDPOSX);
3812
3813     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3814                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
3815                      y - MIDPOSY);
3816
3817     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3818
3819     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3820     {
3821       int dx = 0, dy = 0;
3822       int fx = FX, fy = FY;
3823
3824       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3825       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3826
3827       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3828         break;
3829
3830       scroll_x -= dx;
3831       scroll_y -= dy;
3832
3833       fx += dx * TILEX / 2;
3834       fy += dy * TILEY / 2;
3835
3836       ScrollLevel(dx, dy);
3837       DrawAllPlayers();
3838
3839       /* scroll in two steps of half tile size to make things smoother */
3840       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3841       FlushDisplay();
3842       Delay(wait_delay_value);
3843
3844       /* scroll second step to align at full tile size */
3845       BackToFront();
3846       Delay(wait_delay_value);
3847     }
3848
3849     DrawAllPlayers();
3850     BackToFront();
3851     Delay(wait_delay_value);
3852   }
3853 }
3854
3855 #else
3856
3857 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3858 {
3859   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3860   boolean no_delay = (tape.warp_forward);
3861   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3862   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3863   int jx = player->jx;
3864   int jy = player->jy;
3865
3866   if (quick_relocation)
3867   {
3868     int offset = (setup.scroll_delay ? 3 : 0);
3869
3870     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3871     {
3872       scroll_x = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3873                   player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3874                   player->jx - MIDPOSX);
3875
3876       scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3877                   player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3878                   player->jy - MIDPOSY);
3879     }
3880     else
3881     {
3882       if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
3883           (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3884         scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3885
3886       if ((player->MovDir == MV_UP  && scroll_y > jy - MIDPOSY + offset) ||
3887           (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3888         scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3889
3890       /* don't scroll over playfield boundaries */
3891       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3892         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3893
3894       /* don't scroll over playfield boundaries */
3895       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3896         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3897     }
3898
3899     RedrawPlayfield(TRUE, 0,0,0,0);
3900   }
3901   else
3902   {
3903     int scroll_xx = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3904                      player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3905                      player->jx - MIDPOSX);
3906
3907     int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3908                      player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3909                      player->jy - MIDPOSY);
3910
3911     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3912
3913     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3914     {
3915       int dx = 0, dy = 0;
3916       int fx = FX, fy = FY;
3917
3918       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3919       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3920
3921       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3922         break;
3923
3924       scroll_x -= dx;
3925       scroll_y -= dy;
3926
3927       fx += dx * TILEX / 2;
3928       fy += dy * TILEY / 2;
3929
3930       ScrollLevel(dx, dy);
3931       DrawAllPlayers();
3932
3933       /* scroll in two steps of half tile size to make things smoother */
3934       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3935       FlushDisplay();
3936       Delay(wait_delay_value);
3937
3938       /* scroll second step to align at full tile size */
3939       BackToFront();
3940       Delay(wait_delay_value);
3941     }
3942
3943     DrawPlayer(player);
3944     BackToFront();
3945     Delay(wait_delay_value);
3946   }
3947 }
3948
3949 #endif
3950
3951 void RelocatePlayer(int jx, int jy, int el_player_raw)
3952 {
3953   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3954   int player_nr = GET_PLAYER_NR(el_player);
3955   struct PlayerInfo *player = &stored_player[player_nr];
3956   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3957   boolean no_delay = (tape.warp_forward);
3958   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3959   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3960   int old_jx = player->jx;
3961   int old_jy = player->jy;
3962   int old_element = Feld[old_jx][old_jy];
3963   int element = Feld[jx][jy];
3964   boolean player_relocated = (old_jx != jx || old_jy != jy);
3965
3966   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3967   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
3968   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3969   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
3970   int leave_side_horiz = move_dir_horiz;
3971   int leave_side_vert  = move_dir_vert;
3972   int enter_side = enter_side_horiz | enter_side_vert;
3973   int leave_side = leave_side_horiz | leave_side_vert;
3974
3975   if (player->GameOver)         /* do not reanimate dead player */
3976     return;
3977
3978   if (!player_relocated)        /* no need to relocate the player */
3979     return;
3980
3981   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
3982   {
3983     RemoveField(jx, jy);        /* temporarily remove newly placed player */
3984     DrawLevelField(jx, jy);
3985   }
3986
3987   if (player->present)
3988   {
3989     while (player->MovPos)
3990     {
3991       ScrollPlayer(player, SCROLL_GO_ON);
3992       ScrollScreen(NULL, SCROLL_GO_ON);
3993
3994       AdvanceFrameAndPlayerCounters(player->index_nr);
3995
3996       DrawPlayer(player);
3997
3998       BackToFront();
3999       Delay(wait_delay_value);
4000     }
4001
4002     DrawPlayer(player);         /* needed here only to cleanup last field */
4003     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4004
4005     player->is_moving = FALSE;
4006   }
4007
4008   if (IS_CUSTOM_ELEMENT(old_element))
4009     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4010                                CE_LEFT_BY_PLAYER,
4011                                player->index_bit, leave_side);
4012
4013   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4014                                       CE_PLAYER_LEAVES_X,
4015                                       player->index_bit, leave_side);
4016
4017   Feld[jx][jy] = el_player;
4018   InitPlayerField(jx, jy, el_player, TRUE);
4019
4020   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4021   {
4022     Feld[jx][jy] = element;
4023     InitField(jx, jy, FALSE);
4024   }
4025
4026 #if 1
4027   /* only visually relocate centered player */
4028 #if 1
4029   DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
4030                      level.instant_relocation);
4031 #else
4032   if (player->index_nr == game.centered_player_nr)
4033     DrawRelocatePlayer(player, level.instant_relocation);
4034 #endif
4035 #else
4036   if (player == local_player)   /* only visually relocate local player */
4037     DrawRelocatePlayer(player, level.instant_relocation);
4038 #endif
4039
4040   TestIfPlayerTouchesBadThing(jx, jy);
4041   TestIfPlayerTouchesCustomElement(jx, jy);
4042
4043   if (IS_CUSTOM_ELEMENT(element))
4044     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4045                                player->index_bit, enter_side);
4046
4047   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4048                                       player->index_bit, enter_side);
4049 }
4050
4051 void Explode(int ex, int ey, int phase, int mode)
4052 {
4053   int x, y;
4054   int last_phase;
4055   int border_element;
4056
4057   /* !!! eliminate this variable !!! */
4058   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4059
4060   if (game.explosions_delayed)
4061   {
4062     ExplodeField[ex][ey] = mode;
4063     return;
4064   }
4065
4066   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4067   {
4068     int center_element = Feld[ex][ey];
4069     int artwork_element, explosion_element;     /* set these values later */
4070
4071 #if 0
4072     /* --- This is only really needed (and now handled) in "Impact()". --- */
4073     /* do not explode moving elements that left the explode field in time */
4074     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4075         center_element == EL_EMPTY &&
4076         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4077       return;
4078 #endif
4079
4080 #if 0
4081     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4082     if (mode == EX_TYPE_NORMAL ||
4083         mode == EX_TYPE_CENTER ||
4084         mode == EX_TYPE_CROSS)
4085       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4086 #endif
4087
4088     /* remove things displayed in background while burning dynamite */
4089     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4090       Back[ex][ey] = 0;
4091
4092     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4093     {
4094       /* put moving element to center field (and let it explode there) */
4095       center_element = MovingOrBlocked2Element(ex, ey);
4096       RemoveMovingField(ex, ey);
4097       Feld[ex][ey] = center_element;
4098     }
4099
4100     /* now "center_element" is finally determined -- set related values now */
4101     artwork_element = center_element;           /* for custom player artwork */
4102     explosion_element = center_element;         /* for custom player artwork */
4103
4104     if (IS_PLAYER(ex, ey))
4105     {
4106       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4107
4108       artwork_element = stored_player[player_nr].artwork_element;
4109
4110       if (level.use_explosion_element[player_nr])
4111       {
4112         explosion_element = level.explosion_element[player_nr];
4113         artwork_element = explosion_element;
4114       }
4115     }
4116
4117 #if 1
4118     if (mode == EX_TYPE_NORMAL ||
4119         mode == EX_TYPE_CENTER ||
4120         mode == EX_TYPE_CROSS)
4121       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4122 #endif
4123
4124 #if 1
4125     last_phase = element_info[explosion_element].explosion_delay + 1;
4126 #else
4127     last_phase = element_info[center_element].explosion_delay + 1;
4128 #endif
4129
4130     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4131     {
4132       int xx = x - ex + 1;
4133       int yy = y - ey + 1;
4134       int element;
4135
4136       if (!IN_LEV_FIELD(x, y) ||
4137           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4138           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4139         continue;
4140
4141       element = Feld[x][y];
4142
4143       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4144       {
4145         element = MovingOrBlocked2Element(x, y);
4146
4147         if (!IS_EXPLOSION_PROOF(element))
4148           RemoveMovingField(x, y);
4149       }
4150
4151       /* indestructible elements can only explode in center (but not flames) */
4152       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4153                                            mode == EX_TYPE_BORDER)) ||
4154           element == EL_FLAMES)
4155         continue;
4156
4157       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4158          behaviour, for example when touching a yamyam that explodes to rocks
4159          with active deadly shield, a rock is created under the player !!! */
4160       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4161 #if 0
4162       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4163           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4164            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4165 #else
4166       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4167 #endif
4168       {
4169         if (IS_ACTIVE_BOMB(element))
4170         {
4171           /* re-activate things under the bomb like gate or penguin */
4172           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4173           Back[x][y] = 0;
4174         }
4175
4176         continue;
4177       }
4178
4179       /* save walkable background elements while explosion on same tile */
4180       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4181           (x != ex || y != ey || mode == EX_TYPE_BORDER))
4182         Back[x][y] = element;
4183
4184       /* ignite explodable elements reached by other explosion */
4185       if (element == EL_EXPLOSION)
4186         element = Store2[x][y];
4187
4188       if (AmoebaNr[x][y] &&
4189           (element == EL_AMOEBA_FULL ||
4190            element == EL_BD_AMOEBA ||
4191            element == EL_AMOEBA_GROWING))
4192       {
4193         AmoebaCnt[AmoebaNr[x][y]]--;
4194         AmoebaCnt2[AmoebaNr[x][y]]--;
4195       }
4196
4197       RemoveField(x, y);
4198
4199       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4200       {
4201 #if 1
4202         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4203
4204         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4205 #else
4206         switch(StorePlayer[ex][ey])
4207         {
4208           case EL_PLAYER_2:
4209             Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
4210             break;
4211           case EL_PLAYER_3:
4212             Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
4213             break;
4214           case EL_PLAYER_4:
4215             Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
4216             break;
4217           case EL_PLAYER_1:
4218           default:
4219             Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
4220             break;
4221         }
4222 #endif
4223
4224         if (PLAYERINFO(ex, ey)->use_murphy)
4225           Store[x][y] = EL_EMPTY;
4226       }
4227 #if 1
4228       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4229          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4230       else if (ELEM_IS_PLAYER(center_element))
4231         Store[x][y] = EL_EMPTY;
4232       else if (center_element == EL_YAMYAM)
4233         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4234       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4235         Store[x][y] = element_info[center_element].content.e[xx][yy];
4236 #if 1
4237       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4238          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4239          otherwise) -- FIX THIS !!! */
4240       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4241         Store[x][y] = element_info[element].content.e[1][1];
4242 #else
4243       else if (!CAN_EXPLODE(element))
4244         Store[x][y] = element_info[element].content.e[1][1];
4245 #endif
4246       else
4247         Store[x][y] = EL_EMPTY;
4248 #else
4249       else if (center_element == EL_MOLE)
4250         Store[x][y] = EL_EMERALD_RED;
4251       else if (center_element == EL_PENGUIN)
4252         Store[x][y] = EL_EMERALD_PURPLE;
4253       else if (center_element == EL_BUG)
4254         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
4255       else if (center_element == EL_BD_BUTTERFLY)
4256         Store[x][y] = EL_BD_DIAMOND;
4257       else if (center_element == EL_SP_ELECTRON)
4258         Store[x][y] = EL_SP_INFOTRON;
4259       else if (center_element == EL_AMOEBA_TO_DIAMOND)
4260         Store[x][y] = level.amoeba_content;
4261       else if (center_element == EL_YAMYAM)
4262         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4263       else if (IS_CUSTOM_ELEMENT(center_element) &&
4264                element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4265         Store[x][y] = element_info[center_element].content.e[xx][yy];
4266       else if (element == EL_WALL_EMERALD)
4267         Store[x][y] = EL_EMERALD;
4268       else if (element == EL_WALL_DIAMOND)
4269         Store[x][y] = EL_DIAMOND;
4270       else if (element == EL_WALL_BD_DIAMOND)
4271         Store[x][y] = EL_BD_DIAMOND;
4272       else if (element == EL_WALL_EMERALD_YELLOW)
4273         Store[x][y] = EL_EMERALD_YELLOW;
4274       else if (element == EL_WALL_EMERALD_RED)
4275         Store[x][y] = EL_EMERALD_RED;
4276       else if (element == EL_WALL_EMERALD_PURPLE)
4277         Store[x][y] = EL_EMERALD_PURPLE;
4278       else if (element == EL_WALL_PEARL)
4279         Store[x][y] = EL_PEARL;
4280       else if (element == EL_WALL_CRYSTAL)
4281         Store[x][y] = EL_CRYSTAL;
4282       else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
4283         Store[x][y] = element_info[element].content.e[1][1];
4284       else
4285         Store[x][y] = EL_EMPTY;
4286 #endif
4287
4288       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4289           center_element == EL_AMOEBA_TO_DIAMOND)
4290         Store2[x][y] = element;
4291
4292       Feld[x][y] = EL_EXPLOSION;
4293       GfxElement[x][y] = artwork_element;
4294
4295 #if 0
4296       printf(":: setting gfx(%d,%d) to %d ['%s']\n",
4297              x, y, artwork_element, EL_NAME(artwork_element));
4298 #endif
4299
4300       ExplodePhase[x][y] = 1;
4301       ExplodeDelay[x][y] = last_phase;
4302
4303       Stop[x][y] = TRUE;
4304     }
4305
4306     if (center_element == EL_YAMYAM)
4307       game.yamyam_content_nr =
4308         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4309
4310     return;
4311   }
4312
4313   if (Stop[ex][ey])
4314     return;
4315
4316   x = ex;
4317   y = ey;
4318
4319   if (phase == 1)
4320     GfxFrame[x][y] = 0;         /* restart explosion animation */
4321
4322   last_phase = ExplodeDelay[x][y];
4323
4324   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4325
4326 #ifdef DEBUG
4327
4328   /* activate this even in non-DEBUG version until cause for crash in
4329      getGraphicAnimationFrame() (see below) is found and eliminated */
4330
4331 #endif
4332 #if 1
4333
4334 #if 1
4335   /* this can happen if the player leaves an explosion just in time */
4336   if (GfxElement[x][y] == EL_UNDEFINED)
4337     GfxElement[x][y] = EL_EMPTY;
4338 #else
4339   if (GfxElement[x][y] == EL_UNDEFINED)
4340   {
4341     printf("\n\n");
4342     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4343     printf("Explode(): This should never happen!\n");
4344     printf("\n\n");
4345
4346     GfxElement[x][y] = EL_EMPTY;
4347   }
4348 #endif
4349
4350 #endif
4351
4352   border_element = Store2[x][y];
4353   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4354     border_element = StorePlayer[x][y];
4355
4356   if (phase == element_info[border_element].ignition_delay ||
4357       phase == last_phase)
4358   {
4359     boolean border_explosion = FALSE;
4360
4361     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4362         !PLAYER_EXPLOSION_PROTECTED(x, y))
4363     {
4364       KillPlayerUnlessExplosionProtected(x, y);
4365       border_explosion = TRUE;
4366     }
4367     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4368     {
4369       Feld[x][y] = Store2[x][y];
4370       Store2[x][y] = 0;
4371       Bang(x, y);
4372       border_explosion = TRUE;
4373     }
4374     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4375     {
4376       AmoebeUmwandeln(x, y);
4377       Store2[x][y] = 0;
4378       border_explosion = TRUE;
4379     }
4380
4381     /* if an element just explodes due to another explosion (chain-reaction),
4382        do not immediately end the new explosion when it was the last frame of
4383        the explosion (as it would be done in the following "if"-statement!) */
4384     if (border_explosion && phase == last_phase)
4385       return;
4386   }
4387
4388   if (phase == last_phase)
4389   {
4390     int element;
4391
4392     element = Feld[x][y] = Store[x][y];
4393     Store[x][y] = Store2[x][y] = 0;
4394     GfxElement[x][y] = EL_UNDEFINED;
4395
4396     /* player can escape from explosions and might therefore be still alive */
4397     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4398         element <= EL_PLAYER_IS_EXPLODING_4)
4399     {
4400       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4401       int explosion_element = EL_PLAYER_1 + player_nr;
4402       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4403       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4404
4405       if (level.use_explosion_element[player_nr])
4406         explosion_element = level.explosion_element[player_nr];
4407
4408       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4409                     element_info[explosion_element].content.e[xx][yy]);
4410     }
4411
4412     /* restore probably existing indestructible background element */
4413     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4414       element = Feld[x][y] = Back[x][y];
4415     Back[x][y] = 0;
4416
4417     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4418     GfxDir[x][y] = MV_NONE;
4419     ChangeDelay[x][y] = 0;
4420     ChangePage[x][y] = -1;
4421
4422 #if USE_NEW_CUSTOM_VALUE
4423     CustomValue[x][y] = 0;
4424 #endif
4425
4426     InitField_WithBug2(x, y, FALSE);
4427
4428     DrawLevelField(x, y);
4429
4430     TestIfElementTouchesCustomElement(x, y);
4431
4432     if (GFX_CRUMBLED(element))
4433       DrawLevelFieldCrumbledSandNeighbours(x, y);
4434
4435     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4436       StorePlayer[x][y] = 0;
4437
4438     if (ELEM_IS_PLAYER(element))
4439       RelocatePlayer(x, y, element);
4440   }
4441   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4442   {
4443     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4444     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4445
4446     if (phase == delay)
4447       DrawLevelFieldCrumbledSand(x, y);
4448
4449     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4450     {
4451       DrawLevelElement(x, y, Back[x][y]);
4452       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4453     }
4454     else if (IS_WALKABLE_UNDER(Back[x][y]))
4455     {
4456       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4457       DrawLevelElementThruMask(x, y, Back[x][y]);
4458     }
4459     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4460       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4461   }
4462 }
4463
4464 void DynaExplode(int ex, int ey)
4465 {
4466   int i, j;
4467   int dynabomb_element = Feld[ex][ey];
4468   int dynabomb_size = 1;
4469   boolean dynabomb_xl = FALSE;
4470   struct PlayerInfo *player;
4471   static int xy[4][2] =
4472   {
4473     { 0, -1 },
4474     { -1, 0 },
4475     { +1, 0 },
4476     { 0, +1 }
4477   };
4478
4479   if (IS_ACTIVE_BOMB(dynabomb_element))
4480   {
4481     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4482     dynabomb_size = player->dynabomb_size;
4483     dynabomb_xl = player->dynabomb_xl;
4484     player->dynabombs_left++;
4485   }
4486
4487   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4488
4489   for (i = 0; i < NUM_DIRECTIONS; i++)
4490   {
4491     for (j = 1; j <= dynabomb_size; j++)
4492     {
4493       int x = ex + j * xy[i][0];
4494       int y = ey + j * xy[i][1];
4495       int element;
4496
4497       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4498         break;
4499
4500       element = Feld[x][y];
4501
4502       /* do not restart explosions of fields with active bombs */
4503       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4504         continue;
4505
4506       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4507
4508       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4509           !IS_DIGGABLE(element) && !dynabomb_xl)
4510         break;
4511     }
4512   }
4513 }
4514
4515 void Bang(int x, int y)
4516 {
4517   int element = MovingOrBlocked2Element(x, y);
4518   int explosion_type = EX_TYPE_NORMAL;
4519
4520   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4521   {
4522     struct PlayerInfo *player = PLAYERINFO(x, y);
4523
4524     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4525                             player->element_nr);
4526
4527     if (level.use_explosion_element[player->index_nr])
4528     {
4529       int explosion_element = level.explosion_element[player->index_nr];
4530
4531       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4532         explosion_type = EX_TYPE_CROSS;
4533       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4534         explosion_type = EX_TYPE_CENTER;
4535     }
4536   }
4537
4538   switch(element)
4539   {
4540     case EL_BUG:
4541     case EL_SPACESHIP:
4542     case EL_BD_BUTTERFLY:
4543     case EL_BD_FIREFLY:
4544     case EL_YAMYAM:
4545     case EL_DARK_YAMYAM:
4546     case EL_ROBOT:
4547     case EL_PACMAN:
4548     case EL_MOLE:
4549       RaiseScoreElement(element);
4550       break;
4551
4552     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4553     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4554     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4555     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4556     case EL_DYNABOMB_INCREASE_NUMBER:
4557     case EL_DYNABOMB_INCREASE_SIZE:
4558     case EL_DYNABOMB_INCREASE_POWER:
4559       explosion_type = EX_TYPE_DYNA;
4560       break;
4561
4562     case EL_PENGUIN:
4563     case EL_LAMP:
4564     case EL_LAMP_ACTIVE:
4565     case EL_AMOEBA_TO_DIAMOND:
4566       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4567         explosion_type = EX_TYPE_CENTER;
4568       break;
4569
4570     default:
4571       if (element_info[element].explosion_type == EXPLODES_CROSS)
4572         explosion_type = EX_TYPE_CROSS;
4573       else if (element_info[element].explosion_type == EXPLODES_1X1)
4574         explosion_type = EX_TYPE_CENTER;
4575       break;
4576   }
4577
4578   if (explosion_type == EX_TYPE_DYNA)
4579     DynaExplode(x, y);
4580   else
4581     Explode(x, y, EX_PHASE_START, explosion_type);
4582
4583   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4584 }
4585
4586 void SplashAcid(int x, int y)
4587 {
4588   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4589       (!IN_LEV_FIELD(x - 1, y - 2) ||
4590        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4591     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4592
4593   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4594       (!IN_LEV_FIELD(x + 1, y - 2) ||
4595        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4596     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4597
4598   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4599 }
4600
4601 static void InitBeltMovement()
4602 {
4603   static int belt_base_element[4] =
4604   {
4605     EL_CONVEYOR_BELT_1_LEFT,
4606     EL_CONVEYOR_BELT_2_LEFT,
4607     EL_CONVEYOR_BELT_3_LEFT,
4608     EL_CONVEYOR_BELT_4_LEFT
4609   };
4610   static int belt_base_active_element[4] =
4611   {
4612     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4613     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4614     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4615     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4616   };
4617
4618   int x, y, i, j;
4619
4620   /* set frame order for belt animation graphic according to belt direction */
4621   for (i = 0; i < NUM_BELTS; i++)
4622   {
4623     int belt_nr = i;
4624
4625     for (j = 0; j < NUM_BELT_PARTS; j++)
4626     {
4627       int element = belt_base_active_element[belt_nr] + j;
4628       int graphic = el2img(element);
4629
4630       if (game.belt_dir[i] == MV_LEFT)
4631         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4632       else
4633         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4634     }
4635   }
4636
4637 #if 1
4638   SCAN_PLAYFIELD(x, y)
4639 #else
4640   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4641 #endif
4642   {
4643     int element = Feld[x][y];
4644
4645     for (i = 0; i < NUM_BELTS; i++)
4646     {
4647       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4648       {
4649         int e_belt_nr = getBeltNrFromBeltElement(element);
4650         int belt_nr = i;
4651
4652         if (e_belt_nr == belt_nr)
4653         {
4654           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4655
4656           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4657         }
4658       }
4659     }
4660   }
4661 }
4662
4663 static void ToggleBeltSwitch(int x, int y)
4664 {
4665   static int belt_base_element[4] =
4666   {
4667     EL_CONVEYOR_BELT_1_LEFT,
4668     EL_CONVEYOR_BELT_2_LEFT,
4669     EL_CONVEYOR_BELT_3_LEFT,
4670     EL_CONVEYOR_BELT_4_LEFT
4671   };
4672   static int belt_base_active_element[4] =
4673   {
4674     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4675     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4676     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4677     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4678   };
4679   static int belt_base_switch_element[4] =
4680   {
4681     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4682     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4683     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4684     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4685   };
4686   static int belt_move_dir[4] =
4687   {
4688     MV_LEFT,
4689     MV_NONE,
4690     MV_RIGHT,
4691     MV_NONE,
4692   };
4693
4694   int element = Feld[x][y];
4695   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4696   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4697   int belt_dir = belt_move_dir[belt_dir_nr];
4698   int xx, yy, i;
4699
4700   if (!IS_BELT_SWITCH(element))
4701     return;
4702
4703   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4704   game.belt_dir[belt_nr] = belt_dir;
4705
4706   if (belt_dir_nr == 3)
4707     belt_dir_nr = 1;
4708
4709   /* set frame order for belt animation graphic according to belt direction */
4710   for (i = 0; i < NUM_BELT_PARTS; i++)
4711   {
4712     int element = belt_base_active_element[belt_nr] + i;
4713     int graphic = el2img(element);
4714
4715     if (belt_dir == MV_LEFT)
4716       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4717     else
4718       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4719   }
4720
4721 #if 1
4722   SCAN_PLAYFIELD(xx, yy)
4723 #else
4724   for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4725 #endif
4726   {
4727     int element = Feld[xx][yy];
4728
4729     if (IS_BELT_SWITCH(element))
4730     {
4731       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4732
4733       if (e_belt_nr == belt_nr)
4734       {
4735         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4736         DrawLevelField(xx, yy);
4737       }
4738     }
4739     else if (IS_BELT(element) && belt_dir != MV_NONE)
4740     {
4741       int e_belt_nr = getBeltNrFromBeltElement(element);
4742
4743       if (e_belt_nr == belt_nr)
4744       {
4745         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4746
4747         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4748         DrawLevelField(xx, yy);
4749       }
4750     }
4751     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4752     {
4753       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4754
4755       if (e_belt_nr == belt_nr)
4756       {
4757         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4758
4759         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4760         DrawLevelField(xx, yy);
4761       }
4762     }
4763   }
4764 }
4765
4766 static void ToggleSwitchgateSwitch(int x, int y)
4767 {
4768   int xx, yy;
4769
4770   game.switchgate_pos = !game.switchgate_pos;
4771
4772 #if 1
4773   SCAN_PLAYFIELD(xx, yy)
4774 #else
4775   for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4776 #endif
4777   {
4778     int element = Feld[xx][yy];
4779
4780 #if !USE_BOTH_SWITCHGATE_SWITCHES
4781     if (element == EL_SWITCHGATE_SWITCH_UP ||
4782         element == EL_SWITCHGATE_SWITCH_DOWN)
4783     {
4784       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4785       DrawLevelField(xx, yy);
4786     }
4787 #else
4788     if (element == EL_SWITCHGATE_SWITCH_UP)
4789     {
4790       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4791       DrawLevelField(xx, yy);
4792     }
4793     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4794     {
4795       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4796       DrawLevelField(xx, yy);
4797     }
4798 #endif
4799     else if (element == EL_SWITCHGATE_OPEN ||
4800              element == EL_SWITCHGATE_OPENING)
4801     {
4802       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4803
4804       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4805     }
4806     else if (element == EL_SWITCHGATE_CLOSED ||
4807              element == EL_SWITCHGATE_CLOSING)
4808     {
4809       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4810
4811       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4812     }
4813   }
4814 }
4815
4816 static int getInvisibleActiveFromInvisibleElement(int element)
4817 {
4818   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4819           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
4820           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
4821           element);
4822 }
4823
4824 static int getInvisibleFromInvisibleActiveElement(int element)
4825 {
4826   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4827           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
4828           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
4829           element);
4830 }
4831
4832 static void RedrawAllLightSwitchesAndInvisibleElements()
4833 {
4834   int x, y;
4835
4836 #if 1
4837   SCAN_PLAYFIELD(x, y)
4838 #else
4839   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4840 #endif
4841   {
4842     int element = Feld[x][y];
4843
4844     if (element == EL_LIGHT_SWITCH &&
4845         game.light_time_left > 0)
4846     {
4847       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4848       DrawLevelField(x, y);
4849     }
4850     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4851              game.light_time_left == 0)
4852     {
4853       Feld[x][y] = EL_LIGHT_SWITCH;
4854       DrawLevelField(x, y);
4855     }
4856     else if (element == EL_EMC_DRIPPER &&
4857              game.light_time_left > 0)
4858     {
4859       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4860       DrawLevelField(x, y);
4861     }
4862     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4863              game.light_time_left == 0)
4864     {
4865       Feld[x][y] = EL_EMC_DRIPPER;
4866       DrawLevelField(x, y);
4867     }
4868     else if (element == EL_INVISIBLE_STEELWALL ||
4869              element == EL_INVISIBLE_WALL ||
4870              element == EL_INVISIBLE_SAND)
4871     {
4872       if (game.light_time_left > 0)
4873         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4874
4875       DrawLevelField(x, y);
4876
4877       /* uncrumble neighbour fields, if needed */
4878       if (element == EL_INVISIBLE_SAND)
4879         DrawLevelFieldCrumbledSandNeighbours(x, y);
4880     }
4881     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4882              element == EL_INVISIBLE_WALL_ACTIVE ||
4883              element == EL_INVISIBLE_SAND_ACTIVE)
4884     {
4885       if (game.light_time_left == 0)
4886         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4887
4888       DrawLevelField(x, y);
4889
4890       /* re-crumble neighbour fields, if needed */
4891       if (element == EL_INVISIBLE_SAND)
4892         DrawLevelFieldCrumbledSandNeighbours(x, y);
4893     }
4894   }
4895 }
4896
4897 static void RedrawAllInvisibleElementsForLenses()
4898 {
4899   int x, y;
4900
4901 #if 1
4902   SCAN_PLAYFIELD(x, y)
4903 #else
4904   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4905 #endif
4906   {
4907     int element = Feld[x][y];
4908
4909     if (element == EL_EMC_DRIPPER &&
4910         game.lenses_time_left > 0)
4911     {
4912       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4913       DrawLevelField(x, y);
4914     }
4915     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4916              game.lenses_time_left == 0)
4917     {
4918       Feld[x][y] = EL_EMC_DRIPPER;
4919       DrawLevelField(x, y);
4920     }
4921     else if (element == EL_INVISIBLE_STEELWALL ||
4922              element == EL_INVISIBLE_WALL ||
4923              element == EL_INVISIBLE_SAND)
4924     {
4925       if (game.lenses_time_left > 0)
4926         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4927
4928       DrawLevelField(x, y);
4929
4930       /* uncrumble neighbour fields, if needed */
4931       if (element == EL_INVISIBLE_SAND)
4932         DrawLevelFieldCrumbledSandNeighbours(x, y);
4933     }
4934     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4935              element == EL_INVISIBLE_WALL_ACTIVE ||
4936              element == EL_INVISIBLE_SAND_ACTIVE)
4937     {
4938       if (game.lenses_time_left == 0)
4939         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4940
4941       DrawLevelField(x, y);
4942
4943       /* re-crumble neighbour fields, if needed */
4944       if (element == EL_INVISIBLE_SAND)
4945         DrawLevelFieldCrumbledSandNeighbours(x, y);
4946     }
4947   }
4948 }
4949
4950 static void RedrawAllInvisibleElementsForMagnifier()
4951 {
4952   int x, y;
4953
4954 #if 1
4955   SCAN_PLAYFIELD(x, y)
4956 #else
4957   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4958 #endif
4959   {
4960     int element = Feld[x][y];
4961
4962     if (element == EL_EMC_FAKE_GRASS &&
4963         game.magnify_time_left > 0)
4964     {
4965       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4966       DrawLevelField(x, y);
4967     }
4968     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4969              game.magnify_time_left == 0)
4970     {
4971       Feld[x][y] = EL_EMC_FAKE_GRASS;
4972       DrawLevelField(x, y);
4973     }
4974     else if (IS_GATE_GRAY(element) &&
4975              game.magnify_time_left > 0)
4976     {
4977       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4978                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4979                     IS_EM_GATE_GRAY(element) ?
4980                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4981                     IS_EMC_GATE_GRAY(element) ?
4982                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4983                     element);
4984       DrawLevelField(x, y);
4985     }
4986     else if (IS_GATE_GRAY_ACTIVE(element) &&
4987              game.magnify_time_left == 0)
4988     {
4989       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4990                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4991                     IS_EM_GATE_GRAY_ACTIVE(element) ?
4992                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4993                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
4994                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4995                     element);
4996       DrawLevelField(x, y);
4997     }
4998   }
4999 }
5000
5001 static void ToggleLightSwitch(int x, int y)
5002 {
5003   int element = Feld[x][y];
5004
5005   game.light_time_left =
5006     (element == EL_LIGHT_SWITCH ?
5007      level.time_light * FRAMES_PER_SECOND : 0);
5008
5009   RedrawAllLightSwitchesAndInvisibleElements();
5010 }
5011
5012 static void ActivateTimegateSwitch(int x, int y)
5013 {
5014   int xx, yy;
5015
5016   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5017
5018 #if 1
5019   SCAN_PLAYFIELD(xx, yy)
5020 #else
5021   for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
5022 #endif
5023   {
5024     int element = Feld[xx][yy];
5025
5026     if (element == EL_TIMEGATE_CLOSED ||
5027         element == EL_TIMEGATE_CLOSING)
5028     {
5029       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5030       PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
5031     }
5032
5033     /*
5034     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5035     {
5036       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5037       DrawLevelField(xx, yy);
5038     }
5039     */
5040
5041   }
5042
5043   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5044 }
5045
5046 void Impact(int x, int y)
5047 {
5048   boolean last_line = (y == lev_fieldy - 1);
5049   boolean object_hit = FALSE;
5050   boolean impact = (last_line || object_hit);
5051   int element = Feld[x][y];
5052   int smashed = EL_STEELWALL;
5053
5054   if (!last_line)       /* check if element below was hit */
5055   {
5056     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5057       return;
5058
5059     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5060                                          MovDir[x][y + 1] != MV_DOWN ||
5061                                          MovPos[x][y + 1] <= TILEY / 2));
5062
5063     /* do not smash moving elements that left the smashed field in time */
5064     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5065         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5066       object_hit = FALSE;
5067
5068 #if USE_QUICKSAND_IMPACT_BUGFIX
5069     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5070     {
5071       RemoveMovingField(x, y + 1);
5072       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5073       Feld[x][y + 2] = EL_ROCK;
5074       DrawLevelField(x, y + 2);
5075
5076       object_hit = TRUE;
5077     }
5078 #endif
5079
5080     if (object_hit)
5081       smashed = MovingOrBlocked2Element(x, y + 1);
5082
5083     impact = (last_line || object_hit);
5084   }
5085
5086   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5087   {
5088     SplashAcid(x, y + 1);
5089     return;
5090   }
5091
5092   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5093   /* only reset graphic animation if graphic really changes after impact */
5094   if (impact &&
5095       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5096   {
5097     ResetGfxAnimation(x, y);
5098     DrawLevelField(x, y);
5099   }
5100
5101   if (impact && CAN_EXPLODE_IMPACT(element))
5102   {
5103     Bang(x, y);
5104     return;
5105   }
5106   else if (impact && element == EL_PEARL)
5107   {
5108     ResetGfxAnimation(x, y);
5109
5110     Feld[x][y] = EL_PEARL_BREAKING;
5111     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5112     return;
5113   }
5114   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5115   {
5116     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5117
5118     return;
5119   }
5120
5121   if (impact && element == EL_AMOEBA_DROP)
5122   {
5123     if (object_hit && IS_PLAYER(x, y + 1))
5124       KillPlayerUnlessEnemyProtected(x, y + 1);
5125     else if (object_hit && smashed == EL_PENGUIN)
5126       Bang(x, y + 1);
5127     else
5128     {
5129       Feld[x][y] = EL_AMOEBA_GROWING;
5130       Store[x][y] = EL_AMOEBA_WET;
5131
5132       ResetRandomAnimationValue(x, y);
5133     }
5134     return;
5135   }
5136
5137   if (object_hit)               /* check which object was hit */
5138   {
5139     if (CAN_PASS_MAGIC_WALL(element) && 
5140         (smashed == EL_MAGIC_WALL ||
5141          smashed == EL_BD_MAGIC_WALL))
5142     {
5143       int xx, yy;
5144       int activated_magic_wall =
5145         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5146          EL_BD_MAGIC_WALL_ACTIVE);
5147
5148       /* activate magic wall / mill */
5149 #if 1
5150       SCAN_PLAYFIELD(xx, yy)
5151 #else
5152       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
5153 #endif
5154         if (Feld[xx][yy] == smashed)
5155           Feld[xx][yy] = activated_magic_wall;
5156
5157       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5158       game.magic_wall_active = TRUE;
5159
5160       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5161                             SND_MAGIC_WALL_ACTIVATING :
5162                             SND_BD_MAGIC_WALL_ACTIVATING));
5163     }
5164
5165     if (IS_PLAYER(x, y + 1))
5166     {
5167       if (CAN_SMASH_PLAYER(element))
5168       {
5169         KillPlayerUnlessEnemyProtected(x, y + 1);
5170         return;
5171       }
5172     }
5173     else if (smashed == EL_PENGUIN)
5174     {
5175       if (CAN_SMASH_PLAYER(element))
5176       {
5177         Bang(x, y + 1);
5178         return;
5179       }
5180     }
5181     else if (element == EL_BD_DIAMOND)
5182     {
5183       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5184       {
5185         Bang(x, y + 1);
5186         return;
5187       }
5188     }
5189     else if (((element == EL_SP_INFOTRON ||
5190                element == EL_SP_ZONK) &&
5191               (smashed == EL_SP_SNIKSNAK ||
5192                smashed == EL_SP_ELECTRON ||
5193                smashed == EL_SP_DISK_ORANGE)) ||
5194              (element == EL_SP_INFOTRON &&
5195               smashed == EL_SP_DISK_YELLOW))
5196     {
5197       Bang(x, y + 1);
5198       return;
5199     }
5200     else if (CAN_SMASH_EVERYTHING(element))
5201     {
5202       if (IS_CLASSIC_ENEMY(smashed) ||
5203           CAN_EXPLODE_SMASHED(smashed))
5204       {
5205         Bang(x, y + 1);
5206         return;
5207       }
5208       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5209       {
5210         if (smashed == EL_LAMP ||
5211             smashed == EL_LAMP_ACTIVE)
5212         {
5213           Bang(x, y + 1);
5214           return;
5215         }
5216         else if (smashed == EL_NUT)
5217         {
5218           Feld[x][y + 1] = EL_NUT_BREAKING;
5219           PlayLevelSound(x, y, SND_NUT_BREAKING);
5220           RaiseScoreElement(EL_NUT);
5221           return;
5222         }
5223         else if (smashed == EL_PEARL)
5224         {
5225           ResetGfxAnimation(x, y);
5226
5227           Feld[x][y + 1] = EL_PEARL_BREAKING;
5228           PlayLevelSound(x, y, SND_PEARL_BREAKING);
5229           return;
5230         }
5231         else if (smashed == EL_DIAMOND)
5232         {
5233           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5234           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5235           return;
5236         }
5237         else if (IS_BELT_SWITCH(smashed))
5238         {
5239           ToggleBeltSwitch(x, y + 1);
5240         }
5241         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5242                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
5243         {
5244           ToggleSwitchgateSwitch(x, y + 1);
5245         }
5246         else if (smashed == EL_LIGHT_SWITCH ||
5247                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5248         {
5249           ToggleLightSwitch(x, y + 1);
5250         }
5251         else
5252         {
5253 #if 0
5254           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5255 #endif
5256
5257           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5258
5259           CheckElementChangeBySide(x, y + 1, smashed, element,
5260                                    CE_SWITCHED, CH_SIDE_TOP);
5261           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5262                                             CH_SIDE_TOP);
5263         }
5264       }
5265       else
5266       {
5267         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5268       }
5269     }
5270   }
5271
5272   /* play sound of magic wall / mill */
5273   if (!last_line &&
5274       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5275        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5276   {
5277     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5278       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5279     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5280       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5281
5282     return;
5283   }
5284
5285   /* play sound of object that hits the ground */
5286   if (last_line || object_hit)
5287     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5288 }
5289
5290 inline static void TurnRoundExt(int x, int y)
5291 {
5292   static struct
5293   {
5294     int dx, dy;
5295   } move_xy[] =
5296   {
5297     {  0,  0 },
5298     { -1,  0 },
5299     { +1,  0 },
5300     {  0,  0 },
5301     {  0, -1 },
5302     {  0,  0 }, { 0, 0 }, { 0, 0 },
5303     {  0, +1 }
5304   };
5305   static struct
5306   {
5307     int left, right, back;
5308   } turn[] =
5309   {
5310     { 0,        0,              0        },
5311     { MV_DOWN,  MV_UP,          MV_RIGHT },
5312     { MV_UP,    MV_DOWN,        MV_LEFT  },
5313     { 0,        0,              0        },
5314     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5315     { 0,        0,              0        },
5316     { 0,        0,              0        },
5317     { 0,        0,              0        },
5318     { MV_RIGHT, MV_LEFT,        MV_UP    }
5319   };
5320
5321   int element = Feld[x][y];
5322   int move_pattern = element_info[element].move_pattern;
5323
5324   int old_move_dir = MovDir[x][y];
5325   int left_dir  = turn[old_move_dir].left;
5326   int right_dir = turn[old_move_dir].right;
5327   int back_dir  = turn[old_move_dir].back;
5328
5329   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5330   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5331   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5332   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5333
5334   int left_x  = x + left_dx,  left_y  = y + left_dy;
5335   int right_x = x + right_dx, right_y = y + right_dy;
5336   int move_x  = x + move_dx,  move_y  = y + move_dy;
5337
5338   int xx, yy;
5339
5340   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5341   {
5342     TestIfBadThingTouchesOtherBadThing(x, y);
5343
5344     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5345       MovDir[x][y] = right_dir;
5346     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5347       MovDir[x][y] = left_dir;
5348
5349     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5350       MovDelay[x][y] = 9;
5351     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5352       MovDelay[x][y] = 1;
5353   }
5354   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5355   {
5356     TestIfBadThingTouchesOtherBadThing(x, y);
5357
5358     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5359       MovDir[x][y] = left_dir;
5360     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5361       MovDir[x][y] = right_dir;
5362
5363     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5364       MovDelay[x][y] = 9;
5365     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5366       MovDelay[x][y] = 1;
5367   }
5368   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5369   {
5370     TestIfBadThingTouchesOtherBadThing(x, y);
5371
5372     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5373       MovDir[x][y] = left_dir;
5374     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5375       MovDir[x][y] = right_dir;
5376
5377     if (MovDir[x][y] != old_move_dir)
5378       MovDelay[x][y] = 9;
5379   }
5380   else if (element == EL_YAMYAM)
5381   {
5382     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5383     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, 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_DARK_YAMYAM)
5397   {
5398     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5399                                                          left_x, left_y);
5400     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5401                                                          right_x, right_y);
5402
5403     if (can_turn_left && can_turn_right)
5404       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5405     else if (can_turn_left)
5406       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5407     else if (can_turn_right)
5408       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5409     else
5410       MovDir[x][y] = back_dir;
5411
5412     MovDelay[x][y] = 16 + 16 * RND(3);
5413   }
5414   else if (element == EL_PACMAN)
5415   {
5416     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5417     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5418
5419     if (can_turn_left && can_turn_right)
5420       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5421     else if (can_turn_left)
5422       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5423     else if (can_turn_right)
5424       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5425     else
5426       MovDir[x][y] = back_dir;
5427
5428     MovDelay[x][y] = 6 + RND(40);
5429   }
5430   else if (element == EL_PIG)
5431   {
5432     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5433     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5434     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5435     boolean should_turn_left, should_turn_right, should_move_on;
5436     int rnd_value = 24;
5437     int rnd = RND(rnd_value);
5438
5439     should_turn_left = (can_turn_left &&
5440                         (!can_move_on ||
5441                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5442                                                    y + back_dy + left_dy)));
5443     should_turn_right = (can_turn_right &&
5444                          (!can_move_on ||
5445                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5446                                                     y + back_dy + right_dy)));
5447     should_move_on = (can_move_on &&
5448                       (!can_turn_left ||
5449                        !can_turn_right ||
5450                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5451                                                  y + move_dy + left_dy) ||
5452                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5453                                                  y + move_dy + right_dy)));
5454
5455     if (should_turn_left || should_turn_right || should_move_on)
5456     {
5457       if (should_turn_left && should_turn_right && should_move_on)
5458         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5459                         rnd < 2 * rnd_value / 3 ? right_dir :
5460                         old_move_dir);
5461       else if (should_turn_left && should_turn_right)
5462         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5463       else if (should_turn_left && should_move_on)
5464         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5465       else if (should_turn_right && should_move_on)
5466         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5467       else if (should_turn_left)
5468         MovDir[x][y] = left_dir;
5469       else if (should_turn_right)
5470         MovDir[x][y] = right_dir;
5471       else if (should_move_on)
5472         MovDir[x][y] = old_move_dir;
5473     }
5474     else if (can_move_on && rnd > rnd_value / 8)
5475       MovDir[x][y] = old_move_dir;
5476     else if (can_turn_left && can_turn_right)
5477       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5478     else if (can_turn_left && rnd > rnd_value / 8)
5479       MovDir[x][y] = left_dir;
5480     else if (can_turn_right && rnd > rnd_value/8)
5481       MovDir[x][y] = right_dir;
5482     else
5483       MovDir[x][y] = back_dir;
5484
5485     xx = x + move_xy[MovDir[x][y]].dx;
5486     yy = y + move_xy[MovDir[x][y]].dy;
5487
5488     if (!IN_LEV_FIELD(xx, yy) ||
5489         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5490       MovDir[x][y] = old_move_dir;
5491
5492     MovDelay[x][y] = 0;
5493   }
5494   else if (element == EL_DRAGON)
5495   {
5496     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5497     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5498     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5499     int rnd_value = 24;
5500     int rnd = RND(rnd_value);
5501
5502     if (can_move_on && rnd > rnd_value / 8)
5503       MovDir[x][y] = old_move_dir;
5504     else if (can_turn_left && can_turn_right)
5505       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5506     else if (can_turn_left && rnd > rnd_value / 8)
5507       MovDir[x][y] = left_dir;
5508     else if (can_turn_right && rnd > rnd_value / 8)
5509       MovDir[x][y] = right_dir;
5510     else
5511       MovDir[x][y] = back_dir;
5512
5513     xx = x + move_xy[MovDir[x][y]].dx;
5514     yy = y + move_xy[MovDir[x][y]].dy;
5515
5516     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5517       MovDir[x][y] = old_move_dir;
5518
5519     MovDelay[x][y] = 0;
5520   }
5521   else if (element == EL_MOLE)
5522   {
5523     boolean can_move_on =
5524       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5525                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5526                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5527     if (!can_move_on)
5528     {
5529       boolean can_turn_left =
5530         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5531                               IS_AMOEBOID(Feld[left_x][left_y])));
5532
5533       boolean can_turn_right =
5534         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5535                               IS_AMOEBOID(Feld[right_x][right_y])));
5536
5537       if (can_turn_left && can_turn_right)
5538         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5539       else if (can_turn_left)
5540         MovDir[x][y] = left_dir;
5541       else
5542         MovDir[x][y] = right_dir;
5543     }
5544
5545     if (MovDir[x][y] != old_move_dir)
5546       MovDelay[x][y] = 9;
5547   }
5548   else if (element == EL_BALLOON)
5549   {
5550     MovDir[x][y] = game.wind_direction;
5551     MovDelay[x][y] = 0;
5552   }
5553   else if (element == EL_SPRING)
5554   {
5555 #if USE_NEW_SPRING_BUMPER
5556     if (MovDir[x][y] & MV_HORIZONTAL)
5557     {
5558       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5559           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5560       {
5561         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5562         ResetGfxAnimation(move_x, move_y);
5563         DrawLevelField(move_x, move_y);
5564
5565         MovDir[x][y] = back_dir;
5566       }
5567       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5568                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5569         MovDir[x][y] = MV_NONE;
5570     }
5571 #else
5572     if (MovDir[x][y] & MV_HORIZONTAL &&
5573         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5574          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5575       MovDir[x][y] = MV_NONE;
5576 #endif
5577
5578     MovDelay[x][y] = 0;
5579   }
5580   else if (element == EL_ROBOT ||
5581            element == EL_SATELLITE ||
5582            element == EL_PENGUIN ||
5583            element == EL_EMC_ANDROID)
5584   {
5585     int attr_x = -1, attr_y = -1;
5586
5587     if (AllPlayersGone)
5588     {
5589       attr_x = ExitX;
5590       attr_y = ExitY;
5591     }
5592     else
5593     {
5594       int i;
5595
5596       for (i = 0; i < MAX_PLAYERS; i++)
5597       {
5598         struct PlayerInfo *player = &stored_player[i];
5599         int jx = player->jx, jy = player->jy;
5600
5601         if (!player->active)
5602           continue;
5603
5604         if (attr_x == -1 ||
5605             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5606         {
5607           attr_x = jx;
5608           attr_y = jy;
5609         }
5610       }
5611     }
5612
5613     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5614         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5615          game.engine_version < VERSION_IDENT(3,1,0,0)))
5616     {
5617       attr_x = ZX;
5618       attr_y = ZY;
5619     }
5620
5621     if (element == EL_PENGUIN)
5622     {
5623       int i;
5624       static int xy[4][2] =
5625       {
5626         { 0, -1 },
5627         { -1, 0 },
5628         { +1, 0 },
5629         { 0, +1 }
5630       };
5631
5632       for (i = 0; i < NUM_DIRECTIONS; i++)
5633       {
5634         int ex = x + xy[i][0];
5635         int ey = y + xy[i][1];
5636
5637         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5638         {
5639           attr_x = ex;
5640           attr_y = ey;
5641           break;
5642         }
5643       }
5644     }
5645
5646     MovDir[x][y] = MV_NONE;
5647     if (attr_x < x)
5648       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5649     else if (attr_x > x)
5650       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5651     if (attr_y < y)
5652       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5653     else if (attr_y > y)
5654       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5655
5656     if (element == EL_ROBOT)
5657     {
5658       int newx, newy;
5659
5660       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5661         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5662       Moving2Blocked(x, y, &newx, &newy);
5663
5664       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5665         MovDelay[x][y] = 8 + 8 * !RND(3);
5666       else
5667         MovDelay[x][y] = 16;
5668     }
5669     else if (element == EL_PENGUIN)
5670     {
5671       int newx, newy;
5672
5673       MovDelay[x][y] = 1;
5674
5675       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5676       {
5677         boolean first_horiz = RND(2);
5678         int new_move_dir = MovDir[x][y];
5679
5680         MovDir[x][y] =
5681           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5682         Moving2Blocked(x, y, &newx, &newy);
5683
5684         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5685           return;
5686
5687         MovDir[x][y] =
5688           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5689         Moving2Blocked(x, y, &newx, &newy);
5690
5691         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5692           return;
5693
5694         MovDir[x][y] = old_move_dir;
5695         return;
5696       }
5697     }
5698     else if (element == EL_SATELLITE)
5699     {
5700       int newx, newy;
5701
5702       MovDelay[x][y] = 1;
5703
5704       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5705       {
5706         boolean first_horiz = RND(2);
5707         int new_move_dir = MovDir[x][y];
5708
5709         MovDir[x][y] =
5710           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5711         Moving2Blocked(x, y, &newx, &newy);
5712
5713         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5714           return;
5715
5716         MovDir[x][y] =
5717           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5718         Moving2Blocked(x, y, &newx, &newy);
5719
5720         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5721           return;
5722
5723         MovDir[x][y] = old_move_dir;
5724         return;
5725       }
5726     }
5727     else if (element == EL_EMC_ANDROID)
5728     {
5729       static int check_pos[16] =
5730       {
5731         -1,             /*  0 => (invalid)          */
5732         7,              /*  1 => MV_LEFT            */
5733         3,              /*  2 => MV_RIGHT           */
5734         -1,             /*  3 => (invalid)          */
5735         1,              /*  4 =>            MV_UP   */
5736         0,              /*  5 => MV_LEFT  | MV_UP   */
5737         2,              /*  6 => MV_RIGHT | MV_UP   */
5738         -1,             /*  7 => (invalid)          */
5739         5,              /*  8 =>            MV_DOWN */
5740         6,              /*  9 => MV_LEFT  | MV_DOWN */
5741         4,              /* 10 => MV_RIGHT | MV_DOWN */
5742         -1,             /* 11 => (invalid)          */
5743         -1,             /* 12 => (invalid)          */
5744         -1,             /* 13 => (invalid)          */
5745         -1,             /* 14 => (invalid)          */
5746         -1,             /* 15 => (invalid)          */
5747       };
5748       static struct
5749       {
5750         int dx, dy;
5751         int dir;
5752       } check_xy[8] =
5753       {
5754         { -1, -1,       MV_LEFT  | MV_UP   },
5755         {  0, -1,                  MV_UP   },
5756         { +1, -1,       MV_RIGHT | MV_UP   },
5757         { +1,  0,       MV_RIGHT           },
5758         { +1, +1,       MV_RIGHT | MV_DOWN },
5759         {  0, +1,                  MV_DOWN },
5760         { -1, +1,       MV_LEFT  | MV_DOWN },
5761         { -1,  0,       MV_LEFT            },
5762       };
5763       int start_pos, check_order;
5764       boolean can_clone = FALSE;
5765       int i;
5766
5767       /* check if there is any free field around current position */
5768       for (i = 0; i < 8; i++)
5769       {
5770         int newx = x + check_xy[i].dx;
5771         int newy = y + check_xy[i].dy;
5772
5773         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5774         {
5775           can_clone = TRUE;
5776
5777           break;
5778         }
5779       }
5780
5781       if (can_clone)            /* randomly find an element to clone */
5782       {
5783         can_clone = FALSE;
5784
5785         start_pos = check_pos[RND(8)];
5786         check_order = (RND(2) ? -1 : +1);
5787
5788         for (i = 0; i < 8; i++)
5789         {
5790           int pos_raw = start_pos + i * check_order;
5791           int pos = (pos_raw + 8) % 8;
5792           int newx = x + check_xy[pos].dx;
5793           int newy = y + check_xy[pos].dy;
5794
5795           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5796           {
5797             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5798             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5799
5800             Store[x][y] = Feld[newx][newy];
5801
5802             can_clone = TRUE;
5803
5804             break;
5805           }
5806         }
5807       }
5808
5809       if (can_clone)            /* randomly find a direction to move */
5810       {
5811         can_clone = FALSE;
5812
5813         start_pos = check_pos[RND(8)];
5814         check_order = (RND(2) ? -1 : +1);
5815
5816         for (i = 0; i < 8; i++)
5817         {
5818           int pos_raw = start_pos + i * check_order;
5819           int pos = (pos_raw + 8) % 8;
5820           int newx = x + check_xy[pos].dx;
5821           int newy = y + check_xy[pos].dy;
5822           int new_move_dir = check_xy[pos].dir;
5823
5824           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5825           {
5826             MovDir[x][y] = new_move_dir;
5827             MovDelay[x][y] = level.android_clone_time * 8 + 1;
5828
5829             can_clone = TRUE;
5830
5831             break;
5832           }
5833         }
5834       }
5835
5836       if (can_clone)            /* cloning and moving successful */
5837         return;
5838
5839       /* cannot clone -- try to move towards player */
5840
5841       start_pos = check_pos[MovDir[x][y] & 0x0f];
5842       check_order = (RND(2) ? -1 : +1);
5843
5844       for (i = 0; i < 3; i++)
5845       {
5846         /* first check start_pos, then previous/next or (next/previous) pos */
5847         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5848         int pos = (pos_raw + 8) % 8;
5849         int newx = x + check_xy[pos].dx;
5850         int newy = y + check_xy[pos].dy;
5851         int new_move_dir = check_xy[pos].dir;
5852
5853         if (IS_PLAYER(newx, newy))
5854           break;
5855
5856         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5857         {
5858           MovDir[x][y] = new_move_dir;
5859           MovDelay[x][y] = level.android_move_time * 8 + 1;
5860
5861           break;
5862         }
5863       }
5864     }
5865   }
5866   else if (move_pattern == MV_TURNING_LEFT ||
5867            move_pattern == MV_TURNING_RIGHT ||
5868            move_pattern == MV_TURNING_LEFT_RIGHT ||
5869            move_pattern == MV_TURNING_RIGHT_LEFT ||
5870            move_pattern == MV_TURNING_RANDOM ||
5871            move_pattern == MV_ALL_DIRECTIONS)
5872   {
5873     boolean can_turn_left =
5874       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5875     boolean can_turn_right =
5876       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5877
5878     if (element_info[element].move_stepsize == 0)       /* "not moving" */
5879       return;
5880
5881     if (move_pattern == MV_TURNING_LEFT)
5882       MovDir[x][y] = left_dir;
5883     else if (move_pattern == MV_TURNING_RIGHT)
5884       MovDir[x][y] = right_dir;
5885     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5886       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5887     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5888       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5889     else if (move_pattern == MV_TURNING_RANDOM)
5890       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5891                       can_turn_right && !can_turn_left ? right_dir :
5892                       RND(2) ? left_dir : right_dir);
5893     else if (can_turn_left && can_turn_right)
5894       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5895     else if (can_turn_left)
5896       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5897     else if (can_turn_right)
5898       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5899     else
5900       MovDir[x][y] = back_dir;
5901
5902     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5903   }
5904   else if (move_pattern == MV_HORIZONTAL ||
5905            move_pattern == MV_VERTICAL)
5906   {
5907     if (move_pattern & old_move_dir)
5908       MovDir[x][y] = back_dir;
5909     else if (move_pattern == MV_HORIZONTAL)
5910       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5911     else if (move_pattern == MV_VERTICAL)
5912       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5913
5914     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5915   }
5916   else if (move_pattern & MV_ANY_DIRECTION)
5917   {
5918     MovDir[x][y] = move_pattern;
5919     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5920   }
5921   else if (move_pattern & MV_WIND_DIRECTION)
5922   {
5923     MovDir[x][y] = game.wind_direction;
5924     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5925   }
5926   else if (move_pattern == MV_ALONG_LEFT_SIDE)
5927   {
5928     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5929       MovDir[x][y] = left_dir;
5930     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5931       MovDir[x][y] = right_dir;
5932
5933     if (MovDir[x][y] != old_move_dir)
5934       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5935   }
5936   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5937   {
5938     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5939       MovDir[x][y] = right_dir;
5940     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5941       MovDir[x][y] = left_dir;
5942
5943     if (MovDir[x][y] != old_move_dir)
5944       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5945   }
5946   else if (move_pattern == MV_TOWARDS_PLAYER ||
5947            move_pattern == MV_AWAY_FROM_PLAYER)
5948   {
5949     int attr_x = -1, attr_y = -1;
5950     int newx, newy;
5951     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5952
5953     if (AllPlayersGone)
5954     {
5955       attr_x = ExitX;
5956       attr_y = ExitY;
5957     }
5958     else
5959     {
5960       int i;
5961
5962       for (i = 0; i < MAX_PLAYERS; i++)
5963       {
5964         struct PlayerInfo *player = &stored_player[i];
5965         int jx = player->jx, jy = player->jy;
5966
5967         if (!player->active)
5968           continue;
5969
5970         if (attr_x == -1 ||
5971             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5972         {
5973           attr_x = jx;
5974           attr_y = jy;
5975         }
5976       }
5977     }
5978
5979     MovDir[x][y] = MV_NONE;
5980     if (attr_x < x)
5981       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5982     else if (attr_x > x)
5983       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5984     if (attr_y < y)
5985       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5986     else if (attr_y > y)
5987       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5988
5989     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5990
5991     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5992     {
5993       boolean first_horiz = RND(2);
5994       int new_move_dir = MovDir[x][y];
5995
5996       if (element_info[element].move_stepsize == 0)     /* "not moving" */
5997       {
5998         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5999         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6000
6001         return;
6002       }
6003
6004       MovDir[x][y] =
6005         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6006       Moving2Blocked(x, y, &newx, &newy);
6007
6008       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6009         return;
6010
6011       MovDir[x][y] =
6012         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6013       Moving2Blocked(x, y, &newx, &newy);
6014
6015       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6016         return;
6017
6018       MovDir[x][y] = old_move_dir;
6019     }
6020   }
6021   else if (move_pattern == MV_WHEN_PUSHED ||
6022            move_pattern == MV_WHEN_DROPPED)
6023   {
6024     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6025       MovDir[x][y] = MV_NONE;
6026
6027     MovDelay[x][y] = 0;
6028   }
6029   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6030   {
6031     static int test_xy[7][2] =
6032     {
6033       { 0, -1 },
6034       { -1, 0 },
6035       { +1, 0 },
6036       { 0, +1 },
6037       { 0, -1 },
6038       { -1, 0 },
6039       { +1, 0 },
6040     };
6041     static int test_dir[7] =
6042     {
6043       MV_UP,
6044       MV_LEFT,
6045       MV_RIGHT,
6046       MV_DOWN,
6047       MV_UP,
6048       MV_LEFT,
6049       MV_RIGHT,
6050     };
6051     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6052     int move_preference = -1000000;     /* start with very low preference */
6053     int new_move_dir = MV_NONE;
6054     int start_test = RND(4);
6055     int i;
6056
6057     for (i = 0; i < NUM_DIRECTIONS; i++)
6058     {
6059       int move_dir = test_dir[start_test + i];
6060       int move_dir_preference;
6061
6062       xx = x + test_xy[start_test + i][0];
6063       yy = y + test_xy[start_test + i][1];
6064
6065       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6066           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6067       {
6068         new_move_dir = move_dir;
6069
6070         break;
6071       }
6072
6073       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6074         continue;
6075
6076       move_dir_preference = -1 * RunnerVisit[xx][yy];
6077       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6078         move_dir_preference = PlayerVisit[xx][yy];
6079
6080       if (move_dir_preference > move_preference)
6081       {
6082         /* prefer field that has not been visited for the longest time */
6083         move_preference = move_dir_preference;
6084         new_move_dir = move_dir;
6085       }
6086       else if (move_dir_preference == move_preference &&
6087                move_dir == old_move_dir)
6088       {
6089         /* prefer last direction when all directions are preferred equally */
6090         move_preference = move_dir_preference;
6091         new_move_dir = move_dir;
6092       }
6093     }
6094
6095     MovDir[x][y] = new_move_dir;
6096     if (old_move_dir != new_move_dir)
6097       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6098   }
6099 }
6100
6101 static void TurnRound(int x, int y)
6102 {
6103   int direction = MovDir[x][y];
6104 #if 0
6105   int element, graphic;
6106 #endif
6107
6108   TurnRoundExt(x, y);
6109
6110   GfxDir[x][y] = MovDir[x][y];
6111
6112   if (direction != MovDir[x][y])
6113     GfxFrame[x][y] = 0;
6114
6115   if (MovDelay[x][y])
6116     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6117
6118 #if 1
6119   ResetGfxFrame(x, y, FALSE);
6120 #else
6121   element = Feld[x][y];
6122   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6123
6124   if (graphic_info[graphic].anim_global_sync)
6125     GfxFrame[x][y] = FrameCounter;
6126   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
6127     GfxFrame[x][y] = CustomValue[x][y];
6128   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
6129     GfxFrame[x][y] = element_info[element].collect_score;
6130   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
6131     GfxFrame[x][y] = ChangeDelay[x][y];
6132 #endif
6133 }
6134
6135 static boolean JustBeingPushed(int x, int y)
6136 {
6137   int i;
6138
6139   for (i = 0; i < MAX_PLAYERS; i++)
6140   {
6141     struct PlayerInfo *player = &stored_player[i];
6142
6143     if (player->active && player->is_pushing && player->MovPos)
6144     {
6145       int next_jx = player->jx + (player->jx - player->last_jx);
6146       int next_jy = player->jy + (player->jy - player->last_jy);
6147
6148       if (x == next_jx && y == next_jy)
6149         return TRUE;
6150     }
6151   }
6152
6153   return FALSE;
6154 }
6155
6156 void StartMoving(int x, int y)
6157 {
6158   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6159   int element = Feld[x][y];
6160
6161   if (Stop[x][y])
6162     return;
6163
6164   if (MovDelay[x][y] == 0)
6165     GfxAction[x][y] = ACTION_DEFAULT;
6166
6167   if (CAN_FALL(element) && y < lev_fieldy - 1)
6168   {
6169     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6170         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6171       if (JustBeingPushed(x, y))
6172         return;
6173
6174     if (element == EL_QUICKSAND_FULL)
6175     {
6176       if (IS_FREE(x, y + 1))
6177       {
6178         InitMovingField(x, y, MV_DOWN);
6179         started_moving = TRUE;
6180
6181         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6182 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6183         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6184           Store[x][y] = EL_ROCK;
6185 #else
6186         Store[x][y] = EL_ROCK;
6187 #endif
6188
6189         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6190       }
6191       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6192       {
6193         if (!MovDelay[x][y])
6194           MovDelay[x][y] = TILEY + 1;
6195
6196         if (MovDelay[x][y])
6197         {
6198           MovDelay[x][y]--;
6199           if (MovDelay[x][y])
6200             return;
6201         }
6202
6203         Feld[x][y] = EL_QUICKSAND_EMPTY;
6204         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6205         Store[x][y + 1] = Store[x][y];
6206         Store[x][y] = 0;
6207
6208         PlayLevelSoundAction(x, y, ACTION_FILLING);
6209       }
6210     }
6211     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6212              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6213     {
6214       InitMovingField(x, y, MV_DOWN);
6215       started_moving = TRUE;
6216
6217       Feld[x][y] = EL_QUICKSAND_FILLING;
6218       Store[x][y] = element;
6219
6220       PlayLevelSoundAction(x, y, ACTION_FILLING);
6221     }
6222     else if (element == EL_MAGIC_WALL_FULL)
6223     {
6224       if (IS_FREE(x, y + 1))
6225       {
6226         InitMovingField(x, y, MV_DOWN);
6227         started_moving = TRUE;
6228
6229         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6230         Store[x][y] = EL_CHANGED(Store[x][y]);
6231       }
6232       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6233       {
6234         if (!MovDelay[x][y])
6235           MovDelay[x][y] = TILEY/4 + 1;
6236
6237         if (MovDelay[x][y])
6238         {
6239           MovDelay[x][y]--;
6240           if (MovDelay[x][y])
6241             return;
6242         }
6243
6244         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6245         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6246         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6247         Store[x][y] = 0;
6248       }
6249     }
6250     else if (element == EL_BD_MAGIC_WALL_FULL)
6251     {
6252       if (IS_FREE(x, y + 1))
6253       {
6254         InitMovingField(x, y, MV_DOWN);
6255         started_moving = TRUE;
6256
6257         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6258         Store[x][y] = EL_CHANGED2(Store[x][y]);
6259       }
6260       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6261       {
6262         if (!MovDelay[x][y])
6263           MovDelay[x][y] = TILEY/4 + 1;
6264
6265         if (MovDelay[x][y])
6266         {
6267           MovDelay[x][y]--;
6268           if (MovDelay[x][y])
6269             return;
6270         }
6271
6272         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6273         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6274         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
6275         Store[x][y] = 0;
6276       }
6277     }
6278     else if (CAN_PASS_MAGIC_WALL(element) &&
6279              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6280               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
6281     {
6282       InitMovingField(x, y, MV_DOWN);
6283       started_moving = TRUE;
6284
6285       Feld[x][y] =
6286         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6287          EL_BD_MAGIC_WALL_FILLING);
6288       Store[x][y] = element;
6289     }
6290     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6291     {
6292       SplashAcid(x, y + 1);
6293
6294       InitMovingField(x, y, MV_DOWN);
6295       started_moving = TRUE;
6296
6297       Store[x][y] = EL_ACID;
6298     }
6299     else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6300               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6301
6302              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6303               CAN_FALL(element) && WasJustFalling[x][y] &&
6304               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6305
6306              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6307               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6308               (Feld[x][y + 1] == EL_BLOCKED)))
6309     {
6310       /* this is needed for a special case not covered by calling "Impact()"
6311          from "ContinueMoving()": if an element moves to a tile directly below
6312          another element which was just falling on that tile (which was empty
6313          in the previous frame), the falling element above would just stop
6314          instead of smashing the element below (in previous version, the above
6315          element was just checked for "moving" instead of "falling", resulting
6316          in incorrect smashes caused by horizontal movement of the above
6317          element; also, the case of the player being the element to smash was
6318          simply not covered here... :-/ ) */
6319
6320       CheckCollision[x][y] = 0;
6321
6322       Impact(x, y);
6323     }
6324     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6325     {
6326       if (MovDir[x][y] == MV_NONE)
6327       {
6328         InitMovingField(x, y, MV_DOWN);
6329         started_moving = TRUE;
6330       }
6331     }
6332     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6333     {
6334       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6335         MovDir[x][y] = MV_DOWN;
6336
6337       InitMovingField(x, y, MV_DOWN);
6338       started_moving = TRUE;
6339     }
6340     else if (element == EL_AMOEBA_DROP)
6341     {
6342       Feld[x][y] = EL_AMOEBA_GROWING;
6343       Store[x][y] = EL_AMOEBA_WET;
6344     }
6345     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6346               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6347              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6348              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6349     {
6350       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6351                                 (IS_FREE(x - 1, y + 1) ||
6352                                  Feld[x - 1][y + 1] == EL_ACID));
6353       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6354                                 (IS_FREE(x + 1, y + 1) ||
6355                                  Feld[x + 1][y + 1] == EL_ACID));
6356       boolean can_fall_any  = (can_fall_left || can_fall_right);
6357       boolean can_fall_both = (can_fall_left && can_fall_right);
6358       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6359
6360 #if USE_NEW_ALL_SLIPPERY
6361       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6362       {
6363         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6364           can_fall_right = FALSE;
6365         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6366           can_fall_left = FALSE;
6367         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6368           can_fall_right = FALSE;
6369         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6370           can_fall_left = FALSE;
6371
6372         can_fall_any  = (can_fall_left || can_fall_right);
6373         can_fall_both = FALSE;
6374       }
6375 #else
6376       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6377       {
6378         if (slippery_type == SLIPPERY_ONLY_LEFT)
6379           can_fall_right = FALSE;
6380         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6381           can_fall_left = FALSE;
6382         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6383           can_fall_right = FALSE;
6384         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6385           can_fall_left = FALSE;
6386
6387         can_fall_any  = (can_fall_left || can_fall_right);
6388         can_fall_both = (can_fall_left && can_fall_right);
6389       }
6390 #endif
6391
6392 #if USE_NEW_ALL_SLIPPERY
6393 #else
6394 #if USE_NEW_SP_SLIPPERY
6395       /* !!! better use the same properties as for custom elements here !!! */
6396       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6397                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6398       {
6399         can_fall_right = FALSE;         /* slip down on left side */
6400         can_fall_both = FALSE;
6401       }
6402 #endif
6403 #endif
6404
6405 #if USE_NEW_ALL_SLIPPERY
6406       if (can_fall_both)
6407       {
6408         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6409           can_fall_right = FALSE;       /* slip down on left side */
6410         else
6411           can_fall_left = !(can_fall_right = RND(2));
6412
6413         can_fall_both = FALSE;
6414       }
6415 #else
6416       if (can_fall_both)
6417       {
6418         if (game.emulation == EMU_BOULDERDASH ||
6419             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6420           can_fall_right = FALSE;       /* slip down on left side */
6421         else
6422           can_fall_left = !(can_fall_right = RND(2));
6423
6424         can_fall_both = FALSE;
6425       }
6426 #endif
6427
6428       if (can_fall_any)
6429       {
6430         /* if not determined otherwise, prefer left side for slipping down */
6431         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6432         started_moving = TRUE;
6433       }
6434     }
6435 #if 0
6436     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6437 #else
6438     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6439 #endif
6440     {
6441       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6442       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6443       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6444       int belt_dir = game.belt_dir[belt_nr];
6445
6446       if ((belt_dir == MV_LEFT  && left_is_free) ||
6447           (belt_dir == MV_RIGHT && right_is_free))
6448       {
6449         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6450
6451         InitMovingField(x, y, belt_dir);
6452         started_moving = TRUE;
6453
6454         Pushed[x][y] = TRUE;
6455         Pushed[nextx][y] = TRUE;
6456
6457         GfxAction[x][y] = ACTION_DEFAULT;
6458       }
6459       else
6460       {
6461         MovDir[x][y] = 0;       /* if element was moving, stop it */
6462       }
6463     }
6464   }
6465
6466   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6467 #if 0
6468   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6469 #else
6470   if (CAN_MOVE(element) && !started_moving)
6471 #endif
6472   {
6473     int move_pattern = element_info[element].move_pattern;
6474     int newx, newy;
6475
6476 #if 0
6477 #if DEBUG
6478     if (MovDir[x][y] == MV_NONE)
6479     {
6480       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6481              x, y, element, element_info[element].token_name);
6482       printf("StartMoving(): This should never happen!\n");
6483     }
6484 #endif
6485 #endif
6486
6487     Moving2Blocked(x, y, &newx, &newy);
6488
6489     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6490       return;
6491
6492     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6493         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6494     {
6495       WasJustMoving[x][y] = 0;
6496       CheckCollision[x][y] = 0;
6497
6498       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6499
6500       if (Feld[x][y] != element)        /* element has changed */
6501         return;
6502     }
6503
6504     if (!MovDelay[x][y])        /* start new movement phase */
6505     {
6506       /* all objects that can change their move direction after each step
6507          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6508
6509       if (element != EL_YAMYAM &&
6510           element != EL_DARK_YAMYAM &&
6511           element != EL_PACMAN &&
6512           !(move_pattern & MV_ANY_DIRECTION) &&
6513           move_pattern != MV_TURNING_LEFT &&
6514           move_pattern != MV_TURNING_RIGHT &&
6515           move_pattern != MV_TURNING_LEFT_RIGHT &&
6516           move_pattern != MV_TURNING_RIGHT_LEFT &&
6517           move_pattern != MV_TURNING_RANDOM)
6518       {
6519         TurnRound(x, y);
6520
6521         if (MovDelay[x][y] && (element == EL_BUG ||
6522                                element == EL_SPACESHIP ||
6523                                element == EL_SP_SNIKSNAK ||
6524                                element == EL_SP_ELECTRON ||
6525                                element == EL_MOLE))
6526           DrawLevelField(x, y);
6527       }
6528     }
6529
6530     if (MovDelay[x][y])         /* wait some time before next movement */
6531     {
6532       MovDelay[x][y]--;
6533
6534       if (element == EL_ROBOT ||
6535           element == EL_YAMYAM ||
6536           element == EL_DARK_YAMYAM)
6537       {
6538         DrawLevelElementAnimationIfNeeded(x, y, element);
6539         PlayLevelSoundAction(x, y, ACTION_WAITING);
6540       }
6541       else if (element == EL_SP_ELECTRON)
6542         DrawLevelElementAnimationIfNeeded(x, y, element);
6543       else if (element == EL_DRAGON)
6544       {
6545         int i;
6546         int dir = MovDir[x][y];
6547         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6548         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6549         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6550                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6551                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6552                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6553         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6554
6555         GfxAction[x][y] = ACTION_ATTACKING;
6556
6557         if (IS_PLAYER(x, y))
6558           DrawPlayerField(x, y);
6559         else
6560           DrawLevelField(x, y);
6561
6562         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6563
6564         for (i = 1; i <= 3; i++)
6565         {
6566           int xx = x + i * dx;
6567           int yy = y + i * dy;
6568           int sx = SCREENX(xx);
6569           int sy = SCREENY(yy);
6570           int flame_graphic = graphic + (i - 1);
6571
6572           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6573             break;
6574
6575           if (MovDelay[x][y])
6576           {
6577             int flamed = MovingOrBlocked2Element(xx, yy);
6578
6579             /* !!! */
6580 #if 0
6581             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6582               Bang(xx, yy);
6583             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6584               RemoveMovingField(xx, yy);
6585             else
6586               RemoveField(xx, yy);
6587 #else
6588             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6589               Bang(xx, yy);
6590             else
6591               RemoveMovingField(xx, yy);
6592 #endif
6593
6594             ChangeDelay[xx][yy] = 0;
6595
6596             Feld[xx][yy] = EL_FLAMES;
6597
6598             if (IN_SCR_FIELD(sx, sy))
6599             {
6600               DrawLevelFieldCrumbledSand(xx, yy);
6601               DrawGraphic(sx, sy, flame_graphic, frame);
6602             }
6603           }
6604           else
6605           {
6606             if (Feld[xx][yy] == EL_FLAMES)
6607               Feld[xx][yy] = EL_EMPTY;
6608             DrawLevelField(xx, yy);
6609           }
6610         }
6611       }
6612
6613       if (MovDelay[x][y])       /* element still has to wait some time */
6614       {
6615         PlayLevelSoundAction(x, y, ACTION_WAITING);
6616
6617         return;
6618       }
6619     }
6620
6621     /* now make next step */
6622
6623     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6624
6625     if (DONT_COLLIDE_WITH(element) &&
6626         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6627         !PLAYER_ENEMY_PROTECTED(newx, newy))
6628     {
6629       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6630
6631       return;
6632     }
6633
6634     else if (CAN_MOVE_INTO_ACID(element) &&
6635              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6636              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6637              (MovDir[x][y] == MV_DOWN ||
6638               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6639     {
6640       SplashAcid(newx, newy);
6641       Store[x][y] = EL_ACID;
6642     }
6643     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6644     {
6645       if (Feld[newx][newy] == EL_EXIT_OPEN)
6646       {
6647         RemoveField(x, y);
6648         DrawLevelField(x, y);
6649
6650         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6651         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6652           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6653
6654         local_player->friends_still_needed--;
6655         if (!local_player->friends_still_needed &&
6656             !local_player->GameOver && AllPlayersGone)
6657           local_player->LevelSolved = local_player->GameOver = TRUE;
6658
6659         return;
6660       }
6661       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6662       {
6663         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6664           DrawLevelField(newx, newy);
6665         else
6666           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6667       }
6668       else if (!IS_FREE(newx, newy))
6669       {
6670         GfxAction[x][y] = ACTION_WAITING;
6671
6672         if (IS_PLAYER(x, y))
6673           DrawPlayerField(x, y);
6674         else
6675           DrawLevelField(x, y);
6676
6677         return;
6678       }
6679     }
6680     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6681     {
6682       if (IS_FOOD_PIG(Feld[newx][newy]))
6683       {
6684         if (IS_MOVING(newx, newy))
6685           RemoveMovingField(newx, newy);
6686         else
6687         {
6688           Feld[newx][newy] = EL_EMPTY;
6689           DrawLevelField(newx, newy);
6690         }
6691
6692         PlayLevelSound(x, y, SND_PIG_DIGGING);
6693       }
6694       else if (!IS_FREE(newx, newy))
6695       {
6696         if (IS_PLAYER(x, y))
6697           DrawPlayerField(x, y);
6698         else
6699           DrawLevelField(x, y);
6700
6701         return;
6702       }
6703     }
6704     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6705     {
6706       if (Store[x][y] != EL_EMPTY)
6707       {
6708         boolean can_clone = FALSE;
6709         int xx, yy;
6710
6711         /* check if element to clone is still there */
6712         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6713         {
6714           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6715           {
6716             can_clone = TRUE;
6717
6718             break;
6719           }
6720         }
6721
6722         /* cannot clone or target field not free anymore -- do not clone */
6723         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6724           Store[x][y] = EL_EMPTY;
6725       }
6726
6727       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6728       {
6729         if (IS_MV_DIAGONAL(MovDir[x][y]))
6730         {
6731           int diagonal_move_dir = MovDir[x][y];
6732           int stored = Store[x][y];
6733           int change_delay = 8;
6734           int graphic;
6735
6736           /* android is moving diagonally */
6737
6738           CreateField(x, y, EL_DIAGONAL_SHRINKING);
6739
6740           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6741           GfxElement[x][y] = EL_EMC_ANDROID;
6742           GfxAction[x][y] = ACTION_SHRINKING;
6743           GfxDir[x][y] = diagonal_move_dir;
6744           ChangeDelay[x][y] = change_delay;
6745
6746           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6747                                    GfxDir[x][y]);
6748
6749           DrawLevelGraphicAnimation(x, y, graphic);
6750           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6751
6752           if (Feld[newx][newy] == EL_ACID)
6753           {
6754             SplashAcid(newx, newy);
6755
6756             return;
6757           }
6758
6759           CreateField(newx, newy, EL_DIAGONAL_GROWING);
6760
6761           Store[newx][newy] = EL_EMC_ANDROID;
6762           GfxElement[newx][newy] = EL_EMC_ANDROID;
6763           GfxAction[newx][newy] = ACTION_GROWING;
6764           GfxDir[newx][newy] = diagonal_move_dir;
6765           ChangeDelay[newx][newy] = change_delay;
6766
6767           graphic = el_act_dir2img(GfxElement[newx][newy],
6768                                    GfxAction[newx][newy], GfxDir[newx][newy]);
6769
6770           DrawLevelGraphicAnimation(newx, newy, graphic);
6771           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6772
6773           return;
6774         }
6775         else
6776         {
6777           Feld[newx][newy] = EL_EMPTY;
6778           DrawLevelField(newx, newy);
6779
6780           PlayLevelSoundAction(x, y, ACTION_DIGGING);
6781         }
6782       }
6783       else if (!IS_FREE(newx, newy))
6784       {
6785 #if 0
6786         if (IS_PLAYER(x, y))
6787           DrawPlayerField(x, y);
6788         else
6789           DrawLevelField(x, y);
6790 #endif
6791
6792         return;
6793       }
6794     }
6795     else if (IS_CUSTOM_ELEMENT(element) &&
6796              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6797     {
6798       int new_element = Feld[newx][newy];
6799
6800       if (!IS_FREE(newx, newy))
6801       {
6802         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6803                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6804                       ACTION_BREAKING);
6805
6806         /* no element can dig solid indestructible elements */
6807         if (IS_INDESTRUCTIBLE(new_element) &&
6808             !IS_DIGGABLE(new_element) &&
6809             !IS_COLLECTIBLE(new_element))
6810           return;
6811
6812         if (AmoebaNr[newx][newy] &&
6813             (new_element == EL_AMOEBA_FULL ||
6814              new_element == EL_BD_AMOEBA ||
6815              new_element == EL_AMOEBA_GROWING))
6816         {
6817           AmoebaCnt[AmoebaNr[newx][newy]]--;
6818           AmoebaCnt2[AmoebaNr[newx][newy]]--;
6819         }
6820
6821         if (IS_MOVING(newx, newy))
6822           RemoveMovingField(newx, newy);
6823         else
6824         {
6825           RemoveField(newx, newy);
6826           DrawLevelField(newx, newy);
6827         }
6828
6829         /* if digged element was about to explode, prevent the explosion */
6830         ExplodeField[newx][newy] = EX_TYPE_NONE;
6831
6832         PlayLevelSoundAction(x, y, action);
6833       }
6834
6835       Store[newx][newy] = EL_EMPTY;
6836 #if 1
6837       /* this makes it possible to leave the removed element again */
6838       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6839         Store[newx][newy] = new_element;
6840 #else
6841       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6842       {
6843         int move_leave_element = element_info[element].move_leave_element;
6844
6845         /* this makes it possible to leave the removed element again */
6846         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6847                              new_element : move_leave_element);
6848       }
6849 #endif
6850
6851       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6852       {
6853         RunnerVisit[x][y] = FrameCounter;
6854         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6855       }
6856     }
6857     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6858     {
6859       if (!IS_FREE(newx, newy))
6860       {
6861         if (IS_PLAYER(x, y))
6862           DrawPlayerField(x, y);
6863         else
6864           DrawLevelField(x, y);
6865
6866         return;
6867       }
6868       else
6869       {
6870         boolean wanna_flame = !RND(10);
6871         int dx = newx - x, dy = newy - y;
6872         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6873         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6874         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6875                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6876         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6877                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6878
6879         if ((wanna_flame ||
6880              IS_CLASSIC_ENEMY(element1) ||
6881              IS_CLASSIC_ENEMY(element2)) &&
6882             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6883             element1 != EL_FLAMES && element2 != EL_FLAMES)
6884         {
6885           ResetGfxAnimation(x, y);
6886           GfxAction[x][y] = ACTION_ATTACKING;
6887
6888           if (IS_PLAYER(x, y))
6889             DrawPlayerField(x, y);
6890           else
6891             DrawLevelField(x, y);
6892
6893           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6894
6895           MovDelay[x][y] = 50;
6896
6897           /* !!! */
6898 #if 0
6899           RemoveField(newx, newy);
6900 #endif
6901           Feld[newx][newy] = EL_FLAMES;
6902           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6903           {
6904 #if 0
6905             RemoveField(newx1, newy1);
6906 #endif
6907             Feld[newx1][newy1] = EL_FLAMES;
6908           }
6909           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6910           {
6911 #if 0
6912             RemoveField(newx2, newy2);
6913 #endif
6914             Feld[newx2][newy2] = EL_FLAMES;
6915           }
6916
6917           return;
6918         }
6919       }
6920     }
6921     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6922              Feld[newx][newy] == EL_DIAMOND)
6923     {
6924       if (IS_MOVING(newx, newy))
6925         RemoveMovingField(newx, newy);
6926       else
6927       {
6928         Feld[newx][newy] = EL_EMPTY;
6929         DrawLevelField(newx, newy);
6930       }
6931
6932       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6933     }
6934     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6935              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6936     {
6937       if (AmoebaNr[newx][newy])
6938       {
6939         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6940         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6941             Feld[newx][newy] == EL_BD_AMOEBA)
6942           AmoebaCnt[AmoebaNr[newx][newy]]--;
6943       }
6944
6945 #if 0
6946       /* !!! test !!! */
6947       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6948       {
6949         RemoveMovingField(newx, newy);
6950       }
6951 #else
6952       if (IS_MOVING(newx, newy))
6953       {
6954         RemoveMovingField(newx, newy);
6955       }
6956 #endif
6957       else
6958       {
6959         Feld[newx][newy] = EL_EMPTY;
6960         DrawLevelField(newx, newy);
6961       }
6962
6963       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6964     }
6965     else if ((element == EL_PACMAN || element == EL_MOLE)
6966              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6967     {
6968       if (AmoebaNr[newx][newy])
6969       {
6970         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6971         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6972             Feld[newx][newy] == EL_BD_AMOEBA)
6973           AmoebaCnt[AmoebaNr[newx][newy]]--;
6974       }
6975
6976       if (element == EL_MOLE)
6977       {
6978         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6979         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6980
6981         ResetGfxAnimation(x, y);
6982         GfxAction[x][y] = ACTION_DIGGING;
6983         DrawLevelField(x, y);
6984
6985         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6986
6987         return;                         /* wait for shrinking amoeba */
6988       }
6989       else      /* element == EL_PACMAN */
6990       {
6991         Feld[newx][newy] = EL_EMPTY;
6992         DrawLevelField(newx, newy);
6993         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6994       }
6995     }
6996     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6997              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6998               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6999     {
7000       /* wait for shrinking amoeba to completely disappear */
7001       return;
7002     }
7003     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7004     {
7005       /* object was running against a wall */
7006
7007       TurnRound(x, y);
7008
7009 #if 0
7010       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7011       if (move_pattern & MV_ANY_DIRECTION &&
7012           move_pattern == MovDir[x][y])
7013       {
7014         int blocking_element =
7015           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7016
7017         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7018                                  MovDir[x][y]);
7019
7020         element = Feld[x][y];   /* element might have changed */
7021       }
7022 #endif
7023
7024       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7025         DrawLevelElementAnimation(x, y, element);
7026
7027       if (DONT_TOUCH(element))
7028         TestIfBadThingTouchesPlayer(x, y);
7029
7030       return;
7031     }
7032
7033     InitMovingField(x, y, MovDir[x][y]);
7034
7035     PlayLevelSoundAction(x, y, ACTION_MOVING);
7036   }
7037
7038   if (MovDir[x][y])
7039     ContinueMoving(x, y);
7040 }
7041
7042 void ContinueMoving(int x, int y)
7043 {
7044   int element = Feld[x][y];
7045   struct ElementInfo *ei = &element_info[element];
7046   int direction = MovDir[x][y];
7047   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7048   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7049   int newx = x + dx, newy = y + dy;
7050   int stored = Store[x][y];
7051   int stored_new = Store[newx][newy];
7052   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7053   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7054   boolean last_line = (newy == lev_fieldy - 1);
7055
7056   MovPos[x][y] += getElementMoveStepsize(x, y);
7057
7058   if (pushed_by_player) /* special case: moving object pushed by player */
7059     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7060
7061   if (ABS(MovPos[x][y]) < TILEX)
7062   {
7063     DrawLevelField(x, y);
7064
7065     return;     /* element is still moving */
7066   }
7067
7068   /* element reached destination field */
7069
7070   Feld[x][y] = EL_EMPTY;
7071   Feld[newx][newy] = element;
7072   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7073
7074   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7075   {
7076     element = Feld[newx][newy] = EL_ACID;
7077   }
7078   else if (element == EL_MOLE)
7079   {
7080     Feld[x][y] = EL_SAND;
7081
7082     DrawLevelFieldCrumbledSandNeighbours(x, y);
7083   }
7084   else if (element == EL_QUICKSAND_FILLING)
7085   {
7086     element = Feld[newx][newy] = get_next_element(element);
7087     Store[newx][newy] = Store[x][y];
7088   }
7089   else if (element == EL_QUICKSAND_EMPTYING)
7090   {
7091     Feld[x][y] = get_next_element(element);
7092     element = Feld[newx][newy] = Store[x][y];
7093   }
7094   else if (element == EL_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_MAGIC_WALL_DEAD;
7099     Store[newx][newy] = Store[x][y];
7100   }
7101   else if (element == EL_MAGIC_WALL_EMPTYING)
7102   {
7103     Feld[x][y] = get_next_element(element);
7104     if (!game.magic_wall_active)
7105       Feld[x][y] = EL_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_BD_MAGIC_WALL_FILLING)
7113   {
7114     element = Feld[newx][newy] = get_next_element(element);
7115     if (!game.magic_wall_active)
7116       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7117     Store[newx][newy] = Store[x][y];
7118   }
7119   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7120   {
7121     Feld[x][y] = get_next_element(element);
7122     if (!game.magic_wall_active)
7123       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7124     element = Feld[newx][newy] = Store[x][y];
7125
7126 #if USE_NEW_CUSTOM_VALUE
7127     InitField(newx, newy, FALSE);
7128 #endif
7129   }
7130   else if (element == EL_AMOEBA_DROPPING)
7131   {
7132     Feld[x][y] = get_next_element(element);
7133     element = Feld[newx][newy] = Store[x][y];
7134   }
7135   else if (element == EL_SOKOBAN_OBJECT)
7136   {
7137     if (Back[x][y])
7138       Feld[x][y] = Back[x][y];
7139
7140     if (Back[newx][newy])
7141       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7142
7143     Back[x][y] = Back[newx][newy] = 0;
7144   }
7145
7146   Store[x][y] = EL_EMPTY;
7147   MovPos[x][y] = 0;
7148   MovDir[x][y] = 0;
7149   MovDelay[x][y] = 0;
7150
7151   MovDelay[newx][newy] = 0;
7152
7153 #if 1
7154   if (CAN_CHANGE_OR_HAS_ACTION(element))
7155 #else
7156   if (CAN_CHANGE(element))
7157 #endif
7158   {
7159     /* copy element change control values to new field */
7160     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7161     ChangePage[newx][newy]  = ChangePage[x][y];
7162     ChangeCount[newx][newy] = ChangeCount[x][y];
7163     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7164
7165 #if 0
7166 #if USE_NEW_CUSTOM_VALUE
7167     CustomValue[newx][newy] = CustomValue[x][y];
7168 #endif
7169 #endif
7170   }
7171
7172 #if 1
7173 #if USE_NEW_CUSTOM_VALUE
7174     CustomValue[newx][newy] = CustomValue[x][y];
7175 #endif
7176 #endif
7177
7178   ChangeDelay[x][y] = 0;
7179   ChangePage[x][y] = -1;
7180   ChangeCount[x][y] = 0;
7181   ChangeEvent[x][y] = -1;
7182
7183 #if USE_NEW_CUSTOM_VALUE
7184   CustomValue[x][y] = 0;
7185 #endif
7186
7187   /* copy animation control values to new field */
7188   GfxFrame[newx][newy]  = GfxFrame[x][y];
7189   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7190   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7191   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7192
7193   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7194
7195   /* some elements can leave other elements behind after moving */
7196 #if 1
7197   if (ei->move_leave_element != EL_EMPTY &&
7198       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7199       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7200 #else
7201   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7202       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7203       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7204 #endif
7205   {
7206     int move_leave_element = ei->move_leave_element;
7207
7208 #if 1
7209 #if 1
7210     /* this makes it possible to leave the removed element again */
7211     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7212       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7213 #else
7214     /* this makes it possible to leave the removed element again */
7215     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7216       move_leave_element = stored;
7217 #endif
7218 #else
7219     /* this makes it possible to leave the removed element again */
7220     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7221         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7222       move_leave_element = stored;
7223 #endif
7224
7225     Feld[x][y] = move_leave_element;
7226
7227     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7228       MovDir[x][y] = direction;
7229
7230     InitField(x, y, FALSE);
7231
7232     if (GFX_CRUMBLED(Feld[x][y]))
7233       DrawLevelFieldCrumbledSandNeighbours(x, y);
7234
7235     if (ELEM_IS_PLAYER(move_leave_element))
7236       RelocatePlayer(x, y, move_leave_element);
7237   }
7238
7239   /* do this after checking for left-behind element */
7240   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7241
7242   if (!CAN_MOVE(element) ||
7243       (CAN_FALL(element) && direction == MV_DOWN &&
7244        (element == EL_SPRING ||
7245         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7246         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7247     GfxDir[x][y] = MovDir[newx][newy] = 0;
7248
7249   DrawLevelField(x, y);
7250   DrawLevelField(newx, newy);
7251
7252   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7253
7254   /* prevent pushed element from moving on in pushed direction */
7255   if (pushed_by_player && CAN_MOVE(element) &&
7256       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7257       !(element_info[element].move_pattern & direction))
7258     TurnRound(newx, newy);
7259
7260   /* prevent elements on conveyor belt from moving on in last direction */
7261   if (pushed_by_conveyor && CAN_FALL(element) &&
7262       direction & MV_HORIZONTAL)
7263     MovDir[newx][newy] = 0;
7264
7265   if (!pushed_by_player)
7266   {
7267     int nextx = newx + dx, nexty = newy + dy;
7268     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7269
7270     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7271
7272     if (CAN_FALL(element) && direction == MV_DOWN)
7273       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7274
7275     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7276       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7277   }
7278
7279   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7280   {
7281     TestIfBadThingTouchesPlayer(newx, newy);
7282     TestIfBadThingTouchesFriend(newx, newy);
7283
7284     if (!IS_CUSTOM_ELEMENT(element))
7285       TestIfBadThingTouchesOtherBadThing(newx, newy);
7286   }
7287   else if (element == EL_PENGUIN)
7288     TestIfFriendTouchesBadThing(newx, newy);
7289
7290   /* give the player one last chance (one more frame) to move away */
7291   if (CAN_FALL(element) && direction == MV_DOWN &&
7292       (last_line || (!IS_FREE(x, newy + 1) &&
7293                      (!IS_PLAYER(x, newy + 1) ||
7294                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7295     Impact(x, newy);
7296
7297   if (pushed_by_player && !game.use_change_when_pushing_bug)
7298   {
7299     int push_side = MV_DIR_OPPOSITE(direction);
7300     struct PlayerInfo *player = PLAYERINFO(x, y);
7301
7302     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7303                                player->index_bit, push_side);
7304     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7305                                         player->index_bit, push_side);
7306   }
7307
7308   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7309     MovDelay[newx][newy] = 1;
7310
7311   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7312
7313   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7314
7315 #if 0
7316   if (ChangePage[newx][newy] != -1)             /* delayed change */
7317   {
7318     int page = ChangePage[newx][newy];
7319     struct ElementChangeInfo *change = &ei->change_page[page];
7320
7321     ChangePage[newx][newy] = -1;
7322
7323     if (change->can_change)
7324     {
7325       if (ChangeElement(newx, newy, element, page))
7326       {
7327         if (change->post_change_function)
7328           change->post_change_function(newx, newy);
7329       }
7330     }
7331
7332     if (change->has_action)
7333       ExecuteCustomElementAction(newx, newy, element, page);
7334   }
7335 #endif
7336
7337   TestIfElementHitsCustomElement(newx, newy, direction);
7338   TestIfPlayerTouchesCustomElement(newx, newy);
7339   TestIfElementTouchesCustomElement(newx, newy);
7340
7341 #if 1
7342   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7343       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7344     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7345                                       MV_DIR_OPPOSITE(direction));
7346 #endif
7347 }
7348
7349 int AmoebeNachbarNr(int ax, int ay)
7350 {
7351   int i;
7352   int element = Feld[ax][ay];
7353   int group_nr = 0;
7354   static int xy[4][2] =
7355   {
7356     { 0, -1 },
7357     { -1, 0 },
7358     { +1, 0 },
7359     { 0, +1 }
7360   };
7361
7362   for (i = 0; i < NUM_DIRECTIONS; i++)
7363   {
7364     int x = ax + xy[i][0];
7365     int y = ay + xy[i][1];
7366
7367     if (!IN_LEV_FIELD(x, y))
7368       continue;
7369
7370     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7371       group_nr = AmoebaNr[x][y];
7372   }
7373
7374   return group_nr;
7375 }
7376
7377 void AmoebenVereinigen(int ax, int ay)
7378 {
7379   int i, x, y, xx, yy;
7380   int new_group_nr = AmoebaNr[ax][ay];
7381   static int xy[4][2] =
7382   {
7383     { 0, -1 },
7384     { -1, 0 },
7385     { +1, 0 },
7386     { 0, +1 }
7387   };
7388
7389   if (new_group_nr == 0)
7390     return;
7391
7392   for (i = 0; i < NUM_DIRECTIONS; i++)
7393   {
7394     x = ax + xy[i][0];
7395     y = ay + xy[i][1];
7396
7397     if (!IN_LEV_FIELD(x, y))
7398       continue;
7399
7400     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7401          Feld[x][y] == EL_BD_AMOEBA ||
7402          Feld[x][y] == EL_AMOEBA_DEAD) &&
7403         AmoebaNr[x][y] != new_group_nr)
7404     {
7405       int old_group_nr = AmoebaNr[x][y];
7406
7407       if (old_group_nr == 0)
7408         return;
7409
7410       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7411       AmoebaCnt[old_group_nr] = 0;
7412       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7413       AmoebaCnt2[old_group_nr] = 0;
7414
7415 #if 1
7416       SCAN_PLAYFIELD(xx, yy)
7417 #else
7418       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7419 #endif
7420       {
7421         if (AmoebaNr[xx][yy] == old_group_nr)
7422           AmoebaNr[xx][yy] = new_group_nr;
7423       }
7424     }
7425   }
7426 }
7427
7428 void AmoebeUmwandeln(int ax, int ay)
7429 {
7430   int i, x, y;
7431
7432   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7433   {
7434     int group_nr = AmoebaNr[ax][ay];
7435
7436 #ifdef DEBUG
7437     if (group_nr == 0)
7438     {
7439       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7440       printf("AmoebeUmwandeln(): This should never happen!\n");
7441       return;
7442     }
7443 #endif
7444
7445 #if 1
7446     SCAN_PLAYFIELD(x, y)
7447 #else
7448     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7449 #endif
7450     {
7451       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7452       {
7453         AmoebaNr[x][y] = 0;
7454         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7455       }
7456     }
7457
7458     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7459                             SND_AMOEBA_TURNING_TO_GEM :
7460                             SND_AMOEBA_TURNING_TO_ROCK));
7461     Bang(ax, ay);
7462   }
7463   else
7464   {
7465     static int xy[4][2] =
7466     {
7467       { 0, -1 },
7468       { -1, 0 },
7469       { +1, 0 },
7470       { 0, +1 }
7471     };
7472
7473     for (i = 0; i < NUM_DIRECTIONS; i++)
7474     {
7475       x = ax + xy[i][0];
7476       y = ay + xy[i][1];
7477
7478       if (!IN_LEV_FIELD(x, y))
7479         continue;
7480
7481       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7482       {
7483         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7484                               SND_AMOEBA_TURNING_TO_GEM :
7485                               SND_AMOEBA_TURNING_TO_ROCK));
7486         Bang(x, y);
7487       }
7488     }
7489   }
7490 }
7491
7492 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7493 {
7494   int x, y;
7495   int group_nr = AmoebaNr[ax][ay];
7496   boolean done = FALSE;
7497
7498 #ifdef DEBUG
7499   if (group_nr == 0)
7500   {
7501     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7502     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7503     return;
7504   }
7505 #endif
7506
7507 #if 1
7508   SCAN_PLAYFIELD(x, y)
7509 #else
7510   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7511 #endif
7512   {
7513     if (AmoebaNr[x][y] == group_nr &&
7514         (Feld[x][y] == EL_AMOEBA_DEAD ||
7515          Feld[x][y] == EL_BD_AMOEBA ||
7516          Feld[x][y] == EL_AMOEBA_GROWING))
7517     {
7518       AmoebaNr[x][y] = 0;
7519       Feld[x][y] = new_element;
7520       InitField(x, y, FALSE);
7521       DrawLevelField(x, y);
7522       done = TRUE;
7523     }
7524   }
7525
7526   if (done)
7527     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7528                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7529                             SND_BD_AMOEBA_TURNING_TO_GEM));
7530 }
7531
7532 void AmoebeWaechst(int x, int y)
7533 {
7534   static unsigned long sound_delay = 0;
7535   static unsigned long sound_delay_value = 0;
7536
7537   if (!MovDelay[x][y])          /* start new growing cycle */
7538   {
7539     MovDelay[x][y] = 7;
7540
7541     if (DelayReached(&sound_delay, sound_delay_value))
7542     {
7543       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7544       sound_delay_value = 30;
7545     }
7546   }
7547
7548   if (MovDelay[x][y])           /* wait some time before growing bigger */
7549   {
7550     MovDelay[x][y]--;
7551     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7552     {
7553       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7554                                            6 - MovDelay[x][y]);
7555
7556       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7557     }
7558
7559     if (!MovDelay[x][y])
7560     {
7561       Feld[x][y] = Store[x][y];
7562       Store[x][y] = 0;
7563       DrawLevelField(x, y);
7564     }
7565   }
7566 }
7567
7568 void AmoebaDisappearing(int x, int y)
7569 {
7570   static unsigned long sound_delay = 0;
7571   static unsigned long sound_delay_value = 0;
7572
7573   if (!MovDelay[x][y])          /* start new shrinking cycle */
7574   {
7575     MovDelay[x][y] = 7;
7576
7577     if (DelayReached(&sound_delay, sound_delay_value))
7578       sound_delay_value = 30;
7579   }
7580
7581   if (MovDelay[x][y])           /* wait some time before shrinking */
7582   {
7583     MovDelay[x][y]--;
7584     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7585     {
7586       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7587                                            6 - MovDelay[x][y]);
7588
7589       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7590     }
7591
7592     if (!MovDelay[x][y])
7593     {
7594       Feld[x][y] = EL_EMPTY;
7595       DrawLevelField(x, y);
7596
7597       /* don't let mole enter this field in this cycle;
7598          (give priority to objects falling to this field from above) */
7599       Stop[x][y] = TRUE;
7600     }
7601   }
7602 }
7603
7604 void AmoebeAbleger(int ax, int ay)
7605 {
7606   int i;
7607   int element = Feld[ax][ay];
7608   int graphic = el2img(element);
7609   int newax = ax, neway = ay;
7610   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7611   static int xy[4][2] =
7612   {
7613     { 0, -1 },
7614     { -1, 0 },
7615     { +1, 0 },
7616     { 0, +1 }
7617   };
7618
7619   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7620   {
7621     Feld[ax][ay] = EL_AMOEBA_DEAD;
7622     DrawLevelField(ax, ay);
7623     return;
7624   }
7625
7626   if (IS_ANIMATED(graphic))
7627     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7628
7629   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7630     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7631
7632   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7633   {
7634     MovDelay[ax][ay]--;
7635     if (MovDelay[ax][ay])
7636       return;
7637   }
7638
7639   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7640   {
7641     int start = RND(4);
7642     int x = ax + xy[start][0];
7643     int y = ay + xy[start][1];
7644
7645     if (!IN_LEV_FIELD(x, y))
7646       return;
7647
7648     if (IS_FREE(x, y) ||
7649         CAN_GROW_INTO(Feld[x][y]) ||
7650         Feld[x][y] == EL_QUICKSAND_EMPTY)
7651     {
7652       newax = x;
7653       neway = y;
7654     }
7655
7656     if (newax == ax && neway == ay)
7657       return;
7658   }
7659   else                          /* normal or "filled" (BD style) amoeba */
7660   {
7661     int start = RND(4);
7662     boolean waiting_for_player = FALSE;
7663
7664     for (i = 0; i < NUM_DIRECTIONS; i++)
7665     {
7666       int j = (start + i) % 4;
7667       int x = ax + xy[j][0];
7668       int y = ay + xy[j][1];
7669
7670       if (!IN_LEV_FIELD(x, y))
7671         continue;
7672
7673       if (IS_FREE(x, y) ||
7674           CAN_GROW_INTO(Feld[x][y]) ||
7675           Feld[x][y] == EL_QUICKSAND_EMPTY)
7676       {
7677         newax = x;
7678         neway = y;
7679         break;
7680       }
7681       else if (IS_PLAYER(x, y))
7682         waiting_for_player = TRUE;
7683     }
7684
7685     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7686     {
7687       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7688       {
7689         Feld[ax][ay] = EL_AMOEBA_DEAD;
7690         DrawLevelField(ax, ay);
7691         AmoebaCnt[AmoebaNr[ax][ay]]--;
7692
7693         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7694         {
7695           if (element == EL_AMOEBA_FULL)
7696             AmoebeUmwandeln(ax, ay);
7697           else if (element == EL_BD_AMOEBA)
7698             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7699         }
7700       }
7701       return;
7702     }
7703     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7704     {
7705       /* amoeba gets larger by growing in some direction */
7706
7707       int new_group_nr = AmoebaNr[ax][ay];
7708
7709 #ifdef DEBUG
7710   if (new_group_nr == 0)
7711   {
7712     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7713     printf("AmoebeAbleger(): This should never happen!\n");
7714     return;
7715   }
7716 #endif
7717
7718       AmoebaNr[newax][neway] = new_group_nr;
7719       AmoebaCnt[new_group_nr]++;
7720       AmoebaCnt2[new_group_nr]++;
7721
7722       /* if amoeba touches other amoeba(s) after growing, unify them */
7723       AmoebenVereinigen(newax, neway);
7724
7725       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7726       {
7727         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7728         return;
7729       }
7730     }
7731   }
7732
7733   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7734       (neway == lev_fieldy - 1 && newax != ax))
7735   {
7736     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7737     Store[newax][neway] = element;
7738   }
7739   else if (neway == ay || element == EL_EMC_DRIPPER)
7740   {
7741     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7742
7743     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7744   }
7745   else
7746   {
7747     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7748     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7749     Store[ax][ay] = EL_AMOEBA_DROP;
7750     ContinueMoving(ax, ay);
7751     return;
7752   }
7753
7754   DrawLevelField(newax, neway);
7755 }
7756
7757 void Life(int ax, int ay)
7758 {
7759   int x1, y1, x2, y2;
7760 #if 0
7761   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
7762 #endif
7763   int life_time = 40;
7764   int element = Feld[ax][ay];
7765   int graphic = el2img(element);
7766   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7767                          level.biomaze);
7768   boolean changed = FALSE;
7769
7770   if (IS_ANIMATED(graphic))
7771     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7772
7773   if (Stop[ax][ay])
7774     return;
7775
7776   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7777     MovDelay[ax][ay] = life_time;
7778
7779   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7780   {
7781     MovDelay[ax][ay]--;
7782     if (MovDelay[ax][ay])
7783       return;
7784   }
7785
7786   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7787   {
7788     int xx = ax+x1, yy = ay+y1;
7789     int nachbarn = 0;
7790
7791     if (!IN_LEV_FIELD(xx, yy))
7792       continue;
7793
7794     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7795     {
7796       int x = xx+x2, y = yy+y2;
7797
7798       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7799         continue;
7800
7801       if (((Feld[x][y] == element ||
7802             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7803            !Stop[x][y]) ||
7804           (IS_FREE(x, y) && Stop[x][y]))
7805         nachbarn++;
7806     }
7807
7808     if (xx == ax && yy == ay)           /* field in the middle */
7809     {
7810       if (nachbarn < life_parameter[0] ||
7811           nachbarn > life_parameter[1])
7812       {
7813         Feld[xx][yy] = EL_EMPTY;
7814         if (!Stop[xx][yy])
7815           DrawLevelField(xx, yy);
7816         Stop[xx][yy] = TRUE;
7817         changed = TRUE;
7818       }
7819     }
7820     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7821     {                                   /* free border field */
7822       if (nachbarn >= life_parameter[2] &&
7823           nachbarn <= life_parameter[3])
7824       {
7825         Feld[xx][yy] = element;
7826         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7827         if (!Stop[xx][yy])
7828           DrawLevelField(xx, yy);
7829         Stop[xx][yy] = TRUE;
7830         changed = TRUE;
7831       }
7832     }
7833   }
7834
7835   if (changed)
7836     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7837                    SND_GAME_OF_LIFE_GROWING);
7838 }
7839
7840 static void InitRobotWheel(int x, int y)
7841 {
7842   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7843 }
7844
7845 static void RunRobotWheel(int x, int y)
7846 {
7847   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7848 }
7849
7850 static void StopRobotWheel(int x, int y)
7851 {
7852   if (ZX == x && ZY == y)
7853     ZX = ZY = -1;
7854 }
7855
7856 static void InitTimegateWheel(int x, int y)
7857 {
7858   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7859 }
7860
7861 static void RunTimegateWheel(int x, int y)
7862 {
7863   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7864 }
7865
7866 static void InitMagicBallDelay(int x, int y)
7867 {
7868 #if 1
7869   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7870 #else
7871   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7872 #endif
7873 }
7874
7875 static void ActivateMagicBall(int bx, int by)
7876 {
7877   int x, y;
7878
7879   if (level.ball_random)
7880   {
7881     int pos_border = RND(8);    /* select one of the eight border elements */
7882     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7883     int xx = pos_content % 3;
7884     int yy = pos_content / 3;
7885
7886     x = bx - 1 + xx;
7887     y = by - 1 + yy;
7888
7889     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7890       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7891   }
7892   else
7893   {
7894     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7895     {
7896       int xx = x - bx + 1;
7897       int yy = y - by + 1;
7898
7899       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7900         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7901     }
7902   }
7903
7904   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7905 }
7906
7907 static void InitDiagonalMovingElement(int x, int y)
7908 {
7909 #if 0
7910   MovDelay[x][y] = level.android_move_time;
7911 #endif
7912 }
7913
7914 void CheckExit(int x, int y)
7915 {
7916   if (local_player->gems_still_needed > 0 ||
7917       local_player->sokobanfields_still_needed > 0 ||
7918       local_player->lights_still_needed > 0)
7919   {
7920     int element = Feld[x][y];
7921     int graphic = el2img(element);
7922
7923     if (IS_ANIMATED(graphic))
7924       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7925
7926     return;
7927   }
7928
7929   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7930     return;
7931
7932   Feld[x][y] = EL_EXIT_OPENING;
7933
7934   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7935 }
7936
7937 void CheckExitSP(int x, int y)
7938 {
7939   if (local_player->gems_still_needed > 0)
7940   {
7941     int element = Feld[x][y];
7942     int graphic = el2img(element);
7943
7944     if (IS_ANIMATED(graphic))
7945       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7946
7947     return;
7948   }
7949
7950   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7951     return;
7952
7953   Feld[x][y] = EL_SP_EXIT_OPENING;
7954
7955   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7956 }
7957
7958 static void CloseAllOpenTimegates()
7959 {
7960   int x, y;
7961
7962 #if 1
7963   SCAN_PLAYFIELD(x, y)
7964 #else
7965   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7966 #endif
7967   {
7968     int element = Feld[x][y];
7969
7970     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7971     {
7972       Feld[x][y] = EL_TIMEGATE_CLOSING;
7973
7974       PlayLevelSoundAction(x, y, ACTION_CLOSING);
7975     }
7976   }
7977 }
7978
7979 void EdelsteinFunkeln(int x, int y)
7980 {
7981   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7982     return;
7983
7984   if (Feld[x][y] == EL_BD_DIAMOND)
7985     return;
7986
7987   if (MovDelay[x][y] == 0)      /* next animation frame */
7988     MovDelay[x][y] = 11 * !SimpleRND(500);
7989
7990   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7991   {
7992     MovDelay[x][y]--;
7993
7994     if (setup.direct_draw && MovDelay[x][y])
7995       SetDrawtoField(DRAW_BUFFERED);
7996
7997     DrawLevelElementAnimation(x, y, Feld[x][y]);
7998
7999     if (MovDelay[x][y] != 0)
8000     {
8001       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8002                                            10 - MovDelay[x][y]);
8003
8004       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8005
8006       if (setup.direct_draw)
8007       {
8008         int dest_x, dest_y;
8009
8010         dest_x = FX + SCREENX(x) * TILEX;
8011         dest_y = FY + SCREENY(y) * TILEY;
8012
8013         BlitBitmap(drawto_field, window,
8014                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8015         SetDrawtoField(DRAW_DIRECT);
8016       }
8017     }
8018   }
8019 }
8020
8021 void MauerWaechst(int x, int y)
8022 {
8023   int delay = 6;
8024
8025   if (!MovDelay[x][y])          /* next animation frame */
8026     MovDelay[x][y] = 3 * delay;
8027
8028   if (MovDelay[x][y])           /* wait some time before next frame */
8029   {
8030     MovDelay[x][y]--;
8031
8032     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8033     {
8034       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8035       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8036
8037       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8038     }
8039
8040     if (!MovDelay[x][y])
8041     {
8042       if (MovDir[x][y] == MV_LEFT)
8043       {
8044         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8045           DrawLevelField(x - 1, y);
8046       }
8047       else if (MovDir[x][y] == MV_RIGHT)
8048       {
8049         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8050           DrawLevelField(x + 1, y);
8051       }
8052       else if (MovDir[x][y] == MV_UP)
8053       {
8054         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8055           DrawLevelField(x, y - 1);
8056       }
8057       else
8058       {
8059         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8060           DrawLevelField(x, y + 1);
8061       }
8062
8063       Feld[x][y] = Store[x][y];
8064       Store[x][y] = 0;
8065       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8066       DrawLevelField(x, y);
8067     }
8068   }
8069 }
8070
8071 void MauerAbleger(int ax, int ay)
8072 {
8073   int element = Feld[ax][ay];
8074   int graphic = el2img(element);
8075   boolean oben_frei = FALSE, unten_frei = FALSE;
8076   boolean links_frei = FALSE, rechts_frei = FALSE;
8077   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8078   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8079   boolean new_wall = FALSE;
8080
8081   if (IS_ANIMATED(graphic))
8082     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8083
8084   if (!MovDelay[ax][ay])        /* start building new wall */
8085     MovDelay[ax][ay] = 6;
8086
8087   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8088   {
8089     MovDelay[ax][ay]--;
8090     if (MovDelay[ax][ay])
8091       return;
8092   }
8093
8094   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8095     oben_frei = TRUE;
8096   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8097     unten_frei = TRUE;
8098   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8099     links_frei = TRUE;
8100   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8101     rechts_frei = TRUE;
8102
8103   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8104       element == EL_EXPANDABLE_WALL_ANY)
8105   {
8106     if (oben_frei)
8107     {
8108       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8109       Store[ax][ay-1] = element;
8110       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8111       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8112         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8113                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8114       new_wall = TRUE;
8115     }
8116     if (unten_frei)
8117     {
8118       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8119       Store[ax][ay+1] = element;
8120       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8121       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8122         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8123                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8124       new_wall = TRUE;
8125     }
8126   }
8127
8128   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8129       element == EL_EXPANDABLE_WALL_ANY ||
8130       element == EL_EXPANDABLE_WALL ||
8131       element == EL_BD_EXPANDABLE_WALL)
8132   {
8133     if (links_frei)
8134     {
8135       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8136       Store[ax-1][ay] = element;
8137       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8138       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8139         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8140                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8141       new_wall = TRUE;
8142     }
8143
8144     if (rechts_frei)
8145     {
8146       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8147       Store[ax+1][ay] = element;
8148       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8149       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8150         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8151                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8152       new_wall = TRUE;
8153     }
8154   }
8155
8156   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8157     DrawLevelField(ax, ay);
8158
8159   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8160     oben_massiv = TRUE;
8161   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8162     unten_massiv = TRUE;
8163   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8164     links_massiv = TRUE;
8165   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8166     rechts_massiv = TRUE;
8167
8168   if (((oben_massiv && unten_massiv) ||
8169        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8170        element == EL_EXPANDABLE_WALL) &&
8171       ((links_massiv && rechts_massiv) ||
8172        element == EL_EXPANDABLE_WALL_VERTICAL))
8173     Feld[ax][ay] = EL_WALL;
8174
8175   if (new_wall)
8176     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8177 }
8178
8179 void CheckForDragon(int x, int y)
8180 {
8181   int i, j;
8182   boolean dragon_found = FALSE;
8183   static int xy[4][2] =
8184   {
8185     { 0, -1 },
8186     { -1, 0 },
8187     { +1, 0 },
8188     { 0, +1 }
8189   };
8190
8191   for (i = 0; i < NUM_DIRECTIONS; i++)
8192   {
8193     for (j = 0; j < 4; j++)
8194     {
8195       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8196
8197       if (IN_LEV_FIELD(xx, yy) &&
8198           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8199       {
8200         if (Feld[xx][yy] == EL_DRAGON)
8201           dragon_found = TRUE;
8202       }
8203       else
8204         break;
8205     }
8206   }
8207
8208   if (!dragon_found)
8209   {
8210     for (i = 0; i < NUM_DIRECTIONS; i++)
8211     {
8212       for (j = 0; j < 3; j++)
8213       {
8214         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8215   
8216         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8217         {
8218           Feld[xx][yy] = EL_EMPTY;
8219           DrawLevelField(xx, yy);
8220         }
8221         else
8222           break;
8223       }
8224     }
8225   }
8226 }
8227
8228 static void InitBuggyBase(int x, int y)
8229 {
8230   int element = Feld[x][y];
8231   int activating_delay = FRAMES_PER_SECOND / 4;
8232
8233   ChangeDelay[x][y] =
8234     (element == EL_SP_BUGGY_BASE ?
8235      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8236      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8237      activating_delay :
8238      element == EL_SP_BUGGY_BASE_ACTIVE ?
8239      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8240 }
8241
8242 static void WarnBuggyBase(int x, int y)
8243 {
8244   int i;
8245   static int xy[4][2] =
8246   {
8247     { 0, -1 },
8248     { -1, 0 },
8249     { +1, 0 },
8250     { 0, +1 }
8251   };
8252
8253   for (i = 0; i < NUM_DIRECTIONS; i++)
8254   {
8255     int xx = x + xy[i][0];
8256     int yy = y + xy[i][1];
8257
8258     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8259     {
8260       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8261
8262       break;
8263     }
8264   }
8265 }
8266
8267 static void InitTrap(int x, int y)
8268 {
8269   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8270 }
8271
8272 static void ActivateTrap(int x, int y)
8273 {
8274   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8275 }
8276
8277 static void ChangeActiveTrap(int x, int y)
8278 {
8279   int graphic = IMG_TRAP_ACTIVE;
8280
8281   /* if new animation frame was drawn, correct crumbled sand border */
8282   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8283     DrawLevelFieldCrumbledSand(x, y);
8284 }
8285
8286 static int getSpecialActionElement(int element, int number, int base_element)
8287 {
8288   return (element != EL_EMPTY ? element :
8289           number != -1 ? base_element + number - 1 :
8290           EL_EMPTY);
8291 }
8292
8293 static int getModifiedActionNumber(int value_old, int operator, int operand,
8294                                    int value_min, int value_max)
8295 {
8296   int value_new = (operator == CA_MODE_SET      ? operand :
8297                    operator == CA_MODE_ADD      ? value_old + operand :
8298                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8299                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8300                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8301                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8302                    value_old);
8303
8304   return (value_new < value_min ? value_min :
8305           value_new > value_max ? value_max :
8306           value_new);
8307 }
8308
8309 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8310 {
8311   struct ElementInfo *ei = &element_info[element];
8312   struct ElementChangeInfo *change = &ei->change_page[page];
8313   int target_element = change->target_element;
8314   int action_type = change->action_type;
8315   int action_mode = change->action_mode;
8316   int action_arg = change->action_arg;
8317   int i;
8318
8319   if (!change->has_action)
8320     return;
8321
8322   /* ---------- determine action paramater values -------------------------- */
8323
8324   int level_time_value =
8325     (level.time > 0 ? TimeLeft :
8326      TimePlayed);
8327
8328   int action_arg_element =
8329     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8330      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8331      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8332      EL_EMPTY);
8333
8334   int action_arg_direction =
8335     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8336      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8337      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8338      change->actual_trigger_side :
8339      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8340      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8341      MV_NONE);
8342
8343   int action_arg_number_min =
8344     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8345      CA_ARG_MIN);
8346
8347   int action_arg_number_max =
8348     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8349      action_type == CA_SET_LEVEL_GEMS ? 999 :
8350      action_type == CA_SET_LEVEL_TIME ? 9999 :
8351      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8352      action_type == CA_SET_CE_VALUE ? 9999 :
8353      action_type == CA_SET_CE_SCORE ? 9999 :
8354      CA_ARG_MAX);
8355
8356   int action_arg_number_reset =
8357     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8358      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8359      action_type == CA_SET_LEVEL_TIME ? level.time :
8360      action_type == CA_SET_LEVEL_SCORE ? 0 :
8361 #if 1
8362      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8363 #else
8364      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8365 #endif
8366      action_type == CA_SET_CE_SCORE ? 0 :
8367      0);
8368
8369   int action_arg_number =
8370     (action_arg <= CA_ARG_MAX ? action_arg :
8371      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8372      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8373      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8374      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8375      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8376      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8377 #if USE_NEW_CUSTOM_VALUE
8378      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8379 #else
8380      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8381 #endif
8382      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8383      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8384      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8385      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8386      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8387      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8388      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8389      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8390      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8391      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8392      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8393      -1);
8394
8395   int action_arg_number_old =
8396     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8397      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8398      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8399      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8400      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8401      0);
8402
8403   int action_arg_number_new =
8404     getModifiedActionNumber(action_arg_number_old,
8405                             action_mode, action_arg_number,
8406                             action_arg_number_min, action_arg_number_max);
8407
8408   int trigger_player_bits =
8409     (change->actual_trigger_player >= EL_PLAYER_1 &&
8410      change->actual_trigger_player <= EL_PLAYER_4 ?
8411      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8412      PLAYER_BITS_ANY);
8413
8414   int action_arg_player_bits =
8415     (action_arg >= CA_ARG_PLAYER_1 &&
8416      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8417      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8418      PLAYER_BITS_ANY);
8419
8420   /* ---------- execute action  -------------------------------------------- */
8421
8422   switch(action_type)
8423   {
8424     case CA_NO_ACTION:
8425     {
8426       return;
8427     }
8428
8429     /* ---------- level actions  ------------------------------------------- */
8430
8431     case CA_RESTART_LEVEL:
8432     {
8433       game.restart_level = TRUE;
8434
8435       break;
8436     }
8437
8438     case CA_SHOW_ENVELOPE:
8439     {
8440       int element = getSpecialActionElement(action_arg_element,
8441                                             action_arg_number, EL_ENVELOPE_1);
8442
8443       if (IS_ENVELOPE(element))
8444         local_player->show_envelope = element;
8445
8446       break;
8447     }
8448
8449     case CA_SET_LEVEL_TIME:
8450     {
8451       if (level.time > 0)       /* only modify limited time value */
8452       {
8453         TimeLeft = action_arg_number_new;
8454
8455         DrawGameValue_Time(TimeLeft);
8456
8457         if (!TimeLeft && setup.time_limit)
8458           for (i = 0; i < MAX_PLAYERS; i++)
8459             KillPlayer(&stored_player[i]);
8460       }
8461
8462       break;
8463     }
8464
8465     case CA_SET_LEVEL_SCORE:
8466     {
8467       local_player->score = action_arg_number_new;
8468
8469       DrawGameValue_Score(local_player->score);
8470
8471       break;
8472     }
8473
8474     case CA_SET_LEVEL_GEMS:
8475     {
8476       local_player->gems_still_needed = action_arg_number_new;
8477
8478       DrawGameValue_Emeralds(local_player->gems_still_needed);
8479
8480       break;
8481     }
8482
8483 #if !USE_PLAYER_GRAVITY
8484     case CA_SET_LEVEL_GRAVITY:
8485     {
8486       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
8487                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
8488                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8489                       game.gravity);
8490       break;
8491     }
8492 #endif
8493
8494     case CA_SET_LEVEL_WIND:
8495     {
8496       game.wind_direction = action_arg_direction;
8497
8498       break;
8499     }
8500
8501     /* ---------- player actions  ------------------------------------------ */
8502
8503     case CA_MOVE_PLAYER:
8504     {
8505       /* automatically move to the next field in specified direction */
8506       for (i = 0; i < MAX_PLAYERS; i++)
8507         if (trigger_player_bits & (1 << i))
8508           stored_player[i].programmed_action = action_arg_direction;
8509
8510       break;
8511     }
8512
8513     case CA_EXIT_PLAYER:
8514     {
8515       for (i = 0; i < MAX_PLAYERS; i++)
8516         if (action_arg_player_bits & (1 << i))
8517           stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8518
8519       break;
8520     }
8521
8522     case CA_KILL_PLAYER:
8523     {
8524       for (i = 0; i < MAX_PLAYERS; i++)
8525         if (action_arg_player_bits & (1 << i))
8526           KillPlayer(&stored_player[i]);
8527
8528       break;
8529     }
8530
8531     case CA_SET_PLAYER_KEYS:
8532     {
8533       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8534       int element = getSpecialActionElement(action_arg_element,
8535                                             action_arg_number, EL_KEY_1);
8536
8537       if (IS_KEY(element))
8538       {
8539         for (i = 0; i < MAX_PLAYERS; i++)
8540         {
8541           if (trigger_player_bits & (1 << i))
8542           {
8543             stored_player[i].key[KEY_NR(element)] = key_state;
8544
8545 #if 1
8546             DrawGameDoorValues();
8547 #else
8548             DrawGameValue_Keys(stored_player[i].key);
8549 #endif
8550
8551             redraw_mask |= REDRAW_DOOR_1;
8552           }
8553         }
8554       }
8555
8556       break;
8557     }
8558
8559     case CA_SET_PLAYER_SPEED:
8560     {
8561       for (i = 0; i < MAX_PLAYERS; i++)
8562       {
8563         if (trigger_player_bits & (1 << i))
8564         {
8565           int move_stepsize = TILEX / stored_player[i].move_delay_value;
8566
8567           if (action_arg == CA_ARG_SPEED_FASTER &&
8568               stored_player[i].cannot_move)
8569           {
8570             action_arg_number = STEPSIZE_VERY_SLOW;
8571           }
8572           else if (action_arg == CA_ARG_SPEED_SLOWER ||
8573                    action_arg == CA_ARG_SPEED_FASTER)
8574           {
8575             action_arg_number = 2;
8576             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8577                            CA_MODE_MULTIPLY);
8578           }
8579           else if (action_arg == CA_ARG_NUMBER_RESET)
8580           {
8581             action_arg_number = level.initial_player_stepsize[i];
8582           }
8583
8584           move_stepsize =
8585             getModifiedActionNumber(move_stepsize,
8586                                     action_mode,
8587                                     action_arg_number,
8588                                     action_arg_number_min,
8589                                     action_arg_number_max);
8590
8591 #if 1
8592           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8593 #else
8594           /* make sure that value is power of 2 */
8595           move_stepsize = (1 << log_2(move_stepsize));
8596
8597           /* do no immediately change -- the player might just be moving */
8598           stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8599
8600           stored_player[i].cannot_move =
8601             (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8602 #endif
8603         }
8604       }
8605
8606       break;
8607     }
8608
8609     case CA_SET_PLAYER_SHIELD:
8610     {
8611       for (i = 0; i < MAX_PLAYERS; i++)
8612       {
8613         if (trigger_player_bits & (1 << i))
8614         {
8615           if (action_arg == CA_ARG_SHIELD_OFF)
8616           {
8617             stored_player[i].shield_normal_time_left = 0;
8618             stored_player[i].shield_deadly_time_left = 0;
8619           }
8620           else if (action_arg == CA_ARG_SHIELD_NORMAL)
8621           {
8622             stored_player[i].shield_normal_time_left = 999999;
8623           }
8624           else if (action_arg == CA_ARG_SHIELD_DEADLY)
8625           {
8626             stored_player[i].shield_normal_time_left = 999999;
8627             stored_player[i].shield_deadly_time_left = 999999;
8628           }
8629         }
8630       }
8631
8632       break;
8633     }
8634
8635 #if USE_PLAYER_GRAVITY
8636     case CA_SET_PLAYER_GRAVITY:
8637     {
8638       for (i = 0; i < MAX_PLAYERS; i++)
8639       {
8640         if (trigger_player_bits & (1 << i))
8641         {
8642           stored_player[i].gravity =
8643             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
8644              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
8645              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8646              stored_player[i].gravity);
8647         }
8648       }
8649
8650       break;
8651     }
8652 #endif
8653
8654     case CA_SET_PLAYER_ARTWORK:
8655     {
8656       for (i = 0; i < MAX_PLAYERS; i++)
8657       {
8658         if (trigger_player_bits & (1 << i))
8659         {
8660           int artwork_element = action_arg_element;
8661
8662           if (action_arg == CA_ARG_ELEMENT_RESET)
8663             artwork_element =
8664               (level.use_artwork_element[i] ? level.artwork_element[i] :
8665                stored_player[i].element_nr);
8666
8667           stored_player[i].artwork_element = artwork_element;
8668
8669           SetPlayerWaiting(&stored_player[i], FALSE);
8670
8671           /* set number of special actions for bored and sleeping animation */
8672           stored_player[i].num_special_action_bored =
8673             get_num_special_action(artwork_element,
8674                                    ACTION_BORING_1, ACTION_BORING_LAST);
8675           stored_player[i].num_special_action_sleeping =
8676             get_num_special_action(artwork_element,
8677                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8678         }
8679       }
8680
8681       break;
8682     }
8683
8684     /* ---------- CE actions  ---------------------------------------------- */
8685
8686     case CA_SET_CE_VALUE:
8687     {
8688 #if USE_NEW_CUSTOM_VALUE
8689       int last_ce_value = CustomValue[x][y];
8690
8691       CustomValue[x][y] = action_arg_number_new;
8692
8693 #if 0
8694       printf("::: CE value == %d\n", CustomValue[x][y]);
8695 #endif
8696
8697       if (CustomValue[x][y] != last_ce_value)
8698       {
8699         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8700         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8701
8702         if (CustomValue[x][y] == 0)
8703         {
8704 #if 0
8705           printf("::: CE_VALUE_GETS_ZERO\n");
8706 #endif
8707
8708           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8709           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8710
8711 #if 0
8712           printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8713 #endif
8714         }
8715       }
8716
8717 #endif
8718
8719       break;
8720     }
8721
8722     case CA_SET_CE_SCORE:
8723     {
8724 #if USE_NEW_CUSTOM_VALUE
8725       int last_ce_score = ei->collect_score;
8726
8727       ei->collect_score = action_arg_number_new;
8728
8729 #if 0
8730       printf("::: CE score == %d\n", ei->collect_score);
8731 #endif
8732
8733       if (ei->collect_score != last_ce_score)
8734       {
8735         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8736         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8737
8738         if (ei->collect_score == 0)
8739         {
8740           int xx, yy;
8741
8742 #if 0
8743           printf("::: CE_SCORE_GETS_ZERO\n");
8744 #endif
8745
8746           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8747           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8748
8749 #if 0
8750           printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8751 #endif
8752
8753 #if 1
8754           /*
8755             This is a very special case that seems to be a mixture between
8756             CheckElementChange() and CheckTriggeredElementChange(): while
8757             the first one only affects single elements that are triggered
8758             directly, the second one affects multiple elements in the playfield
8759             that are triggered indirectly by another element. This is a third
8760             case: Changing the CE score always affects multiple identical CEs,
8761             so every affected CE must be checked, not only the single CE for
8762             which the CE score was changed in the first place (as every instance
8763             of that CE shares the same CE score, and therefore also can change)!
8764           */
8765           SCAN_PLAYFIELD(xx, yy)
8766           {
8767             if (Feld[xx][yy] == element)
8768               CheckElementChange(xx, yy, element, EL_UNDEFINED,
8769                                  CE_SCORE_GETS_ZERO);
8770           }
8771 #endif
8772         }
8773       }
8774
8775 #endif
8776
8777       break;
8778     }
8779
8780     /* ---------- engine actions  ------------------------------------------ */
8781
8782     case CA_SET_ENGINE_SCAN_MODE:
8783     {
8784       InitPlayfieldScanMode(action_arg);
8785
8786       break;
8787     }
8788
8789     default:
8790       break;
8791   }
8792 }
8793
8794 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8795 {
8796   int old_element = Feld[x][y];
8797   int new_element = get_element_from_group_element(element);
8798   int previous_move_direction = MovDir[x][y];
8799 #if USE_NEW_CUSTOM_VALUE
8800   int last_ce_value = CustomValue[x][y];
8801 #endif
8802   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8803   boolean add_player_onto_element = (new_element_is_player &&
8804 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8805                                      /* this breaks SnakeBite when a snake is
8806                                         halfway through a door that closes */
8807                                      /* NOW FIXED AT LEVEL INIT IN files.c */
8808                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
8809 #endif
8810                                      IS_WALKABLE(old_element));
8811
8812 #if 0
8813   /* check if element under the player changes from accessible to unaccessible
8814      (needed for special case of dropping element which then changes) */
8815   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8816       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8817   {
8818     Bang(x, y);
8819
8820     return;
8821   }
8822 #endif
8823
8824   if (!add_player_onto_element)
8825   {
8826     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8827       RemoveMovingField(x, y);
8828     else
8829       RemoveField(x, y);
8830
8831     Feld[x][y] = new_element;
8832
8833 #if !USE_GFX_RESET_GFX_ANIMATION
8834     ResetGfxAnimation(x, y);
8835     ResetRandomAnimationValue(x, y);
8836 #endif
8837
8838     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8839       MovDir[x][y] = previous_move_direction;
8840
8841 #if USE_NEW_CUSTOM_VALUE
8842     if (element_info[new_element].use_last_ce_value)
8843       CustomValue[x][y] = last_ce_value;
8844 #endif
8845
8846     InitField_WithBug1(x, y, FALSE);
8847
8848     new_element = Feld[x][y];   /* element may have changed */
8849
8850 #if USE_GFX_RESET_GFX_ANIMATION
8851     ResetGfxAnimation(x, y);
8852     ResetRandomAnimationValue(x, y);
8853 #endif
8854
8855     DrawLevelField(x, y);
8856
8857     if (GFX_CRUMBLED(new_element))
8858       DrawLevelFieldCrumbledSandNeighbours(x, y);
8859   }
8860
8861 #if 1
8862   /* check if element under the player changes from accessible to unaccessible
8863      (needed for special case of dropping element which then changes) */
8864   /* (must be checked after creating new element for walkable group elements) */
8865   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8866       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8867   {
8868     Bang(x, y);
8869
8870     return;
8871   }
8872 #endif
8873
8874   /* "ChangeCount" not set yet to allow "entered by player" change one time */
8875   if (new_element_is_player)
8876     RelocatePlayer(x, y, new_element);
8877
8878   if (is_change)
8879     ChangeCount[x][y]++;        /* count number of changes in the same frame */
8880
8881   TestIfBadThingTouchesPlayer(x, y);
8882   TestIfPlayerTouchesCustomElement(x, y);
8883   TestIfElementTouchesCustomElement(x, y);
8884 }
8885
8886 static void CreateField(int x, int y, int element)
8887 {
8888   CreateFieldExt(x, y, element, FALSE);
8889 }
8890
8891 static void CreateElementFromChange(int x, int y, int element)
8892 {
8893   element = GET_VALID_RUNTIME_ELEMENT(element);
8894
8895 #if USE_STOP_CHANGED_ELEMENTS
8896   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8897   {
8898     int old_element = Feld[x][y];
8899
8900     /* prevent changed element from moving in same engine frame
8901        unless both old and new element can either fall or move */
8902     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8903         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8904       Stop[x][y] = TRUE;
8905   }
8906 #endif
8907
8908   CreateFieldExt(x, y, element, TRUE);
8909 }
8910
8911 static boolean ChangeElement(int x, int y, int element, int page)
8912 {
8913   struct ElementInfo *ei = &element_info[element];
8914   struct ElementChangeInfo *change = &ei->change_page[page];
8915   int ce_value = CustomValue[x][y];
8916   int ce_score = ei->collect_score;
8917   int target_element;
8918   int old_element = Feld[x][y];
8919
8920   /* always use default change event to prevent running into a loop */
8921   if (ChangeEvent[x][y] == -1)
8922     ChangeEvent[x][y] = CE_DELAY;
8923
8924   if (ChangeEvent[x][y] == CE_DELAY)
8925   {
8926     /* reset actual trigger element, trigger player and action element */
8927     change->actual_trigger_element = EL_EMPTY;
8928     change->actual_trigger_player = EL_PLAYER_1;
8929     change->actual_trigger_side = CH_SIDE_NONE;
8930     change->actual_trigger_ce_value = 0;
8931     change->actual_trigger_ce_score = 0;
8932   }
8933
8934   /* do not change elements more than a specified maximum number of changes */
8935   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8936     return FALSE;
8937
8938   ChangeCount[x][y]++;          /* count number of changes in the same frame */
8939
8940   if (change->explode)
8941   {
8942     Bang(x, y);
8943
8944     return TRUE;
8945   }
8946
8947   if (change->use_target_content)
8948   {
8949     boolean complete_replace = TRUE;
8950     boolean can_replace[3][3];
8951     int xx, yy;
8952
8953     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8954     {
8955       boolean is_empty;
8956       boolean is_walkable;
8957       boolean is_diggable;
8958       boolean is_collectible;
8959       boolean is_removable;
8960       boolean is_destructible;
8961       int ex = x + xx - 1;
8962       int ey = y + yy - 1;
8963       int content_element = change->target_content.e[xx][yy];
8964       int e;
8965
8966       can_replace[xx][yy] = TRUE;
8967
8968       if (ex == x && ey == y)   /* do not check changing element itself */
8969         continue;
8970
8971       if (content_element == EL_EMPTY_SPACE)
8972       {
8973         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8974
8975         continue;
8976       }
8977
8978       if (!IN_LEV_FIELD(ex, ey))
8979       {
8980         can_replace[xx][yy] = FALSE;
8981         complete_replace = FALSE;
8982
8983         continue;
8984       }
8985
8986       e = Feld[ex][ey];
8987
8988       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8989         e = MovingOrBlocked2Element(ex, ey);
8990
8991       is_empty = (IS_FREE(ex, ey) ||
8992                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8993
8994       is_walkable     = (is_empty || IS_WALKABLE(e));
8995       is_diggable     = (is_empty || IS_DIGGABLE(e));
8996       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8997       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8998       is_removable    = (is_diggable || is_collectible);
8999
9000       can_replace[xx][yy] =
9001         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
9002           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
9003           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
9004           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
9005           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
9006           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9007          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9008
9009       if (!can_replace[xx][yy])
9010         complete_replace = FALSE;
9011     }
9012
9013     if (!change->only_if_complete || complete_replace)
9014     {
9015       boolean something_has_changed = FALSE;
9016
9017       if (change->only_if_complete && change->use_random_replace &&
9018           RND(100) < change->random_percentage)
9019         return FALSE;
9020
9021       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9022       {
9023         int ex = x + xx - 1;
9024         int ey = y + yy - 1;
9025         int content_element;
9026
9027         if (can_replace[xx][yy] && (!change->use_random_replace ||
9028                                     RND(100) < change->random_percentage))
9029         {
9030           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9031             RemoveMovingField(ex, ey);
9032
9033           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9034
9035           content_element = change->target_content.e[xx][yy];
9036           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9037                                               ce_value, ce_score);
9038
9039           CreateElementFromChange(ex, ey, target_element);
9040
9041           something_has_changed = TRUE;
9042
9043           /* for symmetry reasons, freeze newly created border elements */
9044           if (ex != x || ey != y)
9045             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9046         }
9047       }
9048
9049       if (something_has_changed)
9050       {
9051         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9052         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9053       }
9054     }
9055   }
9056   else
9057   {
9058     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9059                                         ce_value, ce_score);
9060
9061     if (element == EL_DIAGONAL_GROWING ||
9062         element == EL_DIAGONAL_SHRINKING)
9063     {
9064       target_element = Store[x][y];
9065
9066       Store[x][y] = EL_EMPTY;
9067     }
9068
9069     CreateElementFromChange(x, y, target_element);
9070
9071     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9072     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9073   }
9074
9075   /* this uses direct change before indirect change */
9076   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9077
9078   return TRUE;
9079 }
9080
9081 #if USE_NEW_DELAYED_ACTION
9082
9083 static void HandleElementChange(int x, int y, int page)
9084 {
9085   int element = MovingOrBlocked2Element(x, y);
9086   struct ElementInfo *ei = &element_info[element];
9087   struct ElementChangeInfo *change = &ei->change_page[page];
9088
9089 #ifdef DEBUG
9090   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9091       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9092   {
9093     printf("\n\n");
9094     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9095            x, y, element, element_info[element].token_name);
9096     printf("HandleElementChange(): This should never happen!\n");
9097     printf("\n\n");
9098   }
9099 #endif
9100
9101   /* this can happen with classic bombs on walkable, changing elements */
9102   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9103   {
9104 #if 0
9105     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9106       ChangeDelay[x][y] = 0;
9107 #endif
9108
9109     return;
9110   }
9111
9112   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9113   {
9114     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9115
9116     if (change->can_change)
9117     {
9118       ResetGfxAnimation(x, y);
9119       ResetRandomAnimationValue(x, y);
9120
9121       if (change->pre_change_function)
9122         change->pre_change_function(x, y);
9123     }
9124   }
9125
9126   ChangeDelay[x][y]--;
9127
9128   if (ChangeDelay[x][y] != 0)           /* continue element change */
9129   {
9130     if (change->can_change)
9131     {
9132       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9133
9134       if (IS_ANIMATED(graphic))
9135         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9136
9137       if (change->change_function)
9138         change->change_function(x, y);
9139     }
9140   }
9141   else                                  /* finish element change */
9142   {
9143     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9144     {
9145       page = ChangePage[x][y];
9146       ChangePage[x][y] = -1;
9147
9148       change = &ei->change_page[page];
9149     }
9150
9151     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9152     {
9153       ChangeDelay[x][y] = 1;            /* try change after next move step */
9154       ChangePage[x][y] = page;          /* remember page to use for change */
9155
9156       return;
9157     }
9158
9159     if (change->can_change)
9160     {
9161       if (ChangeElement(x, y, element, page))
9162       {
9163         if (change->post_change_function)
9164           change->post_change_function(x, y);
9165       }
9166     }
9167
9168     if (change->has_action)
9169       ExecuteCustomElementAction(x, y, element, page);
9170   }
9171 }
9172
9173 #else
9174
9175 static void HandleElementChange(int x, int y, int page)
9176 {
9177   int element = MovingOrBlocked2Element(x, y);
9178   struct ElementInfo *ei = &element_info[element];
9179   struct ElementChangeInfo *change = &ei->change_page[page];
9180
9181 #ifdef DEBUG
9182   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9183   {
9184     printf("\n\n");
9185     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9186            x, y, element, element_info[element].token_name);
9187     printf("HandleElementChange(): This should never happen!\n");
9188     printf("\n\n");
9189   }
9190 #endif
9191
9192   /* this can happen with classic bombs on walkable, changing elements */
9193   if (!CAN_CHANGE(element))
9194   {
9195 #if 0
9196     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9197       ChangeDelay[x][y] = 0;
9198 #endif
9199
9200     return;
9201   }
9202
9203   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9204   {
9205     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9206
9207     ResetGfxAnimation(x, y);
9208     ResetRandomAnimationValue(x, y);
9209
9210     if (change->pre_change_function)
9211       change->pre_change_function(x, y);
9212   }
9213
9214   ChangeDelay[x][y]--;
9215
9216   if (ChangeDelay[x][y] != 0)           /* continue element change */
9217   {
9218     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9219
9220     if (IS_ANIMATED(graphic))
9221       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9222
9223     if (change->change_function)
9224       change->change_function(x, y);
9225   }
9226   else                                  /* finish element change */
9227   {
9228     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9229     {
9230       page = ChangePage[x][y];
9231       ChangePage[x][y] = -1;
9232
9233       change = &ei->change_page[page];
9234     }
9235
9236     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9237     {
9238       ChangeDelay[x][y] = 1;            /* try change after next move step */
9239       ChangePage[x][y] = page;          /* remember page to use for change */
9240
9241       return;
9242     }
9243
9244     if (ChangeElement(x, y, element, page))
9245     {
9246       if (change->post_change_function)
9247         change->post_change_function(x, y);
9248     }
9249   }
9250 }
9251
9252 #endif
9253
9254 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9255                                               int trigger_element,
9256                                               int trigger_event,
9257                                               int trigger_player,
9258                                               int trigger_side,
9259                                               int trigger_page)
9260 {
9261   boolean change_done_any = FALSE;
9262   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9263   int i;
9264
9265   if (!(trigger_events[trigger_element][trigger_event]))
9266     return FALSE;
9267
9268   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9269   {
9270     int element = EL_CUSTOM_START + i;
9271     boolean change_done = FALSE;
9272     int p;
9273
9274     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9275         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9276       continue;
9277
9278     for (p = 0; p < element_info[element].num_change_pages; p++)
9279     {
9280       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9281
9282       if (change->can_change_or_has_action &&
9283           change->has_event[trigger_event] &&
9284           change->trigger_side & trigger_side &&
9285           change->trigger_player & trigger_player &&
9286           change->trigger_page & trigger_page_bits &&
9287           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9288       {
9289         change->actual_trigger_element = trigger_element;
9290         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9291         change->actual_trigger_side = trigger_side;
9292         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9293         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9294
9295         if ((change->can_change && !change_done) || change->has_action)
9296         {
9297           int x, y;
9298
9299 #if 1
9300           SCAN_PLAYFIELD(x, y)
9301 #else
9302           for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9303 #endif
9304           {
9305             if (Feld[x][y] == element)
9306             {
9307               if (change->can_change && !change_done)
9308               {
9309                 ChangeDelay[x][y] = 1;
9310                 ChangeEvent[x][y] = trigger_event;
9311
9312                 HandleElementChange(x, y, p);
9313               }
9314 #if USE_NEW_DELAYED_ACTION
9315               else if (change->has_action)
9316               {
9317                 ExecuteCustomElementAction(x, y, element, p);
9318                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9319               }
9320 #else
9321               if (change->has_action)
9322               {
9323                 ExecuteCustomElementAction(x, y, element, p);
9324                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9325               }
9326 #endif
9327             }
9328           }
9329
9330           if (change->can_change)
9331           {
9332             change_done = TRUE;
9333             change_done_any = TRUE;
9334           }
9335         }
9336       }
9337     }
9338   }
9339
9340   return change_done_any;
9341 }
9342
9343 static boolean CheckElementChangeExt(int x, int y,
9344                                      int element,
9345                                      int trigger_element,
9346                                      int trigger_event,
9347                                      int trigger_player,
9348                                      int trigger_side)
9349 {
9350   boolean change_done = FALSE;
9351   int p;
9352
9353   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9354       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9355     return FALSE;
9356
9357   if (Feld[x][y] == EL_BLOCKED)
9358   {
9359     Blocked2Moving(x, y, &x, &y);
9360     element = Feld[x][y];
9361   }
9362
9363 #if 0
9364   /* check if element has already changed */
9365   if (Feld[x][y] != element)
9366     return FALSE;
9367 #else
9368   /* check if element has already changed or is about to change after moving */
9369   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9370        Feld[x][y] != element) ||
9371
9372       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9373        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9374         ChangePage[x][y] != -1)))
9375     return FALSE;
9376 #endif
9377
9378   for (p = 0; p < element_info[element].num_change_pages; p++)
9379   {
9380     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9381
9382     boolean check_trigger_element =
9383       (trigger_event == CE_TOUCHING_X ||
9384        trigger_event == CE_HITTING_X ||
9385        trigger_event == CE_HIT_BY_X);
9386
9387     if (change->can_change_or_has_action &&
9388         change->has_event[trigger_event] &&
9389         change->trigger_side & trigger_side &&
9390         change->trigger_player & trigger_player &&
9391         (!check_trigger_element ||
9392          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9393     {
9394       change->actual_trigger_element = trigger_element;
9395       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9396       change->actual_trigger_side = trigger_side;
9397       change->actual_trigger_ce_value = CustomValue[x][y];
9398       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9399
9400       /* special case: trigger element not at (x,y) position for some events */
9401       if (check_trigger_element)
9402       {
9403         static struct
9404         {
9405           int dx, dy;
9406         } move_xy[] =
9407           {
9408             {  0,  0 },
9409             { -1,  0 },
9410             { +1,  0 },
9411             {  0,  0 },
9412             {  0, -1 },
9413             {  0,  0 }, { 0, 0 }, { 0, 0 },
9414             {  0, +1 }
9415           };
9416
9417         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9418         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9419
9420         change->actual_trigger_ce_value = CustomValue[xx][yy];
9421         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9422       }
9423
9424       if (change->can_change && !change_done)
9425       {
9426         ChangeDelay[x][y] = 1;
9427         ChangeEvent[x][y] = trigger_event;
9428
9429         HandleElementChange(x, y, p);
9430
9431         change_done = TRUE;
9432       }
9433 #if USE_NEW_DELAYED_ACTION
9434       else if (change->has_action)
9435       {
9436         ExecuteCustomElementAction(x, y, element, p);
9437         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9438       }
9439 #else
9440       if (change->has_action)
9441       {
9442         ExecuteCustomElementAction(x, y, element, p);
9443         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9444       }
9445 #endif
9446     }
9447   }
9448
9449   return change_done;
9450 }
9451
9452 static void PlayPlayerSound(struct PlayerInfo *player)
9453 {
9454   int jx = player->jx, jy = player->jy;
9455   int sound_element = player->artwork_element;
9456   int last_action = player->last_action_waiting;
9457   int action = player->action_waiting;
9458
9459   if (player->is_waiting)
9460   {
9461     if (action != last_action)
9462       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9463     else
9464       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9465   }
9466   else
9467   {
9468     if (action != last_action)
9469       StopSound(element_info[sound_element].sound[last_action]);
9470
9471     if (last_action == ACTION_SLEEPING)
9472       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9473   }
9474 }
9475
9476 static void PlayAllPlayersSound()
9477 {
9478   int i;
9479
9480   for (i = 0; i < MAX_PLAYERS; i++)
9481     if (stored_player[i].active)
9482       PlayPlayerSound(&stored_player[i]);
9483 }
9484
9485 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9486 {
9487   boolean last_waiting = player->is_waiting;
9488   int move_dir = player->MovDir;
9489
9490   player->dir_waiting = move_dir;
9491   player->last_action_waiting = player->action_waiting;
9492
9493   if (is_waiting)
9494   {
9495     if (!last_waiting)          /* not waiting -> waiting */
9496     {
9497       player->is_waiting = TRUE;
9498
9499       player->frame_counter_bored =
9500         FrameCounter +
9501         game.player_boring_delay_fixed +
9502         SimpleRND(game.player_boring_delay_random);
9503       player->frame_counter_sleeping =
9504         FrameCounter +
9505         game.player_sleeping_delay_fixed +
9506         SimpleRND(game.player_sleeping_delay_random);
9507
9508 #if 1
9509       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9510 #else
9511       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9512 #endif
9513     }
9514
9515     if (game.player_sleeping_delay_fixed +
9516         game.player_sleeping_delay_random > 0 &&
9517         player->anim_delay_counter == 0 &&
9518         player->post_delay_counter == 0 &&
9519         FrameCounter >= player->frame_counter_sleeping)
9520       player->is_sleeping = TRUE;
9521     else if (game.player_boring_delay_fixed +
9522              game.player_boring_delay_random > 0 &&
9523              FrameCounter >= player->frame_counter_bored)
9524       player->is_bored = TRUE;
9525
9526     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9527                               player->is_bored ? ACTION_BORING :
9528                               ACTION_WAITING);
9529
9530 #if 1
9531     if (player->is_sleeping && player->use_murphy)
9532     {
9533       /* special case for sleeping Murphy when leaning against non-free tile */
9534
9535       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9536           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9537            !IS_MOVING(player->jx - 1, player->jy)))
9538         move_dir = MV_LEFT;
9539       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9540                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9541                 !IS_MOVING(player->jx + 1, player->jy)))
9542         move_dir = MV_RIGHT;
9543       else
9544         player->is_sleeping = FALSE;
9545
9546       player->dir_waiting = move_dir;
9547     }
9548 #endif
9549
9550     if (player->is_sleeping)
9551     {
9552       if (player->num_special_action_sleeping > 0)
9553       {
9554         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9555         {
9556           int last_special_action = player->special_action_sleeping;
9557           int num_special_action = player->num_special_action_sleeping;
9558           int special_action =
9559             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9560              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9561              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9562              last_special_action + 1 : ACTION_SLEEPING);
9563           int special_graphic =
9564             el_act_dir2img(player->artwork_element, special_action, move_dir);
9565
9566           player->anim_delay_counter =
9567             graphic_info[special_graphic].anim_delay_fixed +
9568             SimpleRND(graphic_info[special_graphic].anim_delay_random);
9569           player->post_delay_counter =
9570             graphic_info[special_graphic].post_delay_fixed +
9571             SimpleRND(graphic_info[special_graphic].post_delay_random);
9572
9573           player->special_action_sleeping = special_action;
9574         }
9575
9576         if (player->anim_delay_counter > 0)
9577         {
9578           player->action_waiting = player->special_action_sleeping;
9579           player->anim_delay_counter--;
9580         }
9581         else if (player->post_delay_counter > 0)
9582         {
9583           player->post_delay_counter--;
9584         }
9585       }
9586     }
9587     else if (player->is_bored)
9588     {
9589       if (player->num_special_action_bored > 0)
9590       {
9591         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9592         {
9593           int special_action =
9594             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9595           int special_graphic =
9596             el_act_dir2img(player->artwork_element, special_action, move_dir);
9597
9598           player->anim_delay_counter =
9599             graphic_info[special_graphic].anim_delay_fixed +
9600             SimpleRND(graphic_info[special_graphic].anim_delay_random);
9601           player->post_delay_counter =
9602             graphic_info[special_graphic].post_delay_fixed +
9603             SimpleRND(graphic_info[special_graphic].post_delay_random);
9604
9605           player->special_action_bored = special_action;
9606         }
9607
9608         if (player->anim_delay_counter > 0)
9609         {
9610           player->action_waiting = player->special_action_bored;
9611           player->anim_delay_counter--;
9612         }
9613         else if (player->post_delay_counter > 0)
9614         {
9615           player->post_delay_counter--;
9616         }
9617       }
9618     }
9619   }
9620   else if (last_waiting)        /* waiting -> not waiting */
9621   {
9622     player->is_waiting = FALSE;
9623     player->is_bored = FALSE;
9624     player->is_sleeping = FALSE;
9625
9626     player->frame_counter_bored = -1;
9627     player->frame_counter_sleeping = -1;
9628
9629     player->anim_delay_counter = 0;
9630     player->post_delay_counter = 0;
9631
9632     player->dir_waiting = player->MovDir;
9633     player->action_waiting = ACTION_DEFAULT;
9634
9635     player->special_action_bored = ACTION_DEFAULT;
9636     player->special_action_sleeping = ACTION_DEFAULT;
9637   }
9638 }
9639
9640 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9641 {
9642   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9643   int left      = player_action & JOY_LEFT;
9644   int right     = player_action & JOY_RIGHT;
9645   int up        = player_action & JOY_UP;
9646   int down      = player_action & JOY_DOWN;
9647   int button1   = player_action & JOY_BUTTON_1;
9648   int button2   = player_action & JOY_BUTTON_2;
9649   int dx        = (left ? -1 : right ? 1 : 0);
9650   int dy        = (up   ? -1 : down  ? 1 : 0);
9651
9652   if (!player->active || tape.pausing)
9653     return 0;
9654
9655   if (player_action)
9656   {
9657     if (button1)
9658       snapped = SnapField(player, dx, dy);
9659     else
9660     {
9661       if (button2)
9662         dropped = DropElement(player);
9663
9664       moved = MovePlayer(player, dx, dy);
9665     }
9666
9667     if (tape.single_step && tape.recording && !tape.pausing)
9668     {
9669       if (button1 || (dropped && !moved))
9670       {
9671         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9672         SnapField(player, 0, 0);                /* stop snapping */
9673       }
9674     }
9675
9676     SetPlayerWaiting(player, FALSE);
9677
9678     return player_action;
9679   }
9680   else
9681   {
9682     /* no actions for this player (no input at player's configured device) */
9683
9684     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9685     SnapField(player, 0, 0);
9686     CheckGravityMovementWhenNotMoving(player);
9687
9688     if (player->MovPos == 0)
9689       SetPlayerWaiting(player, TRUE);
9690
9691     if (player->MovPos == 0)    /* needed for tape.playing */
9692       player->is_moving = FALSE;
9693
9694     player->is_dropping = FALSE;
9695     player->is_dropping_pressed = FALSE;
9696     player->drop_pressed_delay = 0;
9697
9698     return 0;
9699   }
9700 }
9701
9702 static void CheckLevelTime()
9703 {
9704   int i;
9705
9706   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9707   {
9708     if (level.native_em_level->lev->home == 0)  /* all players at home */
9709     {
9710       local_player->LevelSolved = TRUE;
9711       AllPlayersGone = TRUE;
9712
9713       level.native_em_level->lev->home = -1;
9714     }
9715
9716     if (level.native_em_level->ply[0]->alive == 0 &&
9717         level.native_em_level->ply[1]->alive == 0 &&
9718         level.native_em_level->ply[2]->alive == 0 &&
9719         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9720       AllPlayersGone = TRUE;
9721   }
9722
9723   if (TimeFrames >= FRAMES_PER_SECOND)
9724   {
9725     TimeFrames = 0;
9726     TapeTime++;
9727
9728     for (i = 0; i < MAX_PLAYERS; i++)
9729     {
9730       struct PlayerInfo *player = &stored_player[i];
9731
9732       if (SHIELD_ON(player))
9733       {
9734         player->shield_normal_time_left--;
9735
9736         if (player->shield_deadly_time_left > 0)
9737           player->shield_deadly_time_left--;
9738       }
9739     }
9740
9741     if (!level.use_step_counter)
9742     {
9743       TimePlayed++;
9744
9745       if (TimeLeft > 0)
9746       {
9747         TimeLeft--;
9748
9749         if (TimeLeft <= 10 && setup.time_limit)
9750           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9751
9752         DrawGameValue_Time(TimeLeft);
9753
9754         if (!TimeLeft && setup.time_limit)
9755         {
9756           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9757             level.native_em_level->lev->killed_out_of_time = TRUE;
9758           else
9759             for (i = 0; i < MAX_PLAYERS; i++)
9760               KillPlayer(&stored_player[i]);
9761         }
9762       }
9763       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9764         DrawGameValue_Time(TimePlayed);
9765
9766       level.native_em_level->lev->time =
9767         (level.time == 0 ? TimePlayed : TimeLeft);
9768     }
9769
9770     if (tape.recording || tape.playing)
9771       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9772   }
9773 }
9774
9775 void AdvanceFrameAndPlayerCounters(int player_nr)
9776 {
9777   int i;
9778
9779 #if 0
9780   Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9781         FrameCounter, FrameCounter + 1);
9782 #endif
9783
9784   /* advance frame counters (global frame counter and time frame counter) */
9785   FrameCounter++;
9786   TimeFrames++;
9787
9788   /* advance player counters (counters for move delay, move animation etc.) */
9789   for (i = 0; i < MAX_PLAYERS; i++)
9790   {
9791     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9792     int move_delay_value = stored_player[i].move_delay_value;
9793     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9794
9795     if (!advance_player_counters)       /* not all players may be affected */
9796       continue;
9797
9798 #if USE_NEW_PLAYER_ANIM
9799     if (move_frames == 0)       /* less than one move per game frame */
9800     {
9801       int stepsize = TILEX / move_delay_value;
9802       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9803       int count = (stored_player[i].is_moving ?
9804                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9805
9806       if (count % delay == 0)
9807         move_frames = 1;
9808     }
9809 #endif
9810
9811     stored_player[i].Frame += move_frames;
9812
9813     if (stored_player[i].MovPos != 0)
9814       stored_player[i].StepFrame += move_frames;
9815
9816     if (stored_player[i].move_delay > 0)
9817       stored_player[i].move_delay--;
9818
9819     /* due to bugs in previous versions, counter must count up, not down */
9820     if (stored_player[i].push_delay != -1)
9821       stored_player[i].push_delay++;
9822
9823     if (stored_player[i].drop_delay > 0)
9824       stored_player[i].drop_delay--;
9825
9826     if (stored_player[i].is_dropping_pressed)
9827       stored_player[i].drop_pressed_delay++;
9828   }
9829 }
9830
9831 void StartGameActions(boolean init_network_game, boolean record_tape,
9832                       long random_seed)
9833 {
9834   unsigned long new_random_seed = InitRND(random_seed);
9835
9836   if (record_tape)
9837     TapeStartRecording(new_random_seed);
9838
9839 #if defined(NETWORK_AVALIABLE)
9840   if (init_network_game)
9841   {
9842     SendToServer_StartPlaying();
9843
9844     return;
9845   }
9846 #endif
9847
9848   InitGame();
9849 }
9850
9851 void GameActions()
9852 {
9853   static unsigned long game_frame_delay = 0;
9854   unsigned long game_frame_delay_value;
9855   byte *recorded_player_action;
9856   byte summarized_player_action = 0;
9857   byte tape_action[MAX_PLAYERS];
9858   int i;
9859
9860   if (game.restart_level)
9861     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9862
9863   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9864   {
9865     if (level.native_em_level->lev->home == 0)  /* all players at home */
9866     {
9867       local_player->LevelSolved = TRUE;
9868       AllPlayersGone = TRUE;
9869
9870       level.native_em_level->lev->home = -1;
9871     }
9872
9873     if (level.native_em_level->ply[0]->alive == 0 &&
9874         level.native_em_level->ply[1]->alive == 0 &&
9875         level.native_em_level->ply[2]->alive == 0 &&
9876         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9877       AllPlayersGone = TRUE;
9878   }
9879
9880   if (local_player->LevelSolved)
9881     GameWon();
9882
9883   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9884     TapeStop();
9885
9886   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9887     return;
9888
9889   game_frame_delay_value =
9890     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9891
9892   if (tape.playing && tape.warp_forward && !tape.pausing)
9893     game_frame_delay_value = 0;
9894
9895   /* ---------- main game synchronization point ---------- */
9896
9897   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9898
9899   if (network_playing && !network_player_action_received)
9900   {
9901     /* try to get network player actions in time */
9902
9903 #if defined(NETWORK_AVALIABLE)
9904     /* last chance to get network player actions without main loop delay */
9905     HandleNetworking();
9906 #endif
9907
9908     /* game was quit by network peer */
9909     if (game_status != GAME_MODE_PLAYING)
9910       return;
9911
9912     if (!network_player_action_received)
9913       return;           /* failed to get network player actions in time */
9914
9915     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9916   }
9917
9918   if (tape.pausing)
9919     return;
9920
9921   /* at this point we know that we really continue executing the game */
9922
9923 #if 1
9924   network_player_action_received = FALSE;
9925 #endif
9926
9927   /* when playing tape, read previously recorded player input from tape data */
9928   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9929
9930 #if 1
9931   /* TapePlayAction() may return NULL when toggling to "pause before death" */
9932   if (tape.pausing)
9933     return;
9934 #endif
9935
9936 #if 0
9937   if (tape.playing)
9938   {
9939     if (recorded_player_action == NULL)
9940       printf("!!! THIS SHOULD NOT HAPPEN !!!\n");
9941     else
9942       printf("::: %05d: TAPE PLAYING: %08x\n",
9943              FrameCounter, recorded_player_action[0]);
9944   }
9945 #endif
9946
9947   if (tape.set_centered_player)
9948   {
9949     game.centered_player_nr_next = tape.centered_player_nr_next;
9950     game.set_centered_player = TRUE;
9951   }
9952
9953   for (i = 0; i < MAX_PLAYERS; i++)
9954   {
9955     summarized_player_action |= stored_player[i].action;
9956
9957     if (!network_playing)
9958       stored_player[i].effective_action = stored_player[i].action;
9959   }
9960
9961 #if defined(NETWORK_AVALIABLE)
9962   if (network_playing)
9963     SendToServer_MovePlayer(summarized_player_action);
9964 #endif
9965
9966   if (!options.network && !setup.team_mode)
9967     local_player->effective_action = summarized_player_action;
9968
9969   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9970   {
9971     for (i = 0; i < MAX_PLAYERS; i++)
9972       stored_player[i].effective_action =
9973         (i == game.centered_player_nr ? summarized_player_action : 0);
9974   }
9975
9976   if (recorded_player_action != NULL)
9977     for (i = 0; i < MAX_PLAYERS; i++)
9978       stored_player[i].effective_action = recorded_player_action[i];
9979
9980   for (i = 0; i < MAX_PLAYERS; i++)
9981   {
9982     tape_action[i] = stored_player[i].effective_action;
9983
9984     /* (this can only happen in the R'n'D game engine) */
9985     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9986       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9987   }
9988
9989   /* only record actions from input devices, but not programmed actions */
9990   if (tape.recording)
9991     TapeRecordAction(tape_action);
9992
9993 #if 0
9994   if (tape.recording)
9995     printf("::: %05d: TAPE RECORDING: %08x\n",
9996            FrameCounter, tape_action[0]);
9997 #endif
9998
9999   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10000   {
10001     GameActions_EM_Main();
10002   }
10003   else
10004   {
10005     GameActions_RND();
10006   }
10007 }
10008
10009 void GameActions_EM_Main()
10010 {
10011   byte effective_action[MAX_PLAYERS];
10012   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
10013   int i;
10014
10015   for (i = 0; i < MAX_PLAYERS; i++)
10016     effective_action[i] = stored_player[i].effective_action;
10017
10018 #if 0
10019   printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
10020 #endif
10021
10022   GameActions_EM(effective_action, warp_mode);
10023
10024   CheckLevelTime();
10025
10026   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10027 }
10028
10029 void GameActions_RND()
10030 {
10031   int magic_wall_x = 0, magic_wall_y = 0;
10032   int i, x, y, element, graphic;
10033
10034   InitPlayfieldScanModeVars();
10035
10036 #if USE_ONE_MORE_CHANGE_PER_FRAME
10037   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10038   {
10039     SCAN_PLAYFIELD(x, y)
10040     {
10041       ChangeCount[x][y] = 0;
10042       ChangeEvent[x][y] = -1;
10043     }
10044   }
10045 #endif
10046
10047 #if 1
10048   if (game.set_centered_player)
10049   {
10050     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10051
10052     /* switching to "all players" only possible if all players fit to screen */
10053     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10054     {
10055       game.centered_player_nr_next = game.centered_player_nr;
10056       game.set_centered_player = FALSE;
10057     }
10058
10059     /* do not switch focus to non-existing (or non-active) player */
10060     if (game.centered_player_nr_next >= 0 &&
10061         !stored_player[game.centered_player_nr_next].active)
10062     {
10063       game.centered_player_nr_next = game.centered_player_nr;
10064       game.set_centered_player = FALSE;
10065     }
10066   }
10067
10068   if (game.set_centered_player &&
10069       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10070   {
10071     int sx, sy;
10072
10073     if (game.centered_player_nr_next == -1)
10074     {
10075       setScreenCenteredToAllPlayers(&sx, &sy);
10076     }
10077     else
10078     {
10079       sx = stored_player[game.centered_player_nr_next].jx;
10080       sy = stored_player[game.centered_player_nr_next].jy;
10081     }
10082
10083     game.centered_player_nr = game.centered_player_nr_next;
10084     game.set_centered_player = FALSE;
10085
10086     DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
10087     DrawGameDoorValues();
10088   }
10089 #endif
10090
10091   for (i = 0; i < MAX_PLAYERS; i++)
10092   {
10093     int actual_player_action = stored_player[i].effective_action;
10094
10095 #if 1
10096     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10097        - rnd_equinox_tetrachloride 048
10098        - rnd_equinox_tetrachloride_ii 096
10099        - rnd_emanuel_schmieg 002
10100        - doctor_sloan_ww 001, 020
10101     */
10102     if (stored_player[i].MovPos == 0)
10103       CheckGravityMovement(&stored_player[i]);
10104 #endif
10105
10106     /* overwrite programmed action with tape action */
10107     if (stored_player[i].programmed_action)
10108       actual_player_action = stored_player[i].programmed_action;
10109
10110 #if 1
10111     PlayerActions(&stored_player[i], actual_player_action);
10112 #else
10113     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
10114
10115     if (tape.recording && tape_action[i] && !tape.player_participates[i])
10116       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
10117 #endif
10118
10119     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10120   }
10121
10122 #if 0
10123   network_player_action_received = FALSE;
10124 #endif
10125
10126   ScrollScreen(NULL, SCROLL_GO_ON);
10127
10128   /* for backwards compatibility, the following code emulates a fixed bug that
10129      occured when pushing elements (causing elements that just made their last
10130      pushing step to already (if possible) make their first falling step in the
10131      same game frame, which is bad); this code is also needed to use the famous
10132      "spring push bug" which is used in older levels and might be wanted to be
10133      used also in newer levels, but in this case the buggy pushing code is only
10134      affecting the "spring" element and no other elements */
10135
10136   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10137   {
10138     for (i = 0; i < MAX_PLAYERS; i++)
10139     {
10140       struct PlayerInfo *player = &stored_player[i];
10141       int x = player->jx;
10142       int y = player->jy;
10143
10144       if (player->active && player->is_pushing && player->is_moving &&
10145           IS_MOVING(x, y) &&
10146           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10147            Feld[x][y] == EL_SPRING))
10148       {
10149         ContinueMoving(x, y);
10150
10151         /* continue moving after pushing (this is actually a bug) */
10152         if (!IS_MOVING(x, y))
10153         {
10154           Stop[x][y] = FALSE;
10155         }
10156       }
10157     }
10158   }
10159
10160 #if 1
10161   SCAN_PLAYFIELD(x, y)
10162 #else
10163   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10164 #endif
10165   {
10166     ChangeCount[x][y] = 0;
10167     ChangeEvent[x][y] = -1;
10168
10169     /* this must be handled before main playfield loop */
10170     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10171     {
10172       MovDelay[x][y]--;
10173       if (MovDelay[x][y] <= 0)
10174         RemoveField(x, y);
10175     }
10176
10177 #if USE_NEW_SNAP_DELAY
10178     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10179     {
10180       MovDelay[x][y]--;
10181       if (MovDelay[x][y] <= 0)
10182       {
10183         RemoveField(x, y);
10184         DrawLevelField(x, y);
10185
10186         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10187       }
10188     }
10189 #endif
10190
10191 #if DEBUG
10192     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10193     {
10194       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10195       printf("GameActions(): This should never happen!\n");
10196
10197       ChangePage[x][y] = -1;
10198     }
10199 #endif
10200
10201     Stop[x][y] = FALSE;
10202     if (WasJustMoving[x][y] > 0)
10203       WasJustMoving[x][y]--;
10204     if (WasJustFalling[x][y] > 0)
10205       WasJustFalling[x][y]--;
10206     if (CheckCollision[x][y] > 0)
10207       CheckCollision[x][y]--;
10208
10209     GfxFrame[x][y]++;
10210
10211     /* reset finished pushing action (not done in ContinueMoving() to allow
10212        continuous pushing animation for elements with zero push delay) */
10213     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10214     {
10215       ResetGfxAnimation(x, y);
10216       DrawLevelField(x, y);
10217     }
10218
10219 #if DEBUG
10220     if (IS_BLOCKED(x, y))
10221     {
10222       int oldx, oldy;
10223
10224       Blocked2Moving(x, y, &oldx, &oldy);
10225       if (!IS_MOVING(oldx, oldy))
10226       {
10227         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10228         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10229         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10230         printf("GameActions(): This should never happen!\n");
10231       }
10232     }
10233 #endif
10234   }
10235
10236 #if 1
10237   SCAN_PLAYFIELD(x, y)
10238 #else
10239   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10240 #endif
10241   {
10242     element = Feld[x][y];
10243     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10244
10245 #if 0
10246     printf("::: %d,%d\n", x, y);
10247
10248     if (element == EL_ROCK)
10249       printf("::: Yo man! Rocks can fall!\n");
10250 #endif
10251
10252 #if 1
10253     ResetGfxFrame(x, y, TRUE);
10254 #else
10255     if (graphic_info[graphic].anim_global_sync)
10256       GfxFrame[x][y] = FrameCounter;
10257     else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
10258     {
10259       int old_gfx_frame = GfxFrame[x][y];
10260
10261       GfxFrame[x][y] = CustomValue[x][y];
10262
10263 #if 1
10264       if (GfxFrame[x][y] != old_gfx_frame)
10265 #endif
10266         DrawLevelGraphicAnimation(x, y, graphic);
10267     }
10268     else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
10269     {
10270       int old_gfx_frame = GfxFrame[x][y];
10271
10272       GfxFrame[x][y] = element_info[element].collect_score;
10273
10274 #if 1
10275       if (GfxFrame[x][y] != old_gfx_frame)
10276 #endif
10277         DrawLevelGraphicAnimation(x, y, graphic);
10278     }
10279     else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
10280     {
10281       int old_gfx_frame = GfxFrame[x][y];
10282
10283       GfxFrame[x][y] = ChangeDelay[x][y];
10284
10285 #if 1
10286       if (GfxFrame[x][y] != old_gfx_frame)
10287 #endif
10288         DrawLevelGraphicAnimation(x, y, graphic);
10289     }
10290 #endif
10291
10292     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10293         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10294       ResetRandomAnimationValue(x, y);
10295
10296     SetRandomAnimationValue(x, y);
10297
10298     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10299
10300     if (IS_INACTIVE(element))
10301     {
10302       if (IS_ANIMATED(graphic))
10303         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10304
10305       continue;
10306     }
10307
10308     /* this may take place after moving, so 'element' may have changed */
10309     if (IS_CHANGING(x, y) &&
10310         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10311     {
10312       int page = element_info[element].event_page_nr[CE_DELAY];
10313 #if 0
10314       HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
10315 #else
10316
10317 #if 0
10318       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10319 #endif
10320
10321 #if 0
10322       if (element == EL_CUSTOM_255)
10323         printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10324 #endif
10325
10326 #if 1
10327       HandleElementChange(x, y, page);
10328 #else
10329       if (CAN_CHANGE(element))
10330         HandleElementChange(x, y, page);
10331
10332       if (HAS_ACTION(element))
10333         ExecuteCustomElementAction(x, y, element, page);
10334 #endif
10335
10336 #endif
10337
10338       element = Feld[x][y];
10339       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10340     }
10341
10342     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10343     {
10344       StartMoving(x, y);
10345
10346       element = Feld[x][y];
10347       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10348
10349       if (IS_ANIMATED(graphic) &&
10350           !IS_MOVING(x, y) &&
10351           !Stop[x][y])
10352         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10353
10354       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10355         EdelsteinFunkeln(x, y);
10356     }
10357     else if ((element == EL_ACID ||
10358               element == EL_EXIT_OPEN ||
10359               element == EL_SP_EXIT_OPEN ||
10360               element == EL_SP_TERMINAL ||
10361               element == EL_SP_TERMINAL_ACTIVE ||
10362               element == EL_EXTRA_TIME ||
10363               element == EL_SHIELD_NORMAL ||
10364               element == EL_SHIELD_DEADLY) &&
10365              IS_ANIMATED(graphic))
10366       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10367     else if (IS_MOVING(x, y))
10368       ContinueMoving(x, y);
10369     else if (IS_ACTIVE_BOMB(element))
10370       CheckDynamite(x, y);
10371     else if (element == EL_AMOEBA_GROWING)
10372       AmoebeWaechst(x, y);
10373     else if (element == EL_AMOEBA_SHRINKING)
10374       AmoebaDisappearing(x, y);
10375
10376 #if !USE_NEW_AMOEBA_CODE
10377     else if (IS_AMOEBALIVE(element))
10378       AmoebeAbleger(x, y);
10379 #endif
10380
10381     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10382       Life(x, y);
10383     else if (element == EL_EXIT_CLOSED)
10384       CheckExit(x, y);
10385     else if (element == EL_SP_EXIT_CLOSED)
10386       CheckExitSP(x, y);
10387     else if (element == EL_EXPANDABLE_WALL_GROWING)
10388       MauerWaechst(x, y);
10389     else if (element == EL_EXPANDABLE_WALL ||
10390              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10391              element == EL_EXPANDABLE_WALL_VERTICAL ||
10392              element == EL_EXPANDABLE_WALL_ANY ||
10393              element == EL_BD_EXPANDABLE_WALL)
10394       MauerAbleger(x, y);
10395     else if (element == EL_FLAMES)
10396       CheckForDragon(x, y);
10397     else if (element == EL_EXPLOSION)
10398       ; /* drawing of correct explosion animation is handled separately */
10399     else if (element == EL_ELEMENT_SNAPPING ||
10400              element == EL_DIAGONAL_SHRINKING ||
10401              element == EL_DIAGONAL_GROWING)
10402     {
10403 #if 1
10404       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10405
10406       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10407 #endif
10408     }
10409     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10410       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10411
10412 #if 0
10413     if (element == EL_CUSTOM_255 ||
10414         element == EL_CUSTOM_256)
10415       DrawLevelGraphicAnimation(x, y, graphic);
10416 #endif
10417
10418     if (IS_BELT_ACTIVE(element))
10419       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10420
10421     if (game.magic_wall_active)
10422     {
10423       int jx = local_player->jx, jy = local_player->jy;
10424
10425       /* play the element sound at the position nearest to the player */
10426       if ((element == EL_MAGIC_WALL_FULL ||
10427            element == EL_MAGIC_WALL_ACTIVE ||
10428            element == EL_MAGIC_WALL_EMPTYING ||
10429            element == EL_BD_MAGIC_WALL_FULL ||
10430            element == EL_BD_MAGIC_WALL_ACTIVE ||
10431            element == EL_BD_MAGIC_WALL_EMPTYING) &&
10432           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10433       {
10434         magic_wall_x = x;
10435         magic_wall_y = y;
10436       }
10437     }
10438   }
10439
10440 #if USE_NEW_AMOEBA_CODE
10441   /* new experimental amoeba growth stuff */
10442   if (!(FrameCounter % 8))
10443   {
10444     static unsigned long random = 1684108901;
10445
10446     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10447     {
10448       x = RND(lev_fieldx);
10449       y = RND(lev_fieldy);
10450       element = Feld[x][y];
10451
10452       if (!IS_PLAYER(x,y) &&
10453           (element == EL_EMPTY ||
10454            CAN_GROW_INTO(element) ||
10455            element == EL_QUICKSAND_EMPTY ||
10456            element == EL_ACID_SPLASH_LEFT ||
10457            element == EL_ACID_SPLASH_RIGHT))
10458       {
10459         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10460             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10461             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10462             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10463           Feld[x][y] = EL_AMOEBA_DROP;
10464       }
10465
10466       random = random * 129 + 1;
10467     }
10468   }
10469 #endif
10470
10471 #if 0
10472   if (game.explosions_delayed)
10473 #endif
10474   {
10475     game.explosions_delayed = FALSE;
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 (ExplodeField[x][y])
10486         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10487       else if (element == EL_EXPLOSION)
10488         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10489
10490       ExplodeField[x][y] = EX_TYPE_NONE;
10491     }
10492
10493     game.explosions_delayed = TRUE;
10494   }
10495
10496   if (game.magic_wall_active)
10497   {
10498     if (!(game.magic_wall_time_left % 4))
10499     {
10500       int element = Feld[magic_wall_x][magic_wall_y];
10501
10502       if (element == EL_BD_MAGIC_WALL_FULL ||
10503           element == EL_BD_MAGIC_WALL_ACTIVE ||
10504           element == EL_BD_MAGIC_WALL_EMPTYING)
10505         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10506       else
10507         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10508     }
10509
10510     if (game.magic_wall_time_left > 0)
10511     {
10512       game.magic_wall_time_left--;
10513       if (!game.magic_wall_time_left)
10514       {
10515 #if 1
10516         SCAN_PLAYFIELD(x, y)
10517 #else
10518         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10519 #endif
10520         {
10521           element = Feld[x][y];
10522
10523           if (element == EL_MAGIC_WALL_ACTIVE ||
10524               element == EL_MAGIC_WALL_FULL)
10525           {
10526             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10527             DrawLevelField(x, y);
10528           }
10529           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10530                    element == EL_BD_MAGIC_WALL_FULL)
10531           {
10532             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10533             DrawLevelField(x, y);
10534           }
10535         }
10536
10537         game.magic_wall_active = FALSE;
10538       }
10539     }
10540   }
10541
10542   if (game.light_time_left > 0)
10543   {
10544     game.light_time_left--;
10545
10546     if (game.light_time_left == 0)
10547       RedrawAllLightSwitchesAndInvisibleElements();
10548   }
10549
10550   if (game.timegate_time_left > 0)
10551   {
10552     game.timegate_time_left--;
10553
10554     if (game.timegate_time_left == 0)
10555       CloseAllOpenTimegates();
10556   }
10557
10558   if (game.lenses_time_left > 0)
10559   {
10560     game.lenses_time_left--;
10561
10562     if (game.lenses_time_left == 0)
10563       RedrawAllInvisibleElementsForLenses();
10564   }
10565
10566   if (game.magnify_time_left > 0)
10567   {
10568     game.magnify_time_left--;
10569
10570     if (game.magnify_time_left == 0)
10571       RedrawAllInvisibleElementsForMagnifier();
10572   }
10573
10574   for (i = 0; i < MAX_PLAYERS; i++)
10575   {
10576     struct PlayerInfo *player = &stored_player[i];
10577
10578     if (SHIELD_ON(player))
10579     {
10580       if (player->shield_deadly_time_left)
10581         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10582       else if (player->shield_normal_time_left)
10583         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10584     }
10585   }
10586
10587   CheckLevelTime();
10588
10589   DrawAllPlayers();
10590   PlayAllPlayersSound();
10591
10592   if (options.debug)                    /* calculate frames per second */
10593   {
10594     static unsigned long fps_counter = 0;
10595     static int fps_frames = 0;
10596     unsigned long fps_delay_ms = Counter() - fps_counter;
10597
10598     fps_frames++;
10599
10600     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10601     {
10602       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10603
10604       fps_frames = 0;
10605       fps_counter = Counter();
10606     }
10607
10608     redraw_mask |= REDRAW_FPS;
10609   }
10610
10611   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10612
10613   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10614   {
10615     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10616
10617     local_player->show_envelope = 0;
10618   }
10619
10620   /* use random number generator in every frame to make it less predictable */
10621   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10622     RND(1);
10623 }
10624
10625 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10626 {
10627   int min_x = x, min_y = y, max_x = x, max_y = y;
10628   int i;
10629
10630   for (i = 0; i < MAX_PLAYERS; i++)
10631   {
10632     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10633
10634     if (!stored_player[i].active || &stored_player[i] == player)
10635       continue;
10636
10637     min_x = MIN(min_x, jx);
10638     min_y = MIN(min_y, jy);
10639     max_x = MAX(max_x, jx);
10640     max_y = MAX(max_y, jy);
10641   }
10642
10643   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10644 }
10645
10646 static boolean AllPlayersInVisibleScreen()
10647 {
10648   int i;
10649
10650   for (i = 0; i < MAX_PLAYERS; i++)
10651   {
10652     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10653
10654     if (!stored_player[i].active)
10655       continue;
10656
10657     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10658       return FALSE;
10659   }
10660
10661   return TRUE;
10662 }
10663
10664 void ScrollLevel(int dx, int dy)
10665 {
10666   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10667   int x, y;
10668
10669   BlitBitmap(drawto_field, drawto_field,
10670              FX + TILEX * (dx == -1) - softscroll_offset,
10671              FY + TILEY * (dy == -1) - softscroll_offset,
10672              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10673              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10674              FX + TILEX * (dx == 1) - softscroll_offset,
10675              FY + TILEY * (dy == 1) - softscroll_offset);
10676
10677   if (dx)
10678   {
10679     x = (dx == 1 ? BX1 : BX2);
10680     for (y = BY1; y <= BY2; y++)
10681       DrawScreenField(x, y);
10682   }
10683
10684   if (dy)
10685   {
10686     y = (dy == 1 ? BY1 : BY2);
10687     for (x = BX1; x <= BX2; x++)
10688       DrawScreenField(x, y);
10689   }
10690
10691   redraw_mask |= REDRAW_FIELD;
10692 }
10693
10694 static boolean canFallDown(struct PlayerInfo *player)
10695 {
10696   int jx = player->jx, jy = player->jy;
10697
10698   return (IN_LEV_FIELD(jx, jy + 1) &&
10699           (IS_FREE(jx, jy + 1) ||
10700            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10701           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10702           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10703 }
10704
10705 static boolean canPassField(int x, int y, int move_dir)
10706 {
10707   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10708   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10709   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10710   int nextx = x + dx;
10711   int nexty = y + dy;
10712   int element = Feld[x][y];
10713
10714   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10715           !CAN_MOVE(element) &&
10716           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10717           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10718           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10719 }
10720
10721 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10722 {
10723   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10724   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10725   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10726   int newx = x + dx;
10727   int newy = y + dy;
10728
10729   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10730           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10731           (IS_DIGGABLE(Feld[newx][newy]) ||
10732            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10733            canPassField(newx, newy, move_dir)));
10734 }
10735
10736 static void CheckGravityMovement(struct PlayerInfo *player)
10737 {
10738 #if USE_PLAYER_GRAVITY
10739   if (player->gravity && !player->programmed_action)
10740 #else
10741   if (game.gravity && !player->programmed_action)
10742 #endif
10743   {
10744     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10745     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10746     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10747     int jx = player->jx, jy = player->jy;
10748     boolean player_is_moving_to_valid_field =
10749       (!player_is_snapping &&
10750        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10751         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10752     boolean player_can_fall_down = canFallDown(player);
10753
10754     if (player_can_fall_down &&
10755         !player_is_moving_to_valid_field)
10756       player->programmed_action = MV_DOWN;
10757   }
10758 }
10759
10760 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10761 {
10762   return CheckGravityMovement(player);
10763
10764 #if USE_PLAYER_GRAVITY
10765   if (player->gravity && !player->programmed_action)
10766 #else
10767   if (game.gravity && !player->programmed_action)
10768 #endif
10769   {
10770     int jx = player->jx, jy = player->jy;
10771     boolean field_under_player_is_free =
10772       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10773     boolean player_is_standing_on_valid_field =
10774       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10775        (IS_WALKABLE(Feld[jx][jy]) &&
10776         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10777
10778     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10779       player->programmed_action = MV_DOWN;
10780   }
10781 }
10782
10783 /*
10784   MovePlayerOneStep()
10785   -----------------------------------------------------------------------------
10786   dx, dy:               direction (non-diagonal) to try to move the player to
10787   real_dx, real_dy:     direction as read from input device (can be diagonal)
10788 */
10789
10790 boolean MovePlayerOneStep(struct PlayerInfo *player,
10791                           int dx, int dy, int real_dx, int real_dy)
10792 {
10793   int jx = player->jx, jy = player->jy;
10794   int new_jx = jx + dx, new_jy = jy + dy;
10795 #if !USE_FIXED_DONT_RUN_INTO
10796   int element;
10797 #endif
10798   int can_move;
10799   boolean player_can_move = !player->cannot_move;
10800
10801   if (!player->active || (!dx && !dy))
10802     return MP_NO_ACTION;
10803
10804   player->MovDir = (dx < 0 ? MV_LEFT :
10805                     dx > 0 ? MV_RIGHT :
10806                     dy < 0 ? MV_UP :
10807                     dy > 0 ? MV_DOWN :  MV_NONE);
10808
10809   if (!IN_LEV_FIELD(new_jx, new_jy))
10810     return MP_NO_ACTION;
10811
10812   if (!player_can_move)
10813   {
10814 #if 1
10815     if (player->MovPos == 0)
10816     {
10817       player->is_moving = FALSE;
10818       player->is_digging = FALSE;
10819       player->is_collecting = FALSE;
10820       player->is_snapping = FALSE;
10821       player->is_pushing = FALSE;
10822     }
10823 #else
10824     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10825     SnapField(player, 0, 0);
10826 #endif
10827
10828 #if 0
10829     return MP_NO_ACTION;
10830 #endif
10831   }
10832
10833 #if 1
10834   if (!options.network && game.centered_player_nr == -1 &&
10835       !AllPlayersInSight(player, new_jx, new_jy))
10836     return MP_NO_ACTION;
10837 #else
10838   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10839     return MP_NO_ACTION;
10840 #endif
10841
10842 #if !USE_FIXED_DONT_RUN_INTO
10843   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10844
10845   /* (moved to DigField()) */
10846   if (player_can_move && DONT_RUN_INTO(element))
10847   {
10848     if (element == EL_ACID && dx == 0 && dy == 1)
10849     {
10850       SplashAcid(new_jx, new_jy);
10851       Feld[jx][jy] = EL_PLAYER_1;
10852       InitMovingField(jx, jy, MV_DOWN);
10853       Store[jx][jy] = EL_ACID;
10854       ContinueMoving(jx, jy);
10855       BuryPlayer(player);
10856     }
10857     else
10858       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10859
10860     return MP_MOVING;
10861   }
10862 #endif
10863
10864   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10865 #if 0
10866 #if USE_FIXED_DONT_RUN_INTO
10867   if (can_move == MP_DONT_RUN_INTO)
10868     return MP_MOVING;
10869 #endif
10870 #endif
10871   if (can_move != MP_MOVING)
10872     return can_move;
10873
10874 #if USE_FIXED_DONT_RUN_INTO
10875 #endif
10876
10877   /* check if DigField() has caused relocation of the player */
10878   if (player->jx != jx || player->jy != jy)
10879     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10880
10881   StorePlayer[jx][jy] = 0;
10882   player->last_jx = jx;
10883   player->last_jy = jy;
10884   player->jx = new_jx;
10885   player->jy = new_jy;
10886   StorePlayer[new_jx][new_jy] = player->element_nr;
10887
10888   if (player->move_delay_value_next != -1)
10889   {
10890     player->move_delay_value = player->move_delay_value_next;
10891     player->move_delay_value_next = -1;
10892   }
10893
10894   player->MovPos =
10895     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10896
10897   player->step_counter++;
10898
10899   PlayerVisit[jx][jy] = FrameCounter;
10900
10901 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10902   player->is_moving = TRUE;
10903 #endif
10904
10905 #if 1
10906   /* should better be called in MovePlayer(), but this breaks some tapes */
10907   ScrollPlayer(player, SCROLL_INIT);
10908 #endif
10909
10910   return MP_MOVING;
10911 }
10912
10913 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10914 {
10915   int jx = player->jx, jy = player->jy;
10916   int old_jx = jx, old_jy = jy;
10917   int moved = MP_NO_ACTION;
10918
10919   if (!player->active)
10920     return FALSE;
10921
10922   if (!dx && !dy)
10923   {
10924     if (player->MovPos == 0)
10925     {
10926       player->is_moving = FALSE;
10927       player->is_digging = FALSE;
10928       player->is_collecting = FALSE;
10929       player->is_snapping = FALSE;
10930       player->is_pushing = FALSE;
10931     }
10932
10933     return FALSE;
10934   }
10935
10936   if (player->move_delay > 0)
10937     return FALSE;
10938
10939   player->move_delay = -1;              /* set to "uninitialized" value */
10940
10941   /* store if player is automatically moved to next field */
10942   player->is_auto_moving = (player->programmed_action != MV_NONE);
10943
10944   /* remove the last programmed player action */
10945   player->programmed_action = 0;
10946
10947   if (player->MovPos)
10948   {
10949     /* should only happen if pre-1.2 tape recordings are played */
10950     /* this is only for backward compatibility */
10951
10952     int original_move_delay_value = player->move_delay_value;
10953
10954 #if DEBUG
10955     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10956            tape.counter);
10957 #endif
10958
10959     /* scroll remaining steps with finest movement resolution */
10960     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10961
10962     while (player->MovPos)
10963     {
10964       ScrollPlayer(player, SCROLL_GO_ON);
10965       ScrollScreen(NULL, SCROLL_GO_ON);
10966
10967       AdvanceFrameAndPlayerCounters(player->index_nr);
10968
10969       DrawAllPlayers();
10970       BackToFront();
10971     }
10972
10973     player->move_delay_value = original_move_delay_value;
10974   }
10975
10976   player->is_active = FALSE;
10977
10978   if (player->last_move_dir & MV_HORIZONTAL)
10979   {
10980     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10981       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10982   }
10983   else
10984   {
10985     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10986       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10987   }
10988
10989 #if USE_FIXED_BORDER_RUNNING_GFX
10990   if (!moved && !player->is_active)
10991   {
10992     player->is_moving = FALSE;
10993     player->is_digging = FALSE;
10994     player->is_collecting = FALSE;
10995     player->is_snapping = FALSE;
10996     player->is_pushing = FALSE;
10997   }
10998 #endif
10999
11000   jx = player->jx;
11001   jy = player->jy;
11002
11003 #if 1
11004   if (moved & MP_MOVING && !ScreenMovPos &&
11005       (player->index_nr == game.centered_player_nr ||
11006        game.centered_player_nr == -1))
11007 #else
11008   if (moved & MP_MOVING && !ScreenMovPos &&
11009       (player == local_player || !options.network))
11010 #endif
11011   {
11012     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
11013     int offset = (setup.scroll_delay ? 3 : 0);
11014
11015     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11016     {
11017       /* actual player has left the screen -- scroll in that direction */
11018       if (jx != old_jx)         /* player has moved horizontally */
11019         scroll_x += (jx - old_jx);
11020       else                      /* player has moved vertically */
11021         scroll_y += (jy - old_jy);
11022     }
11023     else
11024     {
11025       if (jx != old_jx)         /* player has moved horizontally */
11026       {
11027         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
11028             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11029           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11030
11031         /* don't scroll over playfield boundaries */
11032         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11033           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11034
11035         /* don't scroll more than one field at a time */
11036         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11037
11038         /* don't scroll against the player's moving direction */
11039         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
11040             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11041           scroll_x = old_scroll_x;
11042       }
11043       else                      /* player has moved vertically */
11044       {
11045         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
11046             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11047           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11048
11049         /* don't scroll over playfield boundaries */
11050         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11051           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11052
11053         /* don't scroll more than one field at a time */
11054         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11055
11056         /* don't scroll against the player's moving direction */
11057         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
11058             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11059           scroll_y = old_scroll_y;
11060       }
11061     }
11062
11063     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11064     {
11065 #if 1
11066       if (!options.network && game.centered_player_nr == -1 &&
11067           !AllPlayersInVisibleScreen())
11068       {
11069         scroll_x = old_scroll_x;
11070         scroll_y = old_scroll_y;
11071       }
11072       else
11073 #else
11074       if (!options.network && !AllPlayersInVisibleScreen())
11075       {
11076         scroll_x = old_scroll_x;
11077         scroll_y = old_scroll_y;
11078       }
11079       else
11080 #endif
11081       {
11082         ScrollScreen(player, SCROLL_INIT);
11083         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11084       }
11085     }
11086   }
11087
11088   player->StepFrame = 0;
11089
11090   if (moved & MP_MOVING)
11091   {
11092     if (old_jx != jx && old_jy == jy)
11093       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11094     else if (old_jx == jx && old_jy != jy)
11095       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11096
11097     DrawLevelField(jx, jy);     /* for "crumbled sand" */
11098
11099     player->last_move_dir = player->MovDir;
11100     player->is_moving = TRUE;
11101     player->is_snapping = FALSE;
11102     player->is_switching = FALSE;
11103     player->is_dropping = FALSE;
11104     player->is_dropping_pressed = FALSE;
11105     player->drop_pressed_delay = 0;
11106
11107 #if 0
11108     /* should better be called here than above, but this breaks some tapes */
11109     ScrollPlayer(player, SCROLL_INIT);
11110 #endif
11111   }
11112   else
11113   {
11114     CheckGravityMovementWhenNotMoving(player);
11115
11116     player->is_moving = FALSE;
11117
11118     /* at this point, the player is allowed to move, but cannot move right now
11119        (e.g. because of something blocking the way) -- ensure that the player
11120        is also allowed to move in the next frame (in old versions before 3.1.1,
11121        the player was forced to wait again for eight frames before next try) */
11122
11123     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11124       player->move_delay = 0;   /* allow direct movement in the next frame */
11125   }
11126
11127   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11128     player->move_delay = player->move_delay_value;
11129
11130   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11131   {
11132     TestIfPlayerTouchesBadThing(jx, jy);
11133     TestIfPlayerTouchesCustomElement(jx, jy);
11134   }
11135
11136   if (!player->active)
11137     RemovePlayer(player);
11138
11139   return moved;
11140 }
11141
11142 void ScrollPlayer(struct PlayerInfo *player, int mode)
11143 {
11144   int jx = player->jx, jy = player->jy;
11145   int last_jx = player->last_jx, last_jy = player->last_jy;
11146   int move_stepsize = TILEX / player->move_delay_value;
11147
11148 #if USE_NEW_PLAYER_SPEED
11149   if (!player->active)
11150     return;
11151
11152   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11153     return;
11154 #else
11155   if (!player->active || player->MovPos == 0)
11156     return;
11157 #endif
11158
11159   if (mode == SCROLL_INIT)
11160   {
11161     player->actual_frame_counter = FrameCounter;
11162     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11163
11164     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11165         Feld[last_jx][last_jy] == EL_EMPTY)
11166     {
11167       int last_field_block_delay = 0;   /* start with no blocking at all */
11168       int block_delay_adjustment = player->block_delay_adjustment;
11169
11170       /* if player blocks last field, add delay for exactly one move */
11171       if (player->block_last_field)
11172       {
11173         last_field_block_delay += player->move_delay_value;
11174
11175         /* when blocking enabled, prevent moving up despite gravity */
11176 #if USE_PLAYER_GRAVITY
11177         if (player->gravity && player->MovDir == MV_UP)
11178           block_delay_adjustment = -1;
11179 #else
11180         if (game.gravity && player->MovDir == MV_UP)
11181           block_delay_adjustment = -1;
11182 #endif
11183       }
11184
11185       /* add block delay adjustment (also possible when not blocking) */
11186       last_field_block_delay += block_delay_adjustment;
11187
11188       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11189       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11190     }
11191
11192 #if USE_NEW_PLAYER_SPEED
11193     if (player->MovPos != 0)    /* player has not yet reached destination */
11194       return;
11195 #else
11196     return;
11197 #endif
11198   }
11199   else if (!FrameReached(&player->actual_frame_counter, 1))
11200     return;
11201
11202 #if 0
11203   printf("::: player->MovPos: %d -> %d\n",
11204          player->MovPos,
11205          player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
11206 #endif
11207
11208 #if USE_NEW_PLAYER_SPEED
11209   if (player->MovPos != 0)
11210   {
11211     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11212     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11213
11214     /* before DrawPlayer() to draw correct player graphic for this case */
11215     if (player->MovPos == 0)
11216       CheckGravityMovement(player);
11217   }
11218 #else
11219   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11220   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11221
11222   /* before DrawPlayer() to draw correct player graphic for this case */
11223   if (player->MovPos == 0)
11224     CheckGravityMovement(player);
11225 #endif
11226
11227   if (player->MovPos == 0)      /* player reached destination field */
11228   {
11229 #if 0
11230     printf("::: player reached destination field\n");
11231 #endif
11232
11233     if (player->move_delay_reset_counter > 0)
11234     {
11235       player->move_delay_reset_counter--;
11236
11237       if (player->move_delay_reset_counter == 0)
11238       {
11239         /* continue with normal speed after quickly moving through gate */
11240         HALVE_PLAYER_SPEED(player);
11241
11242         /* be able to make the next move without delay */
11243         player->move_delay = 0;
11244       }
11245     }
11246
11247     player->last_jx = jx;
11248     player->last_jy = jy;
11249
11250     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11251         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11252         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11253     {
11254       DrawPlayer(player);       /* needed here only to cleanup last field */
11255       RemovePlayer(player);
11256
11257       if (local_player->friends_still_needed == 0 ||
11258           IS_SP_ELEMENT(Feld[jx][jy]))
11259         player->LevelSolved = player->GameOver = TRUE;
11260     }
11261
11262     /* this breaks one level: "machine", level 000 */
11263     {
11264       int move_direction = player->MovDir;
11265       int enter_side = MV_DIR_OPPOSITE(move_direction);
11266       int leave_side = move_direction;
11267       int old_jx = last_jx;
11268       int old_jy = last_jy;
11269       int old_element = Feld[old_jx][old_jy];
11270       int new_element = Feld[jx][jy];
11271
11272       if (IS_CUSTOM_ELEMENT(old_element))
11273         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11274                                    CE_LEFT_BY_PLAYER,
11275                                    player->index_bit, leave_side);
11276
11277       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11278                                           CE_PLAYER_LEAVES_X,
11279                                           player->index_bit, leave_side);
11280
11281       if (IS_CUSTOM_ELEMENT(new_element))
11282         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11283                                    player->index_bit, enter_side);
11284
11285       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11286                                           CE_PLAYER_ENTERS_X,
11287                                           player->index_bit, enter_side);
11288
11289       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11290                                         CE_MOVE_OF_X, move_direction);
11291     }
11292
11293     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11294     {
11295       TestIfPlayerTouchesBadThing(jx, jy);
11296       TestIfPlayerTouchesCustomElement(jx, jy);
11297
11298       /* needed because pushed element has not yet reached its destination,
11299          so it would trigger a change event at its previous field location */
11300       if (!player->is_pushing)
11301         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
11302
11303       if (!player->active)
11304         RemovePlayer(player);
11305     }
11306
11307     if (level.use_step_counter)
11308     {
11309       int i;
11310
11311       TimePlayed++;
11312
11313       if (TimeLeft > 0)
11314       {
11315         TimeLeft--;
11316
11317         if (TimeLeft <= 10 && setup.time_limit)
11318           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11319
11320         DrawGameValue_Time(TimeLeft);
11321
11322         if (!TimeLeft && setup.time_limit)
11323           for (i = 0; i < MAX_PLAYERS; i++)
11324             KillPlayer(&stored_player[i]);
11325       }
11326       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11327         DrawGameValue_Time(TimePlayed);
11328     }
11329
11330     if (tape.single_step && tape.recording && !tape.pausing &&
11331         !player->programmed_action)
11332       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11333   }
11334 }
11335
11336 void ScrollScreen(struct PlayerInfo *player, int mode)
11337 {
11338   static unsigned long screen_frame_counter = 0;
11339
11340   if (mode == SCROLL_INIT)
11341   {
11342     /* set scrolling step size according to actual player's moving speed */
11343     ScrollStepSize = TILEX / player->move_delay_value;
11344
11345     screen_frame_counter = FrameCounter;
11346     ScreenMovDir = player->MovDir;
11347     ScreenMovPos = player->MovPos;
11348     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11349     return;
11350   }
11351   else if (!FrameReached(&screen_frame_counter, 1))
11352     return;
11353
11354   if (ScreenMovPos)
11355   {
11356     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11357     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11358     redraw_mask |= REDRAW_FIELD;
11359   }
11360   else
11361     ScreenMovDir = MV_NONE;
11362 }
11363
11364 void TestIfPlayerTouchesCustomElement(int x, int y)
11365 {
11366   static int xy[4][2] =
11367   {
11368     { 0, -1 },
11369     { -1, 0 },
11370     { +1, 0 },
11371     { 0, +1 }
11372   };
11373   static int trigger_sides[4][2] =
11374   {
11375     /* center side       border side */
11376     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11377     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11378     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11379     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11380   };
11381   static int touch_dir[4] =
11382   {
11383     MV_LEFT | MV_RIGHT,
11384     MV_UP   | MV_DOWN,
11385     MV_UP   | MV_DOWN,
11386     MV_LEFT | MV_RIGHT
11387   };
11388   int center_element = Feld[x][y];      /* should always be non-moving! */
11389   int i;
11390
11391   for (i = 0; i < NUM_DIRECTIONS; i++)
11392   {
11393     int xx = x + xy[i][0];
11394     int yy = y + xy[i][1];
11395     int center_side = trigger_sides[i][0];
11396     int border_side = trigger_sides[i][1];
11397     int border_element;
11398
11399     if (!IN_LEV_FIELD(xx, yy))
11400       continue;
11401
11402     if (IS_PLAYER(x, y))
11403     {
11404       struct PlayerInfo *player = PLAYERINFO(x, y);
11405
11406       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11407         border_element = Feld[xx][yy];          /* may be moving! */
11408       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11409         border_element = Feld[xx][yy];
11410       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11411         border_element = MovingOrBlocked2Element(xx, yy);
11412       else
11413         continue;               /* center and border element do not touch */
11414
11415       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11416                                  player->index_bit, border_side);
11417       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11418                                           CE_PLAYER_TOUCHES_X,
11419                                           player->index_bit, border_side);
11420     }
11421     else if (IS_PLAYER(xx, yy))
11422     {
11423       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11424
11425       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11426       {
11427         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11428           continue;             /* center and border element do not touch */
11429       }
11430
11431       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11432                                  player->index_bit, center_side);
11433       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11434                                           CE_PLAYER_TOUCHES_X,
11435                                           player->index_bit, center_side);
11436       break;
11437     }
11438   }
11439 }
11440
11441 #if USE_ELEMENT_TOUCHING_BUGFIX
11442
11443 void TestIfElementTouchesCustomElement(int x, int y)
11444 {
11445   static int xy[4][2] =
11446   {
11447     { 0, -1 },
11448     { -1, 0 },
11449     { +1, 0 },
11450     { 0, +1 }
11451   };
11452   static int trigger_sides[4][2] =
11453   {
11454     /* center side      border side */
11455     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11456     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11457     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11458     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11459   };
11460   static int touch_dir[4] =
11461   {
11462     MV_LEFT | MV_RIGHT,
11463     MV_UP   | MV_DOWN,
11464     MV_UP   | MV_DOWN,
11465     MV_LEFT | MV_RIGHT
11466   };
11467   boolean change_center_element = FALSE;
11468   int center_element = Feld[x][y];      /* should always be non-moving! */
11469   int border_element_old[NUM_DIRECTIONS];
11470   int i;
11471
11472   for (i = 0; i < NUM_DIRECTIONS; i++)
11473   {
11474     int xx = x + xy[i][0];
11475     int yy = y + xy[i][1];
11476     int border_element;
11477
11478     border_element_old[i] = -1;
11479
11480     if (!IN_LEV_FIELD(xx, yy))
11481       continue;
11482
11483     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11484       border_element = Feld[xx][yy];    /* may be moving! */
11485     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11486       border_element = Feld[xx][yy];
11487     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11488       border_element = MovingOrBlocked2Element(xx, yy);
11489     else
11490       continue;                 /* center and border element do not touch */
11491
11492     border_element_old[i] = border_element;
11493   }
11494
11495   for (i = 0; i < NUM_DIRECTIONS; i++)
11496   {
11497     int xx = x + xy[i][0];
11498     int yy = y + xy[i][1];
11499     int center_side = trigger_sides[i][0];
11500     int border_element = border_element_old[i];
11501
11502     if (border_element == -1)
11503       continue;
11504
11505     /* check for change of border element */
11506     CheckElementChangeBySide(xx, yy, border_element, center_element,
11507                              CE_TOUCHING_X, center_side);
11508   }
11509
11510   for (i = 0; i < NUM_DIRECTIONS; i++)
11511   {
11512     int border_side = trigger_sides[i][1];
11513     int border_element = border_element_old[i];
11514
11515     if (border_element == -1)
11516       continue;
11517
11518     /* check for change of center element (but change it only once) */
11519     if (!change_center_element)
11520       change_center_element =
11521         CheckElementChangeBySide(x, y, center_element, border_element,
11522                                  CE_TOUCHING_X, border_side);
11523   }
11524 }
11525
11526 #else
11527
11528 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11529 {
11530   static int xy[4][2] =
11531   {
11532     { 0, -1 },
11533     { -1, 0 },
11534     { +1, 0 },
11535     { 0, +1 }
11536   };
11537   static int trigger_sides[4][2] =
11538   {
11539     /* center side      border side */
11540     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11541     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11542     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11543     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11544   };
11545   static int touch_dir[4] =
11546   {
11547     MV_LEFT | MV_RIGHT,
11548     MV_UP   | MV_DOWN,
11549     MV_UP   | MV_DOWN,
11550     MV_LEFT | MV_RIGHT
11551   };
11552   boolean change_center_element = FALSE;
11553   int center_element = Feld[x][y];      /* should always be non-moving! */
11554   int i;
11555
11556   for (i = 0; i < NUM_DIRECTIONS; i++)
11557   {
11558     int xx = x + xy[i][0];
11559     int yy = y + xy[i][1];
11560     int center_side = trigger_sides[i][0];
11561     int border_side = trigger_sides[i][1];
11562     int border_element;
11563
11564     if (!IN_LEV_FIELD(xx, yy))
11565       continue;
11566
11567     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11568       border_element = Feld[xx][yy];    /* may be moving! */
11569     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11570       border_element = Feld[xx][yy];
11571     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11572       border_element = MovingOrBlocked2Element(xx, yy);
11573     else
11574       continue;                 /* center and border element do not touch */
11575
11576     /* check for change of center element (but change it only once) */
11577     if (!change_center_element)
11578       change_center_element =
11579         CheckElementChangeBySide(x, y, center_element, border_element,
11580                                  CE_TOUCHING_X, border_side);
11581
11582     /* check for change of border element */
11583     CheckElementChangeBySide(xx, yy, border_element, center_element,
11584                              CE_TOUCHING_X, center_side);
11585   }
11586 }
11587
11588 #endif
11589
11590 void TestIfElementHitsCustomElement(int x, int y, int direction)
11591 {
11592   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11593   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11594   int hitx = x + dx, hity = y + dy;
11595   int hitting_element = Feld[x][y];
11596   int touched_element;
11597
11598   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11599     return;
11600
11601   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11602                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11603
11604   if (IN_LEV_FIELD(hitx, hity))
11605   {
11606     int opposite_direction = MV_DIR_OPPOSITE(direction);
11607     int hitting_side = direction;
11608     int touched_side = opposite_direction;
11609     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11610                           MovDir[hitx][hity] != direction ||
11611                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11612
11613     object_hit = TRUE;
11614
11615     if (object_hit)
11616     {
11617       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11618                                CE_HITTING_X, touched_side);
11619
11620       CheckElementChangeBySide(hitx, hity, touched_element,
11621                                hitting_element, CE_HIT_BY_X, hitting_side);
11622
11623       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11624                                CE_HIT_BY_SOMETHING, opposite_direction);
11625     }
11626   }
11627
11628   /* "hitting something" is also true when hitting the playfield border */
11629   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11630                            CE_HITTING_SOMETHING, direction);
11631 }
11632
11633 #if 0
11634 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11635 {
11636   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11637   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11638   int hitx = x + dx, hity = y + dy;
11639   int hitting_element = Feld[x][y];
11640   int touched_element;
11641 #if 0
11642   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11643                         !IS_FREE(hitx, hity) &&
11644                         (!IS_MOVING(hitx, hity) ||
11645                          MovDir[hitx][hity] != direction ||
11646                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11647 #endif
11648
11649   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11650     return;
11651
11652 #if 0
11653   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11654     return;
11655 #endif
11656
11657   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11658                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11659
11660   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11661                            EP_CAN_SMASH_EVERYTHING, direction);
11662
11663   if (IN_LEV_FIELD(hitx, hity))
11664   {
11665     int opposite_direction = MV_DIR_OPPOSITE(direction);
11666     int hitting_side = direction;
11667     int touched_side = opposite_direction;
11668 #if 0
11669     int touched_element = MovingOrBlocked2Element(hitx, hity);
11670 #endif
11671 #if 1
11672     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11673                           MovDir[hitx][hity] != direction ||
11674                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11675
11676     object_hit = TRUE;
11677 #endif
11678
11679     if (object_hit)
11680     {
11681       int i;
11682
11683       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11684                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11685
11686       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11687                                CE_OTHER_IS_SMASHING, touched_side);
11688
11689       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11690                                CE_OTHER_GETS_SMASHED, hitting_side);
11691     }
11692   }
11693 }
11694 #endif
11695
11696 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11697 {
11698   int i, kill_x = -1, kill_y = -1;
11699
11700   int bad_element = -1;
11701   static int test_xy[4][2] =
11702   {
11703     { 0, -1 },
11704     { -1, 0 },
11705     { +1, 0 },
11706     { 0, +1 }
11707   };
11708   static int test_dir[4] =
11709   {
11710     MV_UP,
11711     MV_LEFT,
11712     MV_RIGHT,
11713     MV_DOWN
11714   };
11715
11716   for (i = 0; i < NUM_DIRECTIONS; i++)
11717   {
11718     int test_x, test_y, test_move_dir, test_element;
11719
11720     test_x = good_x + test_xy[i][0];
11721     test_y = good_y + test_xy[i][1];
11722
11723     if (!IN_LEV_FIELD(test_x, test_y))
11724       continue;
11725
11726     test_move_dir =
11727       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11728
11729     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11730
11731     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11732        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11733     */
11734     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11735         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11736     {
11737       kill_x = test_x;
11738       kill_y = test_y;
11739       bad_element = test_element;
11740
11741       break;
11742     }
11743   }
11744
11745   if (kill_x != -1 || kill_y != -1)
11746   {
11747     if (IS_PLAYER(good_x, good_y))
11748     {
11749       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11750
11751       if (player->shield_deadly_time_left > 0 &&
11752           !IS_INDESTRUCTIBLE(bad_element))
11753         Bang(kill_x, kill_y);
11754       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11755         KillPlayer(player);
11756     }
11757     else
11758       Bang(good_x, good_y);
11759   }
11760 }
11761
11762 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11763 {
11764   int i, kill_x = -1, kill_y = -1;
11765   int bad_element = Feld[bad_x][bad_y];
11766   static int test_xy[4][2] =
11767   {
11768     { 0, -1 },
11769     { -1, 0 },
11770     { +1, 0 },
11771     { 0, +1 }
11772   };
11773   static int touch_dir[4] =
11774   {
11775     MV_LEFT | MV_RIGHT,
11776     MV_UP   | MV_DOWN,
11777     MV_UP   | MV_DOWN,
11778     MV_LEFT | MV_RIGHT
11779   };
11780   static int test_dir[4] =
11781   {
11782     MV_UP,
11783     MV_LEFT,
11784     MV_RIGHT,
11785     MV_DOWN
11786   };
11787
11788   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11789     return;
11790
11791   for (i = 0; i < NUM_DIRECTIONS; i++)
11792   {
11793     int test_x, test_y, test_move_dir, test_element;
11794
11795     test_x = bad_x + test_xy[i][0];
11796     test_y = bad_y + test_xy[i][1];
11797     if (!IN_LEV_FIELD(test_x, test_y))
11798       continue;
11799
11800     test_move_dir =
11801       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11802
11803     test_element = Feld[test_x][test_y];
11804
11805     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11806        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11807     */
11808     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11809         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11810     {
11811       /* good thing is player or penguin that does not move away */
11812       if (IS_PLAYER(test_x, test_y))
11813       {
11814         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11815
11816         if (bad_element == EL_ROBOT && player->is_moving)
11817           continue;     /* robot does not kill player if he is moving */
11818
11819         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11820         {
11821           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11822             continue;           /* center and border element do not touch */
11823         }
11824
11825         kill_x = test_x;
11826         kill_y = test_y;
11827         break;
11828       }
11829       else if (test_element == EL_PENGUIN)
11830       {
11831         kill_x = test_x;
11832         kill_y = test_y;
11833         break;
11834       }
11835     }
11836   }
11837
11838   if (kill_x != -1 || kill_y != -1)
11839   {
11840     if (IS_PLAYER(kill_x, kill_y))
11841     {
11842       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11843
11844       if (player->shield_deadly_time_left > 0 &&
11845           !IS_INDESTRUCTIBLE(bad_element))
11846         Bang(bad_x, bad_y);
11847       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11848         KillPlayer(player);
11849     }
11850     else
11851       Bang(kill_x, kill_y);
11852   }
11853 }
11854
11855 void TestIfPlayerTouchesBadThing(int x, int y)
11856 {
11857   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11858 }
11859
11860 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11861 {
11862   TestIfGoodThingHitsBadThing(x, y, move_dir);
11863 }
11864
11865 void TestIfBadThingTouchesPlayer(int x, int y)
11866 {
11867   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11868 }
11869
11870 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11871 {
11872   TestIfBadThingHitsGoodThing(x, y, move_dir);
11873 }
11874
11875 void TestIfFriendTouchesBadThing(int x, int y)
11876 {
11877   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11878 }
11879
11880 void TestIfBadThingTouchesFriend(int x, int y)
11881 {
11882   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11883 }
11884
11885 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11886 {
11887   int i, kill_x = bad_x, kill_y = bad_y;
11888   static int xy[4][2] =
11889   {
11890     { 0, -1 },
11891     { -1, 0 },
11892     { +1, 0 },
11893     { 0, +1 }
11894   };
11895
11896   for (i = 0; i < NUM_DIRECTIONS; i++)
11897   {
11898     int x, y, element;
11899
11900     x = bad_x + xy[i][0];
11901     y = bad_y + xy[i][1];
11902     if (!IN_LEV_FIELD(x, y))
11903       continue;
11904
11905     element = Feld[x][y];
11906     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11907         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11908     {
11909       kill_x = x;
11910       kill_y = y;
11911       break;
11912     }
11913   }
11914
11915   if (kill_x != bad_x || kill_y != bad_y)
11916     Bang(bad_x, bad_y);
11917 }
11918
11919 void KillPlayer(struct PlayerInfo *player)
11920 {
11921   int jx = player->jx, jy = player->jy;
11922
11923   if (!player->active)
11924     return;
11925
11926   /* remove accessible field at the player's position */
11927   Feld[jx][jy] = EL_EMPTY;
11928
11929   /* deactivate shield (else Bang()/Explode() would not work right) */
11930   player->shield_normal_time_left = 0;
11931   player->shield_deadly_time_left = 0;
11932
11933   Bang(jx, jy);
11934   BuryPlayer(player);
11935 }
11936
11937 static void KillPlayerUnlessEnemyProtected(int x, int y)
11938 {
11939   if (!PLAYER_ENEMY_PROTECTED(x, y))
11940     KillPlayer(PLAYERINFO(x, y));
11941 }
11942
11943 static void KillPlayerUnlessExplosionProtected(int x, int y)
11944 {
11945   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11946     KillPlayer(PLAYERINFO(x, y));
11947 }
11948
11949 void BuryPlayer(struct PlayerInfo *player)
11950 {
11951   int jx = player->jx, jy = player->jy;
11952
11953   if (!player->active)
11954     return;
11955
11956   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11957   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11958
11959   player->GameOver = TRUE;
11960   RemovePlayer(player);
11961 }
11962
11963 void RemovePlayer(struct PlayerInfo *player)
11964 {
11965   int jx = player->jx, jy = player->jy;
11966   int i, found = FALSE;
11967
11968   player->present = FALSE;
11969   player->active = FALSE;
11970
11971   if (!ExplodeField[jx][jy])
11972     StorePlayer[jx][jy] = 0;
11973
11974   if (player->is_moving)
11975     DrawLevelField(player->last_jx, player->last_jy);
11976
11977   for (i = 0; i < MAX_PLAYERS; i++)
11978     if (stored_player[i].active)
11979       found = TRUE;
11980
11981   if (!found)
11982     AllPlayersGone = TRUE;
11983
11984   ExitX = ZX = jx;
11985   ExitY = ZY = jy;
11986 }
11987
11988 #if USE_NEW_SNAP_DELAY
11989 static void setFieldForSnapping(int x, int y, int element, int direction)
11990 {
11991   struct ElementInfo *ei = &element_info[element];
11992   int direction_bit = MV_DIR_TO_BIT(direction);
11993   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11994   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11995                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11996
11997   Feld[x][y] = EL_ELEMENT_SNAPPING;
11998   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11999
12000   ResetGfxAnimation(x, y);
12001
12002   GfxElement[x][y] = element;
12003   GfxAction[x][y] = action;
12004   GfxDir[x][y] = direction;
12005   GfxFrame[x][y] = -1;
12006 }
12007 #endif
12008
12009 /*
12010   =============================================================================
12011   checkDiagonalPushing()
12012   -----------------------------------------------------------------------------
12013   check if diagonal input device direction results in pushing of object
12014   (by checking if the alternative direction is walkable, diggable, ...)
12015   =============================================================================
12016 */
12017
12018 static boolean checkDiagonalPushing(struct PlayerInfo *player,
12019                                     int x, int y, int real_dx, int real_dy)
12020 {
12021   int jx, jy, dx, dy, xx, yy;
12022
12023   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
12024     return TRUE;
12025
12026   /* diagonal direction: check alternative direction */
12027   jx = player->jx;
12028   jy = player->jy;
12029   dx = x - jx;
12030   dy = y - jy;
12031   xx = jx + (dx == 0 ? real_dx : 0);
12032   yy = jy + (dy == 0 ? real_dy : 0);
12033
12034   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12035 }
12036
12037 /*
12038   =============================================================================
12039   DigField()
12040   -----------------------------------------------------------------------------
12041   x, y:                 field next to player (non-diagonal) to try to dig to
12042   real_dx, real_dy:     direction as read from input device (can be diagonal)
12043   =============================================================================
12044 */
12045
12046 int DigField(struct PlayerInfo *player,
12047              int oldx, int oldy, int x, int y,
12048              int real_dx, int real_dy, int mode)
12049 {
12050   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12051   boolean player_was_pushing = player->is_pushing;
12052   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12053   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12054   int jx = oldx, jy = oldy;
12055   int dx = x - jx, dy = y - jy;
12056   int nextx = x + dx, nexty = y + dy;
12057   int move_direction = (dx == -1 ? MV_LEFT  :
12058                         dx == +1 ? MV_RIGHT :
12059                         dy == -1 ? MV_UP    :
12060                         dy == +1 ? MV_DOWN  : MV_NONE);
12061   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12062   int dig_side = MV_DIR_OPPOSITE(move_direction);
12063   int old_element = Feld[jx][jy];
12064 #if USE_FIXED_DONT_RUN_INTO
12065   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12066 #else
12067   int element;
12068 #endif
12069   int collect_count;
12070
12071   if (is_player)                /* function can also be called by EL_PENGUIN */
12072   {
12073     if (player->MovPos == 0)
12074     {
12075       player->is_digging = FALSE;
12076       player->is_collecting = FALSE;
12077     }
12078
12079     if (player->MovPos == 0)    /* last pushing move finished */
12080       player->is_pushing = FALSE;
12081
12082     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
12083     {
12084       player->is_switching = FALSE;
12085       player->push_delay = -1;
12086
12087       return MP_NO_ACTION;
12088     }
12089   }
12090
12091 #if !USE_FIXED_DONT_RUN_INTO
12092   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12093     return MP_NO_ACTION;
12094 #endif
12095
12096   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12097     old_element = Back[jx][jy];
12098
12099   /* in case of element dropped at player position, check background */
12100   else if (Back[jx][jy] != EL_EMPTY &&
12101            game.engine_version >= VERSION_IDENT(2,2,0,0))
12102     old_element = Back[jx][jy];
12103
12104   /* checking here causes player to move into acid even if the current field
12105      cannot be left to that direction */
12106 #if 0
12107 #if USE_FIXED_DONT_RUN_INTO
12108   if (player_can_move && DONT_RUN_INTO(element))
12109   {
12110     if (element == EL_ACID && dx == 0 && dy == 1)
12111     {
12112       SplashAcid(x, y);
12113       Feld[jx][jy] = EL_PLAYER_1;
12114       InitMovingField(jx, jy, MV_DOWN);
12115       Store[jx][jy] = EL_ACID;
12116       ContinueMoving(jx, jy);
12117       BuryPlayer(player);
12118     }
12119     else
12120       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12121
12122     return MP_DONT_RUN_INTO;
12123   }
12124 #endif
12125 #endif
12126
12127 #if 1   /* ------------------------------ NEW ------------------------------ */
12128
12129   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12130     return MP_NO_ACTION;        /* field has no opening in this direction */
12131
12132   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12133     return MP_NO_ACTION;        /* field has no opening in this direction */
12134
12135 #if USE_FIXED_DONT_RUN_INTO
12136   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12137   {
12138     SplashAcid(x, y);
12139 #if 1
12140     Feld[jx][jy] = player->artwork_element;
12141 #else
12142     Feld[jx][jy] = EL_PLAYER_1;
12143 #endif
12144     InitMovingField(jx, jy, MV_DOWN);
12145     Store[jx][jy] = EL_ACID;
12146     ContinueMoving(jx, jy);
12147     BuryPlayer(player);
12148
12149     return MP_DONT_RUN_INTO;
12150   }
12151 #endif
12152
12153 #if USE_FIXED_DONT_RUN_INTO
12154   if (player_can_move && DONT_RUN_INTO(element))
12155   {
12156     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12157
12158     return MP_DONT_RUN_INTO;
12159   }
12160 #endif
12161
12162 #else   /* ------------------------------ OLD ------------------------------ */
12163
12164 #if 1
12165 #if USE_FIXED_DONT_RUN_INTO
12166   if (player_can_move && DONT_RUN_INTO(element))
12167   {
12168     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12169
12170     return MP_DONT_RUN_INTO;
12171   }
12172 #endif
12173 #endif
12174
12175   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12176     return MP_NO_ACTION;        /* field has no opening in this direction */
12177
12178   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12179     return MP_NO_ACTION;        /* field has no opening in this direction */
12180
12181   /* checking here causes player to explode when moving into acid */
12182 #if 1
12183 #if USE_FIXED_DONT_RUN_INTO
12184   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12185   {
12186     SplashAcid(x, y);
12187     Feld[jx][jy] = EL_PLAYER_1;
12188     InitMovingField(jx, jy, MV_DOWN);
12189     Store[jx][jy] = EL_ACID;
12190     ContinueMoving(jx, jy);
12191     BuryPlayer(player);
12192
12193     return MP_DONT_RUN_INTO;
12194   }
12195 #endif
12196 #endif
12197
12198 #endif  /* ------------------------------ END ------------------------------ */
12199
12200 #if 0
12201 #if USE_FIXED_DONT_RUN_INTO
12202   if (player_can_move && DONT_RUN_INTO(element))
12203   {
12204     if (element == EL_ACID && dx == 0 && dy == 1)
12205     {
12206       SplashAcid(x, y);
12207       Feld[jx][jy] = EL_PLAYER_1;
12208       InitMovingField(jx, jy, MV_DOWN);
12209       Store[jx][jy] = EL_ACID;
12210       ContinueMoving(jx, jy);
12211       BuryPlayer(player);
12212     }
12213     else
12214       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12215
12216     return MP_DONT_RUN_INTO;
12217   }
12218 #endif
12219 #endif
12220
12221 #if USE_FIXED_DONT_RUN_INTO
12222   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12223     return MP_NO_ACTION;
12224 #endif
12225
12226 #if !USE_FIXED_DONT_RUN_INTO
12227   element = Feld[x][y];
12228 #endif
12229
12230   collect_count = element_info[element].collect_count_initial;
12231
12232   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12233     return MP_NO_ACTION;
12234
12235   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12236     player_can_move = player_can_move_or_snap;
12237
12238   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12239       game.engine_version >= VERSION_IDENT(2,2,0,0))
12240   {
12241     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12242                                player->index_bit, dig_side);
12243     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12244                                         player->index_bit, dig_side);
12245
12246     if (Feld[x][y] != element)          /* field changed by snapping */
12247       return MP_ACTION;
12248
12249     return MP_NO_ACTION;
12250   }
12251
12252 #if USE_PLAYER_GRAVITY
12253   if (player->gravity && is_player && !player->is_auto_moving &&
12254       canFallDown(player) && move_direction != MV_DOWN &&
12255       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12256     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12257 #else
12258   if (game.gravity && is_player && !player->is_auto_moving &&
12259       canFallDown(player) && move_direction != MV_DOWN &&
12260       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12261     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12262 #endif
12263
12264   if (player_can_move &&
12265       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12266   {
12267     int sound_element = SND_ELEMENT(element);
12268     int sound_action = ACTION_WALKING;
12269
12270     if (IS_RND_GATE(element))
12271     {
12272       if (!player->key[RND_GATE_NR(element)])
12273         return MP_NO_ACTION;
12274     }
12275     else if (IS_RND_GATE_GRAY(element))
12276     {
12277       if (!player->key[RND_GATE_GRAY_NR(element)])
12278         return MP_NO_ACTION;
12279     }
12280     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12281     {
12282       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12283         return MP_NO_ACTION;
12284     }
12285     else if (element == EL_EXIT_OPEN ||
12286              element == EL_SP_EXIT_OPEN ||
12287              element == EL_SP_EXIT_OPENING)
12288     {
12289       sound_action = ACTION_PASSING;    /* player is passing exit */
12290     }
12291     else if (element == EL_EMPTY)
12292     {
12293       sound_action = ACTION_MOVING;             /* nothing to walk on */
12294     }
12295
12296     /* play sound from background or player, whatever is available */
12297     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12298       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12299     else
12300       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12301   }
12302   else if (player_can_move &&
12303            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12304   {
12305     if (!ACCESS_FROM(element, opposite_direction))
12306       return MP_NO_ACTION;      /* field not accessible from this direction */
12307
12308     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12309       return MP_NO_ACTION;
12310
12311     if (IS_EM_GATE(element))
12312     {
12313       if (!player->key[EM_GATE_NR(element)])
12314         return MP_NO_ACTION;
12315     }
12316     else if (IS_EM_GATE_GRAY(element))
12317     {
12318       if (!player->key[EM_GATE_GRAY_NR(element)])
12319         return MP_NO_ACTION;
12320     }
12321     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12322     {
12323       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12324         return MP_NO_ACTION;
12325     }
12326     else if (IS_EMC_GATE(element))
12327     {
12328       if (!player->key[EMC_GATE_NR(element)])
12329         return MP_NO_ACTION;
12330     }
12331     else if (IS_EMC_GATE_GRAY(element))
12332     {
12333       if (!player->key[EMC_GATE_GRAY_NR(element)])
12334         return MP_NO_ACTION;
12335     }
12336     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12337     {
12338       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12339         return MP_NO_ACTION;
12340     }
12341     else if (IS_SP_PORT(element))
12342     {
12343       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12344           element == EL_SP_GRAVITY_PORT_RIGHT ||
12345           element == EL_SP_GRAVITY_PORT_UP ||
12346           element == EL_SP_GRAVITY_PORT_DOWN)
12347 #if USE_PLAYER_GRAVITY
12348         player->gravity = !player->gravity;
12349 #else
12350         game.gravity = !game.gravity;
12351 #endif
12352       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12353                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12354                element == EL_SP_GRAVITY_ON_PORT_UP ||
12355                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12356 #if USE_PLAYER_GRAVITY
12357         player->gravity = TRUE;
12358 #else
12359         game.gravity = TRUE;
12360 #endif
12361       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12362                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12363                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12364                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12365 #if USE_PLAYER_GRAVITY
12366         player->gravity = FALSE;
12367 #else
12368         game.gravity = FALSE;
12369 #endif
12370     }
12371
12372     /* automatically move to the next field with double speed */
12373     player->programmed_action = move_direction;
12374
12375     if (player->move_delay_reset_counter == 0)
12376     {
12377       player->move_delay_reset_counter = 2;     /* two double speed steps */
12378
12379       DOUBLE_PLAYER_SPEED(player);
12380     }
12381
12382     PlayLevelSoundAction(x, y, ACTION_PASSING);
12383   }
12384   else if (player_can_move_or_snap && IS_DIGGABLE(element))
12385   {
12386     RemoveField(x, y);
12387
12388     if (mode != DF_SNAP)
12389     {
12390       GfxElement[x][y] = GFX_ELEMENT(element);
12391       player->is_digging = TRUE;
12392     }
12393
12394     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12395
12396     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12397                                         player->index_bit, dig_side);
12398
12399     if (mode == DF_SNAP)
12400     {
12401 #if USE_NEW_SNAP_DELAY
12402       if (level.block_snap_field)
12403         setFieldForSnapping(x, y, element, move_direction);
12404       else
12405         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12406 #else
12407       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12408 #endif
12409
12410       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12411                                           player->index_bit, dig_side);
12412     }
12413   }
12414   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12415   {
12416     RemoveField(x, y);
12417
12418     if (is_player && mode != DF_SNAP)
12419     {
12420       GfxElement[x][y] = element;
12421       player->is_collecting = TRUE;
12422     }
12423
12424     if (element == EL_SPEED_PILL)
12425     {
12426       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12427     }
12428     else if (element == EL_EXTRA_TIME && level.time > 0)
12429     {
12430       TimeLeft += level.extra_time;
12431       DrawGameValue_Time(TimeLeft);
12432     }
12433     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12434     {
12435       player->shield_normal_time_left += level.shield_normal_time;
12436       if (element == EL_SHIELD_DEADLY)
12437         player->shield_deadly_time_left += level.shield_deadly_time;
12438     }
12439     else if (element == EL_DYNAMITE ||
12440              element == EL_EM_DYNAMITE ||
12441              element == EL_SP_DISK_RED)
12442     {
12443       if (player->inventory_size < MAX_INVENTORY_SIZE)
12444         player->inventory_element[player->inventory_size++] = element;
12445
12446 #if 1
12447       DrawGameDoorValues();
12448 #else
12449       DrawGameValue_Dynamite(local_player->inventory_size);
12450 #endif
12451     }
12452     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12453     {
12454       player->dynabomb_count++;
12455       player->dynabombs_left++;
12456     }
12457     else if (element == EL_DYNABOMB_INCREASE_SIZE)
12458     {
12459       player->dynabomb_size++;
12460     }
12461     else if (element == EL_DYNABOMB_INCREASE_POWER)
12462     {
12463       player->dynabomb_xl = TRUE;
12464     }
12465     else if (IS_KEY(element))
12466     {
12467       player->key[KEY_NR(element)] = TRUE;
12468
12469 #if 1
12470       DrawGameDoorValues();
12471 #else
12472       DrawGameValue_Keys(player->key);
12473 #endif
12474
12475       redraw_mask |= REDRAW_DOOR_1;
12476     }
12477     else if (IS_ENVELOPE(element))
12478     {
12479       player->show_envelope = element;
12480     }
12481     else if (element == EL_EMC_LENSES)
12482     {
12483       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12484
12485       RedrawAllInvisibleElementsForLenses();
12486     }
12487     else if (element == EL_EMC_MAGNIFIER)
12488     {
12489       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12490
12491       RedrawAllInvisibleElementsForMagnifier();
12492     }
12493     else if (IS_DROPPABLE(element) ||
12494              IS_THROWABLE(element))     /* can be collected and dropped */
12495     {
12496       int i;
12497
12498       if (collect_count == 0)
12499         player->inventory_infinite_element = element;
12500       else
12501         for (i = 0; i < collect_count; i++)
12502           if (player->inventory_size < MAX_INVENTORY_SIZE)
12503             player->inventory_element[player->inventory_size++] = element;
12504
12505 #if 1
12506       DrawGameDoorValues();
12507 #else
12508       DrawGameValue_Dynamite(local_player->inventory_size);
12509 #endif
12510     }
12511     else if (collect_count > 0)
12512     {
12513       local_player->gems_still_needed -= collect_count;
12514       if (local_player->gems_still_needed < 0)
12515         local_player->gems_still_needed = 0;
12516
12517       DrawGameValue_Emeralds(local_player->gems_still_needed);
12518     }
12519
12520     RaiseScoreElement(element);
12521     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12522
12523     if (is_player)
12524       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12525                                           player->index_bit, dig_side);
12526
12527     if (mode == DF_SNAP)
12528     {
12529 #if USE_NEW_SNAP_DELAY
12530       if (level.block_snap_field)
12531         setFieldForSnapping(x, y, element, move_direction);
12532       else
12533         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12534 #else
12535       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12536 #endif
12537
12538       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12539                                           player->index_bit, dig_side);
12540     }
12541   }
12542   else if (player_can_move_or_snap && IS_PUSHABLE(element))
12543   {
12544     if (mode == DF_SNAP && element != EL_BD_ROCK)
12545       return MP_NO_ACTION;
12546
12547     if (CAN_FALL(element) && dy)
12548       return MP_NO_ACTION;
12549
12550     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12551         !(element == EL_SPRING && level.use_spring_bug))
12552       return MP_NO_ACTION;
12553
12554     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12555         ((move_direction & MV_VERTICAL &&
12556           ((element_info[element].move_pattern & MV_LEFT &&
12557             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12558            (element_info[element].move_pattern & MV_RIGHT &&
12559             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12560          (move_direction & MV_HORIZONTAL &&
12561           ((element_info[element].move_pattern & MV_UP &&
12562             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12563            (element_info[element].move_pattern & MV_DOWN &&
12564             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12565       return MP_NO_ACTION;
12566
12567     /* do not push elements already moving away faster than player */
12568     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12569         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12570       return MP_NO_ACTION;
12571
12572     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12573     {
12574       if (player->push_delay_value == -1 || !player_was_pushing)
12575         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12576     }
12577     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12578     {
12579       if (player->push_delay_value == -1)
12580         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12581     }
12582     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12583     {
12584       if (!player->is_pushing)
12585         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12586     }
12587
12588     player->is_pushing = TRUE;
12589     player->is_active = TRUE;
12590
12591     if (!(IN_LEV_FIELD(nextx, nexty) &&
12592           (IS_FREE(nextx, nexty) ||
12593            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12594             IS_SB_ELEMENT(element)))))
12595       return MP_NO_ACTION;
12596
12597     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12598       return MP_NO_ACTION;
12599
12600     if (player->push_delay == -1)       /* new pushing; restart delay */
12601       player->push_delay = 0;
12602
12603     if (player->push_delay < player->push_delay_value &&
12604         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12605         element != EL_SPRING && element != EL_BALLOON)
12606     {
12607       /* make sure that there is no move delay before next try to push */
12608       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12609         player->move_delay = 0;
12610
12611       return MP_NO_ACTION;
12612     }
12613
12614     if (IS_SB_ELEMENT(element))
12615     {
12616       if (element == EL_SOKOBAN_FIELD_FULL)
12617       {
12618         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12619         local_player->sokobanfields_still_needed++;
12620       }
12621
12622       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12623       {
12624         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12625         local_player->sokobanfields_still_needed--;
12626       }
12627
12628       Feld[x][y] = EL_SOKOBAN_OBJECT;
12629
12630       if (Back[x][y] == Back[nextx][nexty])
12631         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12632       else if (Back[x][y] != 0)
12633         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12634                                     ACTION_EMPTYING);
12635       else
12636         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12637                                     ACTION_FILLING);
12638
12639       if (local_player->sokobanfields_still_needed == 0 &&
12640           game.emulation == EMU_SOKOBAN)
12641       {
12642         player->LevelSolved = player->GameOver = TRUE;
12643         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12644       }
12645     }
12646     else
12647       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12648
12649     InitMovingField(x, y, move_direction);
12650     GfxAction[x][y] = ACTION_PUSHING;
12651
12652     if (mode == DF_SNAP)
12653       ContinueMoving(x, y);
12654     else
12655       MovPos[x][y] = (dx != 0 ? dx : dy);
12656
12657     Pushed[x][y] = TRUE;
12658     Pushed[nextx][nexty] = TRUE;
12659
12660     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12661       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12662     else
12663       player->push_delay_value = -1;    /* get new value later */
12664
12665     /* check for element change _after_ element has been pushed */
12666     if (game.use_change_when_pushing_bug)
12667     {
12668       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12669                                  player->index_bit, dig_side);
12670       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12671                                           player->index_bit, dig_side);
12672     }
12673   }
12674   else if (IS_SWITCHABLE(element))
12675   {
12676     if (PLAYER_SWITCHING(player, x, y))
12677     {
12678       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12679                                           player->index_bit, dig_side);
12680
12681       return MP_ACTION;
12682     }
12683
12684     player->is_switching = TRUE;
12685     player->switch_x = x;
12686     player->switch_y = y;
12687
12688     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12689
12690     if (element == EL_ROBOT_WHEEL)
12691     {
12692       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12693       ZX = x;
12694       ZY = y;
12695
12696       DrawLevelField(x, y);
12697     }
12698     else if (element == EL_SP_TERMINAL)
12699     {
12700       int xx, yy;
12701
12702 #if 1
12703       SCAN_PLAYFIELD(xx, yy)
12704 #else
12705       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12706 #endif
12707       {
12708         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12709           Bang(xx, yy);
12710         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12711           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12712       }
12713     }
12714     else if (IS_BELT_SWITCH(element))
12715     {
12716       ToggleBeltSwitch(x, y);
12717     }
12718     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12719              element == EL_SWITCHGATE_SWITCH_DOWN)
12720     {
12721       ToggleSwitchgateSwitch(x, y);
12722     }
12723     else if (element == EL_LIGHT_SWITCH ||
12724              element == EL_LIGHT_SWITCH_ACTIVE)
12725     {
12726       ToggleLightSwitch(x, y);
12727     }
12728     else if (element == EL_TIMEGATE_SWITCH)
12729     {
12730       ActivateTimegateSwitch(x, y);
12731     }
12732     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12733              element == EL_BALLOON_SWITCH_RIGHT ||
12734              element == EL_BALLOON_SWITCH_UP    ||
12735              element == EL_BALLOON_SWITCH_DOWN  ||
12736              element == EL_BALLOON_SWITCH_NONE  ||
12737              element == EL_BALLOON_SWITCH_ANY)
12738     {
12739       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12740                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12741                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12742                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12743                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12744                              move_direction);
12745     }
12746     else if (element == EL_LAMP)
12747     {
12748       Feld[x][y] = EL_LAMP_ACTIVE;
12749       local_player->lights_still_needed--;
12750
12751       ResetGfxAnimation(x, y);
12752       DrawLevelField(x, y);
12753     }
12754     else if (element == EL_TIME_ORB_FULL)
12755     {
12756       Feld[x][y] = EL_TIME_ORB_EMPTY;
12757
12758       if (level.time > 0 || level.use_time_orb_bug)
12759       {
12760         TimeLeft += level.time_orb_time;
12761         DrawGameValue_Time(TimeLeft);
12762       }
12763
12764       ResetGfxAnimation(x, y);
12765       DrawLevelField(x, y);
12766     }
12767     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12768              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12769     {
12770       int xx, yy;
12771
12772       game.ball_state = !game.ball_state;
12773
12774 #if 1
12775       SCAN_PLAYFIELD(xx, yy)
12776 #else
12777       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12778 #endif
12779       {
12780         int e = Feld[xx][yy];
12781
12782         if (game.ball_state)
12783         {
12784           if (e == EL_EMC_MAGIC_BALL)
12785             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12786           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12787             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12788         }
12789         else
12790         {
12791           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12792             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12793           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12794             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12795         }
12796       }
12797     }
12798
12799     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12800                                         player->index_bit, dig_side);
12801
12802     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12803                                         player->index_bit, dig_side);
12804
12805     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12806                                         player->index_bit, dig_side);
12807
12808     return MP_ACTION;
12809   }
12810   else
12811   {
12812     if (!PLAYER_SWITCHING(player, x, y))
12813     {
12814       player->is_switching = TRUE;
12815       player->switch_x = x;
12816       player->switch_y = y;
12817
12818       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12819                                  player->index_bit, dig_side);
12820       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12821                                           player->index_bit, dig_side);
12822
12823       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12824                                  player->index_bit, dig_side);
12825       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12826                                           player->index_bit, dig_side);
12827     }
12828
12829     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12830                                player->index_bit, dig_side);
12831     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12832                                         player->index_bit, dig_side);
12833
12834     return MP_NO_ACTION;
12835   }
12836
12837   player->push_delay = -1;
12838
12839   if (is_player)                /* function can also be called by EL_PENGUIN */
12840   {
12841     if (Feld[x][y] != element)          /* really digged/collected something */
12842     {
12843       player->is_collecting = !player->is_digging;
12844       player->is_active = TRUE;
12845     }
12846   }
12847
12848   return MP_MOVING;
12849 }
12850
12851 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12852 {
12853   int jx = player->jx, jy = player->jy;
12854   int x = jx + dx, y = jy + dy;
12855   int snap_direction = (dx == -1 ? MV_LEFT  :
12856                         dx == +1 ? MV_RIGHT :
12857                         dy == -1 ? MV_UP    :
12858                         dy == +1 ? MV_DOWN  : MV_NONE);
12859   boolean can_continue_snapping = (level.continuous_snapping &&
12860                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12861
12862   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12863     return FALSE;
12864
12865   if (!player->active || !IN_LEV_FIELD(x, y))
12866     return FALSE;
12867
12868   if (dx && dy)
12869     return FALSE;
12870
12871   if (!dx && !dy)
12872   {
12873     if (player->MovPos == 0)
12874       player->is_pushing = FALSE;
12875
12876     player->is_snapping = FALSE;
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     return FALSE;
12886   }
12887
12888 #if USE_NEW_CONTINUOUS_SNAPPING
12889   /* prevent snapping with already pressed snap key when not allowed */
12890   if (player->is_snapping && !can_continue_snapping)
12891     return FALSE;
12892 #else
12893   if (player->is_snapping)
12894     return FALSE;
12895 #endif
12896
12897   player->MovDir = snap_direction;
12898
12899   if (player->MovPos == 0)
12900   {
12901     player->is_moving = FALSE;
12902     player->is_digging = FALSE;
12903     player->is_collecting = FALSE;
12904   }
12905
12906   player->is_dropping = FALSE;
12907   player->is_dropping_pressed = FALSE;
12908   player->drop_pressed_delay = 0;
12909
12910   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12911     return FALSE;
12912
12913   player->is_snapping = TRUE;
12914   player->is_active = TRUE;
12915
12916   if (player->MovPos == 0)
12917   {
12918     player->is_moving = FALSE;
12919     player->is_digging = FALSE;
12920     player->is_collecting = FALSE;
12921   }
12922
12923   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12924     DrawLevelField(player->last_jx, player->last_jy);
12925
12926   DrawLevelField(x, y);
12927
12928   return TRUE;
12929 }
12930
12931 boolean DropElement(struct PlayerInfo *player)
12932 {
12933   int old_element, new_element;
12934   int dropx = player->jx, dropy = player->jy;
12935   int drop_direction = player->MovDir;
12936   int drop_side = drop_direction;
12937   int drop_element = (player->inventory_size > 0 ?
12938                       player->inventory_element[player->inventory_size - 1] :
12939                       player->inventory_infinite_element != EL_UNDEFINED ?
12940                       player->inventory_infinite_element :
12941                       player->dynabombs_left > 0 ?
12942                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12943                       EL_UNDEFINED);
12944
12945   player->is_dropping_pressed = TRUE;
12946
12947   /* do not drop an element on top of another element; when holding drop key
12948      pressed without moving, dropped element must move away before the next
12949      element can be dropped (this is especially important if the next element
12950      is dynamite, which can be placed on background for historical reasons) */
12951   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12952     return MP_ACTION;
12953
12954   if (IS_THROWABLE(drop_element))
12955   {
12956     dropx += GET_DX_FROM_DIR(drop_direction);
12957     dropy += GET_DY_FROM_DIR(drop_direction);
12958
12959     if (!IN_LEV_FIELD(dropx, dropy))
12960       return FALSE;
12961   }
12962
12963   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12964   new_element = drop_element;           /* default: no change when dropping */
12965
12966   /* check if player is active, not moving and ready to drop */
12967   if (!player->active || player->MovPos || player->drop_delay > 0)
12968     return FALSE;
12969
12970   /* check if player has anything that can be dropped */
12971   if (new_element == EL_UNDEFINED)
12972     return FALSE;
12973
12974   /* check if drop key was pressed long enough for EM style dynamite */
12975   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12976     return FALSE;
12977
12978   /* check if anything can be dropped at the current position */
12979   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12980     return FALSE;
12981
12982   /* collected custom elements can only be dropped on empty fields */
12983   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12984     return FALSE;
12985
12986   if (old_element != EL_EMPTY)
12987     Back[dropx][dropy] = old_element;   /* store old element on this field */
12988
12989   ResetGfxAnimation(dropx, dropy);
12990   ResetRandomAnimationValue(dropx, dropy);
12991
12992   if (player->inventory_size > 0 ||
12993       player->inventory_infinite_element != EL_UNDEFINED)
12994   {
12995     if (player->inventory_size > 0)
12996     {
12997       player->inventory_size--;
12998
12999 #if 1
13000       DrawGameDoorValues();
13001 #else
13002       DrawGameValue_Dynamite(local_player->inventory_size);
13003 #endif
13004
13005       if (new_element == EL_DYNAMITE)
13006         new_element = EL_DYNAMITE_ACTIVE;
13007       else if (new_element == EL_EM_DYNAMITE)
13008         new_element = EL_EM_DYNAMITE_ACTIVE;
13009       else if (new_element == EL_SP_DISK_RED)
13010         new_element = EL_SP_DISK_RED_ACTIVE;
13011     }
13012
13013     Feld[dropx][dropy] = new_element;
13014
13015     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13016       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13017                           el2img(Feld[dropx][dropy]), 0);
13018
13019     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13020
13021     /* needed if previous element just changed to "empty" in the last frame */
13022     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13023
13024     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13025                                player->index_bit, drop_side);
13026     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13027                                         CE_PLAYER_DROPS_X,
13028                                         player->index_bit, drop_side);
13029
13030     TestIfElementTouchesCustomElement(dropx, dropy);
13031   }
13032   else          /* player is dropping a dyna bomb */
13033   {
13034     player->dynabombs_left--;
13035
13036     Feld[dropx][dropy] = new_element;
13037
13038     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13039       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13040                           el2img(Feld[dropx][dropy]), 0);
13041
13042     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13043   }
13044
13045   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13046     InitField_WithBug1(dropx, dropy, FALSE);
13047
13048   new_element = Feld[dropx][dropy];     /* element might have changed */
13049
13050   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13051       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13052   {
13053     int move_direction, nextx, nexty;
13054
13055     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13056       MovDir[dropx][dropy] = drop_direction;
13057
13058     move_direction = MovDir[dropx][dropy];
13059     nextx = dropx + GET_DX_FROM_DIR(move_direction);
13060     nexty = dropy + GET_DY_FROM_DIR(move_direction);
13061
13062     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13063     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13064   }
13065
13066   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13067   player->is_dropping = TRUE;
13068
13069   player->drop_pressed_delay = 0;
13070   player->is_dropping_pressed = FALSE;
13071
13072   player->drop_x = dropx;
13073   player->drop_y = dropy;
13074
13075   return TRUE;
13076 }
13077
13078 /* ------------------------------------------------------------------------- */
13079 /* game sound playing functions                                              */
13080 /* ------------------------------------------------------------------------- */
13081
13082 static int *loop_sound_frame = NULL;
13083 static int *loop_sound_volume = NULL;
13084
13085 void InitPlayLevelSound()
13086 {
13087   int num_sounds = getSoundListSize();
13088
13089   checked_free(loop_sound_frame);
13090   checked_free(loop_sound_volume);
13091
13092   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
13093   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13094 }
13095
13096 static void PlayLevelSound(int x, int y, int nr)
13097 {
13098   int sx = SCREENX(x), sy = SCREENY(y);
13099   int volume, stereo_position;
13100   int max_distance = 8;
13101   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13102
13103   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13104       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13105     return;
13106
13107   if (!IN_LEV_FIELD(x, y) ||
13108       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13109       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13110     return;
13111
13112   volume = SOUND_MAX_VOLUME;
13113
13114   if (!IN_SCR_FIELD(sx, sy))
13115   {
13116     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13117     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13118
13119     volume -= volume * (dx > dy ? dx : dy) / max_distance;
13120   }
13121
13122   stereo_position = (SOUND_MAX_LEFT +
13123                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13124                      (SCR_FIELDX + 2 * max_distance));
13125
13126   if (IS_LOOP_SOUND(nr))
13127   {
13128     /* This assures that quieter loop sounds do not overwrite louder ones,
13129        while restarting sound volume comparison with each new game frame. */
13130
13131     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13132       return;
13133
13134     loop_sound_volume[nr] = volume;
13135     loop_sound_frame[nr] = FrameCounter;
13136   }
13137
13138   PlaySoundExt(nr, volume, stereo_position, type);
13139 }
13140
13141 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13142 {
13143   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13144                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13145                  y < LEVELY(BY1) ? LEVELY(BY1) :
13146                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13147                  sound_action);
13148 }
13149
13150 static void PlayLevelSoundAction(int x, int y, int action)
13151 {
13152   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13153 }
13154
13155 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13156 {
13157   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13158
13159   if (sound_effect != SND_UNDEFINED)
13160     PlayLevelSound(x, y, sound_effect);
13161 }
13162
13163 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13164                                               int action)
13165 {
13166   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13167
13168   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13169     PlayLevelSound(x, y, sound_effect);
13170 }
13171
13172 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13173 {
13174   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13175
13176   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13177     PlayLevelSound(x, y, sound_effect);
13178 }
13179
13180 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13181 {
13182   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13183
13184   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13185     StopSound(sound_effect);
13186 }
13187
13188 static void PlayLevelMusic()
13189 {
13190   if (levelset.music[level_nr] != MUS_UNDEFINED)
13191     PlayMusic(levelset.music[level_nr]);        /* from config file */
13192   else
13193     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13194 }
13195
13196 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
13197 {
13198   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13199
13200   switch (sample)
13201   {
13202     case SAMPLE_blank:
13203       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13204       break;
13205
13206     case SAMPLE_roll:
13207       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13208       break;
13209
13210     case SAMPLE_stone:
13211       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13212       break;
13213
13214     case SAMPLE_nut:
13215       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13216       break;
13217
13218     case SAMPLE_crack:
13219       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13220       break;
13221
13222     case SAMPLE_bug:
13223       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13224       break;
13225
13226     case SAMPLE_tank:
13227       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13228       break;
13229
13230     case SAMPLE_android_clone:
13231       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13232       break;
13233
13234     case SAMPLE_android_move:
13235       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13236       break;
13237
13238     case SAMPLE_spring:
13239       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13240       break;
13241
13242     case SAMPLE_slurp:
13243       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13244       break;
13245
13246     case SAMPLE_eater:
13247       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13248       break;
13249
13250     case SAMPLE_eater_eat:
13251       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13252       break;
13253
13254     case SAMPLE_alien:
13255       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13256       break;
13257
13258     case SAMPLE_collect:
13259       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13260       break;
13261
13262     case SAMPLE_diamond:
13263       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13264       break;
13265
13266     case SAMPLE_squash:
13267       /* !!! CHECK THIS !!! */
13268 #if 1
13269       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13270 #else
13271       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13272 #endif
13273       break;
13274
13275     case SAMPLE_wonderfall:
13276       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13277       break;
13278
13279     case SAMPLE_drip:
13280       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13281       break;
13282
13283     case SAMPLE_push:
13284       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13285       break;
13286
13287     case SAMPLE_dirt:
13288       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13289       break;
13290
13291     case SAMPLE_acid:
13292       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13293       break;
13294
13295     case SAMPLE_ball:
13296       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13297       break;
13298
13299     case SAMPLE_grow:
13300       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13301       break;
13302
13303     case SAMPLE_wonder:
13304       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13305       break;
13306
13307     case SAMPLE_door:
13308       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13309       break;
13310
13311     case SAMPLE_exit_open:
13312       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13313       break;
13314
13315     case SAMPLE_exit_leave:
13316       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13317       break;
13318
13319     case SAMPLE_dynamite:
13320       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13321       break;
13322
13323     case SAMPLE_tick:
13324       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13325       break;
13326
13327     case SAMPLE_press:
13328       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13329       break;
13330
13331     case SAMPLE_wheel:
13332       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13333       break;
13334
13335     case SAMPLE_boom:
13336       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13337       break;
13338
13339     case SAMPLE_die:
13340       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13341       break;
13342
13343     case SAMPLE_time:
13344       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
13345       break;
13346
13347     default:
13348       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13349       break;
13350   }
13351 }
13352
13353 void RaiseScore(int value)
13354 {
13355   local_player->score += value;
13356
13357   DrawGameValue_Score(local_player->score);
13358 }
13359
13360 void RaiseScoreElement(int element)
13361 {
13362   switch(element)
13363   {
13364     case EL_EMERALD:
13365     case EL_BD_DIAMOND:
13366     case EL_EMERALD_YELLOW:
13367     case EL_EMERALD_RED:
13368     case EL_EMERALD_PURPLE:
13369     case EL_SP_INFOTRON:
13370       RaiseScore(level.score[SC_EMERALD]);
13371       break;
13372     case EL_DIAMOND:
13373       RaiseScore(level.score[SC_DIAMOND]);
13374       break;
13375     case EL_CRYSTAL:
13376       RaiseScore(level.score[SC_CRYSTAL]);
13377       break;
13378     case EL_PEARL:
13379       RaiseScore(level.score[SC_PEARL]);
13380       break;
13381     case EL_BUG:
13382     case EL_BD_BUTTERFLY:
13383     case EL_SP_ELECTRON:
13384       RaiseScore(level.score[SC_BUG]);
13385       break;
13386     case EL_SPACESHIP:
13387     case EL_BD_FIREFLY:
13388     case EL_SP_SNIKSNAK:
13389       RaiseScore(level.score[SC_SPACESHIP]);
13390       break;
13391     case EL_YAMYAM:
13392     case EL_DARK_YAMYAM:
13393       RaiseScore(level.score[SC_YAMYAM]);
13394       break;
13395     case EL_ROBOT:
13396       RaiseScore(level.score[SC_ROBOT]);
13397       break;
13398     case EL_PACMAN:
13399       RaiseScore(level.score[SC_PACMAN]);
13400       break;
13401     case EL_NUT:
13402       RaiseScore(level.score[SC_NUT]);
13403       break;
13404     case EL_DYNAMITE:
13405     case EL_EM_DYNAMITE:
13406     case EL_SP_DISK_RED:
13407     case EL_DYNABOMB_INCREASE_NUMBER:
13408     case EL_DYNABOMB_INCREASE_SIZE:
13409     case EL_DYNABOMB_INCREASE_POWER:
13410       RaiseScore(level.score[SC_DYNAMITE]);
13411       break;
13412     case EL_SHIELD_NORMAL:
13413     case EL_SHIELD_DEADLY:
13414       RaiseScore(level.score[SC_SHIELD]);
13415       break;
13416     case EL_EXTRA_TIME:
13417       RaiseScore(level.extra_time_score);
13418       break;
13419     case EL_KEY_1:
13420     case EL_KEY_2:
13421     case EL_KEY_3:
13422     case EL_KEY_4:
13423     case EL_EM_KEY_1:
13424     case EL_EM_KEY_2:
13425     case EL_EM_KEY_3:
13426     case EL_EM_KEY_4:
13427     case EL_EMC_KEY_5:
13428     case EL_EMC_KEY_6:
13429     case EL_EMC_KEY_7:
13430     case EL_EMC_KEY_8:
13431       RaiseScore(level.score[SC_KEY]);
13432       break;
13433     default:
13434       RaiseScore(element_info[element].collect_score);
13435       break;
13436   }
13437 }
13438
13439 void RequestQuitGame(boolean ask_if_really_quit)
13440 {
13441   if (AllPlayersGone ||
13442       !ask_if_really_quit ||
13443       level_editor_test_game ||
13444       Request("Do you really want to quit the game ?",
13445               REQ_ASK | REQ_STAY_CLOSED))
13446   {
13447 #if defined(NETWORK_AVALIABLE)
13448     if (options.network)
13449       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13450     else
13451 #endif
13452     {
13453       if (!ask_if_really_quit || level_editor_test_game)
13454       {
13455         game_status = GAME_MODE_MAIN;
13456
13457         DrawMainMenu();
13458       }
13459       else
13460       {
13461         FadeOut(REDRAW_FIELD);
13462
13463         game_status = GAME_MODE_MAIN;
13464
13465         DrawAndFadeInMainMenu(REDRAW_FIELD);
13466       }
13467     }
13468   }
13469   else
13470   {
13471     if (tape.playing && tape.deactivate_display)
13472       TapeDeactivateDisplayOff(TRUE);
13473
13474     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13475
13476     if (tape.playing && tape.deactivate_display)
13477       TapeDeactivateDisplayOn();
13478   }
13479 }
13480
13481
13482 /* ---------- new game button stuff ---------------------------------------- */
13483
13484 /* graphic position values for game buttons */
13485 #define GAME_BUTTON_XSIZE       30
13486 #define GAME_BUTTON_YSIZE       30
13487 #define GAME_BUTTON_XPOS        5
13488 #define GAME_BUTTON_YPOS        215
13489 #define SOUND_BUTTON_XPOS       5
13490 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13491
13492 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13493 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13494 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13495 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13496 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13497 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13498
13499 static struct
13500 {
13501   int x, y;
13502   int gadget_id;
13503   char *infotext;
13504 } gamebutton_info[NUM_GAME_BUTTONS] =
13505 {
13506   {
13507     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13508     GAME_CTRL_ID_STOP,
13509     "stop game"
13510   },
13511   {
13512     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13513     GAME_CTRL_ID_PAUSE,
13514     "pause game"
13515   },
13516   {
13517     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13518     GAME_CTRL_ID_PLAY,
13519     "play game"
13520   },
13521   {
13522     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13523     SOUND_CTRL_ID_MUSIC,
13524     "background music on/off"
13525   },
13526   {
13527     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13528     SOUND_CTRL_ID_LOOPS,
13529     "sound loops on/off"
13530   },
13531   {
13532     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13533     SOUND_CTRL_ID_SIMPLE,
13534     "normal sounds on/off"
13535   }
13536 };
13537
13538 void CreateGameButtons()
13539 {
13540   int i;
13541
13542   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13543   {
13544     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13545     struct GadgetInfo *gi;
13546     int button_type;
13547     boolean checked;
13548     unsigned long event_mask;
13549     int gd_xoffset, gd_yoffset;
13550     int gd_x1, gd_x2, gd_y1, gd_y2;
13551     int id = i;
13552
13553     gd_xoffset = gamebutton_info[i].x;
13554     gd_yoffset = gamebutton_info[i].y;
13555     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13556     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13557
13558     if (id == GAME_CTRL_ID_STOP ||
13559         id == GAME_CTRL_ID_PAUSE ||
13560         id == GAME_CTRL_ID_PLAY)
13561     {
13562       button_type = GD_TYPE_NORMAL_BUTTON;
13563       checked = FALSE;
13564       event_mask = GD_EVENT_RELEASED;
13565       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13566       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13567     }
13568     else
13569     {
13570       button_type = GD_TYPE_CHECK_BUTTON;
13571       checked =
13572         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13573          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13574          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13575       event_mask = GD_EVENT_PRESSED;
13576       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
13577       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13578     }
13579
13580     gi = CreateGadget(GDI_CUSTOM_ID, id,
13581                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
13582                       GDI_X, DX + gd_xoffset,
13583                       GDI_Y, DY + gd_yoffset,
13584                       GDI_WIDTH, GAME_BUTTON_XSIZE,
13585                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
13586                       GDI_TYPE, button_type,
13587                       GDI_STATE, GD_BUTTON_UNPRESSED,
13588                       GDI_CHECKED, checked,
13589                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13590                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13591                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13592                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13593                       GDI_EVENT_MASK, event_mask,
13594                       GDI_CALLBACK_ACTION, HandleGameButtons,
13595                       GDI_END);
13596
13597     if (gi == NULL)
13598       Error(ERR_EXIT, "cannot create gadget");
13599
13600     game_gadget[id] = gi;
13601   }
13602 }
13603
13604 void FreeGameButtons()
13605 {
13606   int i;
13607
13608   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13609     FreeGadget(game_gadget[i]);
13610 }
13611
13612 static void MapGameButtons()
13613 {
13614   int i;
13615
13616   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13617     MapGadget(game_gadget[i]);
13618 }
13619
13620 void UnmapGameButtons()
13621 {
13622   int i;
13623
13624   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13625     UnmapGadget(game_gadget[i]);
13626 }
13627
13628 static void HandleGameButtons(struct GadgetInfo *gi)
13629 {
13630   int id = gi->custom_id;
13631
13632   if (game_status != GAME_MODE_PLAYING)
13633     return;
13634
13635   switch (id)
13636   {
13637     case GAME_CTRL_ID_STOP:
13638       if (tape.playing)
13639         TapeStop();
13640       else
13641         RequestQuitGame(TRUE);
13642       break;
13643
13644     case GAME_CTRL_ID_PAUSE:
13645       if (options.network)
13646       {
13647 #if defined(NETWORK_AVALIABLE)
13648         if (tape.pausing)
13649           SendToServer_ContinuePlaying();
13650         else
13651           SendToServer_PausePlaying();
13652 #endif
13653       }
13654       else
13655         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13656       break;
13657
13658     case GAME_CTRL_ID_PLAY:
13659       if (tape.pausing)
13660       {
13661 #if defined(NETWORK_AVALIABLE)
13662         if (options.network)
13663           SendToServer_ContinuePlaying();
13664         else
13665 #endif
13666         {
13667           tape.pausing = FALSE;
13668           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13669         }
13670       }
13671       break;
13672
13673     case SOUND_CTRL_ID_MUSIC:
13674       if (setup.sound_music)
13675       { 
13676         setup.sound_music = FALSE;
13677         FadeMusic();
13678       }
13679       else if (audio.music_available)
13680       { 
13681         setup.sound = setup.sound_music = TRUE;
13682
13683         SetAudioMode(setup.sound);
13684
13685         PlayLevelMusic();
13686       }
13687       break;
13688
13689     case SOUND_CTRL_ID_LOOPS:
13690       if (setup.sound_loops)
13691         setup.sound_loops = FALSE;
13692       else if (audio.loops_available)
13693       {
13694         setup.sound = setup.sound_loops = TRUE;
13695         SetAudioMode(setup.sound);
13696       }
13697       break;
13698
13699     case SOUND_CTRL_ID_SIMPLE:
13700       if (setup.sound_simple)
13701         setup.sound_simple = FALSE;
13702       else if (audio.sound_available)
13703       {
13704         setup.sound = setup.sound_simple = TRUE;
13705         SetAudioMode(setup.sound);
13706       }
13707       break;
13708
13709     default:
13710       break;
13711   }
13712 }