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