rnd-20060816-2-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55 #define USE_NEW_GAME_WON                (USE_NEW_STUFF          * 1)
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #if 1
84
85 #define PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
86
87 /* special positions in the game control window (relative to control window) */
88 #define XX_LEVEL1               (game.panel.level.x)
89 #define XX_LEVEL2               (game.panel.level.x - 1)
90 #define YY_LEVEL                (game.panel.level.y)
91 #define XX_EMERALDS             (game.panel.gems.x)
92 #define YY_EMERALDS             (game.panel.gems.y)
93 #define XX_DYNAMITE             (game.panel.inventory.x)
94 #define YY_DYNAMITE             (game.panel.inventory.y)
95 #define XX_KEYS                 (game.panel.keys.x)
96 #define YY_KEYS                 (game.panel.keys.y)
97 #define XX_SCORE                (game.panel.score.x)
98 #define YY_SCORE                (game.panel.score.y)
99 #define XX_TIME1                (game.panel.time.x)
100 #define XX_TIME2                (game.panel.time.x + 1)
101 #define YY_TIME                 (game.panel.time.y)
102
103 #else
104
105 /* special positions in the game control window (relative to control window) */
106 #define XX_LEVEL                37
107 #define YY_LEVEL                20
108 #define XX_EMERALDS             29
109 #define YY_EMERALDS             54
110 #define XX_DYNAMITE             29
111 #define YY_DYNAMITE             89
112 #define XX_KEYS                 18
113 #define YY_KEYS                 123
114 #define XX_SCORE                15
115 #define YY_SCORE                159
116 #define XX_TIME1                29
117 #define XX_TIME2                30
118 #define YY_TIME                 194
119
120 #endif
121
122 /* special positions in the game control window (relative to main window) */
123 #define DX_LEVEL1               (DX + XX_LEVEL1)
124 #define DX_LEVEL2               (DX + XX_LEVEL2)
125 #define DY_LEVEL                (DY + YY_LEVEL)
126 #define DX_EMERALDS             (DX + XX_EMERALDS)
127 #define DY_EMERALDS             (DY + YY_EMERALDS)
128 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
129 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
130 #define DX_KEYS                 (DX + XX_KEYS)
131 #define DY_KEYS                 (DY + YY_KEYS)
132 #define DX_SCORE                (DX + XX_SCORE)
133 #define DY_SCORE                (DY + YY_SCORE)
134 #define DX_TIME1                (DX + XX_TIME1)
135 #define DX_TIME2                (DX + XX_TIME2)
136 #define DY_TIME                 (DY + YY_TIME)
137
138 /* values for delayed check of falling and moving elements and for collision */
139 #define CHECK_DELAY_MOVING      3
140 #define CHECK_DELAY_FALLING     3
141 #define CHECK_DELAY_COLLISION   2
142
143 /* values for initial player move delay (initial delay counter value) */
144 #define INITIAL_MOVE_DELAY_OFF  -1
145 #define INITIAL_MOVE_DELAY_ON   0
146
147 /* values for player movement speed (which is in fact a delay value) */
148 #define MOVE_DELAY_MIN_SPEED    32
149 #define MOVE_DELAY_NORMAL_SPEED 8
150 #define MOVE_DELAY_HIGH_SPEED   4
151 #define MOVE_DELAY_MAX_SPEED    1
152
153 #if 0
154 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
155 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
156 #else
157 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
158 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
159 #endif
160 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
161 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
162
163 /* values for other actions */
164 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
165 #define MOVE_STEPSIZE_MIN       (1)
166 #define MOVE_STEPSIZE_MAX       (TILEX)
167
168 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
169 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
170
171 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
172
173 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
174                                  RND(element_info[e].push_delay_random))
175 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
176                                  RND(element_info[e].drop_delay_random))
177 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
178                                  RND(element_info[e].move_delay_random))
179 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
180                                     (element_info[e].move_delay_random))
181 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
182                                  RND(element_info[e].ce_value_random_initial))
183 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
184 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
185                                  RND((c)->delay_random * (c)->delay_frames))
186 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
187                                  RND((c)->delay_random))
188
189
190 #if 1
191 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
192          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
193 #else
194 #define GET_VALID_FILE_ELEMENT(e)                                       \
195         ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
196 #endif
197
198 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
199         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
200          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
201          (be) + (e) - EL_SELF)
202
203 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
204         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
205          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
206          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
207          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
208          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
209          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
210          (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
211          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
212          (e))
213
214 #define CAN_GROW_INTO(e)                                                \
215         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
216
217 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
218                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
219                                         (condition)))
220
221 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
222                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
223                                         (CAN_MOVE_INTO_ACID(e) &&       \
224                                          Feld[x][y] == EL_ACID) ||      \
225                                         (condition)))
226
227 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
228                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
229                                         (CAN_MOVE_INTO_ACID(e) &&       \
230                                          Feld[x][y] == EL_ACID) ||      \
231                                         (condition)))
232
233 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
234                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
235                                         (condition) ||                  \
236                                         (CAN_MOVE_INTO_ACID(e) &&       \
237                                          Feld[x][y] == EL_ACID) ||      \
238                                         (DONT_COLLIDE_WITH(e) &&        \
239                                          IS_PLAYER(x, y) &&             \
240                                          !PLAYER_ENEMY_PROTECTED(x, y))))
241
242 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
243         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
244
245 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
246         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
247
248 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
249         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
250
251 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
252         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
253                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
254
255 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
256         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
257
258 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
259         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
260
261 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
262         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
263
264 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
265         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
266
267 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
268         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
269
270 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
271         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
272                                                  IS_FOOD_PENGUIN(Feld[x][y])))
273 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
274         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
275
276 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
277         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
278
279 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
280         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
281
282 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
283         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
284                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
285
286 #if 0
287 #define GROUP_NR(e)             ((e) - EL_GROUP_START)
288 #define IS_IN_GROUP(e, nr)      (element_info[e].in_group[nr] == TRUE)
289 #define IS_IN_GROUP_EL(e, ge)   (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
290
291 #define IS_EQUAL_OR_IN_GROUP(e, ge)                                     \
292         (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
293 #endif
294
295 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
296
297 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
298                 (!IS_PLAYER(x, y) &&                                    \
299                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
300
301 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
302         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
303
304 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
305 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
306
307 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
308 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
309 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
310 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
311
312 /* game button identifiers */
313 #define GAME_CTRL_ID_STOP               0
314 #define GAME_CTRL_ID_PAUSE              1
315 #define GAME_CTRL_ID_PLAY               2
316 #define SOUND_CTRL_ID_MUSIC             3
317 #define SOUND_CTRL_ID_LOOPS             4
318 #define SOUND_CTRL_ID_SIMPLE            5
319
320 #define NUM_GAME_BUTTONS                6
321
322
323 /* forward declaration for internal use */
324
325 static void CreateField(int, int, int);
326
327 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
328 static void AdvanceFrameAndPlayerCounters(int);
329
330 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
331 static boolean MovePlayer(struct PlayerInfo *, int, int);
332 static void ScrollPlayer(struct PlayerInfo *, int);
333 static void ScrollScreen(struct PlayerInfo *, int);
334
335 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
336
337 static void InitBeltMovement(void);
338 static void CloseAllOpenTimegates(void);
339 static void CheckGravityMovement(struct PlayerInfo *);
340 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
341 static void KillPlayerUnlessEnemyProtected(int, int);
342 static void KillPlayerUnlessExplosionProtected(int, int);
343
344 static void TestIfPlayerTouchesCustomElement(int, int);
345 static void TestIfElementTouchesCustomElement(int, int);
346 static void TestIfElementHitsCustomElement(int, int, int);
347 #if 0
348 static void TestIfElementSmashesCustomElement(int, int, int);
349 #endif
350
351 static void HandleElementChange(int, int, int);
352 static void ExecuteCustomElementAction(int, int, int, int);
353 static boolean ChangeElement(int, int, int, int);
354
355 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
356 #define CheckTriggeredElementChange(x, y, e, ev)                        \
357         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
358 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
359         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
360 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
361         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
362 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
363         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
364
365 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
366 #define CheckElementChange(x, y, e, te, ev)                             \
367         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
368 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
369         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
370 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
371         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
372
373 static void PlayLevelSound(int, int, int);
374 static void PlayLevelSoundNearest(int, int, int);
375 static void PlayLevelSoundAction(int, int, int);
376 static void PlayLevelSoundElementAction(int, int, int, int);
377 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
378 static void PlayLevelSoundActionIfLoop(int, int, int);
379 static void StopLevelSoundActionIfLoop(int, int, int);
380 static void PlayLevelMusic();
381
382 static void MapGameButtons();
383 static void HandleGameButtons(struct GadgetInfo *);
384
385 int AmoebeNachbarNr(int, int);
386 void AmoebeUmwandeln(int, int);
387 void ContinueMoving(int, int);
388 void Bang(int, int);
389 void InitMovDir(int, int);
390 void InitAmoebaNr(int, int);
391 int NewHiScore(void);
392
393 void TestIfGoodThingHitsBadThing(int, int, int);
394 void TestIfBadThingHitsGoodThing(int, int, int);
395 void TestIfPlayerTouchesBadThing(int, int);
396 void TestIfPlayerRunsIntoBadThing(int, int, int);
397 void TestIfBadThingTouchesPlayer(int, int);
398 void TestIfBadThingRunsIntoPlayer(int, int, int);
399 void TestIfFriendTouchesBadThing(int, int);
400 void TestIfBadThingTouchesFriend(int, int);
401 void TestIfBadThingTouchesOtherBadThing(int, int);
402
403 void KillPlayer(struct PlayerInfo *);
404 void BuryPlayer(struct PlayerInfo *);
405 void RemovePlayer(struct PlayerInfo *);
406
407 boolean SnapField(struct PlayerInfo *, int, int);
408 boolean DropElement(struct PlayerInfo *);
409
410 static int getInvisibleActiveFromInvisibleElement(int);
411 static int getInvisibleFromInvisibleActiveElement(int);
412
413 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
414
415
416 /* ------------------------------------------------------------------------- */
417 /* definition of elements that automatically change to other elements after  */
418 /* a specified time, eventually calling a function when changing             */
419 /* ------------------------------------------------------------------------- */
420
421 /* forward declaration for changer functions */
422 static void InitBuggyBase(int, int);
423 static void WarnBuggyBase(int, int);
424
425 static void InitTrap(int, int);
426 static void ActivateTrap(int, int);
427 static void ChangeActiveTrap(int, int);
428
429 static void InitRobotWheel(int, int);
430 static void RunRobotWheel(int, int);
431 static void StopRobotWheel(int, int);
432
433 static void InitTimegateWheel(int, int);
434 static void RunTimegateWheel(int, int);
435
436 static void InitMagicBallDelay(int, int);
437 static void ActivateMagicBall(int, int);
438
439 static void InitDiagonalMovingElement(int, int);
440
441 struct ChangingElementInfo
442 {
443   int element;
444   int target_element;
445   int change_delay;
446   void (*pre_change_function)(int x, int y);
447   void (*change_function)(int x, int y);
448   void (*post_change_function)(int x, int y);
449 };
450
451 static struct ChangingElementInfo change_delay_list[] =
452 {
453   {
454     EL_NUT_BREAKING,
455     EL_EMERALD,
456     6,
457     NULL,
458     NULL,
459     NULL
460   },
461   {
462     EL_PEARL_BREAKING,
463     EL_EMPTY,
464     8,
465     NULL,
466     NULL,
467     NULL
468   },
469   {
470     EL_EXIT_OPENING,
471     EL_EXIT_OPEN,
472     29,
473     NULL,
474     NULL,
475     NULL
476   },
477   {
478     EL_EXIT_CLOSING,
479     EL_EXIT_CLOSED,
480     29,
481     NULL,
482     NULL,
483     NULL
484   },
485   {
486     EL_SP_EXIT_OPENING,
487     EL_SP_EXIT_OPEN,
488     29,
489     NULL,
490     NULL,
491     NULL
492   },
493   {
494     EL_SP_EXIT_CLOSING,
495     EL_SP_EXIT_CLOSED,
496     29,
497     NULL,
498     NULL,
499     NULL
500   },
501   {
502     EL_SWITCHGATE_OPENING,
503     EL_SWITCHGATE_OPEN,
504     29,
505     NULL,
506     NULL,
507     NULL
508   },
509   {
510     EL_SWITCHGATE_CLOSING,
511     EL_SWITCHGATE_CLOSED,
512     29,
513     NULL,
514     NULL,
515     NULL
516   },
517   {
518     EL_TIMEGATE_OPENING,
519     EL_TIMEGATE_OPEN,
520     29,
521     NULL,
522     NULL,
523     NULL
524   },
525   {
526     EL_TIMEGATE_CLOSING,
527     EL_TIMEGATE_CLOSED,
528     29,
529     NULL,
530     NULL,
531     NULL
532   },
533
534   {
535     EL_ACID_SPLASH_LEFT,
536     EL_EMPTY,
537     8,
538     NULL,
539     NULL,
540     NULL
541   },
542   {
543     EL_ACID_SPLASH_RIGHT,
544     EL_EMPTY,
545     8,
546     NULL,
547     NULL,
548     NULL
549   },
550   {
551     EL_SP_BUGGY_BASE,
552     EL_SP_BUGGY_BASE_ACTIVATING,
553     0,
554     InitBuggyBase,
555     NULL,
556     NULL
557   },
558   {
559     EL_SP_BUGGY_BASE_ACTIVATING,
560     EL_SP_BUGGY_BASE_ACTIVE,
561     0,
562     InitBuggyBase,
563     NULL,
564     NULL
565   },
566   {
567     EL_SP_BUGGY_BASE_ACTIVE,
568     EL_SP_BUGGY_BASE,
569     0,
570     InitBuggyBase,
571     WarnBuggyBase,
572     NULL
573   },
574   {
575     EL_TRAP,
576     EL_TRAP_ACTIVE,
577     0,
578     InitTrap,
579     NULL,
580     ActivateTrap
581   },
582   {
583     EL_TRAP_ACTIVE,
584     EL_TRAP,
585     31,
586     NULL,
587     ChangeActiveTrap,
588     NULL
589   },
590   {
591     EL_ROBOT_WHEEL_ACTIVE,
592     EL_ROBOT_WHEEL,
593     0,
594     InitRobotWheel,
595     RunRobotWheel,
596     StopRobotWheel
597   },
598   {
599     EL_TIMEGATE_SWITCH_ACTIVE,
600     EL_TIMEGATE_SWITCH,
601     0,
602     InitTimegateWheel,
603     RunTimegateWheel,
604     NULL
605   },
606   {
607     EL_EMC_MAGIC_BALL_ACTIVE,
608     EL_EMC_MAGIC_BALL_ACTIVE,
609     0,
610     InitMagicBallDelay,
611     NULL,
612     ActivateMagicBall
613   },
614   {
615     EL_EMC_SPRING_BUMPER_ACTIVE,
616     EL_EMC_SPRING_BUMPER,
617     8,
618     NULL,
619     NULL,
620     NULL
621   },
622   {
623     EL_DIAGONAL_SHRINKING,
624     EL_UNDEFINED,
625     0,
626     NULL,
627     NULL,
628     NULL
629   },
630   {
631     EL_DIAGONAL_GROWING,
632     EL_UNDEFINED,
633     0,
634     NULL,
635     NULL,
636     InitDiagonalMovingElement
637   },
638
639   {
640     EL_UNDEFINED,
641     EL_UNDEFINED,
642     -1,
643     NULL,
644     NULL,
645     NULL
646   }
647 };
648
649 struct
650 {
651   int element;
652   int push_delay_fixed, push_delay_random;
653 }
654 push_delay_list[] =
655 {
656   { EL_SPRING,                  0, 0 },
657   { EL_BALLOON,                 0, 0 },
658
659   { EL_SOKOBAN_OBJECT,          2, 0 },
660   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
661   { EL_SATELLITE,               2, 0 },
662   { EL_SP_DISK_YELLOW,          2, 0 },
663
664   { EL_UNDEFINED,               0, 0 },
665 };
666
667 struct
668 {
669   int element;
670   int move_stepsize;
671 }
672 move_stepsize_list[] =
673 {
674   { EL_AMOEBA_DROP,             2 },
675   { EL_AMOEBA_DROPPING,         2 },
676   { EL_QUICKSAND_FILLING,       1 },
677   { EL_QUICKSAND_EMPTYING,      1 },
678   { EL_MAGIC_WALL_FILLING,      2 },
679   { EL_BD_MAGIC_WALL_FILLING,   2 },
680   { EL_MAGIC_WALL_EMPTYING,     2 },
681   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
682
683   { EL_UNDEFINED,               0 },
684 };
685
686 struct
687 {
688   int element;
689   int count;
690 }
691 collect_count_list[] =
692 {
693   { EL_EMERALD,                 1 },
694   { EL_BD_DIAMOND,              1 },
695   { EL_EMERALD_YELLOW,          1 },
696   { EL_EMERALD_RED,             1 },
697   { EL_EMERALD_PURPLE,          1 },
698   { EL_DIAMOND,                 3 },
699   { EL_SP_INFOTRON,             1 },
700   { EL_PEARL,                   5 },
701   { EL_CRYSTAL,                 8 },
702
703   { EL_UNDEFINED,               0 },
704 };
705
706 struct
707 {
708   int element;
709   int direction;
710 }
711 access_direction_list[] =
712 {
713   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
714   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
715   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
716   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
717   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
718   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
719   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
720   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
721   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
722   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
723   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
724
725   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
726   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
727   { EL_SP_PORT_UP,                                                   MV_DOWN },
728   { EL_SP_PORT_DOWN,                                         MV_UP           },
729   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
730   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
731   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
732   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
733   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
734   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
735   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
736   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
737   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
738   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
739   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
740   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
741   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
742   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
743   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
744
745   { EL_UNDEFINED,                       MV_NONE                              }
746 };
747
748 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
749
750 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
751 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
752 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
753                                  IS_JUST_CHANGING(x, y))
754
755 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
756
757 /* static variables for playfield scan mode (scanning forward or backward) */
758 static int playfield_scan_start_x = 0;
759 static int playfield_scan_start_y = 0;
760 static int playfield_scan_delta_x = 1;
761 static int playfield_scan_delta_y = 1;
762
763 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
764                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
765                                      (y) += playfield_scan_delta_y)     \
766                                 for ((x) = playfield_scan_start_x;      \
767                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
768                                      (x) += playfield_scan_delta_x)     \
769
770 #ifdef DEBUG
771 void DEBUG_SetMaximumDynamite()
772 {
773   int i;
774
775   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
776     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
777       local_player->inventory_element[local_player->inventory_size++] =
778         EL_DYNAMITE;
779 }
780 #endif
781
782 static void InitPlayfieldScanModeVars()
783 {
784   if (game.use_reverse_scan_direction)
785   {
786     playfield_scan_start_x = lev_fieldx - 1;
787     playfield_scan_start_y = lev_fieldy - 1;
788
789     playfield_scan_delta_x = -1;
790     playfield_scan_delta_y = -1;
791   }
792   else
793   {
794     playfield_scan_start_x = 0;
795     playfield_scan_start_y = 0;
796
797     playfield_scan_delta_x = 1;
798     playfield_scan_delta_y = 1;
799   }
800 }
801
802 static void InitPlayfieldScanMode(int mode)
803 {
804   game.use_reverse_scan_direction =
805     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
806
807   InitPlayfieldScanModeVars();
808 }
809
810 static int get_move_delay_from_stepsize(int move_stepsize)
811 {
812   move_stepsize =
813     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
814
815   /* make sure that stepsize value is always a power of 2 */
816   move_stepsize = (1 << log_2(move_stepsize));
817
818   return TILEX / move_stepsize;
819 }
820
821 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
822                                boolean init_game)
823 {
824   int player_nr = player->index_nr;
825   int move_delay = get_move_delay_from_stepsize(move_stepsize);
826   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
827
828   /* do no immediately change move delay -- the player might just be moving */
829   player->move_delay_value_next = move_delay;
830
831   /* information if player can move must be set separately */
832   player->cannot_move = cannot_move;
833
834   if (init_game)
835   {
836     player->move_delay       = game.initial_move_delay[player_nr];
837     player->move_delay_value = game.initial_move_delay_value[player_nr];
838
839     player->move_delay_value_next = -1;
840
841     player->move_delay_reset_counter = 0;
842   }
843 }
844
845 void GetPlayerConfig()
846 {
847   if (!audio.sound_available)
848     setup.sound_simple = FALSE;
849
850   if (!audio.loops_available)
851     setup.sound_loops = FALSE;
852
853   if (!audio.music_available)
854     setup.sound_music = FALSE;
855
856   if (!video.fullscreen_available)
857     setup.fullscreen = FALSE;
858
859   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
860
861   SetAudioMode(setup.sound);
862   InitJoysticks();
863 }
864
865 static int getBeltNrFromBeltElement(int element)
866 {
867   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
868           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
869           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
870 }
871
872 static int getBeltNrFromBeltActiveElement(int element)
873 {
874   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
875           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
876           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
877 }
878
879 static int getBeltNrFromBeltSwitchElement(int element)
880 {
881   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
882           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
883           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
884 }
885
886 static int getBeltDirNrFromBeltSwitchElement(int element)
887 {
888   static int belt_base_element[4] =
889   {
890     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
891     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
892     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
893     EL_CONVEYOR_BELT_4_SWITCH_LEFT
894   };
895
896   int belt_nr = getBeltNrFromBeltSwitchElement(element);
897   int belt_dir_nr = element - belt_base_element[belt_nr];
898
899   return (belt_dir_nr % 3);
900 }
901
902 static int getBeltDirFromBeltSwitchElement(int element)
903 {
904   static int belt_move_dir[3] =
905   {
906     MV_LEFT,
907     MV_NONE,
908     MV_RIGHT
909   };
910
911   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
912
913   return belt_move_dir[belt_dir_nr];
914 }
915
916 static int get_element_from_group_element(int element)
917 {
918   if (IS_GROUP_ELEMENT(element))
919   {
920     struct ElementGroupInfo *group = element_info[element].group;
921     int last_anim_random_frame = gfx.anim_random_frame;
922     int element_pos;
923
924     if (group->choice_mode == ANIM_RANDOM)
925       gfx.anim_random_frame = RND(group->num_elements_resolved);
926
927     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
928                                     group->choice_mode, 0,
929                                     group->choice_pos);
930
931     if (group->choice_mode == ANIM_RANDOM)
932       gfx.anim_random_frame = last_anim_random_frame;
933
934     group->choice_pos++;
935
936     element = group->element_resolved[element_pos];
937   }
938
939   return element;
940 }
941
942 static void InitPlayerField(int x, int y, int element, boolean init_game)
943 {
944   if (element == EL_SP_MURPHY)
945   {
946     if (init_game)
947     {
948       if (stored_player[0].present)
949       {
950         Feld[x][y] = EL_SP_MURPHY_CLONE;
951
952         return;
953       }
954       else
955       {
956         stored_player[0].use_murphy = TRUE;
957
958         if (!level.use_artwork_element[0])
959           stored_player[0].artwork_element = EL_SP_MURPHY;
960       }
961
962       Feld[x][y] = EL_PLAYER_1;
963     }
964   }
965
966   if (init_game)
967   {
968     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
969     int jx = player->jx, jy = player->jy;
970
971     player->present = TRUE;
972
973     player->block_last_field = (element == EL_SP_MURPHY ?
974                                 level.sp_block_last_field :
975                                 level.block_last_field);
976
977     /* ---------- initialize player's last field block delay --------------- */
978
979     /* always start with reliable default value (no adjustment needed) */
980     player->block_delay_adjustment = 0;
981
982     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
983     if (player->block_last_field && element == EL_SP_MURPHY)
984       player->block_delay_adjustment = 1;
985
986     /* special case 2: in game engines before 3.1.1, blocking was different */
987     if (game.use_block_last_field_bug)
988       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
989
990     if (!options.network || player->connected)
991     {
992       player->active = TRUE;
993
994       /* remove potentially duplicate players */
995       if (StorePlayer[jx][jy] == Feld[x][y])
996         StorePlayer[jx][jy] = 0;
997
998       StorePlayer[x][y] = Feld[x][y];
999
1000       if (options.debug)
1001       {
1002         printf("Player %d activated.\n", player->element_nr);
1003         printf("[Local player is %d and currently %s.]\n",
1004                local_player->element_nr,
1005                local_player->active ? "active" : "not active");
1006       }
1007     }
1008
1009     Feld[x][y] = EL_EMPTY;
1010
1011     player->jx = player->last_jx = x;
1012     player->jy = player->last_jy = y;
1013   }
1014 }
1015
1016 static void InitField(int x, int y, boolean init_game)
1017 {
1018   int element = Feld[x][y];
1019
1020   switch (element)
1021   {
1022     case EL_SP_MURPHY:
1023     case EL_PLAYER_1:
1024     case EL_PLAYER_2:
1025     case EL_PLAYER_3:
1026     case EL_PLAYER_4:
1027       InitPlayerField(x, y, element, init_game);
1028       break;
1029
1030     case EL_SOKOBAN_FIELD_PLAYER:
1031       element = Feld[x][y] = EL_PLAYER_1;
1032       InitField(x, y, init_game);
1033
1034       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1035       InitField(x, y, init_game);
1036       break;
1037
1038     case EL_SOKOBAN_FIELD_EMPTY:
1039       local_player->sokobanfields_still_needed++;
1040       break;
1041
1042     case EL_STONEBLOCK:
1043       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1044         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1045       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1046         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1047       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1048         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1049       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1050         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1051       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1052         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1053       break;
1054
1055     case EL_BUG:
1056     case EL_BUG_RIGHT:
1057     case EL_BUG_UP:
1058     case EL_BUG_LEFT:
1059     case EL_BUG_DOWN:
1060     case EL_SPACESHIP:
1061     case EL_SPACESHIP_RIGHT:
1062     case EL_SPACESHIP_UP:
1063     case EL_SPACESHIP_LEFT:
1064     case EL_SPACESHIP_DOWN:
1065     case EL_BD_BUTTERFLY:
1066     case EL_BD_BUTTERFLY_RIGHT:
1067     case EL_BD_BUTTERFLY_UP:
1068     case EL_BD_BUTTERFLY_LEFT:
1069     case EL_BD_BUTTERFLY_DOWN:
1070     case EL_BD_FIREFLY:
1071     case EL_BD_FIREFLY_RIGHT:
1072     case EL_BD_FIREFLY_UP:
1073     case EL_BD_FIREFLY_LEFT:
1074     case EL_BD_FIREFLY_DOWN:
1075     case EL_PACMAN_RIGHT:
1076     case EL_PACMAN_UP:
1077     case EL_PACMAN_LEFT:
1078     case EL_PACMAN_DOWN:
1079     case EL_YAMYAM:
1080     case EL_YAMYAM_LEFT:
1081     case EL_YAMYAM_RIGHT:
1082     case EL_YAMYAM_UP:
1083     case EL_YAMYAM_DOWN:
1084     case EL_DARK_YAMYAM:
1085     case EL_ROBOT:
1086     case EL_PACMAN:
1087     case EL_SP_SNIKSNAK:
1088     case EL_SP_ELECTRON:
1089     case EL_MOLE:
1090     case EL_MOLE_LEFT:
1091     case EL_MOLE_RIGHT:
1092     case EL_MOLE_UP:
1093     case EL_MOLE_DOWN:
1094       InitMovDir(x, y);
1095       break;
1096
1097     case EL_AMOEBA_FULL:
1098     case EL_BD_AMOEBA:
1099       InitAmoebaNr(x, y);
1100       break;
1101
1102     case EL_AMOEBA_DROP:
1103       if (y == lev_fieldy - 1)
1104       {
1105         Feld[x][y] = EL_AMOEBA_GROWING;
1106         Store[x][y] = EL_AMOEBA_WET;
1107       }
1108       break;
1109
1110     case EL_DYNAMITE_ACTIVE:
1111     case EL_SP_DISK_RED_ACTIVE:
1112     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1113     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1114     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1115     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1116       MovDelay[x][y] = 96;
1117       break;
1118
1119     case EL_EM_DYNAMITE_ACTIVE:
1120       MovDelay[x][y] = 32;
1121       break;
1122
1123     case EL_LAMP:
1124       local_player->lights_still_needed++;
1125       break;
1126
1127     case EL_PENGUIN:
1128       local_player->friends_still_needed++;
1129       break;
1130
1131     case EL_PIG:
1132     case EL_DRAGON:
1133       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1134       break;
1135
1136     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1137     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1138     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1139     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1140     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1141     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1142     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1143     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1144     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1145     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1146     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1147     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1148       if (init_game)
1149       {
1150         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1151         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1152         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1153
1154         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1155         {
1156           game.belt_dir[belt_nr] = belt_dir;
1157           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1158         }
1159         else    /* more than one switch -- set it like the first switch */
1160         {
1161           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1162         }
1163       }
1164       break;
1165
1166 #if !USE_BOTH_SWITCHGATE_SWITCHES
1167     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1168       if (init_game)
1169         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1170       break;
1171 #endif
1172
1173     case EL_LIGHT_SWITCH_ACTIVE:
1174       if (init_game)
1175         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1176       break;
1177
1178     case EL_INVISIBLE_STEELWALL:
1179     case EL_INVISIBLE_WALL:
1180     case EL_INVISIBLE_SAND:
1181       if (game.light_time_left > 0 ||
1182           game.lenses_time_left > 0)
1183         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1184       break;
1185
1186     case EL_EMC_MAGIC_BALL:
1187       if (game.ball_state)
1188         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1189       break;
1190
1191     case EL_EMC_MAGIC_BALL_SWITCH:
1192       if (game.ball_state)
1193         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1194       break;
1195
1196     default:
1197 #if 1
1198       if (IS_CUSTOM_ELEMENT(element))
1199       {
1200         if (CAN_MOVE(element))
1201           InitMovDir(x, y);
1202
1203 #if USE_NEW_CUSTOM_VALUE
1204         if (!element_info[element].use_last_ce_value || init_game)
1205           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1206 #endif
1207       }
1208 #else
1209       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1210         InitMovDir(x, y);
1211 #endif
1212       else if (IS_GROUP_ELEMENT(element))
1213       {
1214 #if 1
1215         Feld[x][y] = get_element_from_group_element(element);
1216
1217         InitField(x, y, init_game);
1218 #else
1219         struct ElementGroupInfo *group = element_info[element].group;
1220         int last_anim_random_frame = gfx.anim_random_frame;
1221         int element_pos;
1222
1223         if (group->choice_mode == ANIM_RANDOM)
1224           gfx.anim_random_frame = RND(group->num_elements_resolved);
1225
1226         element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1227                                         group->choice_mode, 0,
1228                                         group->choice_pos);
1229
1230         if (group->choice_mode == ANIM_RANDOM)
1231           gfx.anim_random_frame = last_anim_random_frame;
1232
1233         group->choice_pos++;
1234
1235         Feld[x][y] = group->element_resolved[element_pos];
1236
1237         InitField(x, y, init_game);
1238 #endif
1239       }
1240
1241       break;
1242   }
1243
1244 #if 1
1245   if (!init_game)
1246     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1247 #endif
1248
1249 #if 0
1250
1251 #if USE_NEW_CUSTOM_VALUE
1252
1253 #if 1
1254   CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1255 #else
1256   CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
1257 #endif
1258
1259 #endif
1260
1261 #endif
1262 }
1263
1264 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1265 {
1266   InitField(x, y, init_game);
1267
1268   /* not needed to call InitMovDir() -- already done by InitField()! */
1269   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1270       CAN_MOVE(Feld[x][y]))
1271     InitMovDir(x, y);
1272 }
1273
1274 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1275 {
1276   int old_element = Feld[x][y];
1277
1278   InitField(x, y, init_game);
1279
1280   /* not needed to call InitMovDir() -- already done by InitField()! */
1281   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1282       CAN_MOVE(old_element) &&
1283       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1284     InitMovDir(x, y);
1285
1286   /* this case is in fact a combination of not less than three bugs:
1287      first, it calls InitMovDir() for elements that can move, although this is
1288      already done by InitField(); then, it checks the element that was at this
1289      field _before_ the call to InitField() (which can change it); lastly, it
1290      was not called for "mole with direction" elements, which were treated as
1291      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1292   */
1293 }
1294
1295 inline void DrawGameValue_Emeralds(int value)
1296 {
1297   int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1298
1299   if (PANEL_DEACTIVATED(game.panel.gems))
1300     return;
1301
1302   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1303 }
1304
1305 inline void DrawGameValue_Dynamite(int value)
1306 {
1307   int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1308
1309   if (PANEL_DEACTIVATED(game.panel.inventory))
1310     return;
1311
1312   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1313 }
1314
1315 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1316 {
1317   int base_key_graphic = EL_KEY_1;
1318   int i;
1319
1320   if (PANEL_DEACTIVATED(game.panel.keys))
1321     return;
1322
1323   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1324     base_key_graphic = EL_EM_KEY_1;
1325
1326   /* currently only 4 of 8 possible keys are displayed */
1327   for (i = 0; i < STD_NUM_KEYS; i++)
1328   {
1329     int x = XX_KEYS + i * MINI_TILEX;
1330     int y = YY_KEYS;
1331
1332     if (key[i])
1333       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
1334     else
1335       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1336                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
1337   }
1338 }
1339
1340 inline void DrawGameValue_Score(int value)
1341 {
1342   int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
1343
1344   if (PANEL_DEACTIVATED(game.panel.score))
1345     return;
1346
1347   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1348 }
1349
1350 inline void DrawGameValue_Time(int value)
1351 {
1352   int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
1353   int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
1354
1355   if (PANEL_DEACTIVATED(game.panel.time))
1356     return;
1357
1358   /* clear background if value just changed its size */
1359   if (value == 999 || value == 1000)
1360     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
1361
1362   if (value < 1000)
1363     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1364   else
1365     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1366 }
1367
1368 inline void DrawGameValue_Level(int value)
1369 {
1370   if (PANEL_DEACTIVATED(game.panel.level))
1371     return;
1372
1373   if (level_nr < 100)
1374     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1375   else
1376 #if 1
1377     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
1378 #else
1379   {
1380     /* misuse area for displaying emeralds to draw bigger level number */
1381     DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1382                 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1383
1384     /* now copy it to the area for displaying level number */
1385     BlitBitmap(drawto, drawto,
1386                DX_EMERALDS, DY_EMERALDS + 1,
1387                getFontWidth(FONT_LEVEL_NUMBER) * 3,
1388                getFontHeight(FONT_LEVEL_NUMBER) - 1,
1389                DX_LEVEL - 1, DY_LEVEL + 1);
1390
1391     /* restore the area for displaying emeralds */
1392     DrawGameValue_Emeralds(local_player->gems_still_needed);
1393
1394     /* yes, this is all really ugly :-) */
1395   }
1396 #endif
1397 }
1398
1399 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1400                        int key_bits)
1401 {
1402   int key[MAX_NUM_KEYS];
1403   int i;
1404
1405   for (i = 0; i < MAX_NUM_KEYS; i++)
1406     key[i] = key_bits & (1 << i);
1407
1408   DrawGameValue_Level(level_nr);
1409
1410   DrawGameValue_Emeralds(emeralds);
1411   DrawGameValue_Dynamite(dynamite);
1412   DrawGameValue_Score(score);
1413   DrawGameValue_Time(time);
1414
1415   DrawGameValue_Keys(key);
1416 }
1417
1418 void DrawGameDoorValues()
1419 {
1420   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
1421   int dynamite_state = 0;
1422   int key_bits = 0;
1423   int i, j;
1424
1425   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1426   {
1427     DrawGameDoorValues_EM();
1428
1429     return;
1430   }
1431
1432 #if 0
1433   DrawGameValue_Level(level_nr);
1434
1435   DrawGameValue_Emeralds(local_player->gems_still_needed);
1436   DrawGameValue_Dynamite(local_player->inventory_size);
1437   DrawGameValue_Score(local_player->score);
1438   DrawGameValue_Time(TimeLeft);
1439
1440 #else
1441
1442   if (game.centered_player_nr == -1)
1443   {
1444     for (i = 0; i < MAX_PLAYERS; i++)
1445     {
1446       for (j = 0; j < MAX_NUM_KEYS; j++)
1447         if (stored_player[i].key[j])
1448           key_bits |= (1 << j);
1449
1450       dynamite_state += stored_player[i].inventory_size;
1451     }
1452
1453 #if 0
1454     DrawGameValue_Keys(stored_player[i].key);
1455 #endif
1456   }
1457   else
1458   {
1459     int player_nr = game.centered_player_nr;
1460
1461     for (i = 0; i < MAX_NUM_KEYS; i++)
1462       if (stored_player[player_nr].key[i])
1463         key_bits |= (1 << i);
1464
1465     dynamite_state = stored_player[player_nr].inventory_size;
1466   }
1467
1468   DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
1469                     local_player->score, time_value, key_bits);
1470 #endif
1471 }
1472
1473 #if 0
1474 static void resolve_group_element(int group_element, int recursion_depth)
1475 {
1476   static int group_nr;
1477   static struct ElementGroupInfo *group;
1478   struct ElementGroupInfo *actual_group = element_info[group_element].group;
1479   int i;
1480
1481   if (recursion_depth > NUM_GROUP_ELEMENTS)     /* recursion too deep */
1482   {
1483     Error(ERR_WARN, "recursion too deep when resolving group element %d",
1484           group_element - EL_GROUP_START + 1);
1485
1486     /* replace element which caused too deep recursion by question mark */
1487     group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1488
1489     return;
1490   }
1491
1492   if (recursion_depth == 0)                     /* initialization */
1493   {
1494     group = element_info[group_element].group;
1495     group_nr = group_element - EL_GROUP_START;
1496
1497     group->num_elements_resolved = 0;
1498     group->choice_pos = 0;
1499   }
1500
1501   for (i = 0; i < actual_group->num_elements; i++)
1502   {
1503     int element = actual_group->element[i];
1504
1505     if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1506       break;
1507
1508     if (IS_GROUP_ELEMENT(element))
1509       resolve_group_element(element, recursion_depth + 1);
1510     else
1511     {
1512       group->element_resolved[group->num_elements_resolved++] = element;
1513       element_info[element].in_group[group_nr] = TRUE;
1514     }
1515   }
1516 }
1517 #endif
1518
1519 #if 0
1520 static void replace_reference_element(int base_element, int *element)
1521 {
1522   if (*element >= EL_LAST_CE_8 && *element <= EL_NEXT_CE_8)
1523   {
1524     *element = base_element + *element - EL_SELF;
1525     *element = (*element < EL_CUSTOM_START ? EL_CUSTOM_START :
1526                 *element > EL_CUSTOM_END   ? EL_CUSTOM_END   : *element);
1527   }
1528 }
1529 #endif
1530
1531 /*
1532   =============================================================================
1533   InitGameEngine()
1534   -----------------------------------------------------------------------------
1535   initialize game engine due to level / tape version number
1536   =============================================================================
1537 */
1538
1539 static void InitGameEngine()
1540 {
1541   int i, j, k, l, x, y;
1542
1543   /* set game engine from tape file when re-playing, else from level file */
1544   game.engine_version = (tape.playing ? tape.engine_version :
1545                          level.game_version);
1546
1547   /* ---------------------------------------------------------------------- */
1548   /* set flags for bugs and changes according to active game engine version */
1549   /* ---------------------------------------------------------------------- */
1550
1551   /*
1552     Summary of bugfix/change:
1553     Fixed handling for custom elements that change when pushed by the player.
1554
1555     Fixed/changed in version:
1556     3.1.0
1557
1558     Description:
1559     Before 3.1.0, custom elements that "change when pushing" changed directly
1560     after the player started pushing them (until then handled in "DigField()").
1561     Since 3.1.0, these custom elements are not changed until the "pushing"
1562     move of the element is finished (now handled in "ContinueMoving()").
1563
1564     Affected levels/tapes:
1565     The first condition is generally needed for all levels/tapes before version
1566     3.1.0, which might use the old behaviour before it was changed; known tapes
1567     that are affected are some tapes from the level set "Walpurgis Gardens" by
1568     Jamie Cullen.
1569     The second condition is an exception from the above case and is needed for
1570     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1571     above (including some development versions of 3.1.0), but before it was
1572     known that this change would break tapes like the above and was fixed in
1573     3.1.1, so that the changed behaviour was active although the engine version
1574     while recording maybe was before 3.1.0. There is at least one tape that is
1575     affected by this exception, which is the tape for the one-level set "Bug
1576     Machine" by Juergen Bonhagen.
1577   */
1578
1579   game.use_change_when_pushing_bug =
1580     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1581      !(tape.playing &&
1582        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1583        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1584
1585   /*
1586     Summary of bugfix/change:
1587     Fixed handling for blocking the field the player leaves when moving.
1588
1589     Fixed/changed in version:
1590     3.1.1
1591
1592     Description:
1593     Before 3.1.1, when "block last field when moving" was enabled, the field
1594     the player is leaving when moving was blocked for the time of the move,
1595     and was directly unblocked afterwards. This resulted in the last field
1596     being blocked for exactly one less than the number of frames of one player
1597     move. Additionally, even when blocking was disabled, the last field was
1598     blocked for exactly one frame.
1599     Since 3.1.1, due to changes in player movement handling, the last field
1600     is not blocked at all when blocking is disabled. When blocking is enabled,
1601     the last field is blocked for exactly the number of frames of one player
1602     move. Additionally, if the player is Murphy, the hero of Supaplex, the
1603     last field is blocked for exactly one more than the number of frames of
1604     one player move.
1605
1606     Affected levels/tapes:
1607     (!!! yet to be determined -- probably many !!!)
1608   */
1609
1610   game.use_block_last_field_bug =
1611     (game.engine_version < VERSION_IDENT(3,1,1,0));
1612
1613   /*
1614     Summary of bugfix/change:
1615     Changed behaviour of CE changes with multiple changes per single frame.
1616
1617     Fixed/changed in version:
1618     3.2.0-6
1619
1620     Description:
1621     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
1622     This resulted in race conditions where CEs seem to behave strange in some
1623     situations (where triggered CE changes were just skipped because there was
1624     already a CE change on that tile in the playfield in that engine frame).
1625     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
1626     (The number of changes per frame must be limited in any case, because else
1627     it is easily possible to define CE changes that would result in an infinite
1628     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
1629     should be set large enough so that it would only be reached in cases where
1630     the corresponding CE change conditions run into a loop. Therefore, it seems
1631     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
1632     maximal number of change pages for custom elements.)
1633
1634     Affected levels/tapes:
1635     Probably many.
1636   */
1637
1638 #if USE_ONLY_ONE_CHANGE_PER_FRAME
1639   game.max_num_changes_per_frame = 1;
1640 #else
1641   game.max_num_changes_per_frame =
1642     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
1643 #endif
1644
1645   /* ---------------------------------------------------------------------- */
1646
1647   /* default scan direction: scan playfield from top/left to bottom/right */
1648   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
1649
1650   /* dynamically adjust element properties according to game engine version */
1651   InitElementPropertiesEngine(game.engine_version);
1652
1653 #if 0
1654   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1655   printf("          tape version == %06d [%s] [file: %06d]\n",
1656          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1657          tape.file_version);
1658   printf("       => game.engine_version == %06d\n", game.engine_version);
1659 #endif
1660
1661 #if 0
1662   /* ---------- recursively resolve group elements ------------------------- */
1663
1664   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1665     for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1666       element_info[i].in_group[j] = FALSE;
1667
1668   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1669     resolve_group_element(EL_GROUP_START + i, 0);
1670 #endif
1671
1672   /* ---------- initialize player's initial move delay --------------------- */
1673
1674 #if 1
1675   /* dynamically adjust player properties according to level information */
1676   for (i = 0; i < MAX_PLAYERS; i++)
1677     game.initial_move_delay_value[i] =
1678       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
1679 #else
1680   /* dynamically adjust player properties according to level information */
1681   game.initial_move_delay_value =
1682     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1683 #endif
1684
1685   /* dynamically adjust player properties according to game engine version */
1686   for (i = 0; i < MAX_PLAYERS; i++)
1687     game.initial_move_delay[i] =
1688       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1689        game.initial_move_delay_value[i] : 0);
1690
1691   /* ---------- initialize player's initial push delay --------------------- */
1692
1693   /* dynamically adjust player properties according to game engine version */
1694   game.initial_push_delay_value =
1695     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1696
1697   /* ---------- initialize changing elements ------------------------------- */
1698
1699   /* initialize changing elements information */
1700   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1701   {
1702     struct ElementInfo *ei = &element_info[i];
1703
1704     /* this pointer might have been changed in the level editor */
1705     ei->change = &ei->change_page[0];
1706
1707     if (!IS_CUSTOM_ELEMENT(i))
1708     {
1709       ei->change->target_element = EL_EMPTY_SPACE;
1710       ei->change->delay_fixed = 0;
1711       ei->change->delay_random = 0;
1712       ei->change->delay_frames = 1;
1713     }
1714
1715     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1716     {
1717       ei->has_change_event[j] = FALSE;
1718
1719       ei->event_page_nr[j] = 0;
1720       ei->event_page[j] = &ei->change_page[0];
1721     }
1722   }
1723
1724   /* add changing elements from pre-defined list */
1725   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1726   {
1727     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1728     struct ElementInfo *ei = &element_info[ch_delay->element];
1729
1730     ei->change->target_element       = ch_delay->target_element;
1731     ei->change->delay_fixed          = ch_delay->change_delay;
1732
1733     ei->change->pre_change_function  = ch_delay->pre_change_function;
1734     ei->change->change_function      = ch_delay->change_function;
1735     ei->change->post_change_function = ch_delay->post_change_function;
1736
1737     ei->change->can_change = TRUE;
1738     ei->change->can_change_or_has_action = TRUE;
1739
1740     ei->has_change_event[CE_DELAY] = TRUE;
1741
1742     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1743     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
1744   }
1745
1746   /* ---------- initialize internal run-time variables ------------- */
1747
1748   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1749   {
1750     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1751
1752     for (j = 0; j < ei->num_change_pages; j++)
1753     {
1754       ei->change_page[j].can_change_or_has_action =
1755         (ei->change_page[j].can_change |
1756          ei->change_page[j].has_action);
1757     }
1758   }
1759
1760   /* add change events from custom element configuration */
1761   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1762   {
1763     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1764
1765     for (j = 0; j < ei->num_change_pages; j++)
1766     {
1767       if (!ei->change_page[j].can_change_or_has_action)
1768         continue;
1769
1770       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1771       {
1772         /* only add event page for the first page found with this event */
1773         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1774         {
1775           ei->has_change_event[k] = TRUE;
1776
1777           ei->event_page_nr[k] = j;
1778           ei->event_page[k] = &ei->change_page[j];
1779         }
1780       }
1781     }
1782   }
1783
1784   /* ---------- initialize run-time trigger player and element ------------- */
1785
1786   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1787   {
1788     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1789
1790     for (j = 0; j < ei->num_change_pages; j++)
1791     {
1792       ei->change_page[j].actual_trigger_element = EL_EMPTY;
1793       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1794       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
1795       ei->change_page[j].actual_trigger_ce_value = 0;
1796       ei->change_page[j].actual_trigger_ce_score = 0;
1797     }
1798   }
1799
1800   /* ---------- initialize trigger events ---------------------------------- */
1801
1802   /* initialize trigger events information */
1803   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1804     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1805       trigger_events[i][j] = FALSE;
1806
1807   /* add trigger events from element change event properties */
1808   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1809   {
1810     struct ElementInfo *ei = &element_info[i];
1811
1812     for (j = 0; j < ei->num_change_pages; j++)
1813     {
1814       if (!ei->change_page[j].can_change_or_has_action)
1815         continue;
1816
1817       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1818       {
1819         int trigger_element = ei->change_page[j].trigger_element;
1820
1821         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1822         {
1823           if (ei->change_page[j].has_event[k])
1824           {
1825             if (IS_GROUP_ELEMENT(trigger_element))
1826             {
1827               struct ElementGroupInfo *group =
1828                 element_info[trigger_element].group;
1829
1830               for (l = 0; l < group->num_elements_resolved; l++)
1831                 trigger_events[group->element_resolved[l]][k] = TRUE;
1832             }
1833 #if 1
1834             else if (trigger_element == EL_ANY_ELEMENT)
1835               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
1836                 trigger_events[l][k] = TRUE;
1837 #endif
1838             else
1839               trigger_events[trigger_element][k] = TRUE;
1840           }
1841         }
1842       }
1843     }
1844   }
1845
1846   /* ---------- initialize push delay -------------------------------------- */
1847
1848   /* initialize push delay values to default */
1849   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1850   {
1851     if (!IS_CUSTOM_ELEMENT(i))
1852     {
1853 #if 1
1854       /* set default push delay values (corrected since version 3.0.7-1) */
1855       if (game.engine_version < VERSION_IDENT(3,0,7,1))
1856       {
1857         element_info[i].push_delay_fixed = 2;
1858         element_info[i].push_delay_random = 8;
1859       }
1860       else
1861       {
1862         element_info[i].push_delay_fixed = 8;
1863         element_info[i].push_delay_random = 8;
1864       }
1865 #else
1866       element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
1867       element_info[i].push_delay_random = game.default_push_delay_random;
1868 #endif
1869     }
1870   }
1871
1872   /* set push delay value for certain elements from pre-defined list */
1873   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1874   {
1875     int e = push_delay_list[i].element;
1876
1877     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1878     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1879   }
1880
1881   /* set push delay value for Supaplex elements for newer engine versions */
1882   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1883   {
1884     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1885     {
1886       if (IS_SP_ELEMENT(i))
1887       {
1888         /* set SP push delay to just enough to push under a falling zonk */
1889         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1890
1891         element_info[i].push_delay_fixed  = delay;
1892         element_info[i].push_delay_random = 0;
1893       }
1894     }
1895   }
1896
1897   /* ---------- initialize move stepsize ----------------------------------- */
1898
1899   /* initialize move stepsize values to default */
1900   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1901     if (!IS_CUSTOM_ELEMENT(i))
1902       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1903
1904   /* set move stepsize value for certain elements from pre-defined list */
1905   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1906   {
1907     int e = move_stepsize_list[i].element;
1908
1909     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1910   }
1911
1912   /* ---------- initialize collect score ----------------------------------- */
1913
1914   /* initialize collect score values for custom elements from initial value */
1915   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1916     if (IS_CUSTOM_ELEMENT(i))
1917       element_info[i].collect_score = element_info[i].collect_score_initial;
1918
1919   /* ---------- initialize collect count ----------------------------------- */
1920
1921   /* initialize collect count values for non-custom elements */
1922   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1923     if (!IS_CUSTOM_ELEMENT(i))
1924       element_info[i].collect_count_initial = 0;
1925
1926   /* add collect count values for all elements from pre-defined list */
1927   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1928     element_info[collect_count_list[i].element].collect_count_initial =
1929       collect_count_list[i].count;
1930
1931   /* ---------- initialize access direction -------------------------------- */
1932
1933   /* initialize access direction values to default (access from every side) */
1934   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1935     if (!IS_CUSTOM_ELEMENT(i))
1936       element_info[i].access_direction = MV_ALL_DIRECTIONS;
1937
1938   /* set access direction value for certain elements from pre-defined list */
1939   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1940     element_info[access_direction_list[i].element].access_direction =
1941       access_direction_list[i].direction;
1942
1943   /* ---------- initialize explosion content ------------------------------- */
1944   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1945   {
1946     if (IS_CUSTOM_ELEMENT(i))
1947       continue;
1948
1949     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1950     {
1951       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
1952
1953       element_info[i].content.e[x][y] =
1954         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
1955          i == EL_PLAYER_2 ? EL_EMERALD_RED :
1956          i == EL_PLAYER_3 ? EL_EMERALD :
1957          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
1958          i == EL_MOLE ? EL_EMERALD_RED :
1959          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
1960          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
1961          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
1962          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
1963          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
1964          i == EL_WALL_EMERALD ? EL_EMERALD :
1965          i == EL_WALL_DIAMOND ? EL_DIAMOND :
1966          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
1967          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
1968          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
1969          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
1970          i == EL_WALL_PEARL ? EL_PEARL :
1971          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
1972          EL_EMPTY);
1973     }
1974   }
1975
1976 #if 0
1977   /* ---------- initialize reference elements ------------------------------- */
1978   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1979   {
1980     int element = EL_CUSTOM_START + i;
1981     struct ElementInfo *ei = &element_info[element];
1982
1983     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1984       replace_reference_element(element, &ei->content.e[x][y]);
1985
1986     for (j = 0; j < ei->num_change_pages; j++)
1987     {
1988       struct ElementChangeInfo *change = &ei->change_page[j];
1989
1990       replace_reference_element(element, &change->target_element);
1991       replace_reference_element(element, &change->trigger_element);
1992
1993       for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
1994         replace_reference_element(element, &change->target_content.e[x][y]);
1995     }
1996   }
1997 #endif
1998 }
1999
2000 int get_num_special_action(int element, int action_first, int action_last)
2001 {
2002   int num_special_action = 0;
2003   int i, j;
2004
2005   for (i = action_first; i <= action_last; i++)
2006   {
2007     boolean found = FALSE;
2008
2009     for (j = 0; j < NUM_DIRECTIONS; j++)
2010       if (el_act_dir2img(element, i, j) !=
2011           el_act_dir2img(element, ACTION_DEFAULT, j))
2012         found = TRUE;
2013
2014     if (found)
2015       num_special_action++;
2016     else
2017       break;
2018   }
2019
2020 #if 0
2021   printf("::: %d->%d: %d\n", action_first, action_last, num_special_action);
2022 #endif
2023
2024   return num_special_action;
2025 }
2026
2027 /*
2028   =============================================================================
2029   InitGame()
2030   -----------------------------------------------------------------------------
2031   initialize and start new game
2032   =============================================================================
2033 */
2034
2035 void InitGame()
2036 {
2037   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
2038   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
2039   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
2040   boolean do_fading = (game_status == GAME_MODE_MAIN);
2041   int i, j, x, y;
2042
2043   game_status = GAME_MODE_PLAYING;
2044
2045   InitGameEngine();
2046
2047   /* don't play tapes over network */
2048   network_playing = (options.network && !tape.playing);
2049
2050   for (i = 0; i < MAX_PLAYERS; i++)
2051   {
2052     struct PlayerInfo *player = &stored_player[i];
2053
2054     player->index_nr = i;
2055     player->index_bit = (1 << i);
2056     player->element_nr = EL_PLAYER_1 + i;
2057
2058     player->present = FALSE;
2059     player->active = FALSE;
2060
2061     player->action = 0;
2062     player->effective_action = 0;
2063     player->programmed_action = 0;
2064
2065     player->score = 0;
2066     player->gems_still_needed = level.gems_needed;
2067     player->sokobanfields_still_needed = 0;
2068     player->lights_still_needed = 0;
2069     player->friends_still_needed = 0;
2070
2071     for (j = 0; j < MAX_NUM_KEYS; j++)
2072       player->key[j] = FALSE;
2073
2074     player->dynabomb_count = 0;
2075     player->dynabomb_size = 1;
2076     player->dynabombs_left = 0;
2077     player->dynabomb_xl = FALSE;
2078
2079     player->MovDir = MV_NONE;
2080     player->MovPos = 0;
2081     player->GfxPos = 0;
2082     player->GfxDir = MV_NONE;
2083     player->GfxAction = ACTION_DEFAULT;
2084     player->Frame = 0;
2085     player->StepFrame = 0;
2086
2087     player->use_murphy = FALSE;
2088     player->artwork_element =
2089       (level.use_artwork_element[i] ? level.artwork_element[i] :
2090        player->element_nr);
2091
2092     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
2093     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
2094
2095     player->gravity = level.initial_player_gravity[i];
2096
2097     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
2098
2099     player->actual_frame_counter = 0;
2100
2101     player->step_counter = 0;
2102
2103     player->last_move_dir = MV_NONE;
2104
2105     player->is_active = FALSE;
2106
2107     player->is_waiting = FALSE;
2108     player->is_moving = FALSE;
2109     player->is_auto_moving = FALSE;
2110     player->is_digging = FALSE;
2111     player->is_snapping = FALSE;
2112     player->is_collecting = FALSE;
2113     player->is_pushing = FALSE;
2114     player->is_switching = FALSE;
2115     player->is_dropping = FALSE;
2116     player->is_dropping_pressed = FALSE;
2117
2118     player->is_bored = FALSE;
2119     player->is_sleeping = FALSE;
2120
2121     player->frame_counter_bored = -1;
2122     player->frame_counter_sleeping = -1;
2123
2124     player->anim_delay_counter = 0;
2125     player->post_delay_counter = 0;
2126
2127     player->dir_waiting = MV_NONE;
2128     player->action_waiting = ACTION_DEFAULT;
2129     player->last_action_waiting = ACTION_DEFAULT;
2130     player->special_action_bored = ACTION_DEFAULT;
2131     player->special_action_sleeping = ACTION_DEFAULT;
2132
2133 #if 1
2134     /* cannot be set here -- could be modified in Init[Player]Field() below */
2135 #else
2136     /* set number of special actions for bored and sleeping animation */
2137     player->num_special_action_bored =
2138       get_num_special_action(player->artwork_element,
2139                              ACTION_BORING_1, ACTION_BORING_LAST);
2140     player->num_special_action_sleeping =
2141       get_num_special_action(player->artwork_element,
2142                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2143 #endif
2144
2145     player->switch_x = -1;
2146     player->switch_y = -1;
2147
2148     player->drop_x = -1;
2149     player->drop_y = -1;
2150
2151     player->show_envelope = 0;
2152
2153 #if 1
2154     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
2155 #else
2156     player->move_delay       = game.initial_move_delay;
2157     player->move_delay_value = game.initial_move_delay_value;
2158
2159     player->move_delay_value_next = -1;
2160
2161     player->move_delay_reset_counter = 0;
2162
2163     player->cannot_move = FALSE;
2164 #endif
2165
2166     player->push_delay       = -1;      /* initialized when pushing starts */
2167     player->push_delay_value = game.initial_push_delay_value;
2168
2169     player->drop_delay = 0;
2170     player->drop_pressed_delay = 0;
2171
2172     player->last_jx = player->last_jy = 0;
2173     player->jx = player->jy = 0;
2174
2175     player->shield_normal_time_left = 0;
2176     player->shield_deadly_time_left = 0;
2177
2178     player->inventory_infinite_element = EL_UNDEFINED;
2179     player->inventory_size = 0;
2180
2181     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
2182     SnapField(player, 0, 0);
2183
2184     player->LevelSolved = FALSE;
2185     player->GameOver = FALSE;
2186
2187     player->LevelSolved_GameEnd = FALSE;
2188     player->LevelSolved_SaveTape = FALSE;
2189     player->LevelSolved_SaveScore = FALSE;
2190   }
2191
2192   network_player_action_received = FALSE;
2193
2194 #if defined(NETWORK_AVALIABLE)
2195   /* initial null action */
2196   if (network_playing)
2197     SendToServer_MovePlayer(MV_NONE);
2198 #endif
2199
2200   ZX = ZY = -1;
2201   ExitX = ExitY = -1;
2202
2203   FrameCounter = 0;
2204   TimeFrames = 0;
2205   TimePlayed = 0;
2206   TimeLeft = level.time;
2207   TapeTime = 0;
2208
2209   ScreenMovDir = MV_NONE;
2210   ScreenMovPos = 0;
2211   ScreenGfxPos = 0;
2212
2213   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
2214
2215   AllPlayersGone = FALSE;
2216
2217   game.yamyam_content_nr = 0;
2218   game.magic_wall_active = FALSE;
2219   game.magic_wall_time_left = 0;
2220   game.light_time_left = 0;
2221   game.timegate_time_left = 0;
2222   game.switchgate_pos = 0;
2223   game.wind_direction = level.wind_direction_initial;
2224
2225 #if !USE_PLAYER_GRAVITY
2226 #if 1
2227   game.gravity = FALSE;
2228 #else
2229   game.gravity = level.initial_gravity;
2230 #endif
2231   game.explosions_delayed = TRUE;
2232 #endif
2233
2234   game.lenses_time_left = 0;
2235   game.magnify_time_left = 0;
2236
2237   game.ball_state = level.ball_state_initial;
2238   game.ball_content_nr = 0;
2239
2240   game.envelope_active = FALSE;
2241
2242   /* set focus to local player for network games, else to all players */
2243   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
2244   game.centered_player_nr_next = game.centered_player_nr;
2245   game.set_centered_player = FALSE;
2246
2247   if (network_playing && tape.recording)
2248   {
2249     /* store client dependent player focus when recording network games */
2250     tape.centered_player_nr_next = game.centered_player_nr_next;
2251     tape.set_centered_player = TRUE;
2252   }
2253
2254 #if 0
2255   printf("::: focus set to player %d [%d]\n",
2256          game.centered_player_nr, local_player->index_nr);
2257 #endif
2258
2259   for (i = 0; i < NUM_BELTS; i++)
2260   {
2261     game.belt_dir[i] = MV_NONE;
2262     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
2263   }
2264
2265   for (i = 0; i < MAX_NUM_AMOEBA; i++)
2266     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
2267
2268 #if 1
2269   SCAN_PLAYFIELD(x, y)
2270 #else
2271   for (x = 0; x < lev_fieldx; x++) for (y = 0; y < lev_fieldy; y++)
2272 #endif
2273   {
2274     Feld[x][y] = level.field[x][y];
2275     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2276     ChangeDelay[x][y] = 0;
2277     ChangePage[x][y] = -1;
2278 #if USE_NEW_CUSTOM_VALUE
2279     CustomValue[x][y] = 0;              /* initialized in InitField() */
2280 #endif
2281     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
2282     AmoebaNr[x][y] = 0;
2283     WasJustMoving[x][y] = 0;
2284     WasJustFalling[x][y] = 0;
2285     CheckCollision[x][y] = 0;
2286     Stop[x][y] = FALSE;
2287     Pushed[x][y] = FALSE;
2288
2289     ChangeCount[x][y] = 0;
2290     ChangeEvent[x][y] = -1;
2291
2292     ExplodePhase[x][y] = 0;
2293     ExplodeDelay[x][y] = 0;
2294     ExplodeField[x][y] = EX_TYPE_NONE;
2295
2296     RunnerVisit[x][y] = 0;
2297     PlayerVisit[x][y] = 0;
2298
2299     GfxFrame[x][y] = 0;
2300     GfxRandom[x][y] = INIT_GFX_RANDOM();
2301     GfxElement[x][y] = EL_UNDEFINED;
2302     GfxAction[x][y] = ACTION_DEFAULT;
2303     GfxDir[x][y] = MV_NONE;
2304   }
2305
2306 #if 1
2307   SCAN_PLAYFIELD(x, y)
2308 #else
2309   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2310 #endif
2311   {
2312     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
2313       emulate_bd = FALSE;
2314     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
2315       emulate_sb = FALSE;
2316     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
2317       emulate_sp = FALSE;
2318
2319     InitField(x, y, TRUE);
2320   }
2321
2322   InitBeltMovement();
2323
2324   for (i = 0; i < MAX_PLAYERS; i++)
2325   {
2326     struct PlayerInfo *player = &stored_player[i];
2327
2328 #if 1
2329     /* set number of special actions for bored and sleeping animation */
2330     player->num_special_action_bored =
2331       get_num_special_action(player->artwork_element,
2332                              ACTION_BORING_1, ACTION_BORING_LAST);
2333     player->num_special_action_sleeping =
2334       get_num_special_action(player->artwork_element,
2335                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
2336 #endif
2337
2338   }
2339
2340   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
2341                     emulate_sb ? EMU_SOKOBAN :
2342                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
2343
2344 #if USE_NEW_ALL_SLIPPERY
2345   /* initialize type of slippery elements */
2346   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2347   {
2348     if (!IS_CUSTOM_ELEMENT(i))
2349     {
2350       /* default: elements slip down either to the left or right randomly */
2351       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
2352
2353       /* SP style elements prefer to slip down on the left side */
2354       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
2355         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2356
2357       /* BD style elements prefer to slip down on the left side */
2358       if (game.emulation == EMU_BOULDERDASH)
2359         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
2360     }
2361   }
2362 #endif
2363
2364   /* initialize explosion and ignition delay */
2365   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2366   {
2367     if (!IS_CUSTOM_ELEMENT(i))
2368     {
2369       int num_phase = 8;
2370       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
2371                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
2372                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
2373       int last_phase = (num_phase + 1) * delay;
2374       int half_phase = (num_phase / 2) * delay;
2375
2376       element_info[i].explosion_delay = last_phase - 1;
2377       element_info[i].ignition_delay = half_phase;
2378
2379       if (i == EL_BLACK_ORB)
2380         element_info[i].ignition_delay = 1;
2381     }
2382
2383 #if 0
2384     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
2385       element_info[i].explosion_delay = 1;
2386
2387     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
2388       element_info[i].ignition_delay = 1;
2389 #endif
2390   }
2391
2392   /* correct non-moving belts to start moving left */
2393   for (i = 0; i < NUM_BELTS; i++)
2394     if (game.belt_dir[i] == MV_NONE)
2395       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
2396
2397   /* check if any connected player was not found in playfield */
2398   for (i = 0; i < MAX_PLAYERS; i++)
2399   {
2400     struct PlayerInfo *player = &stored_player[i];
2401
2402     if (player->connected && !player->present)
2403     {
2404       for (j = 0; j < MAX_PLAYERS; j++)
2405       {
2406         struct PlayerInfo *some_player = &stored_player[j];
2407         int jx = some_player->jx, jy = some_player->jy;
2408
2409         /* assign first free player found that is present in the playfield */
2410         if (some_player->present && !some_player->connected)
2411         {
2412           player->present = TRUE;
2413           player->active = TRUE;
2414
2415           some_player->present = FALSE;
2416           some_player->active = FALSE;
2417
2418 #if 0
2419           player->element_nr = some_player->element_nr;
2420 #endif
2421
2422           player->artwork_element = some_player->artwork_element;
2423
2424           player->block_last_field       = some_player->block_last_field;
2425           player->block_delay_adjustment = some_player->block_delay_adjustment;
2426
2427           StorePlayer[jx][jy] = player->element_nr;
2428           player->jx = player->last_jx = jx;
2429           player->jy = player->last_jy = jy;
2430
2431           break;
2432         }
2433       }
2434     }
2435   }
2436
2437   if (tape.playing)
2438   {
2439     /* when playing a tape, eliminate all players who do not participate */
2440
2441     for (i = 0; i < MAX_PLAYERS; i++)
2442     {
2443       if (stored_player[i].active && !tape.player_participates[i])
2444       {
2445         struct PlayerInfo *player = &stored_player[i];
2446         int jx = player->jx, jy = player->jy;
2447
2448         player->active = FALSE;
2449         StorePlayer[jx][jy] = 0;
2450         Feld[jx][jy] = EL_EMPTY;
2451       }
2452     }
2453   }
2454   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2455   {
2456     /* when in single player mode, eliminate all but the first active player */
2457
2458     for (i = 0; i < MAX_PLAYERS; i++)
2459     {
2460       if (stored_player[i].active)
2461       {
2462         for (j = i + 1; j < MAX_PLAYERS; j++)
2463         {
2464           if (stored_player[j].active)
2465           {
2466             struct PlayerInfo *player = &stored_player[j];
2467             int jx = player->jx, jy = player->jy;
2468
2469             player->active = FALSE;
2470             player->present = FALSE;
2471
2472             StorePlayer[jx][jy] = 0;
2473             Feld[jx][jy] = EL_EMPTY;
2474           }
2475         }
2476       }
2477     }
2478   }
2479
2480   /* when recording the game, store which players take part in the game */
2481   if (tape.recording)
2482   {
2483     for (i = 0; i < MAX_PLAYERS; i++)
2484       if (stored_player[i].active)
2485         tape.player_participates[i] = TRUE;
2486   }
2487
2488   if (options.debug)
2489   {
2490     for (i = 0; i < MAX_PLAYERS; i++)
2491     {
2492       struct PlayerInfo *player = &stored_player[i];
2493
2494       printf("Player %d: present == %d, connected == %d, active == %d.\n",
2495              i+1,
2496              player->present,
2497              player->connected,
2498              player->active);
2499       if (local_player == player)
2500         printf("Player  %d is local player.\n", i+1);
2501     }
2502   }
2503
2504   if (BorderElement == EL_EMPTY)
2505   {
2506     SBX_Left = 0;
2507     SBX_Right = lev_fieldx - SCR_FIELDX;
2508     SBY_Upper = 0;
2509     SBY_Lower = lev_fieldy - SCR_FIELDY;
2510   }
2511   else
2512   {
2513     SBX_Left = -1;
2514     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2515     SBY_Upper = -1;
2516     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2517   }
2518
2519   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2520     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2521
2522   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2523     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2524
2525   /* if local player not found, look for custom element that might create
2526      the player (make some assumptions about the right custom element) */
2527   if (!local_player->present)
2528   {
2529     int start_x = 0, start_y = 0;
2530     int found_rating = 0;
2531     int found_element = EL_UNDEFINED;
2532     int player_nr = local_player->index_nr;
2533
2534 #if 1
2535     SCAN_PLAYFIELD(x, y)
2536 #else
2537     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2538 #endif
2539     {
2540       int element = Feld[x][y];
2541       int content;
2542       int xx, yy;
2543       boolean is_player;
2544
2545       if (level.use_start_element[player_nr] &&
2546           level.start_element[player_nr] == element &&
2547           found_rating < 4)
2548       {
2549         start_x = x;
2550         start_y = y;
2551
2552         found_rating = 4;
2553         found_element = element;
2554       }
2555
2556       if (!IS_CUSTOM_ELEMENT(element))
2557         continue;
2558
2559       if (CAN_CHANGE(element))
2560       {
2561         for (i = 0; i < element_info[element].num_change_pages; i++)
2562         {
2563           /* check for player created from custom element as single target */
2564           content = element_info[element].change_page[i].target_element;
2565           is_player = ELEM_IS_PLAYER(content);
2566
2567           if (is_player && (found_rating < 3 || element < found_element))
2568           {
2569             start_x = x;
2570             start_y = y;
2571
2572             found_rating = 3;
2573             found_element = element;
2574           }
2575         }
2576       }
2577
2578       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2579       {
2580         /* check for player created from custom element as explosion content */
2581         content = element_info[element].content.e[xx][yy];
2582         is_player = ELEM_IS_PLAYER(content);
2583
2584         if (is_player && (found_rating < 2 || element < found_element))
2585         {
2586           start_x = x + xx - 1;
2587           start_y = y + yy - 1;
2588
2589           found_rating = 2;
2590           found_element = element;
2591         }
2592
2593         if (!CAN_CHANGE(element))
2594           continue;
2595
2596         for (i = 0; i < element_info[element].num_change_pages; i++)
2597         {
2598           /* check for player created from custom element as extended target */
2599           content =
2600             element_info[element].change_page[i].target_content.e[xx][yy];
2601
2602           is_player = ELEM_IS_PLAYER(content);
2603
2604           if (is_player && (found_rating < 1 || element < found_element))
2605           {
2606             start_x = x + xx - 1;
2607             start_y = y + yy - 1;
2608
2609             found_rating = 1;
2610             found_element = element;
2611           }
2612         }
2613       }
2614     }
2615
2616     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
2617                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2618                 start_x - MIDPOSX);
2619
2620     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2621                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2622                 start_y - MIDPOSY);
2623   }
2624   else
2625   {
2626     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2627                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2628                 local_player->jx - MIDPOSX);
2629
2630     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2631                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2632                 local_player->jy - MIDPOSY);
2633   }
2634
2635   StopAnimation();
2636
2637   if (!game.restart_level)
2638     CloseDoor(DOOR_CLOSE_1);
2639
2640   if (do_fading)
2641     FadeOut(REDRAW_FIELD);
2642
2643   /* !!! FIX THIS (START) !!! */
2644   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2645   {
2646     InitGameEngine_EM();
2647   }
2648   else
2649   {
2650     DrawLevel();
2651     DrawAllPlayers();
2652
2653     /* after drawing the level, correct some elements */
2654     if (game.timegate_time_left == 0)
2655       CloseAllOpenTimegates();
2656
2657     if (setup.soft_scrolling)
2658       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2659
2660     redraw_mask |= REDRAW_FROM_BACKBUFFER;
2661
2662 #if 0
2663     FadeToFront();
2664 #endif
2665   }
2666   /* !!! FIX THIS (END) !!! */
2667
2668   if (do_fading)
2669     FadeIn(REDRAW_FIELD);
2670
2671   if (!game.restart_level)
2672   {
2673     /* copy default game door content to main double buffer */
2674     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2675                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2676   }
2677
2678 #if 1
2679   SetPanelBackground();
2680   SetDrawBackgroundMask(REDRAW_DOOR_1);
2681 #endif
2682
2683   DrawGameDoorValues();
2684
2685   if (!game.restart_level)
2686   {
2687     UnmapGameButtons();
2688     UnmapTapeButtons();
2689     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2690     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2691     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2692     MapGameButtons();
2693     MapTapeButtons();
2694
2695     /* copy actual game door content to door double buffer for OpenDoor() */
2696     BlitBitmap(drawto, bitmap_db_door,
2697                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2698
2699     OpenDoor(DOOR_OPEN_ALL);
2700
2701     PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2702
2703     if (setup.sound_music)
2704       PlayLevelMusic();
2705
2706     KeyboardAutoRepeatOffUnlessAutoplay();
2707
2708     if (options.debug)
2709     {
2710       for (i = 0; i < MAX_PLAYERS; i++)
2711         printf("Player %d %sactive.\n",
2712                i + 1, (stored_player[i].active ? "" : "not "));
2713     }
2714   }
2715
2716   game.restart_level = FALSE;
2717 }
2718
2719 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2720 {
2721   /* this is used for non-R'n'D game engines to update certain engine values */
2722
2723   /* needed to determine if sounds are played within the visible screen area */
2724   scroll_x = actual_scroll_x;
2725   scroll_y = actual_scroll_y;
2726 }
2727
2728 void InitMovDir(int x, int y)
2729 {
2730   int i, element = Feld[x][y];
2731   static int xy[4][2] =
2732   {
2733     {  0, +1 },
2734     { +1,  0 },
2735     {  0, -1 },
2736     { -1,  0 }
2737   };
2738   static int direction[3][4] =
2739   {
2740     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
2741     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
2742     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
2743   };
2744
2745   switch(element)
2746   {
2747     case EL_BUG_RIGHT:
2748     case EL_BUG_UP:
2749     case EL_BUG_LEFT:
2750     case EL_BUG_DOWN:
2751       Feld[x][y] = EL_BUG;
2752       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2753       break;
2754
2755     case EL_SPACESHIP_RIGHT:
2756     case EL_SPACESHIP_UP:
2757     case EL_SPACESHIP_LEFT:
2758     case EL_SPACESHIP_DOWN:
2759       Feld[x][y] = EL_SPACESHIP;
2760       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2761       break;
2762
2763     case EL_BD_BUTTERFLY_RIGHT:
2764     case EL_BD_BUTTERFLY_UP:
2765     case EL_BD_BUTTERFLY_LEFT:
2766     case EL_BD_BUTTERFLY_DOWN:
2767       Feld[x][y] = EL_BD_BUTTERFLY;
2768       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2769       break;
2770
2771     case EL_BD_FIREFLY_RIGHT:
2772     case EL_BD_FIREFLY_UP:
2773     case EL_BD_FIREFLY_LEFT:
2774     case EL_BD_FIREFLY_DOWN:
2775       Feld[x][y] = EL_BD_FIREFLY;
2776       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2777       break;
2778
2779     case EL_PACMAN_RIGHT:
2780     case EL_PACMAN_UP:
2781     case EL_PACMAN_LEFT:
2782     case EL_PACMAN_DOWN:
2783       Feld[x][y] = EL_PACMAN;
2784       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2785       break;
2786
2787     case EL_YAMYAM_LEFT:
2788     case EL_YAMYAM_RIGHT:
2789     case EL_YAMYAM_UP:
2790     case EL_YAMYAM_DOWN:
2791       Feld[x][y] = EL_YAMYAM;
2792       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
2793       break;
2794
2795     case EL_SP_SNIKSNAK:
2796       MovDir[x][y] = MV_UP;
2797       break;
2798
2799     case EL_SP_ELECTRON:
2800       MovDir[x][y] = MV_LEFT;
2801       break;
2802
2803     case EL_MOLE_LEFT:
2804     case EL_MOLE_RIGHT:
2805     case EL_MOLE_UP:
2806     case EL_MOLE_DOWN:
2807       Feld[x][y] = EL_MOLE;
2808       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2809       break;
2810
2811     default:
2812       if (IS_CUSTOM_ELEMENT(element))
2813       {
2814         struct ElementInfo *ei = &element_info[element];
2815         int move_direction_initial = ei->move_direction_initial;
2816         int move_pattern = ei->move_pattern;
2817
2818         if (move_direction_initial == MV_START_PREVIOUS)
2819         {
2820           if (MovDir[x][y] != MV_NONE)
2821             return;
2822
2823           move_direction_initial = MV_START_AUTOMATIC;
2824         }
2825
2826         if (move_direction_initial == MV_START_RANDOM)
2827           MovDir[x][y] = 1 << RND(4);
2828         else if (move_direction_initial & MV_ANY_DIRECTION)
2829           MovDir[x][y] = move_direction_initial;
2830         else if (move_pattern == MV_ALL_DIRECTIONS ||
2831                  move_pattern == MV_TURNING_LEFT ||
2832                  move_pattern == MV_TURNING_RIGHT ||
2833                  move_pattern == MV_TURNING_LEFT_RIGHT ||
2834                  move_pattern == MV_TURNING_RIGHT_LEFT ||
2835                  move_pattern == MV_TURNING_RANDOM)
2836           MovDir[x][y] = 1 << RND(4);
2837         else if (move_pattern == MV_HORIZONTAL)
2838           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2839         else if (move_pattern == MV_VERTICAL)
2840           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2841         else if (move_pattern & MV_ANY_DIRECTION)
2842           MovDir[x][y] = element_info[element].move_pattern;
2843         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2844                  move_pattern == MV_ALONG_RIGHT_SIDE)
2845         {
2846           /* use random direction as default start direction */
2847           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2848             MovDir[x][y] = 1 << RND(4);
2849
2850           for (i = 0; i < NUM_DIRECTIONS; i++)
2851           {
2852             int x1 = x + xy[i][0];
2853             int y1 = y + xy[i][1];
2854
2855             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2856             {
2857               if (move_pattern == MV_ALONG_RIGHT_SIDE)
2858                 MovDir[x][y] = direction[0][i];
2859               else
2860                 MovDir[x][y] = direction[1][i];
2861
2862               break;
2863             }
2864           }
2865         }                
2866       }
2867       else
2868       {
2869         MovDir[x][y] = 1 << RND(4);
2870
2871         if (element != EL_BUG &&
2872             element != EL_SPACESHIP &&
2873             element != EL_BD_BUTTERFLY &&
2874             element != EL_BD_FIREFLY)
2875           break;
2876
2877         for (i = 0; i < NUM_DIRECTIONS; i++)
2878         {
2879           int x1 = x + xy[i][0];
2880           int y1 = y + xy[i][1];
2881
2882           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2883           {
2884             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2885             {
2886               MovDir[x][y] = direction[0][i];
2887               break;
2888             }
2889             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2890                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2891             {
2892               MovDir[x][y] = direction[1][i];
2893               break;
2894             }
2895           }
2896         }
2897       }
2898       break;
2899   }
2900
2901   GfxDir[x][y] = MovDir[x][y];
2902 }
2903
2904 void InitAmoebaNr(int x, int y)
2905 {
2906   int i;
2907   int group_nr = AmoebeNachbarNr(x, y);
2908
2909   if (group_nr == 0)
2910   {
2911     for (i = 1; i < MAX_NUM_AMOEBA; i++)
2912     {
2913       if (AmoebaCnt[i] == 0)
2914       {
2915         group_nr = i;
2916         break;
2917       }
2918     }
2919   }
2920
2921   AmoebaNr[x][y] = group_nr;
2922   AmoebaCnt[group_nr]++;
2923   AmoebaCnt2[group_nr]++;
2924 }
2925
2926 #if USE_NEW_GAME_WON
2927
2928 void GameWon()
2929 {
2930   static boolean score_done = FALSE;
2931   static boolean player_done = FALSE;
2932   static int game_over_delay = 0;
2933   int game_over_delay_value = 50;
2934
2935   /* do not start end game actions before the player stops moving (to exit) */
2936   if (local_player->MovPos)
2937     return;
2938
2939   if (tape.auto_play)           /* tape might already be stopped here */
2940     tape.auto_play_level_solved = TRUE;
2941
2942   if (!local_player->LevelSolved_GameEnd)
2943   {
2944     local_player->LevelSolved_GameEnd = TRUE;
2945     local_player->LevelSolved_SaveTape = tape.recording;
2946     local_player->LevelSolved_SaveScore = !tape.playing;
2947
2948     score_done = FALSE;
2949     player_done = FALSE;
2950     game_over_delay = 0;
2951   }
2952
2953   PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2954
2955   if (TimeLeft > 0)
2956   {
2957     if (!tape.playing)
2958     {
2959       if (setup.sound_loops)
2960         PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2961                      SND_CTRL_PLAY_LOOP);
2962       else
2963         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2964     }
2965
2966     if (TimeLeft > 100 && TimeLeft % 10 == 0)
2967     {
2968       TimeLeft -= 10;
2969       RaiseScore(level.score[SC_TIME_BONUS] * 10);
2970     }
2971     else
2972     {
2973       TimeLeft--;
2974       RaiseScore(level.score[SC_TIME_BONUS]);
2975     }
2976
2977     DrawGameValue_Time(TimeLeft);
2978
2979 #if 0
2980     if (!tape.playing)
2981       Delay(10);
2982 #endif
2983
2984     if (TimeLeft <= 0 && !tape.playing && setup.sound_loops)
2985       StopSound(SND_GAME_LEVELTIME_BONUS);
2986   }
2987   else if (level.time == 0 && TimePlayed < 999) /* level without time limit */
2988   {
2989     if (!tape.playing)
2990     {
2991       if (setup.sound_loops)
2992         PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2993                      SND_CTRL_PLAY_LOOP);
2994       else
2995         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2996     }
2997
2998     if (TimePlayed < 900 && TimePlayed % 10 == 0)
2999     {
3000       TimePlayed += 10;
3001       RaiseScore(level.score[SC_TIME_BONUS] * 10);
3002     }
3003     else
3004     {
3005       TimePlayed++;
3006       RaiseScore(level.score[SC_TIME_BONUS]);
3007     }
3008
3009     DrawGameValue_Time(TimePlayed);
3010
3011     if (TimePlayed >= 999 && !tape.playing && setup.sound_loops)
3012       StopSound(SND_GAME_LEVELTIME_BONUS);
3013   }
3014   else
3015   {
3016     score_done = TRUE;
3017   }
3018
3019   /* close exit door after last player */
3020   if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
3021       (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3022        Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
3023   {
3024     int element = Feld[ExitX][ExitY];
3025
3026     Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3027                           EL_SP_EXIT_CLOSING);
3028
3029     PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3030   }
3031
3032   /* player disappears */
3033   if (ExitX >= 0 && ExitY >= 0 && !player_done)
3034   {
3035     DrawLevelField(ExitX, ExitY);
3036
3037     player_done = TRUE;
3038   }
3039
3040   game_over_delay++;
3041
3042   if (game_over_delay < game_over_delay_value || !score_done)
3043     return;
3044 }
3045
3046 void GameEnd()
3047 {
3048   int hi_pos;
3049   boolean raise_level = FALSE;
3050
3051   CloseDoor(DOOR_CLOSE_1);
3052
3053   if (local_player->LevelSolved_SaveTape)
3054   {
3055     TapeStop();
3056
3057     SaveTape(tape.level_nr);            /* Ask to save tape */
3058   }
3059
3060   if (!local_player->LevelSolved_SaveScore)
3061   {
3062     FadeOut(REDRAW_FIELD);
3063
3064     game_status = GAME_MODE_MAIN;
3065
3066     DrawAndFadeInMainMenu(REDRAW_FIELD);
3067
3068     return;
3069   }
3070
3071   if (level_nr == leveldir_current->handicap_level)
3072   {
3073     leveldir_current->handicap_level++;
3074     SaveLevelSetup_SeriesInfo();
3075   }
3076
3077   if (level_editor_test_game)
3078     local_player->score = -1;   /* no highscore when playing from editor */
3079   else if (level_nr < leveldir_current->last_level)
3080     raise_level = TRUE;         /* advance to next level */
3081
3082   if ((hi_pos = NewHiScore()) >= 0) 
3083   {
3084     game_status = GAME_MODE_SCORES;
3085
3086     DrawHallOfFame(hi_pos);
3087
3088     if (raise_level)
3089     {
3090       level_nr++;
3091       TapeErase();
3092     }
3093   }
3094   else
3095   {
3096     FadeOut(REDRAW_FIELD);
3097
3098     game_status = GAME_MODE_MAIN;
3099
3100     if (raise_level)
3101     {
3102       level_nr++;
3103       TapeErase();
3104     }
3105
3106     DrawAndFadeInMainMenu(REDRAW_FIELD);
3107   }
3108
3109   local_player->LevelSolved_SaveScore = FALSE;
3110 }
3111
3112 #else
3113
3114 void GameWon()
3115 {
3116   int hi_pos;
3117   boolean raise_level = FALSE;
3118
3119   if (local_player->MovPos)
3120     return;
3121
3122   if (tape.auto_play)           /* tape might already be stopped here */
3123     tape.auto_play_level_solved = TRUE;
3124
3125   local_player->LevelSolved = FALSE;
3126
3127   PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
3128
3129   if (TimeLeft)
3130   {
3131     if (!tape.playing && setup.sound_loops)
3132       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
3133                    SND_CTRL_PLAY_LOOP);
3134
3135     while (TimeLeft > 0)
3136     {
3137       if (!tape.playing && !setup.sound_loops)
3138         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
3139
3140       if (TimeLeft > 100 && TimeLeft % 10 == 0)
3141       {
3142         TimeLeft -= 10;
3143         RaiseScore(level.score[SC_TIME_BONUS] * 10);
3144       }
3145       else
3146       {
3147         TimeLeft--;
3148         RaiseScore(level.score[SC_TIME_BONUS]);
3149       }
3150
3151       DrawGameValue_Time(TimeLeft);
3152
3153       BackToFront();
3154
3155       if (!tape.playing)
3156         Delay(10);
3157     }
3158
3159     if (!tape.playing && setup.sound_loops)
3160       StopSound(SND_GAME_LEVELTIME_BONUS);
3161   }
3162   else if (level.time == 0)             /* level without time limit */
3163   {
3164     if (!tape.playing && setup.sound_loops)
3165       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
3166                    SND_CTRL_PLAY_LOOP);
3167
3168     while (TimePlayed < 999)
3169     {
3170       if (!tape.playing && !setup.sound_loops)
3171         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
3172
3173       if (TimePlayed < 900 && TimePlayed % 10 == 0)
3174       {
3175         TimePlayed += 10;
3176         RaiseScore(level.score[SC_TIME_BONUS] * 10);
3177       }
3178       else
3179       {
3180         TimePlayed++;
3181         RaiseScore(level.score[SC_TIME_BONUS]);
3182       }
3183
3184       DrawGameValue_Time(TimePlayed);
3185
3186       BackToFront();
3187
3188       if (!tape.playing)
3189         Delay(10);
3190     }
3191
3192     if (!tape.playing && setup.sound_loops)
3193       StopSound(SND_GAME_LEVELTIME_BONUS);
3194   }
3195
3196   /* close exit door after last player */
3197   if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
3198       (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
3199        Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
3200   {
3201     int element = Feld[ExitX][ExitY];
3202
3203     Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
3204                           EL_SP_EXIT_CLOSING);
3205
3206     PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
3207   }
3208
3209   /* player disappears */
3210   if (ExitX >= 0 && ExitY >= 0)
3211     DrawLevelField(ExitX, ExitY);
3212
3213   BackToFront();
3214
3215 #if 0
3216   if (tape.playing)
3217     printf("::: TAPE PLAYING -> DO NOT SAVE SCORE\n");
3218   else
3219     printf("::: NO TAPE PLAYING -> SAVING SCORE\n");
3220 #endif
3221
3222   if (tape.playing)
3223     return;
3224
3225   CloseDoor(DOOR_CLOSE_1);
3226
3227   if (tape.recording)
3228   {
3229     TapeStop();
3230     SaveTape(tape.level_nr);            /* Ask to save tape */
3231   }
3232
3233   if (level_nr == leveldir_current->handicap_level)
3234   {
3235     leveldir_current->handicap_level++;
3236     SaveLevelSetup_SeriesInfo();
3237   }
3238
3239   if (level_editor_test_game)
3240     local_player->score = -1;   /* no highscore when playing from editor */
3241   else if (level_nr < leveldir_current->last_level)
3242     raise_level = TRUE;         /* advance to next level */
3243
3244   if ((hi_pos = NewHiScore()) >= 0) 
3245   {
3246     game_status = GAME_MODE_SCORES;
3247     DrawHallOfFame(hi_pos);
3248     if (raise_level)
3249     {
3250       level_nr++;
3251       TapeErase();
3252     }
3253   }
3254   else
3255   {
3256     game_status = GAME_MODE_MAIN;
3257     if (raise_level)
3258     {
3259       level_nr++;
3260       TapeErase();
3261     }
3262     DrawMainMenu();
3263   }
3264
3265   BackToFront();
3266 }
3267
3268 #endif
3269
3270 int NewHiScore()
3271 {
3272   int k, l;
3273   int position = -1;
3274
3275   LoadScore(level_nr);
3276
3277   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
3278       local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
3279     return -1;
3280
3281   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
3282   {
3283     if (local_player->score > highscore[k].Score)
3284     {
3285       /* player has made it to the hall of fame */
3286
3287       if (k < MAX_SCORE_ENTRIES - 1)
3288       {
3289         int m = MAX_SCORE_ENTRIES - 1;
3290
3291 #ifdef ONE_PER_NAME
3292         for (l = k; l < MAX_SCORE_ENTRIES; l++)
3293           if (strEqual(setup.player_name, highscore[l].Name))
3294             m = l;
3295         if (m == k)     /* player's new highscore overwrites his old one */
3296           goto put_into_list;
3297 #endif
3298
3299         for (l = m; l > k; l--)
3300         {
3301           strcpy(highscore[l].Name, highscore[l - 1].Name);
3302           highscore[l].Score = highscore[l - 1].Score;
3303         }
3304       }
3305
3306 #ifdef ONE_PER_NAME
3307       put_into_list:
3308 #endif
3309       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
3310       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
3311       highscore[k].Score = local_player->score; 
3312       position = k;
3313       break;
3314     }
3315
3316 #ifdef ONE_PER_NAME
3317     else if (!strncmp(setup.player_name, highscore[k].Name,
3318                       MAX_PLAYER_NAME_LEN))
3319       break;    /* player already there with a higher score */
3320 #endif
3321
3322   }
3323
3324   if (position >= 0) 
3325     SaveScore(level_nr);
3326
3327   return position;
3328 }
3329
3330 inline static int getElementMoveStepsize(int x, int y)
3331 {
3332   int element = Feld[x][y];
3333   int direction = MovDir[x][y];
3334   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3335   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3336   int horiz_move = (dx != 0);
3337   int sign = (horiz_move ? dx : dy);
3338   int step = sign * element_info[element].move_stepsize;
3339
3340   /* special values for move stepsize for spring and things on conveyor belt */
3341   if (horiz_move)
3342   {
3343 #if 0
3344     if (element == EL_SPRING)
3345       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3346     else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3347              y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3348       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3349 #else
3350     if (CAN_FALL(element) &&
3351         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3352       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3353     else if (element == EL_SPRING)
3354       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3355 #endif
3356   }
3357
3358   return step;
3359 }
3360
3361 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
3362 {
3363   if (player->GfxAction != action || player->GfxDir != dir)
3364   {
3365 #if 0
3366     printf("Player frame reset! (%d => %d, %d => %d)\n",
3367            player->GfxAction, action, player->GfxDir, dir);
3368 #endif
3369
3370     player->GfxAction = action;
3371     player->GfxDir = dir;
3372     player->Frame = 0;
3373     player->StepFrame = 0;
3374   }
3375 }
3376
3377 #if USE_GFX_RESET_GFX_ANIMATION
3378 static void ResetGfxFrame(int x, int y, boolean redraw)
3379 {
3380   int element = Feld[x][y];
3381   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3382   int last_gfx_frame = GfxFrame[x][y];
3383
3384   if (graphic_info[graphic].anim_global_sync)
3385     GfxFrame[x][y] = FrameCounter;
3386   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3387     GfxFrame[x][y] = CustomValue[x][y];
3388   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3389     GfxFrame[x][y] = element_info[element].collect_score;
3390   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3391     GfxFrame[x][y] = ChangeDelay[x][y];
3392
3393   if (redraw && GfxFrame[x][y] != last_gfx_frame)
3394     DrawLevelGraphicAnimation(x, y, graphic);
3395 }
3396 #endif
3397
3398 static void ResetGfxAnimation(int x, int y)
3399 {
3400 #if 0
3401   int element, graphic;
3402 #endif
3403
3404   GfxAction[x][y] = ACTION_DEFAULT;
3405   GfxDir[x][y] = MovDir[x][y];
3406   GfxFrame[x][y] = 0;
3407
3408 #if 0
3409   element = Feld[x][y];
3410   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3411
3412   if (graphic_info[graphic].anim_global_sync)
3413     GfxFrame[x][y] = FrameCounter;
3414   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3415     GfxFrame[x][y] = CustomValue[x][y];
3416   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3417     GfxFrame[x][y] = element_info[element].collect_score;
3418   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3419     GfxFrame[x][y] = ChangeDelay[x][y];
3420 #endif
3421
3422 #if USE_GFX_RESET_GFX_ANIMATION
3423   ResetGfxFrame(x, y, FALSE);
3424 #endif
3425 }
3426
3427 static void ResetRandomAnimationValue(int x, int y)
3428 {
3429   GfxRandom[x][y] = INIT_GFX_RANDOM();
3430 }
3431
3432 void InitMovingField(int x, int y, int direction)
3433 {
3434   int element = Feld[x][y];
3435 #if 0
3436   int graphic;
3437 #endif
3438   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3439   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3440   int newx = x + dx;
3441   int newy = y + dy;
3442
3443   if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3444     ResetGfxAnimation(x, y);
3445
3446   MovDir[x][y] = direction;
3447   GfxDir[x][y] = direction;
3448   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3449                      ACTION_FALLING : ACTION_MOVING);
3450
3451 #if 0
3452   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3453
3454   if (graphic_info[graphic].anim_global_sync)
3455     GfxFrame[x][y] = FrameCounter;
3456   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3457     GfxFrame[x][y] = CustomValue[x][y];
3458   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3459     GfxFrame[x][y] = element_info[element].collect_score;
3460   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
3461     GfxFrame[x][y] = ChangeDelay[x][y];
3462 #endif
3463
3464   /* this is needed for CEs with property "can move" / "not moving" */
3465
3466   if (getElementMoveStepsize(x, y) != 0)        /* moving or being moved */
3467   {
3468     if (Feld[newx][newy] == EL_EMPTY)
3469       Feld[newx][newy] = EL_BLOCKED;
3470
3471     MovDir[newx][newy] = MovDir[x][y];
3472
3473 #if USE_NEW_CUSTOM_VALUE
3474     CustomValue[newx][newy] = CustomValue[x][y];
3475 #endif
3476
3477     GfxFrame[newx][newy] = GfxFrame[x][y];
3478     GfxRandom[newx][newy] = GfxRandom[x][y];
3479     GfxAction[newx][newy] = GfxAction[x][y];
3480     GfxDir[newx][newy] = GfxDir[x][y];
3481   }
3482 }
3483
3484 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3485 {
3486   int direction = MovDir[x][y];
3487 #if 1
3488   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3489   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3490 #else
3491   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3492   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3493 #endif
3494
3495   *goes_to_x = newx;
3496   *goes_to_y = newy;
3497 }
3498
3499 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3500 {
3501   int oldx = x, oldy = y;
3502   int direction = MovDir[x][y];
3503
3504   if (direction == MV_LEFT)
3505     oldx++;
3506   else if (direction == MV_RIGHT)
3507     oldx--;
3508   else if (direction == MV_UP)
3509     oldy++;
3510   else if (direction == MV_DOWN)
3511     oldy--;
3512
3513   *comes_from_x = oldx;
3514   *comes_from_y = oldy;
3515 }
3516
3517 int MovingOrBlocked2Element(int x, int y)
3518 {
3519   int element = Feld[x][y];
3520
3521   if (element == EL_BLOCKED)
3522   {
3523     int oldx, oldy;
3524
3525     Blocked2Moving(x, y, &oldx, &oldy);
3526     return Feld[oldx][oldy];
3527   }
3528   else
3529     return element;
3530 }
3531
3532 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3533 {
3534   /* like MovingOrBlocked2Element(), but if element is moving
3535      and (x,y) is the field the moving element is just leaving,
3536      return EL_BLOCKED instead of the element value */
3537   int element = Feld[x][y];
3538
3539   if (IS_MOVING(x, y))
3540   {
3541     if (element == EL_BLOCKED)
3542     {
3543       int oldx, oldy;
3544
3545       Blocked2Moving(x, y, &oldx, &oldy);
3546       return Feld[oldx][oldy];
3547     }
3548     else
3549       return EL_BLOCKED;
3550   }
3551   else
3552     return element;
3553 }
3554
3555 static void RemoveField(int x, int y)
3556 {
3557   Feld[x][y] = EL_EMPTY;
3558
3559   MovPos[x][y] = 0;
3560   MovDir[x][y] = 0;
3561   MovDelay[x][y] = 0;
3562
3563 #if USE_NEW_CUSTOM_VALUE
3564   CustomValue[x][y] = 0;
3565 #endif
3566
3567   AmoebaNr[x][y] = 0;
3568   ChangeDelay[x][y] = 0;
3569   ChangePage[x][y] = -1;
3570   Pushed[x][y] = FALSE;
3571
3572 #if 0
3573   ExplodeField[x][y] = EX_TYPE_NONE;
3574 #endif
3575
3576   GfxElement[x][y] = EL_UNDEFINED;
3577   GfxAction[x][y] = ACTION_DEFAULT;
3578   GfxDir[x][y] = MV_NONE;
3579 }
3580
3581 void RemoveMovingField(int x, int y)
3582 {
3583   int oldx = x, oldy = y, newx = x, newy = y;
3584   int element = Feld[x][y];
3585   int next_element = EL_UNDEFINED;
3586
3587   if (element != EL_BLOCKED && !IS_MOVING(x, y))
3588     return;
3589
3590   if (IS_MOVING(x, y))
3591   {
3592     Moving2Blocked(x, y, &newx, &newy);
3593
3594     if (Feld[newx][newy] != EL_BLOCKED)
3595     {
3596       /* element is moving, but target field is not free (blocked), but
3597          already occupied by something different (example: acid pool);
3598          in this case, only remove the moving field, but not the target */
3599
3600       RemoveField(oldx, oldy);
3601
3602       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3603
3604       DrawLevelField(oldx, oldy);
3605
3606       return;
3607     }
3608   }
3609   else if (element == EL_BLOCKED)
3610   {
3611     Blocked2Moving(x, y, &oldx, &oldy);
3612     if (!IS_MOVING(oldx, oldy))
3613       return;
3614   }
3615
3616   if (element == EL_BLOCKED &&
3617       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3618        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3619        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3620        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3621     next_element = get_next_element(Feld[oldx][oldy]);
3622
3623   RemoveField(oldx, oldy);
3624   RemoveField(newx, newy);
3625
3626   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3627
3628   if (next_element != EL_UNDEFINED)
3629     Feld[oldx][oldy] = next_element;
3630
3631   DrawLevelField(oldx, oldy);
3632   DrawLevelField(newx, newy);
3633 }
3634
3635 void DrawDynamite(int x, int y)
3636 {
3637   int sx = SCREENX(x), sy = SCREENY(y);
3638   int graphic = el2img(Feld[x][y]);
3639   int frame;
3640
3641   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3642     return;
3643
3644   if (IS_WALKABLE_INSIDE(Back[x][y]))
3645     return;
3646
3647   if (Back[x][y])
3648     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3649   else if (Store[x][y])
3650     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3651
3652   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3653
3654   if (Back[x][y] || Store[x][y])
3655     DrawGraphicThruMask(sx, sy, graphic, frame);
3656   else
3657     DrawGraphic(sx, sy, graphic, frame);
3658 }
3659
3660 void CheckDynamite(int x, int y)
3661 {
3662   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
3663   {
3664     MovDelay[x][y]--;
3665
3666     if (MovDelay[x][y] != 0)
3667     {
3668       DrawDynamite(x, y);
3669       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3670
3671       return;
3672     }
3673   }
3674
3675   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3676
3677   Bang(x, y);
3678 }
3679
3680 #if 1
3681
3682 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3683 {
3684   boolean num_checked_players = 0;
3685   int i;
3686
3687   for (i = 0; i < MAX_PLAYERS; i++)
3688   {
3689     if (stored_player[i].active)
3690     {
3691       int sx = stored_player[i].jx;
3692       int sy = stored_player[i].jy;
3693
3694       if (num_checked_players == 0)
3695       {
3696         *sx1 = *sx2 = sx;
3697         *sy1 = *sy2 = sy;
3698       }
3699       else
3700       {
3701         *sx1 = MIN(*sx1, sx);
3702         *sy1 = MIN(*sy1, sy);
3703         *sx2 = MAX(*sx2, sx);
3704         *sy2 = MAX(*sy2, sy);
3705       }
3706
3707       num_checked_players++;
3708     }
3709   }
3710 }
3711
3712 static boolean checkIfAllPlayersFitToScreen_RND()
3713 {
3714   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3715
3716   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3717
3718   return (sx2 - sx1 < SCR_FIELDX &&
3719           sy2 - sy1 < SCR_FIELDY);
3720 }
3721
3722 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3723 {
3724   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3725
3726   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3727
3728   *sx = (sx1 + sx2) / 2;
3729   *sy = (sy1 + sy2) / 2;
3730 }
3731
3732 #if 0
3733 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3734                                               int center_x, int center_y)
3735 {
3736   int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3737
3738   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3739
3740   *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3741   *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3742 }
3743
3744 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3745 {
3746   int max_dx, max_dy;
3747
3748   setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3749
3750   return (max_dx <= SCR_FIELDX / 2 &&
3751           max_dy <= SCR_FIELDY / 2);
3752 }
3753 #endif
3754
3755 #endif
3756
3757 #if 1
3758
3759 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3760                         boolean quick_relocation)
3761 {
3762   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3763   boolean no_delay = (tape.warp_forward);
3764   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3765   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3766
3767   if (quick_relocation)
3768   {
3769     int offset = (setup.scroll_delay ? 3 : 0);
3770
3771 #if 0
3772     if (center_screen)
3773       offset = 0;
3774 #endif
3775
3776     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3777     {
3778       scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3779                   x > SBX_Right + MIDPOSX ? SBX_Right :
3780                   x - MIDPOSX);
3781
3782       scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3783                   y > SBY_Lower + MIDPOSY ? SBY_Lower :
3784                   y - MIDPOSY);
3785     }
3786     else
3787     {
3788       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
3789           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3790         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3791
3792       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
3793           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3794         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3795
3796       /* don't scroll over playfield boundaries */
3797       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3798         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3799
3800       /* don't scroll over playfield boundaries */
3801       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3802         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3803     }
3804
3805     RedrawPlayfield(TRUE, 0,0,0,0);
3806   }
3807   else
3808   {
3809     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3810                      x > SBX_Right + MIDPOSX ? SBX_Right :
3811                      x - MIDPOSX);
3812
3813     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3814                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
3815                      y - MIDPOSY);
3816
3817     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3818
3819     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3820     {
3821       int dx = 0, dy = 0;
3822       int fx = FX, fy = FY;
3823
3824       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3825       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3826
3827       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3828         break;
3829
3830       scroll_x -= dx;
3831       scroll_y -= dy;
3832
3833       fx += dx * TILEX / 2;
3834       fy += dy * TILEY / 2;
3835
3836       ScrollLevel(dx, dy);
3837       DrawAllPlayers();
3838
3839       /* scroll in two steps of half tile size to make things smoother */
3840       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3841       FlushDisplay();
3842       Delay(wait_delay_value);
3843
3844       /* scroll second step to align at full tile size */
3845       BackToFront();
3846       Delay(wait_delay_value);
3847     }
3848
3849     DrawAllPlayers();
3850     BackToFront();
3851     Delay(wait_delay_value);
3852   }
3853 }
3854
3855 #else
3856
3857 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3858 {
3859   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3860   boolean no_delay = (tape.warp_forward);
3861   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3862   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3863   int jx = player->jx;
3864   int jy = player->jy;
3865
3866   if (quick_relocation)
3867   {
3868     int offset = (setup.scroll_delay ? 3 : 0);
3869
3870     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3871     {
3872       scroll_x = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3873                   player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3874                   player->jx - MIDPOSX);
3875
3876       scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3877                   player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3878                   player->jy - MIDPOSY);
3879     }
3880     else
3881     {
3882       if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
3883           (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3884         scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3885
3886       if ((player->MovDir == MV_UP  && scroll_y > jy - MIDPOSY + offset) ||
3887           (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3888         scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3889
3890       /* don't scroll over playfield boundaries */
3891       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3892         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3893
3894       /* don't scroll over playfield boundaries */
3895       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3896         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3897     }
3898
3899     RedrawPlayfield(TRUE, 0,0,0,0);
3900   }
3901   else
3902   {
3903     int scroll_xx = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3904                      player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3905                      player->jx - MIDPOSX);
3906
3907     int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3908                      player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3909                      player->jy - MIDPOSY);
3910
3911     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3912
3913     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3914     {
3915       int dx = 0, dy = 0;
3916       int fx = FX, fy = FY;
3917
3918       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3919       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3920
3921       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3922         break;
3923
3924       scroll_x -= dx;
3925       scroll_y -= dy;
3926
3927       fx += dx * TILEX / 2;
3928       fy += dy * TILEY / 2;
3929
3930       ScrollLevel(dx, dy);
3931       DrawAllPlayers();
3932
3933       /* scroll in two steps of half tile size to make things smoother */
3934       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3935       FlushDisplay();
3936       Delay(wait_delay_value);
3937
3938       /* scroll second step to align at full tile size */
3939       BackToFront();
3940       Delay(wait_delay_value);
3941     }
3942
3943     DrawPlayer(player);
3944     BackToFront();
3945     Delay(wait_delay_value);
3946   }
3947 }
3948
3949 #endif
3950
3951 void RelocatePlayer(int jx, int jy, int el_player_raw)
3952 {
3953   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3954   int player_nr = GET_PLAYER_NR(el_player);
3955   struct PlayerInfo *player = &stored_player[player_nr];
3956   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3957   boolean no_delay = (tape.warp_forward);
3958   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3959   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3960   int old_jx = player->jx;
3961   int old_jy = player->jy;
3962   int old_element = Feld[old_jx][old_jy];
3963   int element = Feld[jx][jy];
3964   boolean player_relocated = (old_jx != jx || old_jy != jy);
3965
3966   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3967   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
3968   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3969   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
3970   int leave_side_horiz = move_dir_horiz;
3971   int leave_side_vert  = move_dir_vert;
3972   int enter_side = enter_side_horiz | enter_side_vert;
3973   int leave_side = leave_side_horiz | leave_side_vert;
3974
3975   if (player->GameOver)         /* do not reanimate dead player */
3976     return;
3977
3978   if (!player_relocated)        /* no need to relocate the player */
3979     return;
3980
3981   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
3982   {
3983     RemoveField(jx, jy);        /* temporarily remove newly placed player */
3984     DrawLevelField(jx, jy);
3985   }
3986
3987   if (player->present)
3988   {
3989     while (player->MovPos)
3990     {
3991       ScrollPlayer(player, SCROLL_GO_ON);
3992       ScrollScreen(NULL, SCROLL_GO_ON);
3993
3994       AdvanceFrameAndPlayerCounters(player->index_nr);
3995
3996       DrawPlayer(player);
3997
3998       BackToFront();
3999       Delay(wait_delay_value);
4000     }
4001
4002     DrawPlayer(player);         /* needed here only to cleanup last field */
4003     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4004
4005     player->is_moving = FALSE;
4006   }
4007
4008   if (IS_CUSTOM_ELEMENT(old_element))
4009     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4010                                CE_LEFT_BY_PLAYER,
4011                                player->index_bit, leave_side);
4012
4013   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4014                                       CE_PLAYER_LEAVES_X,
4015                                       player->index_bit, leave_side);
4016
4017   Feld[jx][jy] = el_player;
4018   InitPlayerField(jx, jy, el_player, TRUE);
4019
4020   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4021   {
4022     Feld[jx][jy] = element;
4023     InitField(jx, jy, FALSE);
4024   }
4025
4026 #if 1
4027   /* only visually relocate centered player */
4028 #if 1
4029   DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
4030                      level.instant_relocation);
4031 #else
4032   if (player->index_nr == game.centered_player_nr)
4033     DrawRelocatePlayer(player, level.instant_relocation);
4034 #endif
4035 #else
4036   if (player == local_player)   /* only visually relocate local player */
4037     DrawRelocatePlayer(player, level.instant_relocation);
4038 #endif
4039
4040   TestIfPlayerTouchesBadThing(jx, jy);
4041   TestIfPlayerTouchesCustomElement(jx, jy);
4042
4043   if (IS_CUSTOM_ELEMENT(element))
4044     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4045                                player->index_bit, enter_side);
4046
4047   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4048                                       player->index_bit, enter_side);
4049 }
4050
4051 void Explode(int ex, int ey, int phase, int mode)
4052 {
4053   int x, y;
4054   int last_phase;
4055   int border_element;
4056
4057   /* !!! eliminate this variable !!! */
4058   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4059
4060   if (game.explosions_delayed)
4061   {
4062     ExplodeField[ex][ey] = mode;
4063     return;
4064   }
4065
4066   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4067   {
4068     int center_element = Feld[ex][ey];
4069     int artwork_element, explosion_element;     /* set these values later */
4070
4071 #if 0
4072     /* --- This is only really needed (and now handled) in "Impact()". --- */
4073     /* do not explode moving elements that left the explode field in time */
4074     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4075         center_element == EL_EMPTY &&
4076         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4077       return;
4078 #endif
4079
4080 #if 0
4081     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4082     if (mode == EX_TYPE_NORMAL ||
4083         mode == EX_TYPE_CENTER ||
4084         mode == EX_TYPE_CROSS)
4085       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4086 #endif
4087
4088     /* remove things displayed in background while burning dynamite */
4089     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4090       Back[ex][ey] = 0;
4091
4092     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4093     {
4094       /* put moving element to center field (and let it explode there) */
4095       center_element = MovingOrBlocked2Element(ex, ey);
4096       RemoveMovingField(ex, ey);
4097       Feld[ex][ey] = center_element;
4098     }
4099
4100     /* now "center_element" is finally determined -- set related values now */
4101     artwork_element = center_element;           /* for custom player artwork */
4102     explosion_element = center_element;         /* for custom player artwork */
4103
4104     if (IS_PLAYER(ex, ey))
4105     {
4106       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
4107
4108       artwork_element = stored_player[player_nr].artwork_element;
4109
4110       if (level.use_explosion_element[player_nr])
4111       {
4112         explosion_element = level.explosion_element[player_nr];
4113         artwork_element = explosion_element;
4114       }
4115     }
4116
4117 #if 1
4118     if (mode == EX_TYPE_NORMAL ||
4119         mode == EX_TYPE_CENTER ||
4120         mode == EX_TYPE_CROSS)
4121       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4122 #endif
4123
4124 #if 1
4125     last_phase = element_info[explosion_element].explosion_delay + 1;
4126 #else
4127     last_phase = element_info[center_element].explosion_delay + 1;
4128 #endif
4129
4130     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
4131     {
4132       int xx = x - ex + 1;
4133       int yy = y - ey + 1;
4134       int element;
4135
4136       if (!IN_LEV_FIELD(x, y) ||
4137           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
4138           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
4139         continue;
4140
4141       element = Feld[x][y];
4142
4143       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
4144       {
4145         element = MovingOrBlocked2Element(x, y);
4146
4147         if (!IS_EXPLOSION_PROOF(element))
4148           RemoveMovingField(x, y);
4149       }
4150
4151       /* indestructible elements can only explode in center (but not flames) */
4152       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
4153                                            mode == EX_TYPE_BORDER)) ||
4154           element == EL_FLAMES)
4155         continue;
4156
4157       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
4158          behaviour, for example when touching a yamyam that explodes to rocks
4159          with active deadly shield, a rock is created under the player !!! */
4160       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
4161 #if 0
4162       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
4163           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
4164            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
4165 #else
4166       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
4167 #endif
4168       {
4169         if (IS_ACTIVE_BOMB(element))
4170         {
4171           /* re-activate things under the bomb like gate or penguin */
4172           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
4173           Back[x][y] = 0;
4174         }
4175
4176         continue;
4177       }
4178
4179       /* save walkable background elements while explosion on same tile */
4180       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
4181           (x != ex || y != ey || mode == EX_TYPE_BORDER))
4182         Back[x][y] = element;
4183
4184       /* ignite explodable elements reached by other explosion */
4185       if (element == EL_EXPLOSION)
4186         element = Store2[x][y];
4187
4188       if (AmoebaNr[x][y] &&
4189           (element == EL_AMOEBA_FULL ||
4190            element == EL_BD_AMOEBA ||
4191            element == EL_AMOEBA_GROWING))
4192       {
4193         AmoebaCnt[AmoebaNr[x][y]]--;
4194         AmoebaCnt2[AmoebaNr[x][y]]--;
4195       }
4196
4197       RemoveField(x, y);
4198
4199       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
4200       {
4201 #if 1
4202         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
4203
4204         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
4205 #else
4206         switch(StorePlayer[ex][ey])
4207         {
4208           case EL_PLAYER_2:
4209             Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
4210             break;
4211           case EL_PLAYER_3:
4212             Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
4213             break;
4214           case EL_PLAYER_4:
4215             Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
4216             break;
4217           case EL_PLAYER_1:
4218           default:
4219             Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
4220             break;
4221         }
4222 #endif
4223
4224         if (PLAYERINFO(ex, ey)->use_murphy)
4225           Store[x][y] = EL_EMPTY;
4226       }
4227 #if 1
4228       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
4229          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
4230       else if (ELEM_IS_PLAYER(center_element))
4231         Store[x][y] = EL_EMPTY;
4232       else if (center_element == EL_YAMYAM)
4233         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4234       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4235         Store[x][y] = element_info[center_element].content.e[xx][yy];
4236 #if 1
4237       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
4238          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
4239          otherwise) -- FIX THIS !!! */
4240       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
4241         Store[x][y] = element_info[element].content.e[1][1];
4242 #else
4243       else if (!CAN_EXPLODE(element))
4244         Store[x][y] = element_info[element].content.e[1][1];
4245 #endif
4246       else
4247         Store[x][y] = EL_EMPTY;
4248 #else
4249       else if (center_element == EL_MOLE)
4250         Store[x][y] = EL_EMERALD_RED;
4251       else if (center_element == EL_PENGUIN)
4252         Store[x][y] = EL_EMERALD_PURPLE;
4253       else if (center_element == EL_BUG)
4254         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
4255       else if (center_element == EL_BD_BUTTERFLY)
4256         Store[x][y] = EL_BD_DIAMOND;
4257       else if (center_element == EL_SP_ELECTRON)
4258         Store[x][y] = EL_SP_INFOTRON;
4259       else if (center_element == EL_AMOEBA_TO_DIAMOND)
4260         Store[x][y] = level.amoeba_content;
4261       else if (center_element == EL_YAMYAM)
4262         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
4263       else if (IS_CUSTOM_ELEMENT(center_element) &&
4264                element_info[center_element].content.e[xx][yy] != EL_EMPTY)
4265         Store[x][y] = element_info[center_element].content.e[xx][yy];
4266       else if (element == EL_WALL_EMERALD)
4267         Store[x][y] = EL_EMERALD;
4268       else if (element == EL_WALL_DIAMOND)
4269         Store[x][y] = EL_DIAMOND;
4270       else if (element == EL_WALL_BD_DIAMOND)
4271         Store[x][y] = EL_BD_DIAMOND;
4272       else if (element == EL_WALL_EMERALD_YELLOW)
4273         Store[x][y] = EL_EMERALD_YELLOW;
4274       else if (element == EL_WALL_EMERALD_RED)
4275         Store[x][y] = EL_EMERALD_RED;
4276       else if (element == EL_WALL_EMERALD_PURPLE)
4277         Store[x][y] = EL_EMERALD_PURPLE;
4278       else if (element == EL_WALL_PEARL)
4279         Store[x][y] = EL_PEARL;
4280       else if (element == EL_WALL_CRYSTAL)
4281         Store[x][y] = EL_CRYSTAL;
4282       else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
4283         Store[x][y] = element_info[element].content.e[1][1];
4284       else
4285         Store[x][y] = EL_EMPTY;
4286 #endif
4287
4288       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
4289           center_element == EL_AMOEBA_TO_DIAMOND)
4290         Store2[x][y] = element;
4291
4292       Feld[x][y] = EL_EXPLOSION;
4293       GfxElement[x][y] = artwork_element;
4294
4295 #if 0
4296       printf(":: setting gfx(%d,%d) to %d ['%s']\n",
4297              x, y, artwork_element, EL_NAME(artwork_element));
4298 #endif
4299
4300       ExplodePhase[x][y] = 1;
4301       ExplodeDelay[x][y] = last_phase;
4302
4303       Stop[x][y] = TRUE;
4304     }
4305
4306     if (center_element == EL_YAMYAM)
4307       game.yamyam_content_nr =
4308         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
4309
4310     return;
4311   }
4312
4313   if (Stop[ex][ey])
4314     return;
4315
4316   x = ex;
4317   y = ey;
4318
4319   if (phase == 1)
4320     GfxFrame[x][y] = 0;         /* restart explosion animation */
4321
4322   last_phase = ExplodeDelay[x][y];
4323
4324   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
4325
4326 #ifdef DEBUG
4327
4328   /* activate this even in non-DEBUG version until cause for crash in
4329      getGraphicAnimationFrame() (see below) is found and eliminated */
4330
4331 #endif
4332 #if 1
4333
4334 #if 1
4335   /* this can happen if the player leaves an explosion just in time */
4336   if (GfxElement[x][y] == EL_UNDEFINED)
4337     GfxElement[x][y] = EL_EMPTY;
4338 #else
4339   if (GfxElement[x][y] == EL_UNDEFINED)
4340   {
4341     printf("\n\n");
4342     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
4343     printf("Explode(): This should never happen!\n");
4344     printf("\n\n");
4345
4346     GfxElement[x][y] = EL_EMPTY;
4347   }
4348 #endif
4349
4350 #endif
4351
4352   border_element = Store2[x][y];
4353   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4354     border_element = StorePlayer[x][y];
4355
4356   if (phase == element_info[border_element].ignition_delay ||
4357       phase == last_phase)
4358   {
4359     boolean border_explosion = FALSE;
4360
4361     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
4362         !PLAYER_EXPLOSION_PROTECTED(x, y))
4363     {
4364       KillPlayerUnlessExplosionProtected(x, y);
4365       border_explosion = TRUE;
4366     }
4367     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
4368     {
4369       Feld[x][y] = Store2[x][y];
4370       Store2[x][y] = 0;
4371       Bang(x, y);
4372       border_explosion = TRUE;
4373     }
4374     else if (border_element == EL_AMOEBA_TO_DIAMOND)
4375     {
4376       AmoebeUmwandeln(x, y);
4377       Store2[x][y] = 0;
4378       border_explosion = TRUE;
4379     }
4380
4381     /* if an element just explodes due to another explosion (chain-reaction),
4382        do not immediately end the new explosion when it was the last frame of
4383        the explosion (as it would be done in the following "if"-statement!) */
4384     if (border_explosion && phase == last_phase)
4385       return;
4386   }
4387
4388   if (phase == last_phase)
4389   {
4390     int element;
4391
4392     element = Feld[x][y] = Store[x][y];
4393     Store[x][y] = Store2[x][y] = 0;
4394     GfxElement[x][y] = EL_UNDEFINED;
4395
4396     /* player can escape from explosions and might therefore be still alive */
4397     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4398         element <= EL_PLAYER_IS_EXPLODING_4)
4399     {
4400       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4401       int explosion_element = EL_PLAYER_1 + player_nr;
4402       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4403       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4404
4405       if (level.use_explosion_element[player_nr])
4406         explosion_element = level.explosion_element[player_nr];
4407
4408       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4409                     element_info[explosion_element].content.e[xx][yy]);
4410     }
4411
4412     /* restore probably existing indestructible background element */
4413     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4414       element = Feld[x][y] = Back[x][y];
4415     Back[x][y] = 0;
4416
4417     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4418     GfxDir[x][y] = MV_NONE;
4419     ChangeDelay[x][y] = 0;
4420     ChangePage[x][y] = -1;
4421
4422 #if USE_NEW_CUSTOM_VALUE
4423     CustomValue[x][y] = 0;
4424 #endif
4425
4426     InitField_WithBug2(x, y, FALSE);
4427
4428     DrawLevelField(x, y);
4429
4430     TestIfElementTouchesCustomElement(x, y);
4431
4432     if (GFX_CRUMBLED(element))
4433       DrawLevelFieldCrumbledSandNeighbours(x, y);
4434
4435     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4436       StorePlayer[x][y] = 0;
4437
4438     if (ELEM_IS_PLAYER(element))
4439       RelocatePlayer(x, y, element);
4440   }
4441   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4442   {
4443     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4444     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4445
4446     if (phase == delay)
4447       DrawLevelFieldCrumbledSand(x, y);
4448
4449     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4450     {
4451       DrawLevelElement(x, y, Back[x][y]);
4452       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4453     }
4454     else if (IS_WALKABLE_UNDER(Back[x][y]))
4455     {
4456       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4457       DrawLevelElementThruMask(x, y, Back[x][y]);
4458     }
4459     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4460       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4461   }
4462 }
4463
4464 void DynaExplode(int ex, int ey)
4465 {
4466   int i, j;
4467   int dynabomb_element = Feld[ex][ey];
4468   int dynabomb_size = 1;
4469   boolean dynabomb_xl = FALSE;
4470   struct PlayerInfo *player;
4471   static int xy[4][2] =
4472   {
4473     { 0, -1 },
4474     { -1, 0 },
4475     { +1, 0 },
4476     { 0, +1 }
4477   };
4478
4479   if (IS_ACTIVE_BOMB(dynabomb_element))
4480   {
4481     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4482     dynabomb_size = player->dynabomb_size;
4483     dynabomb_xl = player->dynabomb_xl;
4484     player->dynabombs_left++;
4485   }
4486
4487   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4488
4489   for (i = 0; i < NUM_DIRECTIONS; i++)
4490   {
4491     for (j = 1; j <= dynabomb_size; j++)
4492     {
4493       int x = ex + j * xy[i][0];
4494       int y = ey + j * xy[i][1];
4495       int element;
4496
4497       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4498         break;
4499
4500       element = Feld[x][y];
4501
4502       /* do not restart explosions of fields with active bombs */
4503       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4504         continue;
4505
4506       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4507
4508       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4509           !IS_DIGGABLE(element) && !dynabomb_xl)
4510         break;
4511     }
4512   }
4513 }
4514
4515 void Bang(int x, int y)
4516 {
4517   int element = MovingOrBlocked2Element(x, y);
4518   int explosion_type = EX_TYPE_NORMAL;
4519
4520   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4521   {
4522     struct PlayerInfo *player = PLAYERINFO(x, y);
4523
4524     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4525                             player->element_nr);
4526
4527     if (level.use_explosion_element[player->index_nr])
4528     {
4529       int explosion_element = level.explosion_element[player->index_nr];
4530
4531       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4532         explosion_type = EX_TYPE_CROSS;
4533       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4534         explosion_type = EX_TYPE_CENTER;
4535     }
4536   }
4537
4538   switch(element)
4539   {
4540     case EL_BUG:
4541     case EL_SPACESHIP:
4542     case EL_BD_BUTTERFLY:
4543     case EL_BD_FIREFLY:
4544     case EL_YAMYAM:
4545     case EL_DARK_YAMYAM:
4546     case EL_ROBOT:
4547     case EL_PACMAN:
4548     case EL_MOLE:
4549       RaiseScoreElement(element);
4550       break;
4551
4552     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4553     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4554     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4555     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4556     case EL_DYNABOMB_INCREASE_NUMBER:
4557     case EL_DYNABOMB_INCREASE_SIZE:
4558     case EL_DYNABOMB_INCREASE_POWER:
4559       explosion_type = EX_TYPE_DYNA;
4560       break;
4561
4562     case EL_PENGUIN:
4563     case EL_LAMP:
4564     case EL_LAMP_ACTIVE:
4565     case EL_AMOEBA_TO_DIAMOND:
4566       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4567         explosion_type = EX_TYPE_CENTER;
4568       break;
4569
4570     default:
4571       if (element_info[element].explosion_type == EXPLODES_CROSS)
4572         explosion_type = EX_TYPE_CROSS;
4573       else if (element_info[element].explosion_type == EXPLODES_1X1)
4574         explosion_type = EX_TYPE_CENTER;
4575       break;
4576   }
4577
4578   if (explosion_type == EX_TYPE_DYNA)
4579     DynaExplode(x, y);
4580   else
4581     Explode(x, y, EX_PHASE_START, explosion_type);
4582
4583   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4584 }
4585
4586 void SplashAcid(int x, int y)
4587 {
4588   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4589       (!IN_LEV_FIELD(x - 1, y - 2) ||
4590        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4591     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4592
4593   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4594       (!IN_LEV_FIELD(x + 1, y - 2) ||
4595        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4596     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4597
4598   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4599 }
4600
4601 static void InitBeltMovement()
4602 {
4603   static int belt_base_element[4] =
4604   {
4605     EL_CONVEYOR_BELT_1_LEFT,
4606     EL_CONVEYOR_BELT_2_LEFT,
4607     EL_CONVEYOR_BELT_3_LEFT,
4608     EL_CONVEYOR_BELT_4_LEFT
4609   };
4610   static int belt_base_active_element[4] =
4611   {
4612     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4613     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4614     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4615     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4616   };
4617
4618   int x, y, i, j;
4619
4620   /* set frame order for belt animation graphic according to belt direction */
4621   for (i = 0; i < NUM_BELTS; i++)
4622   {
4623     int belt_nr = i;
4624
4625     for (j = 0; j < NUM_BELT_PARTS; j++)
4626     {
4627       int element = belt_base_active_element[belt_nr] + j;
4628       int graphic = el2img(element);
4629
4630       if (game.belt_dir[i] == MV_LEFT)
4631         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4632       else
4633         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4634     }
4635   }
4636
4637 #if 1
4638   SCAN_PLAYFIELD(x, y)
4639 #else
4640   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4641 #endif
4642   {
4643     int element = Feld[x][y];
4644
4645     for (i = 0; i < NUM_BELTS; i++)
4646     {
4647       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4648       {
4649         int e_belt_nr = getBeltNrFromBeltElement(element);
4650         int belt_nr = i;
4651
4652         if (e_belt_nr == belt_nr)
4653         {
4654           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4655
4656           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4657         }
4658       }
4659     }
4660   }
4661 }
4662
4663 static void ToggleBeltSwitch(int x, int y)
4664 {
4665   static int belt_base_element[4] =
4666   {
4667     EL_CONVEYOR_BELT_1_LEFT,
4668     EL_CONVEYOR_BELT_2_LEFT,
4669     EL_CONVEYOR_BELT_3_LEFT,
4670     EL_CONVEYOR_BELT_4_LEFT
4671   };
4672   static int belt_base_active_element[4] =
4673   {
4674     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4675     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4676     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4677     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4678   };
4679   static int belt_base_switch_element[4] =
4680   {
4681     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4682     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4683     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4684     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4685   };
4686   static int belt_move_dir[4] =
4687   {
4688     MV_LEFT,
4689     MV_NONE,
4690     MV_RIGHT,
4691     MV_NONE,
4692   };
4693
4694   int element = Feld[x][y];
4695   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4696   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4697   int belt_dir = belt_move_dir[belt_dir_nr];
4698   int xx, yy, i;
4699
4700   if (!IS_BELT_SWITCH(element))
4701     return;
4702
4703   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4704   game.belt_dir[belt_nr] = belt_dir;
4705
4706   if (belt_dir_nr == 3)
4707     belt_dir_nr = 1;
4708
4709   /* set frame order for belt animation graphic according to belt direction */
4710   for (i = 0; i < NUM_BELT_PARTS; i++)
4711   {
4712     int element = belt_base_active_element[belt_nr] + i;
4713     int graphic = el2img(element);
4714
4715     if (belt_dir == MV_LEFT)
4716       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4717     else
4718       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4719   }
4720
4721 #if 1
4722   SCAN_PLAYFIELD(xx, yy)
4723 #else
4724   for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4725 #endif
4726   {
4727     int element = Feld[xx][yy];
4728
4729     if (IS_BELT_SWITCH(element))
4730     {
4731       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4732
4733       if (e_belt_nr == belt_nr)
4734       {
4735         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4736         DrawLevelField(xx, yy);
4737       }
4738     }
4739     else if (IS_BELT(element) && belt_dir != MV_NONE)
4740     {
4741       int e_belt_nr = getBeltNrFromBeltElement(element);
4742
4743       if (e_belt_nr == belt_nr)
4744       {
4745         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4746
4747         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4748         DrawLevelField(xx, yy);
4749       }
4750     }
4751     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4752     {
4753       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4754
4755       if (e_belt_nr == belt_nr)
4756       {
4757         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4758
4759         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4760         DrawLevelField(xx, yy);
4761       }
4762     }
4763   }
4764 }
4765
4766 static void ToggleSwitchgateSwitch(int x, int y)
4767 {
4768   int xx, yy;
4769
4770   game.switchgate_pos = !game.switchgate_pos;
4771
4772 #if 1
4773   SCAN_PLAYFIELD(xx, yy)
4774 #else
4775   for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4776 #endif
4777   {
4778     int element = Feld[xx][yy];
4779
4780 #if !USE_BOTH_SWITCHGATE_SWITCHES
4781     if (element == EL_SWITCHGATE_SWITCH_UP ||
4782         element == EL_SWITCHGATE_SWITCH_DOWN)
4783     {
4784       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4785       DrawLevelField(xx, yy);
4786     }
4787 #else
4788     if (element == EL_SWITCHGATE_SWITCH_UP)
4789     {
4790       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
4791       DrawLevelField(xx, yy);
4792     }
4793     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
4794     {
4795       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
4796       DrawLevelField(xx, yy);
4797     }
4798 #endif
4799     else if (element == EL_SWITCHGATE_OPEN ||
4800              element == EL_SWITCHGATE_OPENING)
4801     {
4802       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4803
4804       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4805     }
4806     else if (element == EL_SWITCHGATE_CLOSED ||
4807              element == EL_SWITCHGATE_CLOSING)
4808     {
4809       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4810
4811       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4812     }
4813   }
4814 }
4815
4816 static int getInvisibleActiveFromInvisibleElement(int element)
4817 {
4818   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4819           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
4820           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
4821           element);
4822 }
4823
4824 static int getInvisibleFromInvisibleActiveElement(int element)
4825 {
4826   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4827           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
4828           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
4829           element);
4830 }
4831
4832 static void RedrawAllLightSwitchesAndInvisibleElements()
4833 {
4834   int x, y;
4835
4836 #if 1
4837   SCAN_PLAYFIELD(x, y)
4838 #else
4839   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4840 #endif
4841   {
4842     int element = Feld[x][y];
4843
4844     if (element == EL_LIGHT_SWITCH &&
4845         game.light_time_left > 0)
4846     {
4847       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4848       DrawLevelField(x, y);
4849     }
4850     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4851              game.light_time_left == 0)
4852     {
4853       Feld[x][y] = EL_LIGHT_SWITCH;
4854       DrawLevelField(x, y);
4855     }
4856     else if (element == EL_EMC_DRIPPER &&
4857              game.light_time_left > 0)
4858     {
4859       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4860       DrawLevelField(x, y);
4861     }
4862     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4863              game.light_time_left == 0)
4864     {
4865       Feld[x][y] = EL_EMC_DRIPPER;
4866       DrawLevelField(x, y);
4867     }
4868     else if (element == EL_INVISIBLE_STEELWALL ||
4869              element == EL_INVISIBLE_WALL ||
4870              element == EL_INVISIBLE_SAND)
4871     {
4872       if (game.light_time_left > 0)
4873         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4874
4875       DrawLevelField(x, y);
4876
4877       /* uncrumble neighbour fields, if needed */
4878       if (element == EL_INVISIBLE_SAND)
4879         DrawLevelFieldCrumbledSandNeighbours(x, y);
4880     }
4881     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4882              element == EL_INVISIBLE_WALL_ACTIVE ||
4883              element == EL_INVISIBLE_SAND_ACTIVE)
4884     {
4885       if (game.light_time_left == 0)
4886         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4887
4888       DrawLevelField(x, y);
4889
4890       /* re-crumble neighbour fields, if needed */
4891       if (element == EL_INVISIBLE_SAND)
4892         DrawLevelFieldCrumbledSandNeighbours(x, y);
4893     }
4894   }
4895 }
4896
4897 static void RedrawAllInvisibleElementsForLenses()
4898 {
4899   int x, y;
4900
4901 #if 1
4902   SCAN_PLAYFIELD(x, y)
4903 #else
4904   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4905 #endif
4906   {
4907     int element = Feld[x][y];
4908
4909     if (element == EL_EMC_DRIPPER &&
4910         game.lenses_time_left > 0)
4911     {
4912       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4913       DrawLevelField(x, y);
4914     }
4915     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4916              game.lenses_time_left == 0)
4917     {
4918       Feld[x][y] = EL_EMC_DRIPPER;
4919       DrawLevelField(x, y);
4920     }
4921     else if (element == EL_INVISIBLE_STEELWALL ||
4922              element == EL_INVISIBLE_WALL ||
4923              element == EL_INVISIBLE_SAND)
4924     {
4925       if (game.lenses_time_left > 0)
4926         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4927
4928       DrawLevelField(x, y);
4929
4930       /* uncrumble neighbour fields, if needed */
4931       if (element == EL_INVISIBLE_SAND)
4932         DrawLevelFieldCrumbledSandNeighbours(x, y);
4933     }
4934     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4935              element == EL_INVISIBLE_WALL_ACTIVE ||
4936              element == EL_INVISIBLE_SAND_ACTIVE)
4937     {
4938       if (game.lenses_time_left == 0)
4939         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4940
4941       DrawLevelField(x, y);
4942
4943       /* re-crumble neighbour fields, if needed */
4944       if (element == EL_INVISIBLE_SAND)
4945         DrawLevelFieldCrumbledSandNeighbours(x, y);
4946     }
4947   }
4948 }
4949
4950 static void RedrawAllInvisibleElementsForMagnifier()
4951 {
4952   int x, y;
4953
4954 #if 1
4955   SCAN_PLAYFIELD(x, y)
4956 #else
4957   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4958 #endif
4959   {
4960     int element = Feld[x][y];
4961
4962     if (element == EL_EMC_FAKE_GRASS &&
4963         game.magnify_time_left > 0)
4964     {
4965       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4966       DrawLevelField(x, y);
4967     }
4968     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4969              game.magnify_time_left == 0)
4970     {
4971       Feld[x][y] = EL_EMC_FAKE_GRASS;
4972       DrawLevelField(x, y);
4973     }
4974     else if (IS_GATE_GRAY(element) &&
4975              game.magnify_time_left > 0)
4976     {
4977       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4978                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4979                     IS_EM_GATE_GRAY(element) ?
4980                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4981                     IS_EMC_GATE_GRAY(element) ?
4982                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4983                     element);
4984       DrawLevelField(x, y);
4985     }
4986     else if (IS_GATE_GRAY_ACTIVE(element) &&
4987              game.magnify_time_left == 0)
4988     {
4989       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4990                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4991                     IS_EM_GATE_GRAY_ACTIVE(element) ?
4992                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4993                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
4994                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4995                     element);
4996       DrawLevelField(x, y);
4997     }
4998   }
4999 }
5000
5001 static void ToggleLightSwitch(int x, int y)
5002 {
5003   int element = Feld[x][y];
5004
5005   game.light_time_left =
5006     (element == EL_LIGHT_SWITCH ?
5007      level.time_light * FRAMES_PER_SECOND : 0);
5008
5009   RedrawAllLightSwitchesAndInvisibleElements();
5010 }
5011
5012 static void ActivateTimegateSwitch(int x, int y)
5013 {
5014   int xx, yy;
5015
5016   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5017
5018 #if 1
5019   SCAN_PLAYFIELD(xx, yy)
5020 #else
5021   for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
5022 #endif
5023   {
5024     int element = Feld[xx][yy];
5025
5026     if (element == EL_TIMEGATE_CLOSED ||
5027         element == EL_TIMEGATE_CLOSING)
5028     {
5029       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5030       PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
5031     }
5032
5033     /*
5034     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5035     {
5036       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5037       DrawLevelField(xx, yy);
5038     }
5039     */
5040
5041   }
5042
5043   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5044 }
5045
5046 void Impact(int x, int y)
5047 {
5048   boolean last_line = (y == lev_fieldy - 1);
5049   boolean object_hit = FALSE;
5050   boolean impact = (last_line || object_hit);
5051   int element = Feld[x][y];
5052   int smashed = EL_STEELWALL;
5053
5054   if (!last_line)       /* check if element below was hit */
5055   {
5056     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5057       return;
5058
5059     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5060                                          MovDir[x][y + 1] != MV_DOWN ||
5061                                          MovPos[x][y + 1] <= TILEY / 2));
5062
5063     /* do not smash moving elements that left the smashed field in time */
5064     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5065         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5066       object_hit = FALSE;
5067
5068 #if USE_QUICKSAND_IMPACT_BUGFIX
5069     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5070     {
5071       RemoveMovingField(x, y + 1);
5072       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5073       Feld[x][y + 2] = EL_ROCK;
5074       DrawLevelField(x, y + 2);
5075
5076       object_hit = TRUE;
5077     }
5078 #endif
5079
5080     if (object_hit)
5081       smashed = MovingOrBlocked2Element(x, y + 1);
5082
5083     impact = (last_line || object_hit);
5084   }
5085
5086   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5087   {
5088     SplashAcid(x, y + 1);
5089     return;
5090   }
5091
5092   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5093   /* only reset graphic animation if graphic really changes after impact */
5094   if (impact &&
5095       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5096   {
5097     ResetGfxAnimation(x, y);
5098     DrawLevelField(x, y);
5099   }
5100
5101   if (impact && CAN_EXPLODE_IMPACT(element))
5102   {
5103     Bang(x, y);
5104     return;
5105   }
5106   else if (impact && element == EL_PEARL)
5107   {
5108     ResetGfxAnimation(x, y);
5109
5110     Feld[x][y] = EL_PEARL_BREAKING;
5111     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5112     return;
5113   }
5114   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5115   {
5116     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5117
5118     return;
5119   }
5120
5121   if (impact && element == EL_AMOEBA_DROP)
5122   {
5123     if (object_hit && IS_PLAYER(x, y + 1))
5124       KillPlayerUnlessEnemyProtected(x, y + 1);
5125     else if (object_hit && smashed == EL_PENGUIN)
5126       Bang(x, y + 1);
5127     else
5128     {
5129       Feld[x][y] = EL_AMOEBA_GROWING;
5130       Store[x][y] = EL_AMOEBA_WET;
5131
5132       ResetRandomAnimationValue(x, y);
5133     }
5134     return;
5135   }
5136
5137   if (object_hit)               /* check which object was hit */
5138   {
5139     if (CAN_PASS_MAGIC_WALL(element) && 
5140         (smashed == EL_MAGIC_WALL ||
5141          smashed == EL_BD_MAGIC_WALL))
5142     {
5143       int xx, yy;
5144       int activated_magic_wall =
5145         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5146          EL_BD_MAGIC_WALL_ACTIVE);
5147
5148       /* activate magic wall / mill */
5149 #if 1
5150       SCAN_PLAYFIELD(xx, yy)
5151 #else
5152       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
5153 #endif
5154         if (Feld[xx][yy] == smashed)
5155           Feld[xx][yy] = activated_magic_wall;
5156
5157       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
5158       game.magic_wall_active = TRUE;
5159
5160       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
5161                             SND_MAGIC_WALL_ACTIVATING :
5162                             SND_BD_MAGIC_WALL_ACTIVATING));
5163     }
5164
5165     if (IS_PLAYER(x, y + 1))
5166     {
5167       if (CAN_SMASH_PLAYER(element))
5168       {
5169         KillPlayerUnlessEnemyProtected(x, y + 1);
5170         return;
5171       }
5172     }
5173     else if (smashed == EL_PENGUIN)
5174     {
5175       if (CAN_SMASH_PLAYER(element))
5176       {
5177         Bang(x, y + 1);
5178         return;
5179       }
5180     }
5181     else if (element == EL_BD_DIAMOND)
5182     {
5183       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
5184       {
5185         Bang(x, y + 1);
5186         return;
5187       }
5188     }
5189     else if (((element == EL_SP_INFOTRON ||
5190                element == EL_SP_ZONK) &&
5191               (smashed == EL_SP_SNIKSNAK ||
5192                smashed == EL_SP_ELECTRON ||
5193                smashed == EL_SP_DISK_ORANGE)) ||
5194              (element == EL_SP_INFOTRON &&
5195               smashed == EL_SP_DISK_YELLOW))
5196     {
5197       Bang(x, y + 1);
5198       return;
5199     }
5200     else if (CAN_SMASH_EVERYTHING(element))
5201     {
5202       if (IS_CLASSIC_ENEMY(smashed) ||
5203           CAN_EXPLODE_SMASHED(smashed))
5204       {
5205         Bang(x, y + 1);
5206         return;
5207       }
5208       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
5209       {
5210         if (smashed == EL_LAMP ||
5211             smashed == EL_LAMP_ACTIVE)
5212         {
5213           Bang(x, y + 1);
5214           return;
5215         }
5216         else if (smashed == EL_NUT)
5217         {
5218           Feld[x][y + 1] = EL_NUT_BREAKING;
5219           PlayLevelSound(x, y, SND_NUT_BREAKING);
5220           RaiseScoreElement(EL_NUT);
5221           return;
5222         }
5223         else if (smashed == EL_PEARL)
5224         {
5225           ResetGfxAnimation(x, y);
5226
5227           Feld[x][y + 1] = EL_PEARL_BREAKING;
5228           PlayLevelSound(x, y, SND_PEARL_BREAKING);
5229           return;
5230         }
5231         else if (smashed == EL_DIAMOND)
5232         {
5233           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
5234           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
5235           return;
5236         }
5237         else if (IS_BELT_SWITCH(smashed))
5238         {
5239           ToggleBeltSwitch(x, y + 1);
5240         }
5241         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
5242                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
5243         {
5244           ToggleSwitchgateSwitch(x, y + 1);
5245         }
5246         else if (smashed == EL_LIGHT_SWITCH ||
5247                  smashed == EL_LIGHT_SWITCH_ACTIVE)
5248         {
5249           ToggleLightSwitch(x, y + 1);
5250         }
5251         else
5252         {
5253 #if 0
5254           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
5255 #endif
5256
5257           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5258
5259           CheckElementChangeBySide(x, y + 1, smashed, element,
5260                                    CE_SWITCHED, CH_SIDE_TOP);
5261           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
5262                                             CH_SIDE_TOP);
5263         }
5264       }
5265       else
5266       {
5267         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
5268       }
5269     }
5270   }
5271
5272   /* play sound of magic wall / mill */
5273   if (!last_line &&
5274       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5275        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5276   {
5277     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5278       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
5279     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5280       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
5281
5282     return;
5283   }
5284
5285   /* play sound of object that hits the ground */
5286   if (last_line || object_hit)
5287     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5288 }
5289
5290 inline static void TurnRoundExt(int x, int y)
5291 {
5292   static struct
5293   {
5294     int dx, dy;
5295   } move_xy[] =
5296   {
5297     {  0,  0 },
5298     { -1,  0 },
5299     { +1,  0 },
5300     {  0,  0 },
5301     {  0, -1 },
5302     {  0,  0 }, { 0, 0 }, { 0, 0 },
5303     {  0, +1 }
5304   };
5305   static struct
5306   {
5307     int left, right, back;
5308   } turn[] =
5309   {
5310     { 0,        0,              0        },
5311     { MV_DOWN,  MV_UP,          MV_RIGHT },
5312     { MV_UP,    MV_DOWN,        MV_LEFT  },
5313     { 0,        0,              0        },
5314     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
5315     { 0,        0,              0        },
5316     { 0,        0,              0        },
5317     { 0,        0,              0        },
5318     { MV_RIGHT, MV_LEFT,        MV_UP    }
5319   };
5320
5321   int element = Feld[x][y];
5322   int move_pattern = element_info[element].move_pattern;
5323
5324   int old_move_dir = MovDir[x][y];
5325   int left_dir  = turn[old_move_dir].left;
5326   int right_dir = turn[old_move_dir].right;
5327   int back_dir  = turn[old_move_dir].back;
5328
5329   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
5330   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
5331   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
5332   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
5333
5334   int left_x  = x + left_dx,  left_y  = y + left_dy;
5335   int right_x = x + right_dx, right_y = y + right_dy;
5336   int move_x  = x + move_dx,  move_y  = y + move_dy;
5337
5338   int xx, yy;
5339
5340   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
5341   {
5342     TestIfBadThingTouchesOtherBadThing(x, y);
5343
5344     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
5345       MovDir[x][y] = right_dir;
5346     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5347       MovDir[x][y] = left_dir;
5348
5349     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
5350       MovDelay[x][y] = 9;
5351     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
5352       MovDelay[x][y] = 1;
5353   }
5354   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
5355   {
5356     TestIfBadThingTouchesOtherBadThing(x, y);
5357
5358     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
5359       MovDir[x][y] = left_dir;
5360     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
5361       MovDir[x][y] = right_dir;
5362
5363     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
5364       MovDelay[x][y] = 9;
5365     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
5366       MovDelay[x][y] = 1;
5367   }
5368   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
5369   {
5370     TestIfBadThingTouchesOtherBadThing(x, y);
5371
5372     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
5373       MovDir[x][y] = left_dir;
5374     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
5375       MovDir[x][y] = right_dir;
5376
5377     if (MovDir[x][y] != old_move_dir)
5378       MovDelay[x][y] = 9;
5379   }
5380   else if (element == EL_YAMYAM)
5381   {
5382     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
5383     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
5384
5385     if (can_turn_left && can_turn_right)
5386       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5387     else if (can_turn_left)
5388       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5389     else if (can_turn_right)
5390       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5391     else
5392       MovDir[x][y] = back_dir;
5393
5394     MovDelay[x][y] = 16 + 16 * RND(3);
5395   }
5396   else if (element == EL_DARK_YAMYAM)
5397   {
5398     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5399                                                          left_x, left_y);
5400     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
5401                                                          right_x, right_y);
5402
5403     if (can_turn_left && can_turn_right)
5404       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5405     else if (can_turn_left)
5406       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5407     else if (can_turn_right)
5408       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5409     else
5410       MovDir[x][y] = back_dir;
5411
5412     MovDelay[x][y] = 16 + 16 * RND(3);
5413   }
5414   else if (element == EL_PACMAN)
5415   {
5416     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5417     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5418
5419     if (can_turn_left && can_turn_right)
5420       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5421     else if (can_turn_left)
5422       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5423     else if (can_turn_right)
5424       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5425     else
5426       MovDir[x][y] = back_dir;
5427
5428     MovDelay[x][y] = 6 + RND(40);
5429   }
5430   else if (element == EL_PIG)
5431   {
5432     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5433     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5434     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5435     boolean should_turn_left, should_turn_right, should_move_on;
5436     int rnd_value = 24;
5437     int rnd = RND(rnd_value);
5438
5439     should_turn_left = (can_turn_left &&
5440                         (!can_move_on ||
5441                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5442                                                    y + back_dy + left_dy)));
5443     should_turn_right = (can_turn_right &&
5444                          (!can_move_on ||
5445                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5446                                                     y + back_dy + right_dy)));
5447     should_move_on = (can_move_on &&
5448                       (!can_turn_left ||
5449                        !can_turn_right ||
5450                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5451                                                  y + move_dy + left_dy) ||
5452                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5453                                                  y + move_dy + right_dy)));
5454
5455     if (should_turn_left || should_turn_right || should_move_on)
5456     {
5457       if (should_turn_left && should_turn_right && should_move_on)
5458         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5459                         rnd < 2 * rnd_value / 3 ? right_dir :
5460                         old_move_dir);
5461       else if (should_turn_left && should_turn_right)
5462         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5463       else if (should_turn_left && should_move_on)
5464         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5465       else if (should_turn_right && should_move_on)
5466         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5467       else if (should_turn_left)
5468         MovDir[x][y] = left_dir;
5469       else if (should_turn_right)
5470         MovDir[x][y] = right_dir;
5471       else if (should_move_on)
5472         MovDir[x][y] = old_move_dir;
5473     }
5474     else if (can_move_on && rnd > rnd_value / 8)
5475       MovDir[x][y] = old_move_dir;
5476     else if (can_turn_left && can_turn_right)
5477       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5478     else if (can_turn_left && rnd > rnd_value / 8)
5479       MovDir[x][y] = left_dir;
5480     else if (can_turn_right && rnd > rnd_value/8)
5481       MovDir[x][y] = right_dir;
5482     else
5483       MovDir[x][y] = back_dir;
5484
5485     xx = x + move_xy[MovDir[x][y]].dx;
5486     yy = y + move_xy[MovDir[x][y]].dy;
5487
5488     if (!IN_LEV_FIELD(xx, yy) ||
5489         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5490       MovDir[x][y] = old_move_dir;
5491
5492     MovDelay[x][y] = 0;
5493   }
5494   else if (element == EL_DRAGON)
5495   {
5496     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5497     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5498     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5499     int rnd_value = 24;
5500     int rnd = RND(rnd_value);
5501
5502     if (can_move_on && rnd > rnd_value / 8)
5503       MovDir[x][y] = old_move_dir;
5504     else if (can_turn_left && can_turn_right)
5505       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5506     else if (can_turn_left && rnd > rnd_value / 8)
5507       MovDir[x][y] = left_dir;
5508     else if (can_turn_right && rnd > rnd_value / 8)
5509       MovDir[x][y] = right_dir;
5510     else
5511       MovDir[x][y] = back_dir;
5512
5513     xx = x + move_xy[MovDir[x][y]].dx;
5514     yy = y + move_xy[MovDir[x][y]].dy;
5515
5516     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5517       MovDir[x][y] = old_move_dir;
5518
5519     MovDelay[x][y] = 0;
5520   }
5521   else if (element == EL_MOLE)
5522   {
5523     boolean can_move_on =
5524       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5525                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5526                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5527     if (!can_move_on)
5528     {
5529       boolean can_turn_left =
5530         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5531                               IS_AMOEBOID(Feld[left_x][left_y])));
5532
5533       boolean can_turn_right =
5534         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5535                               IS_AMOEBOID(Feld[right_x][right_y])));
5536
5537       if (can_turn_left && can_turn_right)
5538         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5539       else if (can_turn_left)
5540         MovDir[x][y] = left_dir;
5541       else
5542         MovDir[x][y] = right_dir;
5543     }
5544
5545     if (MovDir[x][y] != old_move_dir)
5546       MovDelay[x][y] = 9;
5547   }
5548   else if (element == EL_BALLOON)
5549   {
5550     MovDir[x][y] = game.wind_direction;
5551     MovDelay[x][y] = 0;
5552   }
5553   else if (element == EL_SPRING)
5554   {
5555 #if USE_NEW_SPRING_BUMPER
5556     if (MovDir[x][y] & MV_HORIZONTAL)
5557     {
5558       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5559           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5560       {
5561         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5562         ResetGfxAnimation(move_x, move_y);
5563         DrawLevelField(move_x, move_y);
5564
5565         MovDir[x][y] = back_dir;
5566       }
5567       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5568                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5569         MovDir[x][y] = MV_NONE;
5570     }
5571 #else
5572     if (MovDir[x][y] & MV_HORIZONTAL &&
5573         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5574          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5575       MovDir[x][y] = MV_NONE;
5576 #endif
5577
5578     MovDelay[x][y] = 0;
5579   }
5580   else if (element == EL_ROBOT ||
5581            element == EL_SATELLITE ||
5582            element == EL_PENGUIN ||
5583            element == EL_EMC_ANDROID)
5584   {
5585     int attr_x = -1, attr_y = -1;
5586
5587     if (AllPlayersGone)
5588     {
5589       attr_x = ExitX;
5590       attr_y = ExitY;
5591     }
5592     else
5593     {
5594       int i;
5595
5596       for (i = 0; i < MAX_PLAYERS; i++)
5597       {
5598         struct PlayerInfo *player = &stored_player[i];
5599         int jx = player->jx, jy = player->jy;
5600
5601         if (!player->active)
5602           continue;
5603
5604         if (attr_x == -1 ||
5605             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5606         {
5607           attr_x = jx;
5608           attr_y = jy;
5609         }
5610       }
5611     }
5612
5613     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5614         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5615          game.engine_version < VERSION_IDENT(3,1,0,0)))
5616     {
5617       attr_x = ZX;
5618       attr_y = ZY;
5619     }
5620
5621     if (element == EL_PENGUIN)
5622     {
5623       int i;
5624       static int xy[4][2] =
5625       {
5626         { 0, -1 },
5627         { -1, 0 },
5628         { +1, 0 },
5629         { 0, +1 }
5630       };
5631
5632       for (i = 0; i < NUM_DIRECTIONS; i++)
5633       {
5634         int ex = x + xy[i][0];
5635         int ey = y + xy[i][1];
5636
5637         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5638         {
5639           attr_x = ex;
5640           attr_y = ey;
5641           break;
5642         }
5643       }
5644     }
5645
5646     MovDir[x][y] = MV_NONE;
5647     if (attr_x < x)
5648       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5649     else if (attr_x > x)
5650       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5651     if (attr_y < y)
5652       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5653     else if (attr_y > y)
5654       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5655
5656     if (element == EL_ROBOT)
5657     {
5658       int newx, newy;
5659
5660       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5661         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5662       Moving2Blocked(x, y, &newx, &newy);
5663
5664       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5665         MovDelay[x][y] = 8 + 8 * !RND(3);
5666       else
5667         MovDelay[x][y] = 16;
5668     }
5669     else if (element == EL_PENGUIN)
5670     {
5671       int newx, newy;
5672
5673       MovDelay[x][y] = 1;
5674
5675       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5676       {
5677         boolean first_horiz = RND(2);
5678         int new_move_dir = MovDir[x][y];
5679
5680         MovDir[x][y] =
5681           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5682         Moving2Blocked(x, y, &newx, &newy);
5683
5684         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5685           return;
5686
5687         MovDir[x][y] =
5688           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5689         Moving2Blocked(x, y, &newx, &newy);
5690
5691         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5692           return;
5693
5694         MovDir[x][y] = old_move_dir;
5695         return;
5696       }
5697     }
5698     else if (element == EL_SATELLITE)
5699     {
5700       int newx, newy;
5701
5702       MovDelay[x][y] = 1;
5703
5704       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5705       {
5706         boolean first_horiz = RND(2);
5707         int new_move_dir = MovDir[x][y];
5708
5709         MovDir[x][y] =
5710           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5711         Moving2Blocked(x, y, &newx, &newy);
5712
5713         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5714           return;
5715
5716         MovDir[x][y] =
5717           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5718         Moving2Blocked(x, y, &newx, &newy);
5719
5720         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5721           return;
5722
5723         MovDir[x][y] = old_move_dir;
5724         return;
5725       }
5726     }
5727     else if (element == EL_EMC_ANDROID)
5728     {
5729       static int check_pos[16] =
5730       {
5731         -1,             /*  0 => (invalid)          */
5732         7,              /*  1 => MV_LEFT            */
5733         3,              /*  2 => MV_RIGHT           */
5734         -1,             /*  3 => (invalid)          */
5735         1,              /*  4 =>            MV_UP   */
5736         0,              /*  5 => MV_LEFT  | MV_UP   */
5737         2,              /*  6 => MV_RIGHT | MV_UP   */
5738         -1,             /*  7 => (invalid)          */
5739         5,              /*  8 =>            MV_DOWN */
5740         6,              /*  9 => MV_LEFT  | MV_DOWN */
5741         4,              /* 10 => MV_RIGHT | MV_DOWN */
5742         -1,             /* 11 => (invalid)          */
5743         -1,             /* 12 => (invalid)          */
5744         -1,             /* 13 => (invalid)          */
5745         -1,             /* 14 => (invalid)          */
5746         -1,             /* 15 => (invalid)          */
5747       };
5748       static struct
5749       {
5750         int dx, dy;
5751         int dir;
5752       } check_xy[8] =
5753       {
5754         { -1, -1,       MV_LEFT  | MV_UP   },
5755         {  0, -1,                  MV_UP   },
5756         { +1, -1,       MV_RIGHT | MV_UP   },
5757         { +1,  0,       MV_RIGHT           },
5758         { +1, +1,       MV_RIGHT | MV_DOWN },
5759         {  0, +1,                  MV_DOWN },
5760         { -1, +1,       MV_LEFT  | MV_DOWN },
5761         { -1,  0,       MV_LEFT            },
5762       };
5763       int start_pos, check_order;
5764       boolean can_clone = FALSE;
5765       int i;
5766
5767       /* check if there is any free field around current position */
5768       for (i = 0; i < 8; i++)
5769       {
5770         int newx = x + check_xy[i].dx;
5771         int newy = y + check_xy[i].dy;
5772
5773         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5774         {
5775           can_clone = TRUE;
5776
5777           break;
5778         }
5779       }
5780
5781       if (can_clone)            /* randomly find an element to clone */
5782       {
5783         can_clone = FALSE;
5784
5785         start_pos = check_pos[RND(8)];
5786         check_order = (RND(2) ? -1 : +1);
5787
5788         for (i = 0; i < 8; i++)
5789         {
5790           int pos_raw = start_pos + i * check_order;
5791           int pos = (pos_raw + 8) % 8;
5792           int newx = x + check_xy[pos].dx;
5793           int newy = y + check_xy[pos].dy;
5794
5795           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5796           {
5797             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5798             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5799
5800             Store[x][y] = Feld[newx][newy];
5801
5802             can_clone = TRUE;
5803
5804             break;
5805           }
5806         }
5807       }
5808
5809       if (can_clone)            /* randomly find a direction to move */
5810       {
5811         can_clone = FALSE;
5812
5813         start_pos = check_pos[RND(8)];
5814         check_order = (RND(2) ? -1 : +1);
5815
5816         for (i = 0; i < 8; i++)
5817         {
5818           int pos_raw = start_pos + i * check_order;
5819           int pos = (pos_raw + 8) % 8;
5820           int newx = x + check_xy[pos].dx;
5821           int newy = y + check_xy[pos].dy;
5822           int new_move_dir = check_xy[pos].dir;
5823
5824           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5825           {
5826             MovDir[x][y] = new_move_dir;
5827             MovDelay[x][y] = level.android_clone_time * 8 + 1;
5828
5829             can_clone = TRUE;
5830
5831             break;
5832           }
5833         }
5834       }
5835
5836       if (can_clone)            /* cloning and moving successful */
5837         return;
5838
5839       /* cannot clone -- try to move towards player */
5840
5841       start_pos = check_pos[MovDir[x][y] & 0x0f];
5842       check_order = (RND(2) ? -1 : +1);
5843
5844       for (i = 0; i < 3; i++)
5845       {
5846         /* first check start_pos, then previous/next or (next/previous) pos */
5847         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5848         int pos = (pos_raw + 8) % 8;
5849         int newx = x + check_xy[pos].dx;
5850         int newy = y + check_xy[pos].dy;
5851         int new_move_dir = check_xy[pos].dir;
5852
5853         if (IS_PLAYER(newx, newy))
5854           break;
5855
5856         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5857         {
5858           MovDir[x][y] = new_move_dir;
5859           MovDelay[x][y] = level.android_move_time * 8 + 1;
5860
5861           break;
5862         }
5863       }
5864     }
5865   }
5866   else if (move_pattern == MV_TURNING_LEFT ||
5867            move_pattern == MV_TURNING_RIGHT ||
5868            move_pattern == MV_TURNING_LEFT_RIGHT ||
5869            move_pattern == MV_TURNING_RIGHT_LEFT ||
5870            move_pattern == MV_TURNING_RANDOM ||
5871            move_pattern == MV_ALL_DIRECTIONS)
5872   {
5873     boolean can_turn_left =
5874       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5875     boolean can_turn_right =
5876       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5877
5878     if (element_info[element].move_stepsize == 0)       /* "not moving" */
5879       return;
5880
5881     if (move_pattern == MV_TURNING_LEFT)
5882       MovDir[x][y] = left_dir;
5883     else if (move_pattern == MV_TURNING_RIGHT)
5884       MovDir[x][y] = right_dir;
5885     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5886       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5887     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5888       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5889     else if (move_pattern == MV_TURNING_RANDOM)
5890       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5891                       can_turn_right && !can_turn_left ? right_dir :
5892                       RND(2) ? left_dir : right_dir);
5893     else if (can_turn_left && can_turn_right)
5894       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5895     else if (can_turn_left)
5896       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5897     else if (can_turn_right)
5898       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5899     else
5900       MovDir[x][y] = back_dir;
5901
5902     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5903   }
5904   else if (move_pattern == MV_HORIZONTAL ||
5905            move_pattern == MV_VERTICAL)
5906   {
5907     if (move_pattern & old_move_dir)
5908       MovDir[x][y] = back_dir;
5909     else if (move_pattern == MV_HORIZONTAL)
5910       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5911     else if (move_pattern == MV_VERTICAL)
5912       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5913
5914     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5915   }
5916   else if (move_pattern & MV_ANY_DIRECTION)
5917   {
5918     MovDir[x][y] = move_pattern;
5919     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5920   }
5921   else if (move_pattern & MV_WIND_DIRECTION)
5922   {
5923     MovDir[x][y] = game.wind_direction;
5924     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5925   }
5926   else if (move_pattern == MV_ALONG_LEFT_SIDE)
5927   {
5928     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5929       MovDir[x][y] = left_dir;
5930     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5931       MovDir[x][y] = right_dir;
5932
5933     if (MovDir[x][y] != old_move_dir)
5934       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5935   }
5936   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5937   {
5938     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5939       MovDir[x][y] = right_dir;
5940     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5941       MovDir[x][y] = left_dir;
5942
5943     if (MovDir[x][y] != old_move_dir)
5944       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5945   }
5946   else if (move_pattern == MV_TOWARDS_PLAYER ||
5947            move_pattern == MV_AWAY_FROM_PLAYER)
5948   {
5949     int attr_x = -1, attr_y = -1;
5950     int newx, newy;
5951     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5952
5953     if (AllPlayersGone)
5954     {
5955       attr_x = ExitX;
5956       attr_y = ExitY;
5957     }
5958     else
5959     {
5960       int i;
5961
5962       for (i = 0; i < MAX_PLAYERS; i++)
5963       {
5964         struct PlayerInfo *player = &stored_player[i];
5965         int jx = player->jx, jy = player->jy;
5966
5967         if (!player->active)
5968           continue;
5969
5970         if (attr_x == -1 ||
5971             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5972         {
5973           attr_x = jx;
5974           attr_y = jy;
5975         }
5976       }
5977     }
5978
5979     MovDir[x][y] = MV_NONE;
5980     if (attr_x < x)
5981       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5982     else if (attr_x > x)
5983       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5984     if (attr_y < y)
5985       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5986     else if (attr_y > y)
5987       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5988
5989     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5990
5991     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5992     {
5993       boolean first_horiz = RND(2);
5994       int new_move_dir = MovDir[x][y];
5995
5996       if (element_info[element].move_stepsize == 0)     /* "not moving" */
5997       {
5998         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5999         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6000
6001         return;
6002       }
6003
6004       MovDir[x][y] =
6005         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6006       Moving2Blocked(x, y, &newx, &newy);
6007
6008       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6009         return;
6010
6011       MovDir[x][y] =
6012         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6013       Moving2Blocked(x, y, &newx, &newy);
6014
6015       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6016         return;
6017
6018       MovDir[x][y] = old_move_dir;
6019     }
6020   }
6021   else if (move_pattern == MV_WHEN_PUSHED ||
6022            move_pattern == MV_WHEN_DROPPED)
6023   {
6024     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6025       MovDir[x][y] = MV_NONE;
6026
6027     MovDelay[x][y] = 0;
6028   }
6029   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6030   {
6031     static int test_xy[7][2] =
6032     {
6033       { 0, -1 },
6034       { -1, 0 },
6035       { +1, 0 },
6036       { 0, +1 },
6037       { 0, -1 },
6038       { -1, 0 },
6039       { +1, 0 },
6040     };
6041     static int test_dir[7] =
6042     {
6043       MV_UP,
6044       MV_LEFT,
6045       MV_RIGHT,
6046       MV_DOWN,
6047       MV_UP,
6048       MV_LEFT,
6049       MV_RIGHT,
6050     };
6051     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6052     int move_preference = -1000000;     /* start with very low preference */
6053     int new_move_dir = MV_NONE;
6054     int start_test = RND(4);
6055     int i;
6056
6057     for (i = 0; i < NUM_DIRECTIONS; i++)
6058     {
6059       int move_dir = test_dir[start_test + i];
6060       int move_dir_preference;
6061
6062       xx = x + test_xy[start_test + i][0];
6063       yy = y + test_xy[start_test + i][1];
6064
6065       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6066           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6067       {
6068         new_move_dir = move_dir;
6069
6070         break;
6071       }
6072
6073       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6074         continue;
6075
6076       move_dir_preference = -1 * RunnerVisit[xx][yy];
6077       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6078         move_dir_preference = PlayerVisit[xx][yy];
6079
6080       if (move_dir_preference > move_preference)
6081       {
6082         /* prefer field that has not been visited for the longest time */
6083         move_preference = move_dir_preference;
6084         new_move_dir = move_dir;
6085       }
6086       else if (move_dir_preference == move_preference &&
6087                move_dir == old_move_dir)
6088       {
6089         /* prefer last direction when all directions are preferred equally */
6090         move_preference = move_dir_preference;
6091         new_move_dir = move_dir;
6092       }
6093     }
6094
6095     MovDir[x][y] = new_move_dir;
6096     if (old_move_dir != new_move_dir)
6097       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6098   }
6099 }
6100
6101 static void TurnRound(int x, int y)
6102 {
6103   int direction = MovDir[x][y];
6104 #if 0
6105   int element, graphic;
6106 #endif
6107
6108   TurnRoundExt(x, y);
6109
6110   GfxDir[x][y] = MovDir[x][y];
6111
6112   if (direction != MovDir[x][y])
6113     GfxFrame[x][y] = 0;
6114
6115   if (MovDelay[x][y])
6116     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6117
6118 #if 1
6119   ResetGfxFrame(x, y, FALSE);
6120 #else
6121   element = Feld[x][y];
6122   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6123
6124   if (graphic_info[graphic].anim_global_sync)
6125     GfxFrame[x][y] = FrameCounter;
6126   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
6127     GfxFrame[x][y] = CustomValue[x][y];
6128   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
6129     GfxFrame[x][y] = element_info[element].collect_score;
6130   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
6131     GfxFrame[x][y] = ChangeDelay[x][y];
6132 #endif
6133 }
6134
6135 static boolean JustBeingPushed(int x, int y)
6136 {
6137   int i;
6138
6139   for (i = 0; i < MAX_PLAYERS; i++)
6140   {
6141     struct PlayerInfo *player = &stored_player[i];
6142
6143     if (player->active && player->is_pushing && player->MovPos)
6144     {
6145       int next_jx = player->jx + (player->jx - player->last_jx);
6146       int next_jy = player->jy + (player->jy - player->last_jy);
6147
6148       if (x == next_jx && y == next_jy)
6149         return TRUE;
6150     }
6151   }
6152
6153   return FALSE;
6154 }
6155
6156 void StartMoving(int x, int y)
6157 {
6158   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6159   int element = Feld[x][y];
6160
6161   if (Stop[x][y])
6162     return;
6163
6164   if (MovDelay[x][y] == 0)
6165     GfxAction[x][y] = ACTION_DEFAULT;
6166
6167   if (CAN_FALL(element) && y < lev_fieldy - 1)
6168   {
6169     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
6170         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
6171       if (JustBeingPushed(x, y))
6172         return;
6173
6174     if (element == EL_QUICKSAND_FULL)
6175     {
6176       if (IS_FREE(x, y + 1))
6177       {
6178         InitMovingField(x, y, MV_DOWN);
6179         started_moving = TRUE;
6180
6181         Feld[x][y] = EL_QUICKSAND_EMPTYING;
6182 #if USE_QUICKSAND_BD_ROCK_BUGFIX
6183         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
6184           Store[x][y] = EL_ROCK;
6185 #else
6186         Store[x][y] = EL_ROCK;
6187 #endif
6188
6189         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
6190       }
6191       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6192       {
6193         if (!MovDelay[x][y])
6194           MovDelay[x][y] = TILEY + 1;
6195
6196         if (MovDelay[x][y])
6197         {
6198           MovDelay[x][y]--;
6199           if (MovDelay[x][y])
6200             return;
6201         }
6202
6203         Feld[x][y] = EL_QUICKSAND_EMPTY;
6204         Feld[x][y + 1] = EL_QUICKSAND_FULL;
6205         Store[x][y + 1] = Store[x][y];
6206         Store[x][y] = 0;
6207
6208         PlayLevelSoundAction(x, y, ACTION_FILLING);
6209       }
6210     }
6211     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
6212              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
6213     {
6214       InitMovingField(x, y, MV_DOWN);
6215       started_moving = TRUE;
6216
6217       Feld[x][y] = EL_QUICKSAND_FILLING;
6218       Store[x][y] = element;
6219
6220       PlayLevelSoundAction(x, y, ACTION_FILLING);
6221     }
6222     else if (element == EL_MAGIC_WALL_FULL)
6223     {
6224       if (IS_FREE(x, y + 1))
6225       {
6226         InitMovingField(x, y, MV_DOWN);
6227         started_moving = TRUE;
6228
6229         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
6230         Store[x][y] = EL_CHANGED(Store[x][y]);
6231       }
6232       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6233       {
6234         if (!MovDelay[x][y])
6235           MovDelay[x][y] = TILEY/4 + 1;
6236
6237         if (MovDelay[x][y])
6238         {
6239           MovDelay[x][y]--;
6240           if (MovDelay[x][y])
6241             return;
6242         }
6243
6244         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
6245         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
6246         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
6247         Store[x][y] = 0;
6248       }
6249     }
6250     else if (element == EL_BD_MAGIC_WALL_FULL)
6251     {
6252       if (IS_FREE(x, y + 1))
6253       {
6254         InitMovingField(x, y, MV_DOWN);
6255         started_moving = TRUE;
6256
6257         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
6258         Store[x][y] = EL_CHANGED2(Store[x][y]);
6259       }
6260       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6261       {
6262         if (!MovDelay[x][y])
6263           MovDelay[x][y] = TILEY/4 + 1;
6264
6265         if (MovDelay[x][y])
6266         {
6267           MovDelay[x][y]--;
6268           if (MovDelay[x][y])
6269             return;
6270         }
6271
6272         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
6273         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
6274         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
6275         Store[x][y] = 0;
6276       }
6277     }
6278     else if (CAN_PASS_MAGIC_WALL(element) &&
6279              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6280               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
6281     {
6282       InitMovingField(x, y, MV_DOWN);
6283       started_moving = TRUE;
6284
6285       Feld[x][y] =
6286         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
6287          EL_BD_MAGIC_WALL_FILLING);
6288       Store[x][y] = element;
6289     }
6290     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
6291     {
6292       SplashAcid(x, y + 1);
6293
6294       InitMovingField(x, y, MV_DOWN);
6295       started_moving = TRUE;
6296
6297       Store[x][y] = EL_ACID;
6298     }
6299     else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6300               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
6301
6302              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
6303               CAN_FALL(element) && WasJustFalling[x][y] &&
6304               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
6305
6306              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
6307               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
6308               (Feld[x][y + 1] == EL_BLOCKED)))
6309     {
6310       /* this is needed for a special case not covered by calling "Impact()"
6311          from "ContinueMoving()": if an element moves to a tile directly below
6312          another element which was just falling on that tile (which was empty
6313          in the previous frame), the falling element above would just stop
6314          instead of smashing the element below (in previous version, the above
6315          element was just checked for "moving" instead of "falling", resulting
6316          in incorrect smashes caused by horizontal movement of the above
6317          element; also, the case of the player being the element to smash was
6318          simply not covered here... :-/ ) */
6319
6320       CheckCollision[x][y] = 0;
6321
6322       Impact(x, y);
6323     }
6324     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
6325     {
6326       if (MovDir[x][y] == MV_NONE)
6327       {
6328         InitMovingField(x, y, MV_DOWN);
6329         started_moving = TRUE;
6330       }
6331     }
6332     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
6333     {
6334       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
6335         MovDir[x][y] = MV_DOWN;
6336
6337       InitMovingField(x, y, MV_DOWN);
6338       started_moving = TRUE;
6339     }
6340     else if (element == EL_AMOEBA_DROP)
6341     {
6342       Feld[x][y] = EL_AMOEBA_GROWING;
6343       Store[x][y] = EL_AMOEBA_WET;
6344     }
6345     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
6346               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
6347              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
6348              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
6349     {
6350       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
6351                                 (IS_FREE(x - 1, y + 1) ||
6352                                  Feld[x - 1][y + 1] == EL_ACID));
6353       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
6354                                 (IS_FREE(x + 1, y + 1) ||
6355                                  Feld[x + 1][y + 1] == EL_ACID));
6356       boolean can_fall_any  = (can_fall_left || can_fall_right);
6357       boolean can_fall_both = (can_fall_left && can_fall_right);
6358       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
6359
6360 #if USE_NEW_ALL_SLIPPERY
6361       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
6362       {
6363         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6364           can_fall_right = FALSE;
6365         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6366           can_fall_left = FALSE;
6367         else if (slippery_type == SLIPPERY_ONLY_LEFT)
6368           can_fall_right = FALSE;
6369         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6370           can_fall_left = FALSE;
6371
6372         can_fall_any  = (can_fall_left || can_fall_right);
6373         can_fall_both = FALSE;
6374       }
6375 #else
6376       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
6377       {
6378         if (slippery_type == SLIPPERY_ONLY_LEFT)
6379           can_fall_right = FALSE;
6380         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
6381           can_fall_left = FALSE;
6382         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
6383           can_fall_right = FALSE;
6384         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
6385           can_fall_left = FALSE;
6386
6387         can_fall_any  = (can_fall_left || can_fall_right);
6388         can_fall_both = (can_fall_left && can_fall_right);
6389       }
6390 #endif
6391
6392 #if USE_NEW_ALL_SLIPPERY
6393 #else
6394 #if USE_NEW_SP_SLIPPERY
6395       /* !!! better use the same properties as for custom elements here !!! */
6396       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
6397                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
6398       {
6399         can_fall_right = FALSE;         /* slip down on left side */
6400         can_fall_both = FALSE;
6401       }
6402 #endif
6403 #endif
6404
6405 #if USE_NEW_ALL_SLIPPERY
6406       if (can_fall_both)
6407       {
6408         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6409           can_fall_right = FALSE;       /* slip down on left side */
6410         else
6411           can_fall_left = !(can_fall_right = RND(2));
6412
6413         can_fall_both = FALSE;
6414       }
6415 #else
6416       if (can_fall_both)
6417       {
6418         if (game.emulation == EMU_BOULDERDASH ||
6419             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6420           can_fall_right = FALSE;       /* slip down on left side */
6421         else
6422           can_fall_left = !(can_fall_right = RND(2));
6423
6424         can_fall_both = FALSE;
6425       }
6426 #endif
6427
6428       if (can_fall_any)
6429       {
6430         /* if not determined otherwise, prefer left side for slipping down */
6431         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6432         started_moving = TRUE;
6433       }
6434     }
6435 #if 0
6436     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6437 #else
6438     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6439 #endif
6440     {
6441       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6442       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6443       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6444       int belt_dir = game.belt_dir[belt_nr];
6445
6446       if ((belt_dir == MV_LEFT  && left_is_free) ||
6447           (belt_dir == MV_RIGHT && right_is_free))
6448       {
6449         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6450
6451         InitMovingField(x, y, belt_dir);
6452         started_moving = TRUE;
6453
6454         Pushed[x][y] = TRUE;
6455         Pushed[nextx][y] = TRUE;
6456
6457         GfxAction[x][y] = ACTION_DEFAULT;
6458       }
6459       else
6460       {
6461         MovDir[x][y] = 0;       /* if element was moving, stop it */
6462       }
6463     }
6464   }
6465
6466   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6467 #if 0
6468   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6469 #else
6470   if (CAN_MOVE(element) && !started_moving)
6471 #endif
6472   {
6473     int move_pattern = element_info[element].move_pattern;
6474     int newx, newy;
6475
6476 #if 0
6477 #if DEBUG
6478     if (MovDir[x][y] == MV_NONE)
6479     {
6480       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6481              x, y, element, element_info[element].token_name);
6482       printf("StartMoving(): This should never happen!\n");
6483     }
6484 #endif
6485 #endif
6486
6487     Moving2Blocked(x, y, &newx, &newy);
6488
6489     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6490       return;
6491
6492     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6493         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6494     {
6495       WasJustMoving[x][y] = 0;
6496       CheckCollision[x][y] = 0;
6497
6498       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6499
6500       if (Feld[x][y] != element)        /* element has changed */
6501         return;
6502     }
6503
6504     if (!MovDelay[x][y])        /* start new movement phase */
6505     {
6506       /* all objects that can change their move direction after each step
6507          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6508
6509       if (element != EL_YAMYAM &&
6510           element != EL_DARK_YAMYAM &&
6511           element != EL_PACMAN &&
6512           !(move_pattern & MV_ANY_DIRECTION) &&
6513           move_pattern != MV_TURNING_LEFT &&
6514           move_pattern != MV_TURNING_RIGHT &&
6515           move_pattern != MV_TURNING_LEFT_RIGHT &&
6516           move_pattern != MV_TURNING_RIGHT_LEFT &&
6517           move_pattern != MV_TURNING_RANDOM)
6518       {
6519         TurnRound(x, y);
6520
6521         if (MovDelay[x][y] && (element == EL_BUG ||
6522                                element == EL_SPACESHIP ||
6523                                element == EL_SP_SNIKSNAK ||
6524                                element == EL_SP_ELECTRON ||
6525                                element == EL_MOLE))
6526           DrawLevelField(x, y);
6527       }
6528     }
6529
6530     if (MovDelay[x][y])         /* wait some time before next movement */
6531     {
6532       MovDelay[x][y]--;
6533
6534       if (element == EL_ROBOT ||
6535           element == EL_YAMYAM ||
6536           element == EL_DARK_YAMYAM)
6537       {
6538         DrawLevelElementAnimationIfNeeded(x, y, element);
6539         PlayLevelSoundAction(x, y, ACTION_WAITING);
6540       }
6541       else if (element == EL_SP_ELECTRON)
6542         DrawLevelElementAnimationIfNeeded(x, y, element);
6543       else if (element == EL_DRAGON)
6544       {
6545         int i;
6546         int dir = MovDir[x][y];
6547         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6548         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6549         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6550                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6551                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6552                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6553         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6554
6555         GfxAction[x][y] = ACTION_ATTACKING;
6556
6557         if (IS_PLAYER(x, y))
6558           DrawPlayerField(x, y);
6559         else
6560           DrawLevelField(x, y);
6561
6562         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6563
6564         for (i = 1; i <= 3; i++)
6565         {
6566           int xx = x + i * dx;
6567           int yy = y + i * dy;
6568           int sx = SCREENX(xx);
6569           int sy = SCREENY(yy);
6570           int flame_graphic = graphic + (i - 1);
6571
6572           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6573             break;
6574
6575           if (MovDelay[x][y])
6576           {
6577             int flamed = MovingOrBlocked2Element(xx, yy);
6578
6579             /* !!! */
6580 #if 0
6581             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6582               Bang(xx, yy);
6583             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6584               RemoveMovingField(xx, yy);
6585             else
6586               RemoveField(xx, yy);
6587 #else
6588             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6589               Bang(xx, yy);
6590             else
6591               RemoveMovingField(xx, yy);
6592 #endif
6593
6594             ChangeDelay[xx][yy] = 0;
6595
6596             Feld[xx][yy] = EL_FLAMES;
6597
6598             if (IN_SCR_FIELD(sx, sy))
6599             {
6600               DrawLevelFieldCrumbledSand(xx, yy);
6601               DrawGraphic(sx, sy, flame_graphic, frame);
6602             }
6603           }
6604           else
6605           {
6606             if (Feld[xx][yy] == EL_FLAMES)
6607               Feld[xx][yy] = EL_EMPTY;
6608             DrawLevelField(xx, yy);
6609           }
6610         }
6611       }
6612
6613       if (MovDelay[x][y])       /* element still has to wait some time */
6614       {
6615         PlayLevelSoundAction(x, y, ACTION_WAITING);
6616
6617         return;
6618       }
6619     }
6620
6621     /* now make next step */
6622
6623     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6624
6625     if (DONT_COLLIDE_WITH(element) &&
6626         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6627         !PLAYER_ENEMY_PROTECTED(newx, newy))
6628     {
6629       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6630
6631       return;
6632     }
6633
6634     else if (CAN_MOVE_INTO_ACID(element) &&
6635              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6636              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6637              (MovDir[x][y] == MV_DOWN ||
6638               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6639     {
6640       SplashAcid(newx, newy);
6641       Store[x][y] = EL_ACID;
6642     }
6643     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6644     {
6645       if (Feld[newx][newy] == EL_EXIT_OPEN)
6646       {
6647         RemoveField(x, y);
6648         DrawLevelField(x, y);
6649
6650         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6651         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6652           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6653
6654         local_player->friends_still_needed--;
6655         if (!local_player->friends_still_needed &&
6656             !local_player->GameOver && AllPlayersGone)
6657           local_player->LevelSolved = local_player->GameOver = TRUE;
6658
6659         return;
6660       }
6661       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6662       {
6663         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6664           DrawLevelField(newx, newy);
6665         else
6666           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6667       }
6668       else if (!IS_FREE(newx, newy))
6669       {
6670         GfxAction[x][y] = ACTION_WAITING;
6671
6672         if (IS_PLAYER(x, y))
6673           DrawPlayerField(x, y);
6674         else
6675           DrawLevelField(x, y);
6676
6677         return;
6678       }
6679     }
6680     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6681     {
6682       if (IS_FOOD_PIG(Feld[newx][newy]))
6683       {
6684         if (IS_MOVING(newx, newy))
6685           RemoveMovingField(newx, newy);
6686         else
6687         {
6688           Feld[newx][newy] = EL_EMPTY;
6689           DrawLevelField(newx, newy);
6690         }
6691
6692         PlayLevelSound(x, y, SND_PIG_DIGGING);
6693       }
6694       else if (!IS_FREE(newx, newy))
6695       {
6696         if (IS_PLAYER(x, y))
6697           DrawPlayerField(x, y);
6698         else
6699           DrawLevelField(x, y);
6700
6701         return;
6702       }
6703     }
6704     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6705     {
6706       if (Store[x][y] != EL_EMPTY)
6707       {
6708         boolean can_clone = FALSE;
6709         int xx, yy;
6710
6711         /* check if element to clone is still there */
6712         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6713         {
6714           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6715           {
6716             can_clone = TRUE;
6717
6718             break;
6719           }
6720         }
6721
6722         /* cannot clone or target field not free anymore -- do not clone */
6723         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6724           Store[x][y] = EL_EMPTY;
6725       }
6726
6727       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6728       {
6729         if (IS_MV_DIAGONAL(MovDir[x][y]))
6730         {
6731           int diagonal_move_dir = MovDir[x][y];
6732           int stored = Store[x][y];
6733           int change_delay = 8;
6734           int graphic;
6735
6736           /* android is moving diagonally */
6737
6738           CreateField(x, y, EL_DIAGONAL_SHRINKING);
6739
6740           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6741           GfxElement[x][y] = EL_EMC_ANDROID;
6742           GfxAction[x][y] = ACTION_SHRINKING;
6743           GfxDir[x][y] = diagonal_move_dir;
6744           ChangeDelay[x][y] = change_delay;
6745
6746           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6747                                    GfxDir[x][y]);
6748
6749           DrawLevelGraphicAnimation(x, y, graphic);
6750           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6751
6752           if (Feld[newx][newy] == EL_ACID)
6753           {
6754             SplashAcid(newx, newy);
6755
6756             return;
6757           }
6758
6759           CreateField(newx, newy, EL_DIAGONAL_GROWING);
6760
6761           Store[newx][newy] = EL_EMC_ANDROID;
6762           GfxElement[newx][newy] = EL_EMC_ANDROID;
6763           GfxAction[newx][newy] = ACTION_GROWING;
6764           GfxDir[newx][newy] = diagonal_move_dir;
6765           ChangeDelay[newx][newy] = change_delay;
6766
6767           graphic = el_act_dir2img(GfxElement[newx][newy],
6768                                    GfxAction[newx][newy], GfxDir[newx][newy]);
6769
6770           DrawLevelGraphicAnimation(newx, newy, graphic);
6771           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6772
6773           return;
6774         }
6775         else
6776         {
6777           Feld[newx][newy] = EL_EMPTY;
6778           DrawLevelField(newx, newy);
6779
6780           PlayLevelSoundAction(x, y, ACTION_DIGGING);
6781         }
6782       }
6783       else if (!IS_FREE(newx, newy))
6784       {
6785 #if 0
6786         if (IS_PLAYER(x, y))
6787           DrawPlayerField(x, y);
6788         else
6789           DrawLevelField(x, y);
6790 #endif
6791
6792         return;
6793       }
6794     }
6795     else if (IS_CUSTOM_ELEMENT(element) &&
6796              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6797     {
6798       int new_element = Feld[newx][newy];
6799
6800       if (!IS_FREE(newx, newy))
6801       {
6802         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6803                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6804                       ACTION_BREAKING);
6805
6806         /* no element can dig solid indestructible elements */
6807         if (IS_INDESTRUCTIBLE(new_element) &&
6808             !IS_DIGGABLE(new_element) &&
6809             !IS_COLLECTIBLE(new_element))
6810           return;
6811
6812         if (AmoebaNr[newx][newy] &&
6813             (new_element == EL_AMOEBA_FULL ||
6814              new_element == EL_BD_AMOEBA ||
6815              new_element == EL_AMOEBA_GROWING))
6816         {
6817           AmoebaCnt[AmoebaNr[newx][newy]]--;
6818           AmoebaCnt2[AmoebaNr[newx][newy]]--;
6819         }
6820
6821         if (IS_MOVING(newx, newy))
6822           RemoveMovingField(newx, newy);
6823         else
6824         {
6825           RemoveField(newx, newy);
6826           DrawLevelField(newx, newy);
6827         }
6828
6829         /* if digged element was about to explode, prevent the explosion */
6830         ExplodeField[newx][newy] = EX_TYPE_NONE;
6831
6832         PlayLevelSoundAction(x, y, action);
6833       }
6834
6835       Store[newx][newy] = EL_EMPTY;
6836 #if 1
6837       /* this makes it possible to leave the removed element again */
6838       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6839         Store[newx][newy] = new_element;
6840 #else
6841       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6842       {
6843         int move_leave_element = element_info[element].move_leave_element;
6844
6845         /* this makes it possible to leave the removed element again */
6846         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6847                              new_element : move_leave_element);
6848       }
6849 #endif
6850
6851       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6852       {
6853         RunnerVisit[x][y] = FrameCounter;
6854         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6855       }
6856     }
6857     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6858     {
6859       if (!IS_FREE(newx, newy))
6860       {
6861         if (IS_PLAYER(x, y))
6862           DrawPlayerField(x, y);
6863         else
6864           DrawLevelField(x, y);
6865
6866         return;
6867       }
6868       else
6869       {
6870         boolean wanna_flame = !RND(10);
6871         int dx = newx - x, dy = newy - y;
6872         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6873         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6874         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6875                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6876         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6877                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6878
6879         if ((wanna_flame ||
6880              IS_CLASSIC_ENEMY(element1) ||
6881              IS_CLASSIC_ENEMY(element2)) &&
6882             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6883             element1 != EL_FLAMES && element2 != EL_FLAMES)
6884         {
6885           ResetGfxAnimation(x, y);
6886           GfxAction[x][y] = ACTION_ATTACKING;
6887
6888           if (IS_PLAYER(x, y))
6889             DrawPlayerField(x, y);
6890           else
6891             DrawLevelField(x, y);
6892
6893           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6894
6895           MovDelay[x][y] = 50;
6896
6897           /* !!! */
6898 #if 0
6899           RemoveField(newx, newy);
6900 #endif
6901           Feld[newx][newy] = EL_FLAMES;
6902           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6903           {
6904 #if 0
6905             RemoveField(newx1, newy1);
6906 #endif
6907             Feld[newx1][newy1] = EL_FLAMES;
6908           }
6909           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6910           {
6911 #if 0
6912             RemoveField(newx2, newy2);
6913 #endif
6914             Feld[newx2][newy2] = EL_FLAMES;
6915           }
6916
6917           return;
6918         }
6919       }
6920     }
6921     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6922              Feld[newx][newy] == EL_DIAMOND)
6923     {
6924       if (IS_MOVING(newx, newy))
6925         RemoveMovingField(newx, newy);
6926       else
6927       {
6928         Feld[newx][newy] = EL_EMPTY;
6929         DrawLevelField(newx, newy);
6930       }
6931
6932       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6933     }
6934     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6935              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6936     {
6937       if (AmoebaNr[newx][newy])
6938       {
6939         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6940         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6941             Feld[newx][newy] == EL_BD_AMOEBA)
6942           AmoebaCnt[AmoebaNr[newx][newy]]--;
6943       }
6944
6945 #if 0
6946       /* !!! test !!! */
6947       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6948       {
6949         RemoveMovingField(newx, newy);
6950       }
6951 #else
6952       if (IS_MOVING(newx, newy))
6953       {
6954         RemoveMovingField(newx, newy);
6955       }
6956 #endif
6957       else
6958       {
6959         Feld[newx][newy] = EL_EMPTY;
6960         DrawLevelField(newx, newy);
6961       }
6962
6963       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6964     }
6965     else if ((element == EL_PACMAN || element == EL_MOLE)
6966              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6967     {
6968       if (AmoebaNr[newx][newy])
6969       {
6970         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6971         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6972             Feld[newx][newy] == EL_BD_AMOEBA)
6973           AmoebaCnt[AmoebaNr[newx][newy]]--;
6974       }
6975
6976       if (element == EL_MOLE)
6977       {
6978         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6979         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6980
6981         ResetGfxAnimation(x, y);
6982         GfxAction[x][y] = ACTION_DIGGING;
6983         DrawLevelField(x, y);
6984
6985         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6986
6987         return;                         /* wait for shrinking amoeba */
6988       }
6989       else      /* element == EL_PACMAN */
6990       {
6991         Feld[newx][newy] = EL_EMPTY;
6992         DrawLevelField(newx, newy);
6993         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6994       }
6995     }
6996     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6997              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6998               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6999     {
7000       /* wait for shrinking amoeba to completely disappear */
7001       return;
7002     }
7003     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7004     {
7005       /* object was running against a wall */
7006
7007       TurnRound(x, y);
7008
7009 #if 0
7010       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7011       if (move_pattern & MV_ANY_DIRECTION &&
7012           move_pattern == MovDir[x][y])
7013       {
7014         int blocking_element =
7015           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7016
7017         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7018                                  MovDir[x][y]);
7019
7020         element = Feld[x][y];   /* element might have changed */
7021       }
7022 #endif
7023
7024       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7025         DrawLevelElementAnimation(x, y, element);
7026
7027       if (DONT_TOUCH(element))
7028         TestIfBadThingTouchesPlayer(x, y);
7029
7030       return;
7031     }
7032
7033     InitMovingField(x, y, MovDir[x][y]);
7034
7035     PlayLevelSoundAction(x, y, ACTION_MOVING);
7036   }
7037
7038   if (MovDir[x][y])
7039     ContinueMoving(x, y);
7040 }
7041
7042 void ContinueMoving(int x, int y)
7043 {
7044   int element = Feld[x][y];
7045   struct ElementInfo *ei = &element_info[element];
7046   int direction = MovDir[x][y];
7047   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7048   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7049   int newx = x + dx, newy = y + dy;
7050   int stored = Store[x][y];
7051   int stored_new = Store[newx][newy];
7052   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7053   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7054   boolean last_line = (newy == lev_fieldy - 1);
7055
7056   MovPos[x][y] += getElementMoveStepsize(x, y);
7057
7058   if (pushed_by_player) /* special case: moving object pushed by player */
7059     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7060
7061   if (ABS(MovPos[x][y]) < TILEX)
7062   {
7063     DrawLevelField(x, y);
7064
7065     return;     /* element is still moving */
7066   }
7067
7068   /* element reached destination field */
7069
7070   Feld[x][y] = EL_EMPTY;
7071   Feld[newx][newy] = element;
7072   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7073
7074   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7075   {
7076     element = Feld[newx][newy] = EL_ACID;
7077   }
7078   else if (element == EL_MOLE)
7079   {
7080     Feld[x][y] = EL_SAND;
7081
7082     DrawLevelFieldCrumbledSandNeighbours(x, y);
7083   }
7084   else if (element == EL_QUICKSAND_FILLING)
7085   {
7086     element = Feld[newx][newy] = get_next_element(element);
7087     Store[newx][newy] = Store[x][y];
7088   }
7089   else if (element == EL_QUICKSAND_EMPTYING)
7090   {
7091     Feld[x][y] = get_next_element(element);
7092     element = Feld[newx][newy] = Store[x][y];
7093   }
7094   else if (element == EL_MAGIC_WALL_FILLING)
7095   {
7096     element = Feld[newx][newy] = get_next_element(element);
7097     if (!game.magic_wall_active)
7098       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
7099     Store[newx][newy] = Store[x][y];
7100   }
7101   else if (element == EL_MAGIC_WALL_EMPTYING)
7102   {
7103     Feld[x][y] = get_next_element(element);
7104     if (!game.magic_wall_active)
7105       Feld[x][y] = EL_MAGIC_WALL_DEAD;
7106     element = Feld[newx][newy] = Store[x][y];
7107
7108 #if USE_NEW_CUSTOM_VALUE
7109     InitField(newx, newy, FALSE);
7110 #endif
7111   }
7112   else if (element == EL_BD_MAGIC_WALL_FILLING)
7113   {
7114     element = Feld[newx][newy] = get_next_element(element);
7115     if (!game.magic_wall_active)
7116       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
7117     Store[newx][newy] = Store[x][y];
7118   }
7119   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
7120   {
7121     Feld[x][y] = get_next_element(element);
7122     if (!game.magic_wall_active)
7123       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7124     element = Feld[newx][newy] = Store[x][y];
7125
7126 #if USE_NEW_CUSTOM_VALUE
7127     InitField(newx, newy, FALSE);
7128 #endif
7129   }
7130   else if (element == EL_AMOEBA_DROPPING)
7131   {
7132     Feld[x][y] = get_next_element(element);
7133     element = Feld[newx][newy] = Store[x][y];
7134   }
7135   else if (element == EL_SOKOBAN_OBJECT)
7136   {
7137     if (Back[x][y])
7138       Feld[x][y] = Back[x][y];
7139
7140     if (Back[newx][newy])
7141       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
7142
7143     Back[x][y] = Back[newx][newy] = 0;
7144   }
7145
7146   Store[x][y] = EL_EMPTY;
7147   MovPos[x][y] = 0;
7148   MovDir[x][y] = 0;
7149   MovDelay[x][y] = 0;
7150
7151   MovDelay[newx][newy] = 0;
7152
7153 #if 1
7154   if (CAN_CHANGE_OR_HAS_ACTION(element))
7155 #else
7156   if (CAN_CHANGE(element))
7157 #endif
7158   {
7159     /* copy element change control values to new field */
7160     ChangeDelay[newx][newy] = ChangeDelay[x][y];
7161     ChangePage[newx][newy]  = ChangePage[x][y];
7162     ChangeCount[newx][newy] = ChangeCount[x][y];
7163     ChangeEvent[newx][newy] = ChangeEvent[x][y];
7164
7165 #if 0
7166 #if USE_NEW_CUSTOM_VALUE
7167     CustomValue[newx][newy] = CustomValue[x][y];
7168 #endif
7169 #endif
7170   }
7171
7172 #if 1
7173 #if USE_NEW_CUSTOM_VALUE
7174     CustomValue[newx][newy] = CustomValue[x][y];
7175 #endif
7176 #endif
7177
7178   ChangeDelay[x][y] = 0;
7179   ChangePage[x][y] = -1;
7180   ChangeCount[x][y] = 0;
7181   ChangeEvent[x][y] = -1;
7182
7183 #if USE_NEW_CUSTOM_VALUE
7184   CustomValue[x][y] = 0;
7185 #endif
7186
7187   /* copy animation control values to new field */
7188   GfxFrame[newx][newy]  = GfxFrame[x][y];
7189   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
7190   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
7191   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
7192
7193   Pushed[x][y] = Pushed[newx][newy] = FALSE;
7194
7195   /* some elements can leave other elements behind after moving */
7196 #if 1
7197   if (ei->move_leave_element != EL_EMPTY &&
7198       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7199       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7200 #else
7201   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
7202       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
7203       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
7204 #endif
7205   {
7206     int move_leave_element = ei->move_leave_element;
7207
7208 #if 1
7209 #if 1
7210     /* this makes it possible to leave the removed element again */
7211     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7212       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
7213 #else
7214     /* this makes it possible to leave the removed element again */
7215     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
7216       move_leave_element = stored;
7217 #endif
7218 #else
7219     /* this makes it possible to leave the removed element again */
7220     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
7221         ei->move_leave_element == EL_TRIGGER_ELEMENT)
7222       move_leave_element = stored;
7223 #endif
7224
7225     Feld[x][y] = move_leave_element;
7226
7227     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7228       MovDir[x][y] = direction;
7229
7230     InitField(x, y, FALSE);
7231
7232     if (GFX_CRUMBLED(Feld[x][y]))
7233       DrawLevelFieldCrumbledSandNeighbours(x, y);
7234
7235     if (ELEM_IS_PLAYER(move_leave_element))
7236       RelocatePlayer(x, y, move_leave_element);
7237   }
7238
7239   /* do this after checking for left-behind element */
7240   ResetGfxAnimation(x, y);      /* reset animation values for old field */
7241
7242   if (!CAN_MOVE(element) ||
7243       (CAN_FALL(element) && direction == MV_DOWN &&
7244        (element == EL_SPRING ||
7245         element_info[element].move_pattern == MV_WHEN_PUSHED ||
7246         element_info[element].move_pattern == MV_WHEN_DROPPED)))
7247     GfxDir[x][y] = MovDir[newx][newy] = 0;
7248
7249   DrawLevelField(x, y);
7250   DrawLevelField(newx, newy);
7251
7252   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
7253
7254   /* prevent pushed element from moving on in pushed direction */
7255   if (pushed_by_player && CAN_MOVE(element) &&
7256       element_info[element].move_pattern & MV_ANY_DIRECTION &&
7257       !(element_info[element].move_pattern & direction))
7258     TurnRound(newx, newy);
7259
7260   /* prevent elements on conveyor belt from moving on in last direction */
7261   if (pushed_by_conveyor && CAN_FALL(element) &&
7262       direction & MV_HORIZONTAL)
7263     MovDir[newx][newy] = 0;
7264
7265   if (!pushed_by_player)
7266   {
7267     int nextx = newx + dx, nexty = newy + dy;
7268     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
7269
7270     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
7271
7272     if (CAN_FALL(element) && direction == MV_DOWN)
7273       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
7274
7275     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
7276       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
7277   }
7278
7279   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
7280   {
7281     TestIfBadThingTouchesPlayer(newx, newy);
7282     TestIfBadThingTouchesFriend(newx, newy);
7283
7284     if (!IS_CUSTOM_ELEMENT(element))
7285       TestIfBadThingTouchesOtherBadThing(newx, newy);
7286   }
7287   else if (element == EL_PENGUIN)
7288     TestIfFriendTouchesBadThing(newx, newy);
7289
7290   /* give the player one last chance (one more frame) to move away */
7291   if (CAN_FALL(element) && direction == MV_DOWN &&
7292       (last_line || (!IS_FREE(x, newy + 1) &&
7293                      (!IS_PLAYER(x, newy + 1) ||
7294                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
7295     Impact(x, newy);
7296
7297   if (pushed_by_player && !game.use_change_when_pushing_bug)
7298   {
7299     int push_side = MV_DIR_OPPOSITE(direction);
7300     struct PlayerInfo *player = PLAYERINFO(x, y);
7301
7302     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
7303                                player->index_bit, push_side);
7304     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
7305                                         player->index_bit, push_side);
7306   }
7307
7308   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
7309     MovDelay[newx][newy] = 1;
7310
7311   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
7312
7313   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
7314
7315 #if 0
7316   if (ChangePage[newx][newy] != -1)             /* delayed change */
7317   {
7318     int page = ChangePage[newx][newy];
7319     struct ElementChangeInfo *change = &ei->change_page[page];
7320
7321     ChangePage[newx][newy] = -1;
7322
7323     if (change->can_change)
7324     {
7325       if (ChangeElement(newx, newy, element, page))
7326       {
7327         if (change->post_change_function)
7328           change->post_change_function(newx, newy);
7329       }
7330     }
7331
7332     if (change->has_action)
7333       ExecuteCustomElementAction(newx, newy, element, page);
7334   }
7335 #endif
7336
7337   TestIfElementHitsCustomElement(newx, newy, direction);
7338   TestIfPlayerTouchesCustomElement(newx, newy);
7339   TestIfElementTouchesCustomElement(newx, newy);
7340
7341 #if 1
7342   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
7343       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
7344     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
7345                                       MV_DIR_OPPOSITE(direction));
7346 #endif
7347 }
7348
7349 int AmoebeNachbarNr(int ax, int ay)
7350 {
7351   int i;
7352   int element = Feld[ax][ay];
7353   int group_nr = 0;
7354   static int xy[4][2] =
7355   {
7356     { 0, -1 },
7357     { -1, 0 },
7358     { +1, 0 },
7359     { 0, +1 }
7360   };
7361
7362   for (i = 0; i < NUM_DIRECTIONS; i++)
7363   {
7364     int x = ax + xy[i][0];
7365     int y = ay + xy[i][1];
7366
7367     if (!IN_LEV_FIELD(x, y))
7368       continue;
7369
7370     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
7371       group_nr = AmoebaNr[x][y];
7372   }
7373
7374   return group_nr;
7375 }
7376
7377 void AmoebenVereinigen(int ax, int ay)
7378 {
7379   int i, x, y, xx, yy;
7380   int new_group_nr = AmoebaNr[ax][ay];
7381   static int xy[4][2] =
7382   {
7383     { 0, -1 },
7384     { -1, 0 },
7385     { +1, 0 },
7386     { 0, +1 }
7387   };
7388
7389   if (new_group_nr == 0)
7390     return;
7391
7392   for (i = 0; i < NUM_DIRECTIONS; i++)
7393   {
7394     x = ax + xy[i][0];
7395     y = ay + xy[i][1];
7396
7397     if (!IN_LEV_FIELD(x, y))
7398       continue;
7399
7400     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7401          Feld[x][y] == EL_BD_AMOEBA ||
7402          Feld[x][y] == EL_AMOEBA_DEAD) &&
7403         AmoebaNr[x][y] != new_group_nr)
7404     {
7405       int old_group_nr = AmoebaNr[x][y];
7406
7407       if (old_group_nr == 0)
7408         return;
7409
7410       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7411       AmoebaCnt[old_group_nr] = 0;
7412       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7413       AmoebaCnt2[old_group_nr] = 0;
7414
7415 #if 1
7416       SCAN_PLAYFIELD(xx, yy)
7417 #else
7418       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7419 #endif
7420       {
7421         if (AmoebaNr[xx][yy] == old_group_nr)
7422           AmoebaNr[xx][yy] = new_group_nr;
7423       }
7424     }
7425   }
7426 }
7427
7428 void AmoebeUmwandeln(int ax, int ay)
7429 {
7430   int i, x, y;
7431
7432   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7433   {
7434     int group_nr = AmoebaNr[ax][ay];
7435
7436 #ifdef DEBUG
7437     if (group_nr == 0)
7438     {
7439       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7440       printf("AmoebeUmwandeln(): This should never happen!\n");
7441       return;
7442     }
7443 #endif
7444
7445 #if 1
7446     SCAN_PLAYFIELD(x, y)
7447 #else
7448     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7449 #endif
7450     {
7451       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7452       {
7453         AmoebaNr[x][y] = 0;
7454         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7455       }
7456     }
7457
7458     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7459                             SND_AMOEBA_TURNING_TO_GEM :
7460                             SND_AMOEBA_TURNING_TO_ROCK));
7461     Bang(ax, ay);
7462   }
7463   else
7464   {
7465     static int xy[4][2] =
7466     {
7467       { 0, -1 },
7468       { -1, 0 },
7469       { +1, 0 },
7470       { 0, +1 }
7471     };
7472
7473     for (i = 0; i < NUM_DIRECTIONS; i++)
7474     {
7475       x = ax + xy[i][0];
7476       y = ay + xy[i][1];
7477
7478       if (!IN_LEV_FIELD(x, y))
7479         continue;
7480
7481       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7482       {
7483         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7484                               SND_AMOEBA_TURNING_TO_GEM :
7485                               SND_AMOEBA_TURNING_TO_ROCK));
7486         Bang(x, y);
7487       }
7488     }
7489   }
7490 }
7491
7492 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7493 {
7494   int x, y;
7495   int group_nr = AmoebaNr[ax][ay];
7496   boolean done = FALSE;
7497
7498 #ifdef DEBUG
7499   if (group_nr == 0)
7500   {
7501     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7502     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7503     return;
7504   }
7505 #endif
7506
7507 #if 1
7508   SCAN_PLAYFIELD(x, y)
7509 #else
7510   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7511 #endif
7512   {
7513     if (AmoebaNr[x][y] == group_nr &&
7514         (Feld[x][y] == EL_AMOEBA_DEAD ||
7515          Feld[x][y] == EL_BD_AMOEBA ||
7516          Feld[x][y] == EL_AMOEBA_GROWING))
7517     {
7518       AmoebaNr[x][y] = 0;
7519       Feld[x][y] = new_element;
7520       InitField(x, y, FALSE);
7521       DrawLevelField(x, y);
7522       done = TRUE;
7523     }
7524   }
7525
7526   if (done)
7527     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7528                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7529                             SND_BD_AMOEBA_TURNING_TO_GEM));
7530 }
7531
7532 void AmoebeWaechst(int x, int y)
7533 {
7534   static unsigned long sound_delay = 0;
7535   static unsigned long sound_delay_value = 0;
7536
7537   if (!MovDelay[x][y])          /* start new growing cycle */
7538   {
7539     MovDelay[x][y] = 7;
7540
7541     if (DelayReached(&sound_delay, sound_delay_value))
7542     {
7543       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7544       sound_delay_value = 30;
7545     }
7546   }
7547
7548   if (MovDelay[x][y])           /* wait some time before growing bigger */
7549   {
7550     MovDelay[x][y]--;
7551     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7552     {
7553       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7554                                            6 - MovDelay[x][y]);
7555
7556       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7557     }
7558
7559     if (!MovDelay[x][y])
7560     {
7561       Feld[x][y] = Store[x][y];
7562       Store[x][y] = 0;
7563       DrawLevelField(x, y);
7564     }
7565   }
7566 }
7567
7568 void AmoebaDisappearing(int x, int y)
7569 {
7570   static unsigned long sound_delay = 0;
7571   static unsigned long sound_delay_value = 0;
7572
7573   if (!MovDelay[x][y])          /* start new shrinking cycle */
7574   {
7575     MovDelay[x][y] = 7;
7576
7577     if (DelayReached(&sound_delay, sound_delay_value))
7578       sound_delay_value = 30;
7579   }
7580
7581   if (MovDelay[x][y])           /* wait some time before shrinking */
7582   {
7583     MovDelay[x][y]--;
7584     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7585     {
7586       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7587                                            6 - MovDelay[x][y]);
7588
7589       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7590     }
7591
7592     if (!MovDelay[x][y])
7593     {
7594       Feld[x][y] = EL_EMPTY;
7595       DrawLevelField(x, y);
7596
7597       /* don't let mole enter this field in this cycle;
7598          (give priority to objects falling to this field from above) */
7599       Stop[x][y] = TRUE;
7600     }
7601   }
7602 }
7603
7604 void AmoebeAbleger(int ax, int ay)
7605 {
7606   int i;
7607   int element = Feld[ax][ay];
7608   int graphic = el2img(element);
7609   int newax = ax, neway = ay;
7610   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7611   static int xy[4][2] =
7612   {
7613     { 0, -1 },
7614     { -1, 0 },
7615     { +1, 0 },
7616     { 0, +1 }
7617   };
7618
7619   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7620   {
7621     Feld[ax][ay] = EL_AMOEBA_DEAD;
7622     DrawLevelField(ax, ay);
7623     return;
7624   }
7625
7626   if (IS_ANIMATED(graphic))
7627     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7628
7629   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7630     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7631
7632   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7633   {
7634     MovDelay[ax][ay]--;
7635     if (MovDelay[ax][ay])
7636       return;
7637   }
7638
7639   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7640   {
7641     int start = RND(4);
7642     int x = ax + xy[start][0];
7643     int y = ay + xy[start][1];
7644
7645     if (!IN_LEV_FIELD(x, y))
7646       return;
7647
7648     if (IS_FREE(x, y) ||
7649         CAN_GROW_INTO(Feld[x][y]) ||
7650         Feld[x][y] == EL_QUICKSAND_EMPTY)
7651     {
7652       newax = x;
7653       neway = y;
7654     }
7655
7656     if (newax == ax && neway == ay)
7657       return;
7658   }
7659   else                          /* normal or "filled" (BD style) amoeba */
7660   {
7661     int start = RND(4);
7662     boolean waiting_for_player = FALSE;
7663
7664     for (i = 0; i < NUM_DIRECTIONS; i++)
7665     {
7666       int j = (start + i) % 4;
7667       int x = ax + xy[j][0];
7668       int y = ay + xy[j][1];
7669
7670       if (!IN_LEV_FIELD(x, y))
7671         continue;
7672
7673       if (IS_FREE(x, y) ||
7674           CAN_GROW_INTO(Feld[x][y]) ||
7675           Feld[x][y] == EL_QUICKSAND_EMPTY)
7676       {
7677         newax = x;
7678         neway = y;
7679         break;
7680       }
7681       else if (IS_PLAYER(x, y))
7682         waiting_for_player = TRUE;
7683     }
7684
7685     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7686     {
7687       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7688       {
7689         Feld[ax][ay] = EL_AMOEBA_DEAD;
7690         DrawLevelField(ax, ay);
7691         AmoebaCnt[AmoebaNr[ax][ay]]--;
7692
7693         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7694         {
7695           if (element == EL_AMOEBA_FULL)
7696             AmoebeUmwandeln(ax, ay);
7697           else if (element == EL_BD_AMOEBA)
7698             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7699         }
7700       }
7701       return;
7702     }
7703     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7704     {
7705       /* amoeba gets larger by growing in some direction */
7706
7707       int new_group_nr = AmoebaNr[ax][ay];
7708
7709 #ifdef DEBUG
7710   if (new_group_nr == 0)
7711   {
7712     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7713     printf("AmoebeAbleger(): This should never happen!\n");
7714     return;
7715   }
7716 #endif
7717
7718       AmoebaNr[newax][neway] = new_group_nr;
7719       AmoebaCnt[new_group_nr]++;
7720       AmoebaCnt2[new_group_nr]++;
7721
7722       /* if amoeba touches other amoeba(s) after growing, unify them */
7723       AmoebenVereinigen(newax, neway);
7724
7725       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7726       {
7727         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7728         return;
7729       }
7730     }
7731   }
7732
7733   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7734       (neway == lev_fieldy - 1 && newax != ax))
7735   {
7736     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7737     Store[newax][neway] = element;
7738   }
7739   else if (neway == ay || element == EL_EMC_DRIPPER)
7740   {
7741     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7742
7743     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7744   }
7745   else
7746   {
7747     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7748     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7749     Store[ax][ay] = EL_AMOEBA_DROP;
7750     ContinueMoving(ax, ay);
7751     return;
7752   }
7753
7754   DrawLevelField(newax, neway);
7755 }
7756
7757 void Life(int ax, int ay)
7758 {
7759   int x1, y1, x2, y2;
7760 #if 0
7761   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
7762 #endif
7763   int life_time = 40;
7764   int element = Feld[ax][ay];
7765   int graphic = el2img(element);
7766   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7767                          level.biomaze);
7768   boolean changed = FALSE;
7769
7770   if (IS_ANIMATED(graphic))
7771     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7772
7773   if (Stop[ax][ay])
7774     return;
7775
7776   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7777     MovDelay[ax][ay] = life_time;
7778
7779   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7780   {
7781     MovDelay[ax][ay]--;
7782     if (MovDelay[ax][ay])
7783       return;
7784   }
7785
7786   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7787   {
7788     int xx = ax+x1, yy = ay+y1;
7789     int nachbarn = 0;
7790
7791     if (!IN_LEV_FIELD(xx, yy))
7792       continue;
7793
7794     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7795     {
7796       int x = xx+x2, y = yy+y2;
7797
7798       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7799         continue;
7800
7801       if (((Feld[x][y] == element ||
7802             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7803            !Stop[x][y]) ||
7804           (IS_FREE(x, y) && Stop[x][y]))
7805         nachbarn++;
7806     }
7807
7808     if (xx == ax && yy == ay)           /* field in the middle */
7809     {
7810       if (nachbarn < life_parameter[0] ||
7811           nachbarn > life_parameter[1])
7812       {
7813         Feld[xx][yy] = EL_EMPTY;
7814         if (!Stop[xx][yy])
7815           DrawLevelField(xx, yy);
7816         Stop[xx][yy] = TRUE;
7817         changed = TRUE;
7818       }
7819     }
7820     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7821     {                                   /* free border field */
7822       if (nachbarn >= life_parameter[2] &&
7823           nachbarn <= life_parameter[3])
7824       {
7825         Feld[xx][yy] = element;
7826         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7827         if (!Stop[xx][yy])
7828           DrawLevelField(xx, yy);
7829         Stop[xx][yy] = TRUE;
7830         changed = TRUE;
7831       }
7832     }
7833   }
7834
7835   if (changed)
7836     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7837                    SND_GAME_OF_LIFE_GROWING);
7838 }
7839
7840 static void InitRobotWheel(int x, int y)
7841 {
7842   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7843 }
7844
7845 static void RunRobotWheel(int x, int y)
7846 {
7847   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7848 }
7849
7850 static void StopRobotWheel(int x, int y)
7851 {
7852   if (ZX == x && ZY == y)
7853     ZX = ZY = -1;
7854 }
7855
7856 static void InitTimegateWheel(int x, int y)
7857 {
7858   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7859 }
7860
7861 static void RunTimegateWheel(int x, int y)
7862 {
7863   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7864 }
7865
7866 static void InitMagicBallDelay(int x, int y)
7867 {
7868 #if 1
7869   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7870 #else
7871   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7872 #endif
7873 }
7874
7875 static void ActivateMagicBall(int bx, int by)
7876 {
7877   int x, y;
7878
7879   if (level.ball_random)
7880   {
7881     int pos_border = RND(8);    /* select one of the eight border elements */
7882     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7883     int xx = pos_content % 3;
7884     int yy = pos_content / 3;
7885
7886     x = bx - 1 + xx;
7887     y = by - 1 + yy;
7888
7889     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7890       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7891   }
7892   else
7893   {
7894     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7895     {
7896       int xx = x - bx + 1;
7897       int yy = y - by + 1;
7898
7899       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7900         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7901     }
7902   }
7903
7904   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7905 }
7906
7907 static void InitDiagonalMovingElement(int x, int y)
7908 {
7909 #if 0
7910   MovDelay[x][y] = level.android_move_time;
7911 #endif
7912 }
7913
7914 void CheckExit(int x, int y)
7915 {
7916   if (local_player->gems_still_needed > 0 ||
7917       local_player->sokobanfields_still_needed > 0 ||
7918       local_player->lights_still_needed > 0)
7919   {
7920     int element = Feld[x][y];
7921     int graphic = el2img(element);
7922
7923     if (IS_ANIMATED(graphic))
7924       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7925
7926     return;
7927   }
7928
7929   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7930     return;
7931
7932   Feld[x][y] = EL_EXIT_OPENING;
7933
7934   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7935 }
7936
7937 void CheckExitSP(int x, int y)
7938 {
7939   if (local_player->gems_still_needed > 0)
7940   {
7941     int element = Feld[x][y];
7942     int graphic = el2img(element);
7943
7944     if (IS_ANIMATED(graphic))
7945       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7946
7947     return;
7948   }
7949
7950   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7951     return;
7952
7953   Feld[x][y] = EL_SP_EXIT_OPENING;
7954
7955   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7956 }
7957
7958 static void CloseAllOpenTimegates()
7959 {
7960   int x, y;
7961
7962 #if 1
7963   SCAN_PLAYFIELD(x, y)
7964 #else
7965   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7966 #endif
7967   {
7968     int element = Feld[x][y];
7969
7970     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7971     {
7972       Feld[x][y] = EL_TIMEGATE_CLOSING;
7973
7974       PlayLevelSoundAction(x, y, ACTION_CLOSING);
7975     }
7976   }
7977 }
7978
7979 void EdelsteinFunkeln(int x, int y)
7980 {
7981   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7982     return;
7983
7984   if (Feld[x][y] == EL_BD_DIAMOND)
7985     return;
7986
7987   if (MovDelay[x][y] == 0)      /* next animation frame */
7988     MovDelay[x][y] = 11 * !SimpleRND(500);
7989
7990   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7991   {
7992     MovDelay[x][y]--;
7993
7994     if (setup.direct_draw && MovDelay[x][y])
7995       SetDrawtoField(DRAW_BUFFERED);
7996
7997     DrawLevelElementAnimation(x, y, Feld[x][y]);
7998
7999     if (MovDelay[x][y] != 0)
8000     {
8001       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8002                                            10 - MovDelay[x][y]);
8003
8004       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8005
8006       if (setup.direct_draw)
8007       {
8008         int dest_x, dest_y;
8009
8010         dest_x = FX + SCREENX(x) * TILEX;
8011         dest_y = FY + SCREENY(y) * TILEY;
8012
8013         BlitBitmap(drawto_field, window,
8014                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
8015         SetDrawtoField(DRAW_DIRECT);
8016       }
8017     }
8018   }
8019 }
8020
8021 void MauerWaechst(int x, int y)
8022 {
8023   int delay = 6;
8024
8025   if (!MovDelay[x][y])          /* next animation frame */
8026     MovDelay[x][y] = 3 * delay;
8027
8028   if (MovDelay[x][y])           /* wait some time before next frame */
8029   {
8030     MovDelay[x][y]--;
8031
8032     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8033     {
8034       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8035       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8036
8037       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8038     }
8039
8040     if (!MovDelay[x][y])
8041     {
8042       if (MovDir[x][y] == MV_LEFT)
8043       {
8044         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8045           DrawLevelField(x - 1, y);
8046       }
8047       else if (MovDir[x][y] == MV_RIGHT)
8048       {
8049         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8050           DrawLevelField(x + 1, y);
8051       }
8052       else if (MovDir[x][y] == MV_UP)
8053       {
8054         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8055           DrawLevelField(x, y - 1);
8056       }
8057       else
8058       {
8059         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8060           DrawLevelField(x, y + 1);
8061       }
8062
8063       Feld[x][y] = Store[x][y];
8064       Store[x][y] = 0;
8065       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8066       DrawLevelField(x, y);
8067     }
8068   }
8069 }
8070
8071 void MauerAbleger(int ax, int ay)
8072 {
8073   int element = Feld[ax][ay];
8074   int graphic = el2img(element);
8075   boolean oben_frei = FALSE, unten_frei = FALSE;
8076   boolean links_frei = FALSE, rechts_frei = FALSE;
8077   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8078   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8079   boolean new_wall = FALSE;
8080
8081   if (IS_ANIMATED(graphic))
8082     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8083
8084   if (!MovDelay[ax][ay])        /* start building new wall */
8085     MovDelay[ax][ay] = 6;
8086
8087   if (MovDelay[ax][ay])         /* wait some time before building new wall */
8088   {
8089     MovDelay[ax][ay]--;
8090     if (MovDelay[ax][ay])
8091       return;
8092   }
8093
8094   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
8095     oben_frei = TRUE;
8096   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
8097     unten_frei = TRUE;
8098   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
8099     links_frei = TRUE;
8100   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
8101     rechts_frei = TRUE;
8102
8103   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
8104       element == EL_EXPANDABLE_WALL_ANY)
8105   {
8106     if (oben_frei)
8107     {
8108       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
8109       Store[ax][ay-1] = element;
8110       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
8111       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
8112         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
8113                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
8114       new_wall = TRUE;
8115     }
8116     if (unten_frei)
8117     {
8118       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
8119       Store[ax][ay+1] = element;
8120       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
8121       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
8122         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
8123                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
8124       new_wall = TRUE;
8125     }
8126   }
8127
8128   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8129       element == EL_EXPANDABLE_WALL_ANY ||
8130       element == EL_EXPANDABLE_WALL ||
8131       element == EL_BD_EXPANDABLE_WALL)
8132   {
8133     if (links_frei)
8134     {
8135       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
8136       Store[ax-1][ay] = element;
8137       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
8138       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
8139         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
8140                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
8141       new_wall = TRUE;
8142     }
8143
8144     if (rechts_frei)
8145     {
8146       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
8147       Store[ax+1][ay] = element;
8148       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
8149       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
8150         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
8151                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
8152       new_wall = TRUE;
8153     }
8154   }
8155
8156   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
8157     DrawLevelField(ax, ay);
8158
8159   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
8160     oben_massiv = TRUE;
8161   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
8162     unten_massiv = TRUE;
8163   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
8164     links_massiv = TRUE;
8165   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
8166     rechts_massiv = TRUE;
8167
8168   if (((oben_massiv && unten_massiv) ||
8169        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8170        element == EL_EXPANDABLE_WALL) &&
8171       ((links_massiv && rechts_massiv) ||
8172        element == EL_EXPANDABLE_WALL_VERTICAL))
8173     Feld[ax][ay] = EL_WALL;
8174
8175   if (new_wall)
8176     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
8177 }
8178
8179 void CheckForDragon(int x, int y)
8180 {
8181   int i, j;
8182   boolean dragon_found = FALSE;
8183   static int xy[4][2] =
8184   {
8185     { 0, -1 },
8186     { -1, 0 },
8187     { +1, 0 },
8188     { 0, +1 }
8189   };
8190
8191   for (i = 0; i < NUM_DIRECTIONS; i++)
8192   {
8193     for (j = 0; j < 4; j++)
8194     {
8195       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8196
8197       if (IN_LEV_FIELD(xx, yy) &&
8198           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
8199       {
8200         if (Feld[xx][yy] == EL_DRAGON)
8201           dragon_found = TRUE;
8202       }
8203       else
8204         break;
8205     }
8206   }
8207
8208   if (!dragon_found)
8209   {
8210     for (i = 0; i < NUM_DIRECTIONS; i++)
8211     {
8212       for (j = 0; j < 3; j++)
8213       {
8214         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
8215   
8216         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
8217         {
8218           Feld[xx][yy] = EL_EMPTY;
8219           DrawLevelField(xx, yy);
8220         }
8221         else
8222           break;
8223       }
8224     }
8225   }
8226 }
8227
8228 static void InitBuggyBase(int x, int y)
8229 {
8230   int element = Feld[x][y];
8231   int activating_delay = FRAMES_PER_SECOND / 4;
8232
8233   ChangeDelay[x][y] =
8234     (element == EL_SP_BUGGY_BASE ?
8235      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
8236      element == EL_SP_BUGGY_BASE_ACTIVATING ?
8237      activating_delay :
8238      element == EL_SP_BUGGY_BASE_ACTIVE ?
8239      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
8240 }
8241
8242 static void WarnBuggyBase(int x, int y)
8243 {
8244   int i;
8245   static int xy[4][2] =
8246   {
8247     { 0, -1 },
8248     { -1, 0 },
8249     { +1, 0 },
8250     { 0, +1 }
8251   };
8252
8253   for (i = 0; i < NUM_DIRECTIONS; i++)
8254   {
8255     int xx = x + xy[i][0];
8256     int yy = y + xy[i][1];
8257
8258     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
8259     {
8260       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
8261
8262       break;
8263     }
8264   }
8265 }
8266
8267 static void InitTrap(int x, int y)
8268 {
8269   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
8270 }
8271
8272 static void ActivateTrap(int x, int y)
8273 {
8274   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
8275 }
8276
8277 static void ChangeActiveTrap(int x, int y)
8278 {
8279   int graphic = IMG_TRAP_ACTIVE;
8280
8281   /* if new animation frame was drawn, correct crumbled sand border */
8282   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
8283     DrawLevelFieldCrumbledSand(x, y);
8284 }
8285
8286 static int getSpecialActionElement(int element, int number, int base_element)
8287 {
8288   return (element != EL_EMPTY ? element :
8289           number != -1 ? base_element + number - 1 :
8290           EL_EMPTY);
8291 }
8292
8293 static int getModifiedActionNumber(int value_old, int operator, int operand,
8294                                    int value_min, int value_max)
8295 {
8296   int value_new = (operator == CA_MODE_SET      ? operand :
8297                    operator == CA_MODE_ADD      ? value_old + operand :
8298                    operator == CA_MODE_SUBTRACT ? value_old - operand :
8299                    operator == CA_MODE_MULTIPLY ? value_old * operand :
8300                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
8301                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
8302                    value_old);
8303
8304   return (value_new < value_min ? value_min :
8305           value_new > value_max ? value_max :
8306           value_new);
8307 }
8308
8309 static void ExecuteCustomElementAction(int x, int y, int element, int page)
8310 {
8311   struct ElementInfo *ei = &element_info[element];
8312   struct ElementChangeInfo *change = &ei->change_page[page];
8313   int target_element = change->target_element;
8314   int action_type = change->action_type;
8315   int action_mode = change->action_mode;
8316   int action_arg = change->action_arg;
8317   int i;
8318
8319   if (!change->has_action)
8320     return;
8321
8322   /* ---------- determine action paramater values -------------------------- */
8323
8324   int level_time_value =
8325     (level.time > 0 ? TimeLeft :
8326      TimePlayed);
8327
8328   int action_arg_element =
8329     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
8330      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
8331      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
8332      EL_EMPTY);
8333
8334   int action_arg_direction =
8335     (action_arg >= CA_ARG_DIRECTION_LEFT &&
8336      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
8337      action_arg == CA_ARG_DIRECTION_TRIGGER ?
8338      change->actual_trigger_side :
8339      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
8340      MV_DIR_OPPOSITE(change->actual_trigger_side) :
8341      MV_NONE);
8342
8343   int action_arg_number_min =
8344     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
8345      CA_ARG_MIN);
8346
8347   int action_arg_number_max =
8348     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
8349      action_type == CA_SET_LEVEL_GEMS ? 999 :
8350      action_type == CA_SET_LEVEL_TIME ? 9999 :
8351      action_type == CA_SET_LEVEL_SCORE ? 99999 :
8352      action_type == CA_SET_CE_VALUE ? 9999 :
8353      action_type == CA_SET_CE_SCORE ? 9999 :
8354      CA_ARG_MAX);
8355
8356   int action_arg_number_reset =
8357     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
8358      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
8359      action_type == CA_SET_LEVEL_TIME ? level.time :
8360      action_type == CA_SET_LEVEL_SCORE ? 0 :
8361 #if 1
8362      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
8363 #else
8364      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
8365 #endif
8366      action_type == CA_SET_CE_SCORE ? 0 :
8367      0);
8368
8369   int action_arg_number =
8370     (action_arg <= CA_ARG_MAX ? action_arg :
8371      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
8372      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
8373      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
8374      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
8375      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
8376      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
8377 #if USE_NEW_CUSTOM_VALUE
8378      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
8379 #else
8380      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
8381 #endif
8382      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
8383      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
8384      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
8385      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
8386      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
8387      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
8388      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
8389      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
8390      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
8391      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
8392      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
8393      -1);
8394
8395   int action_arg_number_old =
8396     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
8397      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
8398      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
8399      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
8400      action_type == CA_SET_CE_SCORE ? ei->collect_score :
8401      0);
8402
8403   int action_arg_number_new =
8404     getModifiedActionNumber(action_arg_number_old,
8405                             action_mode, action_arg_number,
8406                             action_arg_number_min, action_arg_number_max);
8407
8408   int trigger_player_bits =
8409     (change->actual_trigger_player >= EL_PLAYER_1 &&
8410      change->actual_trigger_player <= EL_PLAYER_4 ?
8411      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
8412      PLAYER_BITS_ANY);
8413
8414   int action_arg_player_bits =
8415     (action_arg >= CA_ARG_PLAYER_1 &&
8416      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8417      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8418      PLAYER_BITS_ANY);
8419
8420   /* ---------- execute action  -------------------------------------------- */
8421
8422   switch(action_type)
8423   {
8424     case CA_NO_ACTION:
8425     {
8426       return;
8427     }
8428
8429     /* ---------- level actions  ------------------------------------------- */
8430
8431     case CA_RESTART_LEVEL:
8432     {
8433       game.restart_level = TRUE;
8434
8435       break;
8436     }
8437
8438     case CA_SHOW_ENVELOPE:
8439     {
8440       int element = getSpecialActionElement(action_arg_element,
8441                                             action_arg_number, EL_ENVELOPE_1);
8442
8443       if (IS_ENVELOPE(element))
8444         local_player->show_envelope = element;
8445
8446       break;
8447     }
8448
8449     case CA_SET_LEVEL_TIME:
8450     {
8451       if (level.time > 0)       /* only modify limited time value */
8452       {
8453         TimeLeft = action_arg_number_new;
8454
8455         DrawGameValue_Time(TimeLeft);
8456
8457         if (!TimeLeft && setup.time_limit)
8458           for (i = 0; i < MAX_PLAYERS; i++)
8459             KillPlayer(&stored_player[i]);
8460       }
8461
8462       break;
8463     }
8464
8465     case CA_SET_LEVEL_SCORE:
8466     {
8467       local_player->score = action_arg_number_new;
8468
8469       DrawGameValue_Score(local_player->score);
8470
8471       break;
8472     }
8473
8474     case CA_SET_LEVEL_GEMS:
8475     {
8476       local_player->gems_still_needed = action_arg_number_new;
8477
8478       DrawGameValue_Emeralds(local_player->gems_still_needed);
8479
8480       break;
8481     }
8482
8483 #if !USE_PLAYER_GRAVITY
8484     case CA_SET_LEVEL_GRAVITY:
8485     {
8486       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
8487                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
8488                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8489                       game.gravity);
8490       break;
8491     }
8492 #endif
8493
8494     case CA_SET_LEVEL_WIND:
8495     {
8496       game.wind_direction = action_arg_direction;
8497
8498       break;
8499     }
8500
8501     /* ---------- player actions  ------------------------------------------ */
8502
8503     case CA_MOVE_PLAYER:
8504     {
8505       /* automatically move to the next field in specified direction */
8506       for (i = 0; i < MAX_PLAYERS; i++)
8507         if (trigger_player_bits & (1 << i))
8508           stored_player[i].programmed_action = action_arg_direction;
8509
8510       break;
8511     }
8512
8513     case CA_EXIT_PLAYER:
8514     {
8515       for (i = 0; i < MAX_PLAYERS; i++)
8516         if (action_arg_player_bits & (1 << i))
8517           stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8518
8519       break;
8520     }
8521
8522     case CA_KILL_PLAYER:
8523     {
8524       for (i = 0; i < MAX_PLAYERS; i++)
8525         if (action_arg_player_bits & (1 << i))
8526           KillPlayer(&stored_player[i]);
8527
8528       break;
8529     }
8530
8531     case CA_SET_PLAYER_KEYS:
8532     {
8533       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8534       int element = getSpecialActionElement(action_arg_element,
8535                                             action_arg_number, EL_KEY_1);
8536
8537       if (IS_KEY(element))
8538       {
8539         for (i = 0; i < MAX_PLAYERS; i++)
8540         {
8541           if (trigger_player_bits & (1 << i))
8542           {
8543             stored_player[i].key[KEY_NR(element)] = key_state;
8544
8545 #if 1
8546             DrawGameDoorValues();
8547 #else
8548             DrawGameValue_Keys(stored_player[i].key);
8549 #endif
8550
8551             redraw_mask |= REDRAW_DOOR_1;
8552           }
8553         }
8554       }
8555
8556       break;
8557     }
8558
8559     case CA_SET_PLAYER_SPEED:
8560     {
8561       for (i = 0; i < MAX_PLAYERS; i++)
8562       {
8563         if (trigger_player_bits & (1 << i))
8564         {
8565           int move_stepsize = TILEX / stored_player[i].move_delay_value;
8566
8567           if (action_arg == CA_ARG_SPEED_FASTER &&
8568               stored_player[i].cannot_move)
8569           {
8570             action_arg_number = STEPSIZE_VERY_SLOW;
8571           }
8572           else if (action_arg == CA_ARG_SPEED_SLOWER ||
8573                    action_arg == CA_ARG_SPEED_FASTER)
8574           {
8575             action_arg_number = 2;
8576             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8577                            CA_MODE_MULTIPLY);
8578           }
8579           else if (action_arg == CA_ARG_NUMBER_RESET)
8580           {
8581             action_arg_number = level.initial_player_stepsize[i];
8582           }
8583
8584           move_stepsize =
8585             getModifiedActionNumber(move_stepsize,
8586                                     action_mode,
8587                                     action_arg_number,
8588                                     action_arg_number_min,
8589                                     action_arg_number_max);
8590
8591 #if 1
8592           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8593 #else
8594           /* make sure that value is power of 2 */
8595           move_stepsize = (1 << log_2(move_stepsize));
8596
8597           /* do no immediately change -- the player might just be moving */
8598           stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8599
8600           stored_player[i].cannot_move =
8601             (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8602 #endif
8603         }
8604       }
8605
8606       break;
8607     }
8608
8609     case CA_SET_PLAYER_SHIELD:
8610     {
8611       for (i = 0; i < MAX_PLAYERS; i++)
8612       {
8613         if (trigger_player_bits & (1 << i))
8614         {
8615           if (action_arg == CA_ARG_SHIELD_OFF)
8616           {
8617             stored_player[i].shield_normal_time_left = 0;
8618             stored_player[i].shield_deadly_time_left = 0;
8619           }
8620           else if (action_arg == CA_ARG_SHIELD_NORMAL)
8621           {
8622             stored_player[i].shield_normal_time_left = 999999;
8623           }
8624           else if (action_arg == CA_ARG_SHIELD_DEADLY)
8625           {
8626             stored_player[i].shield_normal_time_left = 999999;
8627             stored_player[i].shield_deadly_time_left = 999999;
8628           }
8629         }
8630       }
8631
8632       break;
8633     }
8634
8635 #if USE_PLAYER_GRAVITY
8636     case CA_SET_PLAYER_GRAVITY:
8637     {
8638       for (i = 0; i < MAX_PLAYERS; i++)
8639       {
8640         if (trigger_player_bits & (1 << i))
8641         {
8642           stored_player[i].gravity =
8643             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
8644              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
8645              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
8646              stored_player[i].gravity);
8647         }
8648       }
8649
8650       break;
8651     }
8652 #endif
8653
8654     case CA_SET_PLAYER_ARTWORK:
8655     {
8656       for (i = 0; i < MAX_PLAYERS; i++)
8657       {
8658         if (trigger_player_bits & (1 << i))
8659         {
8660           int artwork_element = action_arg_element;
8661
8662           if (action_arg == CA_ARG_ELEMENT_RESET)
8663             artwork_element =
8664               (level.use_artwork_element[i] ? level.artwork_element[i] :
8665                stored_player[i].element_nr);
8666
8667           stored_player[i].artwork_element = artwork_element;
8668
8669           SetPlayerWaiting(&stored_player[i], FALSE);
8670
8671           /* set number of special actions for bored and sleeping animation */
8672           stored_player[i].num_special_action_bored =
8673             get_num_special_action(artwork_element,
8674                                    ACTION_BORING_1, ACTION_BORING_LAST);
8675           stored_player[i].num_special_action_sleeping =
8676             get_num_special_action(artwork_element,
8677                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8678         }
8679       }
8680
8681       break;
8682     }
8683
8684     /* ---------- CE actions  ---------------------------------------------- */
8685
8686     case CA_SET_CE_VALUE:
8687     {
8688 #if USE_NEW_CUSTOM_VALUE
8689       int last_ce_value = CustomValue[x][y];
8690
8691       CustomValue[x][y] = action_arg_number_new;
8692
8693 #if 0
8694       printf("::: CE value == %d\n", CustomValue[x][y]);
8695 #endif
8696
8697       if (CustomValue[x][y] != last_ce_value)
8698       {
8699         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
8700         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
8701
8702         if (CustomValue[x][y] == 0)
8703         {
8704 #if 0
8705           printf("::: CE_VALUE_GETS_ZERO\n");
8706 #endif
8707
8708           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8709           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8710
8711 #if 0
8712           printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8713 #endif
8714         }
8715       }
8716
8717 #endif
8718
8719       break;
8720     }
8721
8722     case CA_SET_CE_SCORE:
8723     {
8724 #if USE_NEW_CUSTOM_VALUE
8725       int last_ce_score = ei->collect_score;
8726
8727       ei->collect_score = action_arg_number_new;
8728
8729 #if 0
8730       printf("::: CE score == %d\n", ei->collect_score);
8731 #endif
8732
8733       if (ei->collect_score != last_ce_score)
8734       {
8735         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
8736         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
8737
8738         if (ei->collect_score == 0)
8739         {
8740           int xx, yy;
8741
8742 #if 0
8743           printf("::: CE_SCORE_GETS_ZERO\n");
8744 #endif
8745
8746           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8747           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8748
8749 #if 0
8750           printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8751 #endif
8752
8753 #if 1
8754           /*
8755             This is a very special case that seems to be a mixture between
8756             CheckElementChange() and CheckTriggeredElementChange(): while
8757             the first one only affects single elements that are triggered
8758             directly, the second one affects multiple elements in the playfield
8759             that are triggered indirectly by another element. This is a third
8760             case: Changing the CE score always affects multiple identical CEs,
8761             so every affected CE must be checked, not only the single CE for
8762             which the CE score was changed in the first place (as every instance
8763             of that CE shares the same CE score, and therefore also can change)!
8764           */
8765           SCAN_PLAYFIELD(xx, yy)
8766           {
8767             if (Feld[xx][yy] == element)
8768               CheckElementChange(xx, yy, element, EL_UNDEFINED,
8769                                  CE_SCORE_GETS_ZERO);
8770           }
8771 #endif
8772         }
8773       }
8774
8775 #endif
8776
8777       break;
8778     }
8779
8780     /* ---------- engine actions  ------------------------------------------ */
8781
8782     case CA_SET_ENGINE_SCAN_MODE:
8783     {
8784       InitPlayfieldScanMode(action_arg);
8785
8786       break;
8787     }
8788
8789     default:
8790       break;
8791   }
8792 }
8793
8794 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8795 {
8796   int old_element = Feld[x][y];
8797   int new_element = get_element_from_group_element(element);
8798   int previous_move_direction = MovDir[x][y];
8799 #if USE_NEW_CUSTOM_VALUE
8800   int last_ce_value = CustomValue[x][y];
8801 #endif
8802   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
8803   boolean add_player_onto_element = (new_element_is_player &&
8804 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
8805                                      /* this breaks SnakeBite when a snake is
8806                                         halfway through a door that closes */
8807                                      /* NOW FIXED AT LEVEL INIT IN files.c */
8808                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
8809 #endif
8810                                      IS_WALKABLE(old_element));
8811
8812 #if 0
8813   /* check if element under the player changes from accessible to unaccessible
8814      (needed for special case of dropping element which then changes) */
8815   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8816       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8817   {
8818     Bang(x, y);
8819
8820     return;
8821   }
8822 #endif
8823
8824   if (!add_player_onto_element)
8825   {
8826     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8827       RemoveMovingField(x, y);
8828     else
8829       RemoveField(x, y);
8830
8831     Feld[x][y] = new_element;
8832
8833 #if !USE_GFX_RESET_GFX_ANIMATION
8834     ResetGfxAnimation(x, y);
8835     ResetRandomAnimationValue(x, y);
8836 #endif
8837
8838     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8839       MovDir[x][y] = previous_move_direction;
8840
8841 #if USE_NEW_CUSTOM_VALUE
8842     if (element_info[new_element].use_last_ce_value)
8843       CustomValue[x][y] = last_ce_value;
8844 #endif
8845
8846     InitField_WithBug1(x, y, FALSE);
8847
8848     new_element = Feld[x][y];   /* element may have changed */
8849
8850 #if USE_GFX_RESET_GFX_ANIMATION
8851     ResetGfxAnimation(x, y);
8852     ResetRandomAnimationValue(x, y);
8853 #endif
8854
8855     DrawLevelField(x, y);
8856
8857     if (GFX_CRUMBLED(new_element))
8858       DrawLevelFieldCrumbledSandNeighbours(x, y);
8859   }
8860
8861 #if 1
8862   /* check if element under the player changes from accessible to unaccessible
8863      (needed for special case of dropping element which then changes) */
8864   /* (must be checked after creating new element for walkable group elements) */
8865   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8866       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8867   {
8868     Bang(x, y);
8869
8870     return;
8871   }
8872 #endif
8873
8874   /* "ChangeCount" not set yet to allow "entered by player" change one time */
8875   if (new_element_is_player)
8876     RelocatePlayer(x, y, new_element);
8877
8878   if (is_change)
8879     ChangeCount[x][y]++;        /* count number of changes in the same frame */
8880
8881   TestIfBadThingTouchesPlayer(x, y);
8882   TestIfPlayerTouchesCustomElement(x, y);
8883   TestIfElementTouchesCustomElement(x, y);
8884 }
8885
8886 static void CreateField(int x, int y, int element)
8887 {
8888   CreateFieldExt(x, y, element, FALSE);
8889 }
8890
8891 static void CreateElementFromChange(int x, int y, int element)
8892 {
8893   element = GET_VALID_RUNTIME_ELEMENT(element);
8894
8895 #if USE_STOP_CHANGED_ELEMENTS
8896   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8897   {
8898     int old_element = Feld[x][y];
8899
8900     /* prevent changed element from moving in same engine frame
8901        unless both old and new element can either fall or move */
8902     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8903         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8904       Stop[x][y] = TRUE;
8905   }
8906 #endif
8907
8908   CreateFieldExt(x, y, element, TRUE);
8909 }
8910
8911 static boolean ChangeElement(int x, int y, int element, int page)
8912 {
8913   struct ElementInfo *ei = &element_info[element];
8914   struct ElementChangeInfo *change = &ei->change_page[page];
8915   int ce_value = CustomValue[x][y];
8916   int ce_score = ei->collect_score;
8917   int target_element;
8918   int old_element = Feld[x][y];
8919
8920   /* always use default change event to prevent running into a loop */
8921   if (ChangeEvent[x][y] == -1)
8922     ChangeEvent[x][y] = CE_DELAY;
8923
8924   if (ChangeEvent[x][y] == CE_DELAY)
8925   {
8926     /* reset actual trigger element, trigger player and action element */
8927     change->actual_trigger_element = EL_EMPTY;
8928     change->actual_trigger_player = EL_PLAYER_1;
8929     change->actual_trigger_side = CH_SIDE_NONE;
8930     change->actual_trigger_ce_value = 0;
8931     change->actual_trigger_ce_score = 0;
8932   }
8933
8934   /* do not change elements more than a specified maximum number of changes */
8935   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8936     return FALSE;
8937
8938   ChangeCount[x][y]++;          /* count number of changes in the same frame */
8939
8940   if (change->explode)
8941   {
8942     Bang(x, y);
8943
8944     return TRUE;
8945   }
8946
8947   if (change->use_target_content)
8948   {
8949     boolean complete_replace = TRUE;
8950     boolean can_replace[3][3];
8951     int xx, yy;
8952
8953     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8954     {
8955       boolean is_empty;
8956       boolean is_walkable;
8957       boolean is_diggable;
8958       boolean is_collectible;
8959       boolean is_removable;
8960       boolean is_destructible;
8961       int ex = x + xx - 1;
8962       int ey = y + yy - 1;
8963       int content_element = change->target_content.e[xx][yy];
8964       int e;
8965
8966       can_replace[xx][yy] = TRUE;
8967
8968       if (ex == x && ey == y)   /* do not check changing element itself */
8969         continue;
8970
8971       if (content_element == EL_EMPTY_SPACE)
8972       {
8973         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8974
8975         continue;
8976       }
8977
8978       if (!IN_LEV_FIELD(ex, ey))
8979       {
8980         can_replace[xx][yy] = FALSE;
8981         complete_replace = FALSE;
8982
8983         continue;
8984       }
8985
8986       e = Feld[ex][ey];
8987
8988       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8989         e = MovingOrBlocked2Element(ex, ey);
8990
8991       is_empty = (IS_FREE(ex, ey) ||
8992                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8993
8994       is_walkable     = (is_empty || IS_WALKABLE(e));
8995       is_diggable     = (is_empty || IS_DIGGABLE(e));
8996       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8997       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8998       is_removable    = (is_diggable || is_collectible);
8999
9000       can_replace[xx][yy] =
9001         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
9002           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
9003           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
9004           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
9005           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
9006           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
9007          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
9008
9009       if (!can_replace[xx][yy])
9010         complete_replace = FALSE;
9011     }
9012
9013     if (!change->only_if_complete || complete_replace)
9014     {
9015       boolean something_has_changed = FALSE;
9016
9017       if (change->only_if_complete && change->use_random_replace &&
9018           RND(100) < change->random_percentage)
9019         return FALSE;
9020
9021       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
9022       {
9023         int ex = x + xx - 1;
9024         int ey = y + yy - 1;
9025         int content_element;
9026
9027         if (can_replace[xx][yy] && (!change->use_random_replace ||
9028                                     RND(100) < change->random_percentage))
9029         {
9030           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
9031             RemoveMovingField(ex, ey);
9032
9033           ChangeEvent[ex][ey] = ChangeEvent[x][y];
9034
9035           content_element = change->target_content.e[xx][yy];
9036           target_element = GET_TARGET_ELEMENT(element, content_element, change,
9037                                               ce_value, ce_score);
9038
9039           CreateElementFromChange(ex, ey, target_element);
9040
9041           something_has_changed = TRUE;
9042
9043           /* for symmetry reasons, freeze newly created border elements */
9044           if (ex != x || ey != y)
9045             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
9046         }
9047       }
9048
9049       if (something_has_changed)
9050       {
9051         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9052         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9053       }
9054     }
9055   }
9056   else
9057   {
9058     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
9059                                         ce_value, ce_score);
9060
9061     if (element == EL_DIAGONAL_GROWING ||
9062         element == EL_DIAGONAL_SHRINKING)
9063     {
9064       target_element = Store[x][y];
9065
9066       Store[x][y] = EL_EMPTY;
9067     }
9068
9069     CreateElementFromChange(x, y, target_element);
9070
9071     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
9072     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
9073   }
9074
9075   /* this uses direct change before indirect change */
9076   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
9077
9078   return TRUE;
9079 }
9080
9081 #if USE_NEW_DELAYED_ACTION
9082
9083 static void HandleElementChange(int x, int y, int page)
9084 {
9085   int element = MovingOrBlocked2Element(x, y);
9086   struct ElementInfo *ei = &element_info[element];
9087   struct ElementChangeInfo *change = &ei->change_page[page];
9088
9089 #ifdef DEBUG
9090   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
9091       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
9092   {
9093     printf("\n\n");
9094     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9095            x, y, element, element_info[element].token_name);
9096     printf("HandleElementChange(): This should never happen!\n");
9097     printf("\n\n");
9098   }
9099 #endif
9100
9101   /* this can happen with classic bombs on walkable, changing elements */
9102   if (!CAN_CHANGE_OR_HAS_ACTION(element))
9103   {
9104 #if 0
9105     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9106       ChangeDelay[x][y] = 0;
9107 #endif
9108
9109     return;
9110   }
9111
9112   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9113   {
9114     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9115
9116     if (change->can_change)
9117     {
9118       ResetGfxAnimation(x, y);
9119       ResetRandomAnimationValue(x, y);
9120
9121       if (change->pre_change_function)
9122         change->pre_change_function(x, y);
9123     }
9124   }
9125
9126   ChangeDelay[x][y]--;
9127
9128   if (ChangeDelay[x][y] != 0)           /* continue element change */
9129   {
9130     if (change->can_change)
9131     {
9132       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9133
9134       if (IS_ANIMATED(graphic))
9135         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9136
9137       if (change->change_function)
9138         change->change_function(x, y);
9139     }
9140   }
9141   else                                  /* finish element change */
9142   {
9143     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9144     {
9145       page = ChangePage[x][y];
9146       ChangePage[x][y] = -1;
9147
9148       change = &ei->change_page[page];
9149     }
9150
9151     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9152     {
9153       ChangeDelay[x][y] = 1;            /* try change after next move step */
9154       ChangePage[x][y] = page;          /* remember page to use for change */
9155
9156       return;
9157     }
9158
9159     if (change->can_change)
9160     {
9161       if (ChangeElement(x, y, element, page))
9162       {
9163         if (change->post_change_function)
9164           change->post_change_function(x, y);
9165       }
9166     }
9167
9168     if (change->has_action)
9169       ExecuteCustomElementAction(x, y, element, page);
9170   }
9171 }
9172
9173 #else
9174
9175 static void HandleElementChange(int x, int y, int page)
9176 {
9177   int element = MovingOrBlocked2Element(x, y);
9178   struct ElementInfo *ei = &element_info[element];
9179   struct ElementChangeInfo *change = &ei->change_page[page];
9180
9181 #ifdef DEBUG
9182   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
9183   {
9184     printf("\n\n");
9185     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
9186            x, y, element, element_info[element].token_name);
9187     printf("HandleElementChange(): This should never happen!\n");
9188     printf("\n\n");
9189   }
9190 #endif
9191
9192   /* this can happen with classic bombs on walkable, changing elements */
9193   if (!CAN_CHANGE(element))
9194   {
9195 #if 0
9196     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
9197       ChangeDelay[x][y] = 0;
9198 #endif
9199
9200     return;
9201   }
9202
9203   if (ChangeDelay[x][y] == 0)           /* initialize element change */
9204   {
9205     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
9206
9207     ResetGfxAnimation(x, y);
9208     ResetRandomAnimationValue(x, y);
9209
9210     if (change->pre_change_function)
9211       change->pre_change_function(x, y);
9212   }
9213
9214   ChangeDelay[x][y]--;
9215
9216   if (ChangeDelay[x][y] != 0)           /* continue element change */
9217   {
9218     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9219
9220     if (IS_ANIMATED(graphic))
9221       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9222
9223     if (change->change_function)
9224       change->change_function(x, y);
9225   }
9226   else                                  /* finish element change */
9227   {
9228     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
9229     {
9230       page = ChangePage[x][y];
9231       ChangePage[x][y] = -1;
9232
9233       change = &ei->change_page[page];
9234     }
9235
9236     if (IS_MOVING(x, y))                /* never change a running system ;-) */
9237     {
9238       ChangeDelay[x][y] = 1;            /* try change after next move step */
9239       ChangePage[x][y] = page;          /* remember page to use for change */
9240
9241       return;
9242     }
9243
9244     if (ChangeElement(x, y, element, page))
9245     {
9246       if (change->post_change_function)
9247         change->post_change_function(x, y);
9248     }
9249   }
9250 }
9251
9252 #endif
9253
9254 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
9255                                               int trigger_element,
9256                                               int trigger_event,
9257                                               int trigger_player,
9258                                               int trigger_side,
9259                                               int trigger_page)
9260 {
9261   boolean change_done_any = FALSE;
9262   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
9263   int i;
9264
9265   if (!(trigger_events[trigger_element][trigger_event]))
9266     return FALSE;
9267
9268   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
9269   {
9270     int element = EL_CUSTOM_START + i;
9271     boolean change_done = FALSE;
9272     int p;
9273
9274     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9275         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9276       continue;
9277
9278     for (p = 0; p < element_info[element].num_change_pages; p++)
9279     {
9280       struct ElementChangeInfo *change = &element_info[element].change_page[p];
9281
9282       if (change->can_change_or_has_action &&
9283           change->has_event[trigger_event] &&
9284           change->trigger_side & trigger_side &&
9285           change->trigger_player & trigger_player &&
9286           change->trigger_page & trigger_page_bits &&
9287           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
9288       {
9289         change->actual_trigger_element = trigger_element;
9290         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9291         change->actual_trigger_side = trigger_side;
9292         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
9293         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9294
9295         if ((change->can_change && !change_done) || change->has_action)
9296         {
9297           int x, y;
9298
9299 #if 1
9300           SCAN_PLAYFIELD(x, y)
9301 #else
9302           for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9303 #endif
9304           {
9305             if (Feld[x][y] == element)
9306             {
9307               if (change->can_change && !change_done)
9308               {
9309                 ChangeDelay[x][y] = 1;
9310                 ChangeEvent[x][y] = trigger_event;
9311
9312                 HandleElementChange(x, y, p);
9313               }
9314 #if USE_NEW_DELAYED_ACTION
9315               else if (change->has_action)
9316               {
9317                 ExecuteCustomElementAction(x, y, element, p);
9318                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9319               }
9320 #else
9321               if (change->has_action)
9322               {
9323                 ExecuteCustomElementAction(x, y, element, p);
9324                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9325               }
9326 #endif
9327             }
9328           }
9329
9330           if (change->can_change)
9331           {
9332             change_done = TRUE;
9333             change_done_any = TRUE;
9334           }
9335         }
9336       }
9337     }
9338   }
9339
9340   return change_done_any;
9341 }
9342
9343 static boolean CheckElementChangeExt(int x, int y,
9344                                      int element,
9345                                      int trigger_element,
9346                                      int trigger_event,
9347                                      int trigger_player,
9348                                      int trigger_side)
9349 {
9350   boolean change_done = FALSE;
9351   int p;
9352
9353   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
9354       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
9355     return FALSE;
9356
9357   if (Feld[x][y] == EL_BLOCKED)
9358   {
9359     Blocked2Moving(x, y, &x, &y);
9360     element = Feld[x][y];
9361   }
9362
9363 #if 0
9364   /* check if element has already changed */
9365   if (Feld[x][y] != element)
9366     return FALSE;
9367 #else
9368   /* check if element has already changed or is about to change after moving */
9369   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
9370        Feld[x][y] != element) ||
9371
9372       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
9373        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
9374         ChangePage[x][y] != -1)))
9375     return FALSE;
9376 #endif
9377
9378   for (p = 0; p < element_info[element].num_change_pages; p++)
9379   {
9380     struct ElementChangeInfo *change = &element_info[element].change_page[p];
9381
9382     boolean check_trigger_element =
9383       (trigger_event == CE_TOUCHING_X ||
9384        trigger_event == CE_HITTING_X ||
9385        trigger_event == CE_HIT_BY_X);
9386
9387     if (change->can_change_or_has_action &&
9388         change->has_event[trigger_event] &&
9389         change->trigger_side & trigger_side &&
9390         change->trigger_player & trigger_player &&
9391         (!check_trigger_element ||
9392          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
9393     {
9394       change->actual_trigger_element = trigger_element;
9395       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
9396       change->actual_trigger_side = trigger_side;
9397       change->actual_trigger_ce_value = CustomValue[x][y];
9398       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9399
9400       /* special case: trigger element not at (x,y) position for some events */
9401       if (check_trigger_element)
9402       {
9403         static struct
9404         {
9405           int dx, dy;
9406         } move_xy[] =
9407           {
9408             {  0,  0 },
9409             { -1,  0 },
9410             { +1,  0 },
9411             {  0,  0 },
9412             {  0, -1 },
9413             {  0,  0 }, { 0, 0 }, { 0, 0 },
9414             {  0, +1 }
9415           };
9416
9417         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
9418         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
9419
9420         change->actual_trigger_ce_value = CustomValue[xx][yy];
9421         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
9422       }
9423
9424       if (change->can_change && !change_done)
9425       {
9426         ChangeDelay[x][y] = 1;
9427         ChangeEvent[x][y] = trigger_event;
9428
9429         HandleElementChange(x, y, p);
9430
9431         change_done = TRUE;
9432       }
9433 #if USE_NEW_DELAYED_ACTION
9434       else if (change->has_action)
9435       {
9436         ExecuteCustomElementAction(x, y, element, p);
9437         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9438       }
9439 #else
9440       if (change->has_action)
9441       {
9442         ExecuteCustomElementAction(x, y, element, p);
9443         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
9444       }
9445 #endif
9446     }
9447   }
9448
9449   return change_done;
9450 }
9451
9452 static void PlayPlayerSound(struct PlayerInfo *player)
9453 {
9454   int jx = player->jx, jy = player->jy;
9455   int sound_element = player->artwork_element;
9456   int last_action = player->last_action_waiting;
9457   int action = player->action_waiting;
9458
9459   if (player->is_waiting)
9460   {
9461     if (action != last_action)
9462       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9463     else
9464       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9465   }
9466   else
9467   {
9468     if (action != last_action)
9469       StopSound(element_info[sound_element].sound[last_action]);
9470
9471     if (last_action == ACTION_SLEEPING)
9472       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9473   }
9474 }
9475
9476 static void PlayAllPlayersSound()
9477 {
9478   int i;
9479
9480   for (i = 0; i < MAX_PLAYERS; i++)
9481     if (stored_player[i].active)
9482       PlayPlayerSound(&stored_player[i]);
9483 }
9484
9485 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9486 {
9487   boolean last_waiting = player->is_waiting;
9488   int move_dir = player->MovDir;
9489
9490   player->dir_waiting = move_dir;
9491   player->last_action_waiting = player->action_waiting;
9492
9493   if (is_waiting)
9494   {
9495     if (!last_waiting)          /* not waiting -> waiting */
9496     {
9497       player->is_waiting = TRUE;
9498
9499       player->frame_counter_bored =
9500         FrameCounter +
9501         game.player_boring_delay_fixed +
9502         SimpleRND(game.player_boring_delay_random);
9503       player->frame_counter_sleeping =
9504         FrameCounter +
9505         game.player_sleeping_delay_fixed +
9506         SimpleRND(game.player_sleeping_delay_random);
9507
9508 #if 1
9509       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9510 #else
9511       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9512 #endif
9513     }
9514
9515     if (game.player_sleeping_delay_fixed +
9516         game.player_sleeping_delay_random > 0 &&
9517         player->anim_delay_counter == 0 &&
9518         player->post_delay_counter == 0 &&
9519         FrameCounter >= player->frame_counter_sleeping)
9520       player->is_sleeping = TRUE;
9521     else if (game.player_boring_delay_fixed +
9522              game.player_boring_delay_random > 0 &&
9523              FrameCounter >= player->frame_counter_bored)
9524       player->is_bored = TRUE;
9525
9526     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9527                               player->is_bored ? ACTION_BORING :
9528                               ACTION_WAITING);
9529
9530 #if 1
9531     if (player->is_sleeping && player->use_murphy)
9532     {
9533       /* special case for sleeping Murphy when leaning against non-free tile */
9534
9535       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9536           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9537            !IS_MOVING(player->jx - 1, player->jy)))
9538         move_dir = MV_LEFT;
9539       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9540                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9541                 !IS_MOVING(player->jx + 1, player->jy)))
9542         move_dir = MV_RIGHT;
9543       else
9544         player->is_sleeping = FALSE;
9545
9546       player->dir_waiting = move_dir;
9547     }
9548 #endif
9549
9550     if (player->is_sleeping)
9551     {
9552       if (player->num_special_action_sleeping > 0)
9553       {
9554         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9555         {
9556           int last_special_action = player->special_action_sleeping;
9557           int num_special_action = player->num_special_action_sleeping;
9558           int special_action =
9559             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9560              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9561              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9562              last_special_action + 1 : ACTION_SLEEPING);
9563           int special_graphic =
9564             el_act_dir2img(player->artwork_element, special_action, move_dir);
9565
9566           player->anim_delay_counter =
9567             graphic_info[special_graphic].anim_delay_fixed +
9568             SimpleRND(graphic_info[special_graphic].anim_delay_random);
9569           player->post_delay_counter =
9570             graphic_info[special_graphic].post_delay_fixed +
9571             SimpleRND(graphic_info[special_graphic].post_delay_random);
9572
9573           player->special_action_sleeping = special_action;
9574         }
9575
9576         if (player->anim_delay_counter > 0)
9577         {
9578           player->action_waiting = player->special_action_sleeping;
9579           player->anim_delay_counter--;
9580         }
9581         else if (player->post_delay_counter > 0)
9582         {
9583           player->post_delay_counter--;
9584         }
9585       }
9586     }
9587     else if (player->is_bored)
9588     {
9589       if (player->num_special_action_bored > 0)
9590       {
9591         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9592         {
9593           int special_action =
9594             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9595           int special_graphic =
9596             el_act_dir2img(player->artwork_element, special_action, move_dir);
9597
9598           player->anim_delay_counter =
9599             graphic_info[special_graphic].anim_delay_fixed +
9600             SimpleRND(graphic_info[special_graphic].anim_delay_random);
9601           player->post_delay_counter =
9602             graphic_info[special_graphic].post_delay_fixed +
9603             SimpleRND(graphic_info[special_graphic].post_delay_random);
9604
9605           player->special_action_bored = special_action;
9606         }
9607
9608         if (player->anim_delay_counter > 0)
9609         {
9610           player->action_waiting = player->special_action_bored;
9611           player->anim_delay_counter--;
9612         }
9613         else if (player->post_delay_counter > 0)
9614         {
9615           player->post_delay_counter--;
9616         }
9617       }
9618     }
9619   }
9620   else if (last_waiting)        /* waiting -> not waiting */
9621   {
9622     player->is_waiting = FALSE;
9623     player->is_bored = FALSE;
9624     player->is_sleeping = FALSE;
9625
9626     player->frame_counter_bored = -1;
9627     player->frame_counter_sleeping = -1;
9628
9629     player->anim_delay_counter = 0;
9630     player->post_delay_counter = 0;
9631
9632     player->dir_waiting = player->MovDir;
9633     player->action_waiting = ACTION_DEFAULT;
9634
9635     player->special_action_bored = ACTION_DEFAULT;
9636     player->special_action_sleeping = ACTION_DEFAULT;
9637   }
9638 }
9639
9640 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9641 {
9642   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9643   int left      = player_action & JOY_LEFT;
9644   int right     = player_action & JOY_RIGHT;
9645   int up        = player_action & JOY_UP;
9646   int down      = player_action & JOY_DOWN;
9647   int button1   = player_action & JOY_BUTTON_1;
9648   int button2   = player_action & JOY_BUTTON_2;
9649   int dx        = (left ? -1 : right ? 1 : 0);
9650   int dy        = (up   ? -1 : down  ? 1 : 0);
9651
9652   if (!player->active || tape.pausing)
9653     return 0;
9654
9655   if (player_action)
9656   {
9657     if (button1)
9658       snapped = SnapField(player, dx, dy);
9659     else
9660     {
9661       if (button2)
9662         dropped = DropElement(player);
9663
9664       moved = MovePlayer(player, dx, dy);
9665     }
9666
9667     if (tape.single_step && tape.recording && !tape.pausing)
9668     {
9669       if (button1 || (dropped && !moved))
9670       {
9671         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9672         SnapField(player, 0, 0);                /* stop snapping */
9673       }
9674     }
9675
9676     SetPlayerWaiting(player, FALSE);
9677
9678     return player_action;
9679   }
9680   else
9681   {
9682     /* no actions for this player (no input at player's configured device) */
9683
9684     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9685     SnapField(player, 0, 0);
9686     CheckGravityMovementWhenNotMoving(player);
9687
9688     if (player->MovPos == 0)
9689       SetPlayerWaiting(player, TRUE);
9690
9691     if (player->MovPos == 0)    /* needed for tape.playing */
9692       player->is_moving = FALSE;
9693
9694     player->is_dropping = FALSE;
9695     player->is_dropping_pressed = FALSE;
9696     player->drop_pressed_delay = 0;
9697
9698     return 0;
9699   }
9700 }
9701
9702 static void CheckLevelTime()
9703 {
9704   int i;
9705
9706   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9707   {
9708     if (level.native_em_level->lev->home == 0)  /* all players at home */
9709     {
9710       local_player->LevelSolved = TRUE;
9711       AllPlayersGone = TRUE;
9712
9713       level.native_em_level->lev->home = -1;
9714     }
9715
9716     if (level.native_em_level->ply[0]->alive == 0 &&
9717         level.native_em_level->ply[1]->alive == 0 &&
9718         level.native_em_level->ply[2]->alive == 0 &&
9719         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9720       AllPlayersGone = TRUE;
9721   }
9722
9723   if (TimeFrames >= FRAMES_PER_SECOND)
9724   {
9725     TimeFrames = 0;
9726     TapeTime++;
9727
9728     for (i = 0; i < MAX_PLAYERS; i++)
9729     {
9730       struct PlayerInfo *player = &stored_player[i];
9731
9732       if (SHIELD_ON(player))
9733       {
9734         player->shield_normal_time_left--;
9735
9736         if (player->shield_deadly_time_left > 0)
9737           player->shield_deadly_time_left--;
9738       }
9739     }
9740
9741     if (!level.use_step_counter)
9742     {
9743       TimePlayed++;
9744
9745       if (TimeLeft > 0)
9746       {
9747         TimeLeft--;
9748
9749         if (TimeLeft <= 10 && setup.time_limit)
9750           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9751
9752         DrawGameValue_Time(TimeLeft);
9753
9754         if (!TimeLeft && setup.time_limit)
9755         {
9756           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9757             level.native_em_level->lev->killed_out_of_time = TRUE;
9758           else
9759             for (i = 0; i < MAX_PLAYERS; i++)
9760               KillPlayer(&stored_player[i]);
9761         }
9762       }
9763       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9764         DrawGameValue_Time(TimePlayed);
9765
9766       level.native_em_level->lev->time =
9767         (level.time == 0 ? TimePlayed : TimeLeft);
9768     }
9769
9770     if (tape.recording || tape.playing)
9771       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9772   }
9773 }
9774
9775 void AdvanceFrameAndPlayerCounters(int player_nr)
9776 {
9777   int i;
9778
9779 #if 0
9780   Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9781         FrameCounter, FrameCounter + 1);
9782 #endif
9783
9784   /* advance frame counters (global frame counter and time frame counter) */
9785   FrameCounter++;
9786   TimeFrames++;
9787
9788   /* advance player counters (counters for move delay, move animation etc.) */
9789   for (i = 0; i < MAX_PLAYERS; i++)
9790   {
9791     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9792     int move_delay_value = stored_player[i].move_delay_value;
9793     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9794
9795     if (!advance_player_counters)       /* not all players may be affected */
9796       continue;
9797
9798 #if USE_NEW_PLAYER_ANIM
9799     if (move_frames == 0)       /* less than one move per game frame */
9800     {
9801       int stepsize = TILEX / move_delay_value;
9802       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9803       int count = (stored_player[i].is_moving ?
9804                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9805
9806       if (count % delay == 0)
9807         move_frames = 1;
9808     }
9809 #endif
9810
9811     stored_player[i].Frame += move_frames;
9812
9813     if (stored_player[i].MovPos != 0)
9814       stored_player[i].StepFrame += move_frames;
9815
9816     if (stored_player[i].move_delay > 0)
9817       stored_player[i].move_delay--;
9818
9819     /* due to bugs in previous versions, counter must count up, not down */
9820     if (stored_player[i].push_delay != -1)
9821       stored_player[i].push_delay++;
9822
9823     if (stored_player[i].drop_delay > 0)
9824       stored_player[i].drop_delay--;
9825
9826     if (stored_player[i].is_dropping_pressed)
9827       stored_player[i].drop_pressed_delay++;
9828   }
9829 }
9830
9831 void StartGameActions(boolean init_network_game, boolean record_tape,
9832                       long random_seed)
9833 {
9834   unsigned long new_random_seed = InitRND(random_seed);
9835
9836   if (record_tape)
9837     TapeStartRecording(new_random_seed);
9838
9839 #if defined(NETWORK_AVALIABLE)
9840   if (init_network_game)
9841   {
9842     SendToServer_StartPlaying();
9843
9844     return;
9845   }
9846 #endif
9847
9848   InitGame();
9849 }
9850
9851 void GameActions()
9852 {
9853   static unsigned long game_frame_delay = 0;
9854   unsigned long game_frame_delay_value;
9855   byte *recorded_player_action;
9856   byte summarized_player_action = 0;
9857   byte tape_action[MAX_PLAYERS];
9858   int i;
9859
9860   if (game.restart_level)
9861     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9862
9863   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9864   {
9865     if (level.native_em_level->lev->home == 0)  /* all players at home */
9866     {
9867       local_player->LevelSolved = TRUE;
9868       AllPlayersGone = TRUE;
9869
9870       level.native_em_level->lev->home = -1;
9871     }
9872
9873     if (level.native_em_level->ply[0]->alive == 0 &&
9874         level.native_em_level->ply[1]->alive == 0 &&
9875         level.native_em_level->ply[2]->alive == 0 &&
9876         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9877       AllPlayersGone = TRUE;
9878   }
9879
9880   if (local_player->LevelSolved)
9881     GameWon();
9882
9883   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9884     TapeStop();
9885
9886   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9887     return;
9888
9889   game_frame_delay_value =
9890     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9891
9892   if (tape.playing && tape.warp_forward && !tape.pausing)
9893     game_frame_delay_value = 0;
9894
9895   /* ---------- main game synchronization point ---------- */
9896
9897   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9898
9899   if (network_playing && !network_player_action_received)
9900   {
9901     /* try to get network player actions in time */
9902
9903 #if defined(NETWORK_AVALIABLE)
9904     /* last chance to get network player actions without main loop delay */
9905     HandleNetworking();
9906 #endif
9907
9908     /* game was quit by network peer */
9909     if (game_status != GAME_MODE_PLAYING)
9910       return;
9911
9912     if (!network_player_action_received)
9913       return;           /* failed to get network player actions in time */
9914
9915     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9916   }
9917
9918   if (tape.pausing)
9919     return;
9920
9921   /* at this point we know that we really continue executing the game */
9922
9923 #if 1
9924   network_player_action_received = FALSE;
9925 #endif
9926
9927   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9928
9929   if (tape.set_centered_player)
9930   {
9931     game.centered_player_nr_next = tape.centered_player_nr_next;
9932     game.set_centered_player = TRUE;
9933   }
9934
9935   for (i = 0; i < MAX_PLAYERS; i++)
9936   {
9937     summarized_player_action |= stored_player[i].action;
9938
9939     if (!network_playing)
9940       stored_player[i].effective_action = stored_player[i].action;
9941   }
9942
9943 #if defined(NETWORK_AVALIABLE)
9944   if (network_playing)
9945     SendToServer_MovePlayer(summarized_player_action);
9946 #endif
9947
9948   if (!options.network && !setup.team_mode)
9949     local_player->effective_action = summarized_player_action;
9950
9951   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9952   {
9953     for (i = 0; i < MAX_PLAYERS; i++)
9954       stored_player[i].effective_action =
9955         (i == game.centered_player_nr ? summarized_player_action : 0);
9956   }
9957
9958   if (recorded_player_action != NULL)
9959     for (i = 0; i < MAX_PLAYERS; i++)
9960       stored_player[i].effective_action = recorded_player_action[i];
9961
9962   for (i = 0; i < MAX_PLAYERS; i++)
9963   {
9964     tape_action[i] = stored_player[i].effective_action;
9965
9966     /* (this can only happen in the R'n'D game engine) */
9967     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9968       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9969   }
9970
9971   /* only record actions from input devices, but not programmed actions */
9972   if (tape.recording)
9973     TapeRecordAction(tape_action);
9974
9975   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9976   {
9977     GameActions_EM_Main();
9978   }
9979   else
9980   {
9981     GameActions_RND();
9982   }
9983 }
9984
9985 void GameActions_EM_Main()
9986 {
9987   byte effective_action[MAX_PLAYERS];
9988   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9989   int i;
9990
9991   for (i = 0; i < MAX_PLAYERS; i++)
9992     effective_action[i] = stored_player[i].effective_action;
9993
9994 #if 0
9995   printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
9996 #endif
9997
9998   GameActions_EM(effective_action, warp_mode);
9999
10000   CheckLevelTime();
10001
10002   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10003 }
10004
10005 void GameActions_RND()
10006 {
10007   int magic_wall_x = 0, magic_wall_y = 0;
10008   int i, x, y, element, graphic;
10009
10010   InitPlayfieldScanModeVars();
10011
10012 #if USE_ONE_MORE_CHANGE_PER_FRAME
10013   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10014   {
10015     SCAN_PLAYFIELD(x, y)
10016     {
10017       ChangeCount[x][y] = 0;
10018       ChangeEvent[x][y] = -1;
10019     }
10020   }
10021 #endif
10022
10023 #if 1
10024   if (game.set_centered_player)
10025   {
10026     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
10027
10028     /* switching to "all players" only possible if all players fit to screen */
10029     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
10030     {
10031       game.centered_player_nr_next = game.centered_player_nr;
10032       game.set_centered_player = FALSE;
10033     }
10034
10035     /* do not switch focus to non-existing (or non-active) player */
10036     if (game.centered_player_nr_next >= 0 &&
10037         !stored_player[game.centered_player_nr_next].active)
10038     {
10039       game.centered_player_nr_next = game.centered_player_nr;
10040       game.set_centered_player = FALSE;
10041     }
10042   }
10043
10044   if (game.set_centered_player &&
10045       ScreenMovPos == 0)        /* screen currently aligned at tile position */
10046   {
10047     int sx, sy;
10048
10049     if (game.centered_player_nr_next == -1)
10050     {
10051       setScreenCenteredToAllPlayers(&sx, &sy);
10052     }
10053     else
10054     {
10055       sx = stored_player[game.centered_player_nr_next].jx;
10056       sy = stored_player[game.centered_player_nr_next].jy;
10057     }
10058
10059     game.centered_player_nr = game.centered_player_nr_next;
10060     game.set_centered_player = FALSE;
10061
10062     DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
10063     DrawGameDoorValues();
10064   }
10065 #endif
10066
10067   for (i = 0; i < MAX_PLAYERS; i++)
10068   {
10069     int actual_player_action = stored_player[i].effective_action;
10070
10071 #if 1
10072     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
10073        - rnd_equinox_tetrachloride 048
10074        - rnd_equinox_tetrachloride_ii 096
10075        - rnd_emanuel_schmieg 002
10076        - doctor_sloan_ww 001, 020
10077     */
10078     if (stored_player[i].MovPos == 0)
10079       CheckGravityMovement(&stored_player[i]);
10080 #endif
10081
10082     /* overwrite programmed action with tape action */
10083     if (stored_player[i].programmed_action)
10084       actual_player_action = stored_player[i].programmed_action;
10085
10086 #if 1
10087     PlayerActions(&stored_player[i], actual_player_action);
10088 #else
10089     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
10090
10091     if (tape.recording && tape_action[i] && !tape.player_participates[i])
10092       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
10093 #endif
10094
10095     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
10096   }
10097
10098 #if 0
10099   network_player_action_received = FALSE;
10100 #endif
10101
10102   ScrollScreen(NULL, SCROLL_GO_ON);
10103
10104   /* for backwards compatibility, the following code emulates a fixed bug that
10105      occured when pushing elements (causing elements that just made their last
10106      pushing step to already (if possible) make their first falling step in the
10107      same game frame, which is bad); this code is also needed to use the famous
10108      "spring push bug" which is used in older levels and might be wanted to be
10109      used also in newer levels, but in this case the buggy pushing code is only
10110      affecting the "spring" element and no other elements */
10111
10112   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
10113   {
10114     for (i = 0; i < MAX_PLAYERS; i++)
10115     {
10116       struct PlayerInfo *player = &stored_player[i];
10117       int x = player->jx;
10118       int y = player->jy;
10119
10120       if (player->active && player->is_pushing && player->is_moving &&
10121           IS_MOVING(x, y) &&
10122           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
10123            Feld[x][y] == EL_SPRING))
10124       {
10125         ContinueMoving(x, y);
10126
10127         /* continue moving after pushing (this is actually a bug) */
10128         if (!IS_MOVING(x, y))
10129         {
10130           Stop[x][y] = FALSE;
10131         }
10132       }
10133     }
10134   }
10135
10136 #if 1
10137   SCAN_PLAYFIELD(x, y)
10138 #else
10139   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10140 #endif
10141   {
10142     ChangeCount[x][y] = 0;
10143     ChangeEvent[x][y] = -1;
10144
10145     /* this must be handled before main playfield loop */
10146     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
10147     {
10148       MovDelay[x][y]--;
10149       if (MovDelay[x][y] <= 0)
10150         RemoveField(x, y);
10151     }
10152
10153 #if USE_NEW_SNAP_DELAY
10154     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
10155     {
10156       MovDelay[x][y]--;
10157       if (MovDelay[x][y] <= 0)
10158       {
10159         RemoveField(x, y);
10160         DrawLevelField(x, y);
10161
10162         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
10163       }
10164     }
10165 #endif
10166
10167 #if DEBUG
10168     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
10169     {
10170       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
10171       printf("GameActions(): This should never happen!\n");
10172
10173       ChangePage[x][y] = -1;
10174     }
10175 #endif
10176
10177     Stop[x][y] = FALSE;
10178     if (WasJustMoving[x][y] > 0)
10179       WasJustMoving[x][y]--;
10180     if (WasJustFalling[x][y] > 0)
10181       WasJustFalling[x][y]--;
10182     if (CheckCollision[x][y] > 0)
10183       CheckCollision[x][y]--;
10184
10185     GfxFrame[x][y]++;
10186
10187     /* reset finished pushing action (not done in ContinueMoving() to allow
10188        continuous pushing animation for elements with zero push delay) */
10189     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
10190     {
10191       ResetGfxAnimation(x, y);
10192       DrawLevelField(x, y);
10193     }
10194
10195 #if DEBUG
10196     if (IS_BLOCKED(x, y))
10197     {
10198       int oldx, oldy;
10199
10200       Blocked2Moving(x, y, &oldx, &oldy);
10201       if (!IS_MOVING(oldx, oldy))
10202       {
10203         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
10204         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
10205         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
10206         printf("GameActions(): This should never happen!\n");
10207       }
10208     }
10209 #endif
10210   }
10211
10212 #if 1
10213   SCAN_PLAYFIELD(x, y)
10214 #else
10215   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10216 #endif
10217   {
10218     element = Feld[x][y];
10219     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10220
10221 #if 0
10222     printf("::: %d,%d\n", x, y);
10223
10224     if (element == EL_ROCK)
10225       printf("::: Yo man! Rocks can fall!\n");
10226 #endif
10227
10228 #if 1
10229     ResetGfxFrame(x, y, TRUE);
10230 #else
10231     if (graphic_info[graphic].anim_global_sync)
10232       GfxFrame[x][y] = FrameCounter;
10233     else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
10234     {
10235       int old_gfx_frame = GfxFrame[x][y];
10236
10237       GfxFrame[x][y] = CustomValue[x][y];
10238
10239 #if 1
10240       if (GfxFrame[x][y] != old_gfx_frame)
10241 #endif
10242         DrawLevelGraphicAnimation(x, y, graphic);
10243     }
10244     else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
10245     {
10246       int old_gfx_frame = GfxFrame[x][y];
10247
10248       GfxFrame[x][y] = element_info[element].collect_score;
10249
10250 #if 1
10251       if (GfxFrame[x][y] != old_gfx_frame)
10252 #endif
10253         DrawLevelGraphicAnimation(x, y, graphic);
10254     }
10255     else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
10256     {
10257       int old_gfx_frame = GfxFrame[x][y];
10258
10259       GfxFrame[x][y] = ChangeDelay[x][y];
10260
10261 #if 1
10262       if (GfxFrame[x][y] != old_gfx_frame)
10263 #endif
10264         DrawLevelGraphicAnimation(x, y, graphic);
10265     }
10266 #endif
10267
10268     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
10269         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
10270       ResetRandomAnimationValue(x, y);
10271
10272     SetRandomAnimationValue(x, y);
10273
10274     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
10275
10276     if (IS_INACTIVE(element))
10277     {
10278       if (IS_ANIMATED(graphic))
10279         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10280
10281       continue;
10282     }
10283
10284     /* this may take place after moving, so 'element' may have changed */
10285     if (IS_CHANGING(x, y) &&
10286         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
10287     {
10288       int page = element_info[element].event_page_nr[CE_DELAY];
10289 #if 0
10290       HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
10291 #else
10292
10293 #if 0
10294       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10295 #endif
10296
10297 #if 0
10298       if (element == EL_CUSTOM_255)
10299         printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
10300 #endif
10301
10302 #if 1
10303       HandleElementChange(x, y, page);
10304 #else
10305       if (CAN_CHANGE(element))
10306         HandleElementChange(x, y, page);
10307
10308       if (HAS_ACTION(element))
10309         ExecuteCustomElementAction(x, y, element, page);
10310 #endif
10311
10312 #endif
10313
10314       element = Feld[x][y];
10315       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10316     }
10317
10318     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
10319     {
10320       StartMoving(x, y);
10321
10322       element = Feld[x][y];
10323       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10324
10325       if (IS_ANIMATED(graphic) &&
10326           !IS_MOVING(x, y) &&
10327           !Stop[x][y])
10328         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10329
10330       if (IS_GEM(element) || element == EL_SP_INFOTRON)
10331         EdelsteinFunkeln(x, y);
10332     }
10333     else if ((element == EL_ACID ||
10334               element == EL_EXIT_OPEN ||
10335               element == EL_SP_EXIT_OPEN ||
10336               element == EL_SP_TERMINAL ||
10337               element == EL_SP_TERMINAL_ACTIVE ||
10338               element == EL_EXTRA_TIME ||
10339               element == EL_SHIELD_NORMAL ||
10340               element == EL_SHIELD_DEADLY) &&
10341              IS_ANIMATED(graphic))
10342       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10343     else if (IS_MOVING(x, y))
10344       ContinueMoving(x, y);
10345     else if (IS_ACTIVE_BOMB(element))
10346       CheckDynamite(x, y);
10347     else if (element == EL_AMOEBA_GROWING)
10348       AmoebeWaechst(x, y);
10349     else if (element == EL_AMOEBA_SHRINKING)
10350       AmoebaDisappearing(x, y);
10351
10352 #if !USE_NEW_AMOEBA_CODE
10353     else if (IS_AMOEBALIVE(element))
10354       AmoebeAbleger(x, y);
10355 #endif
10356
10357     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
10358       Life(x, y);
10359     else if (element == EL_EXIT_CLOSED)
10360       CheckExit(x, y);
10361     else if (element == EL_SP_EXIT_CLOSED)
10362       CheckExitSP(x, y);
10363     else if (element == EL_EXPANDABLE_WALL_GROWING)
10364       MauerWaechst(x, y);
10365     else if (element == EL_EXPANDABLE_WALL ||
10366              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10367              element == EL_EXPANDABLE_WALL_VERTICAL ||
10368              element == EL_EXPANDABLE_WALL_ANY ||
10369              element == EL_BD_EXPANDABLE_WALL)
10370       MauerAbleger(x, y);
10371     else if (element == EL_FLAMES)
10372       CheckForDragon(x, y);
10373     else if (element == EL_EXPLOSION)
10374       ; /* drawing of correct explosion animation is handled separately */
10375     else if (element == EL_ELEMENT_SNAPPING ||
10376              element == EL_DIAGONAL_SHRINKING ||
10377              element == EL_DIAGONAL_GROWING)
10378     {
10379 #if 1
10380       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
10381
10382       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10383 #endif
10384     }
10385     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
10386       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10387
10388 #if 0
10389     if (element == EL_CUSTOM_255 ||
10390         element == EL_CUSTOM_256)
10391       DrawLevelGraphicAnimation(x, y, graphic);
10392 #endif
10393
10394     if (IS_BELT_ACTIVE(element))
10395       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
10396
10397     if (game.magic_wall_active)
10398     {
10399       int jx = local_player->jx, jy = local_player->jy;
10400
10401       /* play the element sound at the position nearest to the player */
10402       if ((element == EL_MAGIC_WALL_FULL ||
10403            element == EL_MAGIC_WALL_ACTIVE ||
10404            element == EL_MAGIC_WALL_EMPTYING ||
10405            element == EL_BD_MAGIC_WALL_FULL ||
10406            element == EL_BD_MAGIC_WALL_ACTIVE ||
10407            element == EL_BD_MAGIC_WALL_EMPTYING) &&
10408           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
10409       {
10410         magic_wall_x = x;
10411         magic_wall_y = y;
10412       }
10413     }
10414   }
10415
10416 #if USE_NEW_AMOEBA_CODE
10417   /* new experimental amoeba growth stuff */
10418   if (!(FrameCounter % 8))
10419   {
10420     static unsigned long random = 1684108901;
10421
10422     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
10423     {
10424       x = RND(lev_fieldx);
10425       y = RND(lev_fieldy);
10426       element = Feld[x][y];
10427
10428       if (!IS_PLAYER(x,y) &&
10429           (element == EL_EMPTY ||
10430            CAN_GROW_INTO(element) ||
10431            element == EL_QUICKSAND_EMPTY ||
10432            element == EL_ACID_SPLASH_LEFT ||
10433            element == EL_ACID_SPLASH_RIGHT))
10434       {
10435         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
10436             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
10437             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
10438             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
10439           Feld[x][y] = EL_AMOEBA_DROP;
10440       }
10441
10442       random = random * 129 + 1;
10443     }
10444   }
10445 #endif
10446
10447 #if 0
10448   if (game.explosions_delayed)
10449 #endif
10450   {
10451     game.explosions_delayed = FALSE;
10452
10453 #if 1
10454     SCAN_PLAYFIELD(x, y)
10455 #else
10456     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10457 #endif
10458     {
10459       element = Feld[x][y];
10460
10461       if (ExplodeField[x][y])
10462         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10463       else if (element == EL_EXPLOSION)
10464         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10465
10466       ExplodeField[x][y] = EX_TYPE_NONE;
10467     }
10468
10469     game.explosions_delayed = TRUE;
10470   }
10471
10472   if (game.magic_wall_active)
10473   {
10474     if (!(game.magic_wall_time_left % 4))
10475     {
10476       int element = Feld[magic_wall_x][magic_wall_y];
10477
10478       if (element == EL_BD_MAGIC_WALL_FULL ||
10479           element == EL_BD_MAGIC_WALL_ACTIVE ||
10480           element == EL_BD_MAGIC_WALL_EMPTYING)
10481         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10482       else
10483         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10484     }
10485
10486     if (game.magic_wall_time_left > 0)
10487     {
10488       game.magic_wall_time_left--;
10489       if (!game.magic_wall_time_left)
10490       {
10491 #if 1
10492         SCAN_PLAYFIELD(x, y)
10493 #else
10494         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10495 #endif
10496         {
10497           element = Feld[x][y];
10498
10499           if (element == EL_MAGIC_WALL_ACTIVE ||
10500               element == EL_MAGIC_WALL_FULL)
10501           {
10502             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10503             DrawLevelField(x, y);
10504           }
10505           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10506                    element == EL_BD_MAGIC_WALL_FULL)
10507           {
10508             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10509             DrawLevelField(x, y);
10510           }
10511         }
10512
10513         game.magic_wall_active = FALSE;
10514       }
10515     }
10516   }
10517
10518   if (game.light_time_left > 0)
10519   {
10520     game.light_time_left--;
10521
10522     if (game.light_time_left == 0)
10523       RedrawAllLightSwitchesAndInvisibleElements();
10524   }
10525
10526   if (game.timegate_time_left > 0)
10527   {
10528     game.timegate_time_left--;
10529
10530     if (game.timegate_time_left == 0)
10531       CloseAllOpenTimegates();
10532   }
10533
10534   if (game.lenses_time_left > 0)
10535   {
10536     game.lenses_time_left--;
10537
10538     if (game.lenses_time_left == 0)
10539       RedrawAllInvisibleElementsForLenses();
10540   }
10541
10542   if (game.magnify_time_left > 0)
10543   {
10544     game.magnify_time_left--;
10545
10546     if (game.magnify_time_left == 0)
10547       RedrawAllInvisibleElementsForMagnifier();
10548   }
10549
10550   for (i = 0; i < MAX_PLAYERS; i++)
10551   {
10552     struct PlayerInfo *player = &stored_player[i];
10553
10554     if (SHIELD_ON(player))
10555     {
10556       if (player->shield_deadly_time_left)
10557         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10558       else if (player->shield_normal_time_left)
10559         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10560     }
10561   }
10562
10563   CheckLevelTime();
10564
10565   DrawAllPlayers();
10566   PlayAllPlayersSound();
10567
10568   if (options.debug)                    /* calculate frames per second */
10569   {
10570     static unsigned long fps_counter = 0;
10571     static int fps_frames = 0;
10572     unsigned long fps_delay_ms = Counter() - fps_counter;
10573
10574     fps_frames++;
10575
10576     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10577     {
10578       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10579
10580       fps_frames = 0;
10581       fps_counter = Counter();
10582     }
10583
10584     redraw_mask |= REDRAW_FPS;
10585   }
10586
10587   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10588
10589   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10590   {
10591     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10592
10593     local_player->show_envelope = 0;
10594   }
10595
10596   /* use random number generator in every frame to make it less predictable */
10597   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10598     RND(1);
10599 }
10600
10601 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10602 {
10603   int min_x = x, min_y = y, max_x = x, max_y = y;
10604   int i;
10605
10606   for (i = 0; i < MAX_PLAYERS; i++)
10607   {
10608     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10609
10610     if (!stored_player[i].active || &stored_player[i] == player)
10611       continue;
10612
10613     min_x = MIN(min_x, jx);
10614     min_y = MIN(min_y, jy);
10615     max_x = MAX(max_x, jx);
10616     max_y = MAX(max_y, jy);
10617   }
10618
10619   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10620 }
10621
10622 static boolean AllPlayersInVisibleScreen()
10623 {
10624   int i;
10625
10626   for (i = 0; i < MAX_PLAYERS; i++)
10627   {
10628     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10629
10630     if (!stored_player[i].active)
10631       continue;
10632
10633     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10634       return FALSE;
10635   }
10636
10637   return TRUE;
10638 }
10639
10640 void ScrollLevel(int dx, int dy)
10641 {
10642   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10643   int x, y;
10644
10645   BlitBitmap(drawto_field, drawto_field,
10646              FX + TILEX * (dx == -1) - softscroll_offset,
10647              FY + TILEY * (dy == -1) - softscroll_offset,
10648              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10649              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10650              FX + TILEX * (dx == 1) - softscroll_offset,
10651              FY + TILEY * (dy == 1) - softscroll_offset);
10652
10653   if (dx)
10654   {
10655     x = (dx == 1 ? BX1 : BX2);
10656     for (y = BY1; y <= BY2; y++)
10657       DrawScreenField(x, y);
10658   }
10659
10660   if (dy)
10661   {
10662     y = (dy == 1 ? BY1 : BY2);
10663     for (x = BX1; x <= BX2; x++)
10664       DrawScreenField(x, y);
10665   }
10666
10667   redraw_mask |= REDRAW_FIELD;
10668 }
10669
10670 static boolean canFallDown(struct PlayerInfo *player)
10671 {
10672   int jx = player->jx, jy = player->jy;
10673
10674   return (IN_LEV_FIELD(jx, jy + 1) &&
10675           (IS_FREE(jx, jy + 1) ||
10676            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10677           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10678           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10679 }
10680
10681 static boolean canPassField(int x, int y, int move_dir)
10682 {
10683   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10684   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10685   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10686   int nextx = x + dx;
10687   int nexty = y + dy;
10688   int element = Feld[x][y];
10689
10690   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10691           !CAN_MOVE(element) &&
10692           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10693           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10694           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10695 }
10696
10697 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10698 {
10699   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10700   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10701   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10702   int newx = x + dx;
10703   int newy = y + dy;
10704
10705   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10706           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10707           (IS_DIGGABLE(Feld[newx][newy]) ||
10708            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10709            canPassField(newx, newy, move_dir)));
10710 }
10711
10712 static void CheckGravityMovement(struct PlayerInfo *player)
10713 {
10714 #if USE_PLAYER_GRAVITY
10715   if (player->gravity && !player->programmed_action)
10716 #else
10717   if (game.gravity && !player->programmed_action)
10718 #endif
10719   {
10720     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10721     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10722     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
10723     int jx = player->jx, jy = player->jy;
10724     boolean player_is_moving_to_valid_field =
10725       (!player_is_snapping &&
10726        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10727         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10728     boolean player_can_fall_down = canFallDown(player);
10729
10730     if (player_can_fall_down &&
10731         !player_is_moving_to_valid_field)
10732       player->programmed_action = MV_DOWN;
10733   }
10734 }
10735
10736 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10737 {
10738   return CheckGravityMovement(player);
10739
10740 #if USE_PLAYER_GRAVITY
10741   if (player->gravity && !player->programmed_action)
10742 #else
10743   if (game.gravity && !player->programmed_action)
10744 #endif
10745   {
10746     int jx = player->jx, jy = player->jy;
10747     boolean field_under_player_is_free =
10748       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10749     boolean player_is_standing_on_valid_field =
10750       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10751        (IS_WALKABLE(Feld[jx][jy]) &&
10752         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10753
10754     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10755       player->programmed_action = MV_DOWN;
10756   }
10757 }
10758
10759 /*
10760   MovePlayerOneStep()
10761   -----------------------------------------------------------------------------
10762   dx, dy:               direction (non-diagonal) to try to move the player to
10763   real_dx, real_dy:     direction as read from input device (can be diagonal)
10764 */
10765
10766 boolean MovePlayerOneStep(struct PlayerInfo *player,
10767                           int dx, int dy, int real_dx, int real_dy)
10768 {
10769   int jx = player->jx, jy = player->jy;
10770   int new_jx = jx + dx, new_jy = jy + dy;
10771 #if !USE_FIXED_DONT_RUN_INTO
10772   int element;
10773 #endif
10774   int can_move;
10775   boolean player_can_move = !player->cannot_move;
10776
10777   if (!player->active || (!dx && !dy))
10778     return MP_NO_ACTION;
10779
10780   player->MovDir = (dx < 0 ? MV_LEFT :
10781                     dx > 0 ? MV_RIGHT :
10782                     dy < 0 ? MV_UP :
10783                     dy > 0 ? MV_DOWN :  MV_NONE);
10784
10785   if (!IN_LEV_FIELD(new_jx, new_jy))
10786     return MP_NO_ACTION;
10787
10788   if (!player_can_move)
10789   {
10790 #if 1
10791     if (player->MovPos == 0)
10792     {
10793       player->is_moving = FALSE;
10794       player->is_digging = FALSE;
10795       player->is_collecting = FALSE;
10796       player->is_snapping = FALSE;
10797       player->is_pushing = FALSE;
10798     }
10799 #else
10800     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10801     SnapField(player, 0, 0);
10802 #endif
10803
10804 #if 0
10805     return MP_NO_ACTION;
10806 #endif
10807   }
10808
10809 #if 1
10810   if (!options.network && game.centered_player_nr == -1 &&
10811       !AllPlayersInSight(player, new_jx, new_jy))
10812     return MP_NO_ACTION;
10813 #else
10814   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10815     return MP_NO_ACTION;
10816 #endif
10817
10818 #if !USE_FIXED_DONT_RUN_INTO
10819   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10820
10821   /* (moved to DigField()) */
10822   if (player_can_move && DONT_RUN_INTO(element))
10823   {
10824     if (element == EL_ACID && dx == 0 && dy == 1)
10825     {
10826       SplashAcid(new_jx, new_jy);
10827       Feld[jx][jy] = EL_PLAYER_1;
10828       InitMovingField(jx, jy, MV_DOWN);
10829       Store[jx][jy] = EL_ACID;
10830       ContinueMoving(jx, jy);
10831       BuryPlayer(player);
10832     }
10833     else
10834       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10835
10836     return MP_MOVING;
10837   }
10838 #endif
10839
10840   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10841 #if 0
10842 #if USE_FIXED_DONT_RUN_INTO
10843   if (can_move == MP_DONT_RUN_INTO)
10844     return MP_MOVING;
10845 #endif
10846 #endif
10847   if (can_move != MP_MOVING)
10848     return can_move;
10849
10850 #if USE_FIXED_DONT_RUN_INTO
10851 #endif
10852
10853   /* check if DigField() has caused relocation of the player */
10854   if (player->jx != jx || player->jy != jy)
10855     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10856
10857   StorePlayer[jx][jy] = 0;
10858   player->last_jx = jx;
10859   player->last_jy = jy;
10860   player->jx = new_jx;
10861   player->jy = new_jy;
10862   StorePlayer[new_jx][new_jy] = player->element_nr;
10863
10864   if (player->move_delay_value_next != -1)
10865   {
10866     player->move_delay_value = player->move_delay_value_next;
10867     player->move_delay_value_next = -1;
10868   }
10869
10870   player->MovPos =
10871     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10872
10873   player->step_counter++;
10874
10875   PlayerVisit[jx][jy] = FrameCounter;
10876
10877 #if USE_UFAST_PLAYER_EXIT_BUGFIX
10878   player->is_moving = TRUE;
10879 #endif
10880
10881 #if 1
10882   /* should better be called in MovePlayer(), but this breaks some tapes */
10883   ScrollPlayer(player, SCROLL_INIT);
10884 #endif
10885
10886   return MP_MOVING;
10887 }
10888
10889 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10890 {
10891   int jx = player->jx, jy = player->jy;
10892   int old_jx = jx, old_jy = jy;
10893   int moved = MP_NO_ACTION;
10894
10895   if (!player->active)
10896     return FALSE;
10897
10898   if (!dx && !dy)
10899   {
10900     if (player->MovPos == 0)
10901     {
10902       player->is_moving = FALSE;
10903       player->is_digging = FALSE;
10904       player->is_collecting = FALSE;
10905       player->is_snapping = FALSE;
10906       player->is_pushing = FALSE;
10907     }
10908
10909     return FALSE;
10910   }
10911
10912   if (player->move_delay > 0)
10913     return FALSE;
10914
10915   player->move_delay = -1;              /* set to "uninitialized" value */
10916
10917   /* store if player is automatically moved to next field */
10918   player->is_auto_moving = (player->programmed_action != MV_NONE);
10919
10920   /* remove the last programmed player action */
10921   player->programmed_action = 0;
10922
10923   if (player->MovPos)
10924   {
10925     /* should only happen if pre-1.2 tape recordings are played */
10926     /* this is only for backward compatibility */
10927
10928     int original_move_delay_value = player->move_delay_value;
10929
10930 #if DEBUG
10931     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10932            tape.counter);
10933 #endif
10934
10935     /* scroll remaining steps with finest movement resolution */
10936     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10937
10938     while (player->MovPos)
10939     {
10940       ScrollPlayer(player, SCROLL_GO_ON);
10941       ScrollScreen(NULL, SCROLL_GO_ON);
10942
10943       AdvanceFrameAndPlayerCounters(player->index_nr);
10944
10945       DrawAllPlayers();
10946       BackToFront();
10947     }
10948
10949     player->move_delay_value = original_move_delay_value;
10950   }
10951
10952   player->is_active = FALSE;
10953
10954   if (player->last_move_dir & MV_HORIZONTAL)
10955   {
10956     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10957       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10958   }
10959   else
10960   {
10961     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10962       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10963   }
10964
10965 #if USE_FIXED_BORDER_RUNNING_GFX
10966   if (!moved && !player->is_active)
10967   {
10968     player->is_moving = FALSE;
10969     player->is_digging = FALSE;
10970     player->is_collecting = FALSE;
10971     player->is_snapping = FALSE;
10972     player->is_pushing = FALSE;
10973   }
10974 #endif
10975
10976   jx = player->jx;
10977   jy = player->jy;
10978
10979 #if 1
10980   if (moved & MP_MOVING && !ScreenMovPos &&
10981       (player->index_nr == game.centered_player_nr ||
10982        game.centered_player_nr == -1))
10983 #else
10984   if (moved & MP_MOVING && !ScreenMovPos &&
10985       (player == local_player || !options.network))
10986 #endif
10987   {
10988     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10989     int offset = (setup.scroll_delay ? 3 : 0);
10990
10991     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10992     {
10993       /* actual player has left the screen -- scroll in that direction */
10994       if (jx != old_jx)         /* player has moved horizontally */
10995         scroll_x += (jx - old_jx);
10996       else                      /* player has moved vertically */
10997         scroll_y += (jy - old_jy);
10998     }
10999     else
11000     {
11001       if (jx != old_jx)         /* player has moved horizontally */
11002       {
11003         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
11004             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
11005           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
11006
11007         /* don't scroll over playfield boundaries */
11008         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
11009           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
11010
11011         /* don't scroll more than one field at a time */
11012         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
11013
11014         /* don't scroll against the player's moving direction */
11015         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
11016             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
11017           scroll_x = old_scroll_x;
11018       }
11019       else                      /* player has moved vertically */
11020       {
11021         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
11022             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
11023           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
11024
11025         /* don't scroll over playfield boundaries */
11026         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
11027           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
11028
11029         /* don't scroll more than one field at a time */
11030         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
11031
11032         /* don't scroll against the player's moving direction */
11033         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
11034             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
11035           scroll_y = old_scroll_y;
11036       }
11037     }
11038
11039     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
11040     {
11041 #if 1
11042       if (!options.network && game.centered_player_nr == -1 &&
11043           !AllPlayersInVisibleScreen())
11044       {
11045         scroll_x = old_scroll_x;
11046         scroll_y = old_scroll_y;
11047       }
11048       else
11049 #else
11050       if (!options.network && !AllPlayersInVisibleScreen())
11051       {
11052         scroll_x = old_scroll_x;
11053         scroll_y = old_scroll_y;
11054       }
11055       else
11056 #endif
11057       {
11058         ScrollScreen(player, SCROLL_INIT);
11059         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
11060       }
11061     }
11062   }
11063
11064   player->StepFrame = 0;
11065
11066   if (moved & MP_MOVING)
11067   {
11068     if (old_jx != jx && old_jy == jy)
11069       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
11070     else if (old_jx == jx && old_jy != jy)
11071       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
11072
11073     DrawLevelField(jx, jy);     /* for "crumbled sand" */
11074
11075     player->last_move_dir = player->MovDir;
11076     player->is_moving = TRUE;
11077     player->is_snapping = FALSE;
11078     player->is_switching = FALSE;
11079     player->is_dropping = FALSE;
11080     player->is_dropping_pressed = FALSE;
11081     player->drop_pressed_delay = 0;
11082
11083 #if 0
11084     /* should better be called here than above, but this breaks some tapes */
11085     ScrollPlayer(player, SCROLL_INIT);
11086 #endif
11087   }
11088   else
11089   {
11090     CheckGravityMovementWhenNotMoving(player);
11091
11092     player->is_moving = FALSE;
11093
11094     /* at this point, the player is allowed to move, but cannot move right now
11095        (e.g. because of something blocking the way) -- ensure that the player
11096        is also allowed to move in the next frame (in old versions before 3.1.1,
11097        the player was forced to wait again for eight frames before next try) */
11098
11099     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11100       player->move_delay = 0;   /* allow direct movement in the next frame */
11101   }
11102
11103   if (player->move_delay == -1)         /* not yet initialized by DigField() */
11104     player->move_delay = player->move_delay_value;
11105
11106   if (game.engine_version < VERSION_IDENT(3,0,7,0))
11107   {
11108     TestIfPlayerTouchesBadThing(jx, jy);
11109     TestIfPlayerTouchesCustomElement(jx, jy);
11110   }
11111
11112   if (!player->active)
11113     RemovePlayer(player);
11114
11115   return moved;
11116 }
11117
11118 void ScrollPlayer(struct PlayerInfo *player, int mode)
11119 {
11120   int jx = player->jx, jy = player->jy;
11121   int last_jx = player->last_jx, last_jy = player->last_jy;
11122   int move_stepsize = TILEX / player->move_delay_value;
11123
11124 #if USE_NEW_PLAYER_SPEED
11125   if (!player->active)
11126     return;
11127
11128   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
11129     return;
11130 #else
11131   if (!player->active || player->MovPos == 0)
11132     return;
11133 #endif
11134
11135   if (mode == SCROLL_INIT)
11136   {
11137     player->actual_frame_counter = FrameCounter;
11138     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11139
11140     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
11141         Feld[last_jx][last_jy] == EL_EMPTY)
11142     {
11143       int last_field_block_delay = 0;   /* start with no blocking at all */
11144       int block_delay_adjustment = player->block_delay_adjustment;
11145
11146       /* if player blocks last field, add delay for exactly one move */
11147       if (player->block_last_field)
11148       {
11149         last_field_block_delay += player->move_delay_value;
11150
11151         /* when blocking enabled, prevent moving up despite gravity */
11152 #if USE_PLAYER_GRAVITY
11153         if (player->gravity && player->MovDir == MV_UP)
11154           block_delay_adjustment = -1;
11155 #else
11156         if (game.gravity && player->MovDir == MV_UP)
11157           block_delay_adjustment = -1;
11158 #endif
11159       }
11160
11161       /* add block delay adjustment (also possible when not blocking) */
11162       last_field_block_delay += block_delay_adjustment;
11163
11164       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
11165       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
11166     }
11167
11168 #if USE_NEW_PLAYER_SPEED
11169     if (player->MovPos != 0)    /* player has not yet reached destination */
11170       return;
11171 #else
11172     return;
11173 #endif
11174   }
11175   else if (!FrameReached(&player->actual_frame_counter, 1))
11176     return;
11177
11178 #if 0
11179   printf("::: player->MovPos: %d -> %d\n",
11180          player->MovPos,
11181          player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
11182 #endif
11183
11184 #if USE_NEW_PLAYER_SPEED
11185   if (player->MovPos != 0)
11186   {
11187     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11188     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11189
11190     /* before DrawPlayer() to draw correct player graphic for this case */
11191     if (player->MovPos == 0)
11192       CheckGravityMovement(player);
11193   }
11194 #else
11195   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
11196   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
11197
11198   /* before DrawPlayer() to draw correct player graphic for this case */
11199   if (player->MovPos == 0)
11200     CheckGravityMovement(player);
11201 #endif
11202
11203   if (player->MovPos == 0)      /* player reached destination field */
11204   {
11205 #if 0
11206     printf("::: player reached destination field\n");
11207 #endif
11208
11209     if (player->move_delay_reset_counter > 0)
11210     {
11211       player->move_delay_reset_counter--;
11212
11213       if (player->move_delay_reset_counter == 0)
11214       {
11215         /* continue with normal speed after quickly moving through gate */
11216         HALVE_PLAYER_SPEED(player);
11217
11218         /* be able to make the next move without delay */
11219         player->move_delay = 0;
11220       }
11221     }
11222
11223     player->last_jx = jx;
11224     player->last_jy = jy;
11225
11226     if (Feld[jx][jy] == EL_EXIT_OPEN ||
11227         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
11228         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
11229     {
11230       DrawPlayer(player);       /* needed here only to cleanup last field */
11231       RemovePlayer(player);
11232
11233       if (local_player->friends_still_needed == 0 ||
11234           IS_SP_ELEMENT(Feld[jx][jy]))
11235         player->LevelSolved = player->GameOver = TRUE;
11236     }
11237
11238     /* this breaks one level: "machine", level 000 */
11239     {
11240       int move_direction = player->MovDir;
11241       int enter_side = MV_DIR_OPPOSITE(move_direction);
11242       int leave_side = move_direction;
11243       int old_jx = last_jx;
11244       int old_jy = last_jy;
11245       int old_element = Feld[old_jx][old_jy];
11246       int new_element = Feld[jx][jy];
11247
11248       if (IS_CUSTOM_ELEMENT(old_element))
11249         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
11250                                    CE_LEFT_BY_PLAYER,
11251                                    player->index_bit, leave_side);
11252
11253       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
11254                                           CE_PLAYER_LEAVES_X,
11255                                           player->index_bit, leave_side);
11256
11257       if (IS_CUSTOM_ELEMENT(new_element))
11258         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
11259                                    player->index_bit, enter_side);
11260
11261       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
11262                                           CE_PLAYER_ENTERS_X,
11263                                           player->index_bit, enter_side);
11264
11265       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
11266                                         CE_MOVE_OF_X, move_direction);
11267     }
11268
11269     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11270     {
11271       TestIfPlayerTouchesBadThing(jx, jy);
11272       TestIfPlayerTouchesCustomElement(jx, jy);
11273
11274       /* needed because pushed element has not yet reached its destination,
11275          so it would trigger a change event at its previous field location */
11276       if (!player->is_pushing)
11277         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
11278
11279       if (!player->active)
11280         RemovePlayer(player);
11281     }
11282
11283     if (level.use_step_counter)
11284     {
11285       int i;
11286
11287       TimePlayed++;
11288
11289       if (TimeLeft > 0)
11290       {
11291         TimeLeft--;
11292
11293         if (TimeLeft <= 10 && setup.time_limit)
11294           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
11295
11296         DrawGameValue_Time(TimeLeft);
11297
11298         if (!TimeLeft && setup.time_limit)
11299           for (i = 0; i < MAX_PLAYERS; i++)
11300             KillPlayer(&stored_player[i]);
11301       }
11302       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11303         DrawGameValue_Time(TimePlayed);
11304     }
11305
11306     if (tape.single_step && tape.recording && !tape.pausing &&
11307         !player->programmed_action)
11308       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11309   }
11310 }
11311
11312 void ScrollScreen(struct PlayerInfo *player, int mode)
11313 {
11314   static unsigned long screen_frame_counter = 0;
11315
11316   if (mode == SCROLL_INIT)
11317   {
11318     /* set scrolling step size according to actual player's moving speed */
11319     ScrollStepSize = TILEX / player->move_delay_value;
11320
11321     screen_frame_counter = FrameCounter;
11322     ScreenMovDir = player->MovDir;
11323     ScreenMovPos = player->MovPos;
11324     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11325     return;
11326   }
11327   else if (!FrameReached(&screen_frame_counter, 1))
11328     return;
11329
11330   if (ScreenMovPos)
11331   {
11332     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11333     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11334     redraw_mask |= REDRAW_FIELD;
11335   }
11336   else
11337     ScreenMovDir = MV_NONE;
11338 }
11339
11340 void TestIfPlayerTouchesCustomElement(int x, int y)
11341 {
11342   static int xy[4][2] =
11343   {
11344     { 0, -1 },
11345     { -1, 0 },
11346     { +1, 0 },
11347     { 0, +1 }
11348   };
11349   static int trigger_sides[4][2] =
11350   {
11351     /* center side       border side */
11352     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11353     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11354     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11355     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11356   };
11357   static int touch_dir[4] =
11358   {
11359     MV_LEFT | MV_RIGHT,
11360     MV_UP   | MV_DOWN,
11361     MV_UP   | MV_DOWN,
11362     MV_LEFT | MV_RIGHT
11363   };
11364   int center_element = Feld[x][y];      /* should always be non-moving! */
11365   int i;
11366
11367   for (i = 0; i < NUM_DIRECTIONS; i++)
11368   {
11369     int xx = x + xy[i][0];
11370     int yy = y + xy[i][1];
11371     int center_side = trigger_sides[i][0];
11372     int border_side = trigger_sides[i][1];
11373     int border_element;
11374
11375     if (!IN_LEV_FIELD(xx, yy))
11376       continue;
11377
11378     if (IS_PLAYER(x, y))
11379     {
11380       struct PlayerInfo *player = PLAYERINFO(x, y);
11381
11382       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11383         border_element = Feld[xx][yy];          /* may be moving! */
11384       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11385         border_element = Feld[xx][yy];
11386       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11387         border_element = MovingOrBlocked2Element(xx, yy);
11388       else
11389         continue;               /* center and border element do not touch */
11390
11391       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11392                                  player->index_bit, border_side);
11393       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11394                                           CE_PLAYER_TOUCHES_X,
11395                                           player->index_bit, border_side);
11396     }
11397     else if (IS_PLAYER(xx, yy))
11398     {
11399       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11400
11401       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11402       {
11403         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11404           continue;             /* center and border element do not touch */
11405       }
11406
11407       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11408                                  player->index_bit, center_side);
11409       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11410                                           CE_PLAYER_TOUCHES_X,
11411                                           player->index_bit, center_side);
11412       break;
11413     }
11414   }
11415 }
11416
11417 #if USE_ELEMENT_TOUCHING_BUGFIX
11418
11419 void TestIfElementTouchesCustomElement(int x, int y)
11420 {
11421   static int xy[4][2] =
11422   {
11423     { 0, -1 },
11424     { -1, 0 },
11425     { +1, 0 },
11426     { 0, +1 }
11427   };
11428   static int trigger_sides[4][2] =
11429   {
11430     /* center side      border side */
11431     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11432     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11433     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11434     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11435   };
11436   static int touch_dir[4] =
11437   {
11438     MV_LEFT | MV_RIGHT,
11439     MV_UP   | MV_DOWN,
11440     MV_UP   | MV_DOWN,
11441     MV_LEFT | MV_RIGHT
11442   };
11443   boolean change_center_element = FALSE;
11444   int center_element = Feld[x][y];      /* should always be non-moving! */
11445   int border_element_old[NUM_DIRECTIONS];
11446   int i;
11447
11448   for (i = 0; i < NUM_DIRECTIONS; i++)
11449   {
11450     int xx = x + xy[i][0];
11451     int yy = y + xy[i][1];
11452     int border_element;
11453
11454     border_element_old[i] = -1;
11455
11456     if (!IN_LEV_FIELD(xx, yy))
11457       continue;
11458
11459     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11460       border_element = Feld[xx][yy];    /* may be moving! */
11461     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11462       border_element = Feld[xx][yy];
11463     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11464       border_element = MovingOrBlocked2Element(xx, yy);
11465     else
11466       continue;                 /* center and border element do not touch */
11467
11468     border_element_old[i] = border_element;
11469   }
11470
11471   for (i = 0; i < NUM_DIRECTIONS; i++)
11472   {
11473     int xx = x + xy[i][0];
11474     int yy = y + xy[i][1];
11475     int center_side = trigger_sides[i][0];
11476     int border_element = border_element_old[i];
11477
11478     if (border_element == -1)
11479       continue;
11480
11481     /* check for change of border element */
11482     CheckElementChangeBySide(xx, yy, border_element, center_element,
11483                              CE_TOUCHING_X, center_side);
11484   }
11485
11486   for (i = 0; i < NUM_DIRECTIONS; i++)
11487   {
11488     int border_side = trigger_sides[i][1];
11489     int border_element = border_element_old[i];
11490
11491     if (border_element == -1)
11492       continue;
11493
11494     /* check for change of center element (but change it only once) */
11495     if (!change_center_element)
11496       change_center_element =
11497         CheckElementChangeBySide(x, y, center_element, border_element,
11498                                  CE_TOUCHING_X, border_side);
11499   }
11500 }
11501
11502 #else
11503
11504 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11505 {
11506   static int xy[4][2] =
11507   {
11508     { 0, -1 },
11509     { -1, 0 },
11510     { +1, 0 },
11511     { 0, +1 }
11512   };
11513   static int trigger_sides[4][2] =
11514   {
11515     /* center side      border side */
11516     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11517     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11518     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11519     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11520   };
11521   static int touch_dir[4] =
11522   {
11523     MV_LEFT | MV_RIGHT,
11524     MV_UP   | MV_DOWN,
11525     MV_UP   | MV_DOWN,
11526     MV_LEFT | MV_RIGHT
11527   };
11528   boolean change_center_element = FALSE;
11529   int center_element = Feld[x][y];      /* should always be non-moving! */
11530   int i;
11531
11532   for (i = 0; i < NUM_DIRECTIONS; i++)
11533   {
11534     int xx = x + xy[i][0];
11535     int yy = y + xy[i][1];
11536     int center_side = trigger_sides[i][0];
11537     int border_side = trigger_sides[i][1];
11538     int border_element;
11539
11540     if (!IN_LEV_FIELD(xx, yy))
11541       continue;
11542
11543     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11544       border_element = Feld[xx][yy];    /* may be moving! */
11545     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11546       border_element = Feld[xx][yy];
11547     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11548       border_element = MovingOrBlocked2Element(xx, yy);
11549     else
11550       continue;                 /* center and border element do not touch */
11551
11552     /* check for change of center element (but change it only once) */
11553     if (!change_center_element)
11554       change_center_element =
11555         CheckElementChangeBySide(x, y, center_element, border_element,
11556                                  CE_TOUCHING_X, border_side);
11557
11558     /* check for change of border element */
11559     CheckElementChangeBySide(xx, yy, border_element, center_element,
11560                              CE_TOUCHING_X, center_side);
11561   }
11562 }
11563
11564 #endif
11565
11566 void TestIfElementHitsCustomElement(int x, int y, int direction)
11567 {
11568   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11569   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11570   int hitx = x + dx, hity = y + dy;
11571   int hitting_element = Feld[x][y];
11572   int touched_element;
11573
11574   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11575     return;
11576
11577   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11578                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11579
11580   if (IN_LEV_FIELD(hitx, hity))
11581   {
11582     int opposite_direction = MV_DIR_OPPOSITE(direction);
11583     int hitting_side = direction;
11584     int touched_side = opposite_direction;
11585     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11586                           MovDir[hitx][hity] != direction ||
11587                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11588
11589     object_hit = TRUE;
11590
11591     if (object_hit)
11592     {
11593       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11594                                CE_HITTING_X, touched_side);
11595
11596       CheckElementChangeBySide(hitx, hity, touched_element,
11597                                hitting_element, CE_HIT_BY_X, hitting_side);
11598
11599       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11600                                CE_HIT_BY_SOMETHING, opposite_direction);
11601     }
11602   }
11603
11604   /* "hitting something" is also true when hitting the playfield border */
11605   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11606                            CE_HITTING_SOMETHING, direction);
11607 }
11608
11609 #if 0
11610 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11611 {
11612   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11613   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11614   int hitx = x + dx, hity = y + dy;
11615   int hitting_element = Feld[x][y];
11616   int touched_element;
11617 #if 0
11618   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11619                         !IS_FREE(hitx, hity) &&
11620                         (!IS_MOVING(hitx, hity) ||
11621                          MovDir[hitx][hity] != direction ||
11622                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11623 #endif
11624
11625   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11626     return;
11627
11628 #if 0
11629   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11630     return;
11631 #endif
11632
11633   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11634                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11635
11636   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11637                            EP_CAN_SMASH_EVERYTHING, direction);
11638
11639   if (IN_LEV_FIELD(hitx, hity))
11640   {
11641     int opposite_direction = MV_DIR_OPPOSITE(direction);
11642     int hitting_side = direction;
11643     int touched_side = opposite_direction;
11644 #if 0
11645     int touched_element = MovingOrBlocked2Element(hitx, hity);
11646 #endif
11647 #if 1
11648     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11649                           MovDir[hitx][hity] != direction ||
11650                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11651
11652     object_hit = TRUE;
11653 #endif
11654
11655     if (object_hit)
11656     {
11657       int i;
11658
11659       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11660                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11661
11662       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11663                                CE_OTHER_IS_SMASHING, touched_side);
11664
11665       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11666                                CE_OTHER_GETS_SMASHED, hitting_side);
11667     }
11668   }
11669 }
11670 #endif
11671
11672 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11673 {
11674   int i, kill_x = -1, kill_y = -1;
11675
11676   int bad_element = -1;
11677   static int test_xy[4][2] =
11678   {
11679     { 0, -1 },
11680     { -1, 0 },
11681     { +1, 0 },
11682     { 0, +1 }
11683   };
11684   static int test_dir[4] =
11685   {
11686     MV_UP,
11687     MV_LEFT,
11688     MV_RIGHT,
11689     MV_DOWN
11690   };
11691
11692   for (i = 0; i < NUM_DIRECTIONS; i++)
11693   {
11694     int test_x, test_y, test_move_dir, test_element;
11695
11696     test_x = good_x + test_xy[i][0];
11697     test_y = good_y + test_xy[i][1];
11698
11699     if (!IN_LEV_FIELD(test_x, test_y))
11700       continue;
11701
11702     test_move_dir =
11703       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11704
11705     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11706
11707     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11708        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11709     */
11710     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11711         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11712     {
11713       kill_x = test_x;
11714       kill_y = test_y;
11715       bad_element = test_element;
11716
11717       break;
11718     }
11719   }
11720
11721   if (kill_x != -1 || kill_y != -1)
11722   {
11723     if (IS_PLAYER(good_x, good_y))
11724     {
11725       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11726
11727       if (player->shield_deadly_time_left > 0 &&
11728           !IS_INDESTRUCTIBLE(bad_element))
11729         Bang(kill_x, kill_y);
11730       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11731         KillPlayer(player);
11732     }
11733     else
11734       Bang(good_x, good_y);
11735   }
11736 }
11737
11738 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11739 {
11740   int i, kill_x = -1, kill_y = -1;
11741   int bad_element = Feld[bad_x][bad_y];
11742   static int test_xy[4][2] =
11743   {
11744     { 0, -1 },
11745     { -1, 0 },
11746     { +1, 0 },
11747     { 0, +1 }
11748   };
11749   static int touch_dir[4] =
11750   {
11751     MV_LEFT | MV_RIGHT,
11752     MV_UP   | MV_DOWN,
11753     MV_UP   | MV_DOWN,
11754     MV_LEFT | MV_RIGHT
11755   };
11756   static int test_dir[4] =
11757   {
11758     MV_UP,
11759     MV_LEFT,
11760     MV_RIGHT,
11761     MV_DOWN
11762   };
11763
11764   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11765     return;
11766
11767   for (i = 0; i < NUM_DIRECTIONS; i++)
11768   {
11769     int test_x, test_y, test_move_dir, test_element;
11770
11771     test_x = bad_x + test_xy[i][0];
11772     test_y = bad_y + test_xy[i][1];
11773     if (!IN_LEV_FIELD(test_x, test_y))
11774       continue;
11775
11776     test_move_dir =
11777       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11778
11779     test_element = Feld[test_x][test_y];
11780
11781     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11782        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11783     */
11784     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11785         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11786     {
11787       /* good thing is player or penguin that does not move away */
11788       if (IS_PLAYER(test_x, test_y))
11789       {
11790         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11791
11792         if (bad_element == EL_ROBOT && player->is_moving)
11793           continue;     /* robot does not kill player if he is moving */
11794
11795         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11796         {
11797           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11798             continue;           /* center and border element do not touch */
11799         }
11800
11801         kill_x = test_x;
11802         kill_y = test_y;
11803         break;
11804       }
11805       else if (test_element == EL_PENGUIN)
11806       {
11807         kill_x = test_x;
11808         kill_y = test_y;
11809         break;
11810       }
11811     }
11812   }
11813
11814   if (kill_x != -1 || kill_y != -1)
11815   {
11816     if (IS_PLAYER(kill_x, kill_y))
11817     {
11818       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11819
11820       if (player->shield_deadly_time_left > 0 &&
11821           !IS_INDESTRUCTIBLE(bad_element))
11822         Bang(bad_x, bad_y);
11823       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11824         KillPlayer(player);
11825     }
11826     else
11827       Bang(kill_x, kill_y);
11828   }
11829 }
11830
11831 void TestIfPlayerTouchesBadThing(int x, int y)
11832 {
11833   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11834 }
11835
11836 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11837 {
11838   TestIfGoodThingHitsBadThing(x, y, move_dir);
11839 }
11840
11841 void TestIfBadThingTouchesPlayer(int x, int y)
11842 {
11843   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11844 }
11845
11846 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11847 {
11848   TestIfBadThingHitsGoodThing(x, y, move_dir);
11849 }
11850
11851 void TestIfFriendTouchesBadThing(int x, int y)
11852 {
11853   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11854 }
11855
11856 void TestIfBadThingTouchesFriend(int x, int y)
11857 {
11858   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11859 }
11860
11861 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11862 {
11863   int i, kill_x = bad_x, kill_y = bad_y;
11864   static int xy[4][2] =
11865   {
11866     { 0, -1 },
11867     { -1, 0 },
11868     { +1, 0 },
11869     { 0, +1 }
11870   };
11871
11872   for (i = 0; i < NUM_DIRECTIONS; i++)
11873   {
11874     int x, y, element;
11875
11876     x = bad_x + xy[i][0];
11877     y = bad_y + xy[i][1];
11878     if (!IN_LEV_FIELD(x, y))
11879       continue;
11880
11881     element = Feld[x][y];
11882     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11883         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11884     {
11885       kill_x = x;
11886       kill_y = y;
11887       break;
11888     }
11889   }
11890
11891   if (kill_x != bad_x || kill_y != bad_y)
11892     Bang(bad_x, bad_y);
11893 }
11894
11895 void KillPlayer(struct PlayerInfo *player)
11896 {
11897   int jx = player->jx, jy = player->jy;
11898
11899   if (!player->active)
11900     return;
11901
11902   /* remove accessible field at the player's position */
11903   Feld[jx][jy] = EL_EMPTY;
11904
11905   /* deactivate shield (else Bang()/Explode() would not work right) */
11906   player->shield_normal_time_left = 0;
11907   player->shield_deadly_time_left = 0;
11908
11909   Bang(jx, jy);
11910   BuryPlayer(player);
11911 }
11912
11913 static void KillPlayerUnlessEnemyProtected(int x, int y)
11914 {
11915   if (!PLAYER_ENEMY_PROTECTED(x, y))
11916     KillPlayer(PLAYERINFO(x, y));
11917 }
11918
11919 static void KillPlayerUnlessExplosionProtected(int x, int y)
11920 {
11921   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11922     KillPlayer(PLAYERINFO(x, y));
11923 }
11924
11925 void BuryPlayer(struct PlayerInfo *player)
11926 {
11927   int jx = player->jx, jy = player->jy;
11928
11929   if (!player->active)
11930     return;
11931
11932   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11933   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11934
11935   player->GameOver = TRUE;
11936   RemovePlayer(player);
11937 }
11938
11939 void RemovePlayer(struct PlayerInfo *player)
11940 {
11941   int jx = player->jx, jy = player->jy;
11942   int i, found = FALSE;
11943
11944   player->present = FALSE;
11945   player->active = FALSE;
11946
11947   if (!ExplodeField[jx][jy])
11948     StorePlayer[jx][jy] = 0;
11949
11950   if (player->is_moving)
11951     DrawLevelField(player->last_jx, player->last_jy);
11952
11953   for (i = 0; i < MAX_PLAYERS; i++)
11954     if (stored_player[i].active)
11955       found = TRUE;
11956
11957   if (!found)
11958     AllPlayersGone = TRUE;
11959
11960   ExitX = ZX = jx;
11961   ExitY = ZY = jy;
11962 }
11963
11964 #if USE_NEW_SNAP_DELAY
11965 static void setFieldForSnapping(int x, int y, int element, int direction)
11966 {
11967   struct ElementInfo *ei = &element_info[element];
11968   int direction_bit = MV_DIR_TO_BIT(direction);
11969   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11970   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11971                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11972
11973   Feld[x][y] = EL_ELEMENT_SNAPPING;
11974   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11975
11976   ResetGfxAnimation(x, y);
11977
11978   GfxElement[x][y] = element;
11979   GfxAction[x][y] = action;
11980   GfxDir[x][y] = direction;
11981   GfxFrame[x][y] = -1;
11982 }
11983 #endif
11984
11985 /*
11986   =============================================================================
11987   checkDiagonalPushing()
11988   -----------------------------------------------------------------------------
11989   check if diagonal input device direction results in pushing of object
11990   (by checking if the alternative direction is walkable, diggable, ...)
11991   =============================================================================
11992 */
11993
11994 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11995                                     int x, int y, int real_dx, int real_dy)
11996 {
11997   int jx, jy, dx, dy, xx, yy;
11998
11999   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
12000     return TRUE;
12001
12002   /* diagonal direction: check alternative direction */
12003   jx = player->jx;
12004   jy = player->jy;
12005   dx = x - jx;
12006   dy = y - jy;
12007   xx = jx + (dx == 0 ? real_dx : 0);
12008   yy = jy + (dy == 0 ? real_dy : 0);
12009
12010   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
12011 }
12012
12013 /*
12014   =============================================================================
12015   DigField()
12016   -----------------------------------------------------------------------------
12017   x, y:                 field next to player (non-diagonal) to try to dig to
12018   real_dx, real_dy:     direction as read from input device (can be diagonal)
12019   =============================================================================
12020 */
12021
12022 int DigField(struct PlayerInfo *player,
12023              int oldx, int oldy, int x, int y,
12024              int real_dx, int real_dy, int mode)
12025 {
12026   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
12027   boolean player_was_pushing = player->is_pushing;
12028   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
12029   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
12030   int jx = oldx, jy = oldy;
12031   int dx = x - jx, dy = y - jy;
12032   int nextx = x + dx, nexty = y + dy;
12033   int move_direction = (dx == -1 ? MV_LEFT  :
12034                         dx == +1 ? MV_RIGHT :
12035                         dy == -1 ? MV_UP    :
12036                         dy == +1 ? MV_DOWN  : MV_NONE);
12037   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
12038   int dig_side = MV_DIR_OPPOSITE(move_direction);
12039   int old_element = Feld[jx][jy];
12040 #if USE_FIXED_DONT_RUN_INTO
12041   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
12042 #else
12043   int element;
12044 #endif
12045   int collect_count;
12046
12047   if (is_player)                /* function can also be called by EL_PENGUIN */
12048   {
12049     if (player->MovPos == 0)
12050     {
12051       player->is_digging = FALSE;
12052       player->is_collecting = FALSE;
12053     }
12054
12055     if (player->MovPos == 0)    /* last pushing move finished */
12056       player->is_pushing = FALSE;
12057
12058     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
12059     {
12060       player->is_switching = FALSE;
12061       player->push_delay = -1;
12062
12063       return MP_NO_ACTION;
12064     }
12065   }
12066
12067 #if !USE_FIXED_DONT_RUN_INTO
12068   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12069     return MP_NO_ACTION;
12070 #endif
12071
12072   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12073     old_element = Back[jx][jy];
12074
12075   /* in case of element dropped at player position, check background */
12076   else if (Back[jx][jy] != EL_EMPTY &&
12077            game.engine_version >= VERSION_IDENT(2,2,0,0))
12078     old_element = Back[jx][jy];
12079
12080   /* checking here causes player to move into acid even if the current field
12081      cannot be left to that direction */
12082 #if 0
12083 #if USE_FIXED_DONT_RUN_INTO
12084   if (player_can_move && DONT_RUN_INTO(element))
12085   {
12086     if (element == EL_ACID && dx == 0 && dy == 1)
12087     {
12088       SplashAcid(x, y);
12089       Feld[jx][jy] = EL_PLAYER_1;
12090       InitMovingField(jx, jy, MV_DOWN);
12091       Store[jx][jy] = EL_ACID;
12092       ContinueMoving(jx, jy);
12093       BuryPlayer(player);
12094     }
12095     else
12096       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12097
12098     return MP_DONT_RUN_INTO;
12099   }
12100 #endif
12101 #endif
12102
12103 #if 1   /* ------------------------------ NEW ------------------------------ */
12104
12105   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12106     return MP_NO_ACTION;        /* field has no opening in this direction */
12107
12108   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12109     return MP_NO_ACTION;        /* field has no opening in this direction */
12110
12111 #if USE_FIXED_DONT_RUN_INTO
12112   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12113   {
12114     SplashAcid(x, y);
12115 #if 1
12116     Feld[jx][jy] = player->artwork_element;
12117 #else
12118     Feld[jx][jy] = EL_PLAYER_1;
12119 #endif
12120     InitMovingField(jx, jy, MV_DOWN);
12121     Store[jx][jy] = EL_ACID;
12122     ContinueMoving(jx, jy);
12123     BuryPlayer(player);
12124
12125     return MP_DONT_RUN_INTO;
12126   }
12127 #endif
12128
12129 #if USE_FIXED_DONT_RUN_INTO
12130   if (player_can_move && DONT_RUN_INTO(element))
12131   {
12132     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12133
12134     return MP_DONT_RUN_INTO;
12135   }
12136 #endif
12137
12138 #else   /* ------------------------------ OLD ------------------------------ */
12139
12140 #if 1
12141 #if USE_FIXED_DONT_RUN_INTO
12142   if (player_can_move && DONT_RUN_INTO(element))
12143   {
12144     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12145
12146     return MP_DONT_RUN_INTO;
12147   }
12148 #endif
12149 #endif
12150
12151   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12152     return MP_NO_ACTION;        /* field has no opening in this direction */
12153
12154   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12155     return MP_NO_ACTION;        /* field has no opening in this direction */
12156
12157   /* checking here causes player to explode when moving into acid */
12158 #if 1
12159 #if USE_FIXED_DONT_RUN_INTO
12160   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
12161   {
12162     SplashAcid(x, y);
12163     Feld[jx][jy] = EL_PLAYER_1;
12164     InitMovingField(jx, jy, MV_DOWN);
12165     Store[jx][jy] = EL_ACID;
12166     ContinueMoving(jx, jy);
12167     BuryPlayer(player);
12168
12169     return MP_DONT_RUN_INTO;
12170   }
12171 #endif
12172 #endif
12173
12174 #endif  /* ------------------------------ END ------------------------------ */
12175
12176 #if 0
12177 #if USE_FIXED_DONT_RUN_INTO
12178   if (player_can_move && DONT_RUN_INTO(element))
12179   {
12180     if (element == EL_ACID && dx == 0 && dy == 1)
12181     {
12182       SplashAcid(x, y);
12183       Feld[jx][jy] = EL_PLAYER_1;
12184       InitMovingField(jx, jy, MV_DOWN);
12185       Store[jx][jy] = EL_ACID;
12186       ContinueMoving(jx, jy);
12187       BuryPlayer(player);
12188     }
12189     else
12190       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12191
12192     return MP_DONT_RUN_INTO;
12193   }
12194 #endif
12195 #endif
12196
12197 #if USE_FIXED_DONT_RUN_INTO
12198   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
12199     return MP_NO_ACTION;
12200 #endif
12201
12202 #if !USE_FIXED_DONT_RUN_INTO
12203   element = Feld[x][y];
12204 #endif
12205
12206   collect_count = element_info[element].collect_count_initial;
12207
12208   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12209     return MP_NO_ACTION;
12210
12211   if (game.engine_version < VERSION_IDENT(2,2,0,0))
12212     player_can_move = player_can_move_or_snap;
12213
12214   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12215       game.engine_version >= VERSION_IDENT(2,2,0,0))
12216   {
12217     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
12218                                player->index_bit, dig_side);
12219     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12220                                         player->index_bit, dig_side);
12221
12222     if (Feld[x][y] != element)          /* field changed by snapping */
12223       return MP_ACTION;
12224
12225     return MP_NO_ACTION;
12226   }
12227
12228 #if USE_PLAYER_GRAVITY
12229   if (player->gravity && is_player && !player->is_auto_moving &&
12230       canFallDown(player) && move_direction != MV_DOWN &&
12231       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12232     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12233 #else
12234   if (game.gravity && is_player && !player->is_auto_moving &&
12235       canFallDown(player) && move_direction != MV_DOWN &&
12236       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12237     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
12238 #endif
12239
12240   if (player_can_move &&
12241       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12242   {
12243     int sound_element = SND_ELEMENT(element);
12244     int sound_action = ACTION_WALKING;
12245
12246     if (IS_RND_GATE(element))
12247     {
12248       if (!player->key[RND_GATE_NR(element)])
12249         return MP_NO_ACTION;
12250     }
12251     else if (IS_RND_GATE_GRAY(element))
12252     {
12253       if (!player->key[RND_GATE_GRAY_NR(element)])
12254         return MP_NO_ACTION;
12255     }
12256     else if (IS_RND_GATE_GRAY_ACTIVE(element))
12257     {
12258       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
12259         return MP_NO_ACTION;
12260     }
12261     else if (element == EL_EXIT_OPEN ||
12262              element == EL_SP_EXIT_OPEN ||
12263              element == EL_SP_EXIT_OPENING)
12264     {
12265       sound_action = ACTION_PASSING;    /* player is passing exit */
12266     }
12267     else if (element == EL_EMPTY)
12268     {
12269       sound_action = ACTION_MOVING;             /* nothing to walk on */
12270     }
12271
12272     /* play sound from background or player, whatever is available */
12273     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12274       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12275     else
12276       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
12277   }
12278   else if (player_can_move &&
12279            IS_PASSABLE(element) && canPassField(x, y, move_direction))
12280   {
12281     if (!ACCESS_FROM(element, opposite_direction))
12282       return MP_NO_ACTION;      /* field not accessible from this direction */
12283
12284     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
12285       return MP_NO_ACTION;
12286
12287     if (IS_EM_GATE(element))
12288     {
12289       if (!player->key[EM_GATE_NR(element)])
12290         return MP_NO_ACTION;
12291     }
12292     else if (IS_EM_GATE_GRAY(element))
12293     {
12294       if (!player->key[EM_GATE_GRAY_NR(element)])
12295         return MP_NO_ACTION;
12296     }
12297     else if (IS_EM_GATE_GRAY_ACTIVE(element))
12298     {
12299       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
12300         return MP_NO_ACTION;
12301     }
12302     else if (IS_EMC_GATE(element))
12303     {
12304       if (!player->key[EMC_GATE_NR(element)])
12305         return MP_NO_ACTION;
12306     }
12307     else if (IS_EMC_GATE_GRAY(element))
12308     {
12309       if (!player->key[EMC_GATE_GRAY_NR(element)])
12310         return MP_NO_ACTION;
12311     }
12312     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
12313     {
12314       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
12315         return MP_NO_ACTION;
12316     }
12317     else if (IS_SP_PORT(element))
12318     {
12319       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12320           element == EL_SP_GRAVITY_PORT_RIGHT ||
12321           element == EL_SP_GRAVITY_PORT_UP ||
12322           element == EL_SP_GRAVITY_PORT_DOWN)
12323 #if USE_PLAYER_GRAVITY
12324         player->gravity = !player->gravity;
12325 #else
12326         game.gravity = !game.gravity;
12327 #endif
12328       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12329                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12330                element == EL_SP_GRAVITY_ON_PORT_UP ||
12331                element == EL_SP_GRAVITY_ON_PORT_DOWN)
12332 #if USE_PLAYER_GRAVITY
12333         player->gravity = TRUE;
12334 #else
12335         game.gravity = TRUE;
12336 #endif
12337       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12338                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12339                element == EL_SP_GRAVITY_OFF_PORT_UP ||
12340                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12341 #if USE_PLAYER_GRAVITY
12342         player->gravity = FALSE;
12343 #else
12344         game.gravity = FALSE;
12345 #endif
12346     }
12347
12348     /* automatically move to the next field with double speed */
12349     player->programmed_action = move_direction;
12350
12351     if (player->move_delay_reset_counter == 0)
12352     {
12353       player->move_delay_reset_counter = 2;     /* two double speed steps */
12354
12355       DOUBLE_PLAYER_SPEED(player);
12356     }
12357
12358     PlayLevelSoundAction(x, y, ACTION_PASSING);
12359   }
12360   else if (player_can_move_or_snap && IS_DIGGABLE(element))
12361   {
12362     RemoveField(x, y);
12363
12364     if (mode != DF_SNAP)
12365     {
12366       GfxElement[x][y] = GFX_ELEMENT(element);
12367       player->is_digging = TRUE;
12368     }
12369
12370     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12371
12372     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12373                                         player->index_bit, dig_side);
12374
12375     if (mode == DF_SNAP)
12376     {
12377 #if USE_NEW_SNAP_DELAY
12378       if (level.block_snap_field)
12379         setFieldForSnapping(x, y, element, move_direction);
12380       else
12381         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12382 #else
12383       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12384 #endif
12385
12386       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12387                                           player->index_bit, dig_side);
12388     }
12389   }
12390   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
12391   {
12392     RemoveField(x, y);
12393
12394     if (is_player && mode != DF_SNAP)
12395     {
12396       GfxElement[x][y] = element;
12397       player->is_collecting = TRUE;
12398     }
12399
12400     if (element == EL_SPEED_PILL)
12401     {
12402       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12403     }
12404     else if (element == EL_EXTRA_TIME && level.time > 0)
12405     {
12406       TimeLeft += level.extra_time;
12407       DrawGameValue_Time(TimeLeft);
12408     }
12409     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12410     {
12411       player->shield_normal_time_left += level.shield_normal_time;
12412       if (element == EL_SHIELD_DEADLY)
12413         player->shield_deadly_time_left += level.shield_deadly_time;
12414     }
12415     else if (element == EL_DYNAMITE ||
12416              element == EL_EM_DYNAMITE ||
12417              element == EL_SP_DISK_RED)
12418     {
12419       if (player->inventory_size < MAX_INVENTORY_SIZE)
12420         player->inventory_element[player->inventory_size++] = element;
12421
12422 #if 1
12423       DrawGameDoorValues();
12424 #else
12425       DrawGameValue_Dynamite(local_player->inventory_size);
12426 #endif
12427     }
12428     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12429     {
12430       player->dynabomb_count++;
12431       player->dynabombs_left++;
12432     }
12433     else if (element == EL_DYNABOMB_INCREASE_SIZE)
12434     {
12435       player->dynabomb_size++;
12436     }
12437     else if (element == EL_DYNABOMB_INCREASE_POWER)
12438     {
12439       player->dynabomb_xl = TRUE;
12440     }
12441     else if (IS_KEY(element))
12442     {
12443       player->key[KEY_NR(element)] = TRUE;
12444
12445 #if 1
12446       DrawGameDoorValues();
12447 #else
12448       DrawGameValue_Keys(player->key);
12449 #endif
12450
12451       redraw_mask |= REDRAW_DOOR_1;
12452     }
12453     else if (IS_ENVELOPE(element))
12454     {
12455       player->show_envelope = element;
12456     }
12457     else if (element == EL_EMC_LENSES)
12458     {
12459       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
12460
12461       RedrawAllInvisibleElementsForLenses();
12462     }
12463     else if (element == EL_EMC_MAGNIFIER)
12464     {
12465       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
12466
12467       RedrawAllInvisibleElementsForMagnifier();
12468     }
12469     else if (IS_DROPPABLE(element) ||
12470              IS_THROWABLE(element))     /* can be collected and dropped */
12471     {
12472       int i;
12473
12474       if (collect_count == 0)
12475         player->inventory_infinite_element = element;
12476       else
12477         for (i = 0; i < collect_count; i++)
12478           if (player->inventory_size < MAX_INVENTORY_SIZE)
12479             player->inventory_element[player->inventory_size++] = element;
12480
12481 #if 1
12482       DrawGameDoorValues();
12483 #else
12484       DrawGameValue_Dynamite(local_player->inventory_size);
12485 #endif
12486     }
12487     else if (collect_count > 0)
12488     {
12489       local_player->gems_still_needed -= collect_count;
12490       if (local_player->gems_still_needed < 0)
12491         local_player->gems_still_needed = 0;
12492
12493       DrawGameValue_Emeralds(local_player->gems_still_needed);
12494     }
12495
12496     RaiseScoreElement(element);
12497     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12498
12499     if (is_player)
12500       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
12501                                           player->index_bit, dig_side);
12502
12503     if (mode == DF_SNAP)
12504     {
12505 #if USE_NEW_SNAP_DELAY
12506       if (level.block_snap_field)
12507         setFieldForSnapping(x, y, element, move_direction);
12508       else
12509         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
12510 #else
12511       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
12512 #endif
12513
12514       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
12515                                           player->index_bit, dig_side);
12516     }
12517   }
12518   else if (player_can_move_or_snap && IS_PUSHABLE(element))
12519   {
12520     if (mode == DF_SNAP && element != EL_BD_ROCK)
12521       return MP_NO_ACTION;
12522
12523     if (CAN_FALL(element) && dy)
12524       return MP_NO_ACTION;
12525
12526     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12527         !(element == EL_SPRING && level.use_spring_bug))
12528       return MP_NO_ACTION;
12529
12530     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12531         ((move_direction & MV_VERTICAL &&
12532           ((element_info[element].move_pattern & MV_LEFT &&
12533             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12534            (element_info[element].move_pattern & MV_RIGHT &&
12535             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12536          (move_direction & MV_HORIZONTAL &&
12537           ((element_info[element].move_pattern & MV_UP &&
12538             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12539            (element_info[element].move_pattern & MV_DOWN &&
12540             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12541       return MP_NO_ACTION;
12542
12543     /* do not push elements already moving away faster than player */
12544     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12545         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12546       return MP_NO_ACTION;
12547
12548     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12549     {
12550       if (player->push_delay_value == -1 || !player_was_pushing)
12551         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12552     }
12553     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12554     {
12555       if (player->push_delay_value == -1)
12556         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12557     }
12558     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12559     {
12560       if (!player->is_pushing)
12561         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12562     }
12563
12564     player->is_pushing = TRUE;
12565     player->is_active = TRUE;
12566
12567     if (!(IN_LEV_FIELD(nextx, nexty) &&
12568           (IS_FREE(nextx, nexty) ||
12569            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12570             IS_SB_ELEMENT(element)))))
12571       return MP_NO_ACTION;
12572
12573     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12574       return MP_NO_ACTION;
12575
12576     if (player->push_delay == -1)       /* new pushing; restart delay */
12577       player->push_delay = 0;
12578
12579     if (player->push_delay < player->push_delay_value &&
12580         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12581         element != EL_SPRING && element != EL_BALLOON)
12582     {
12583       /* make sure that there is no move delay before next try to push */
12584       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12585         player->move_delay = 0;
12586
12587       return MP_NO_ACTION;
12588     }
12589
12590     if (IS_SB_ELEMENT(element))
12591     {
12592       if (element == EL_SOKOBAN_FIELD_FULL)
12593       {
12594         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12595         local_player->sokobanfields_still_needed++;
12596       }
12597
12598       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12599       {
12600         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12601         local_player->sokobanfields_still_needed--;
12602       }
12603
12604       Feld[x][y] = EL_SOKOBAN_OBJECT;
12605
12606       if (Back[x][y] == Back[nextx][nexty])
12607         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12608       else if (Back[x][y] != 0)
12609         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12610                                     ACTION_EMPTYING);
12611       else
12612         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12613                                     ACTION_FILLING);
12614
12615       if (local_player->sokobanfields_still_needed == 0 &&
12616           game.emulation == EMU_SOKOBAN)
12617       {
12618         player->LevelSolved = player->GameOver = TRUE;
12619         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12620       }
12621     }
12622     else
12623       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12624
12625     InitMovingField(x, y, move_direction);
12626     GfxAction[x][y] = ACTION_PUSHING;
12627
12628     if (mode == DF_SNAP)
12629       ContinueMoving(x, y);
12630     else
12631       MovPos[x][y] = (dx != 0 ? dx : dy);
12632
12633     Pushed[x][y] = TRUE;
12634     Pushed[nextx][nexty] = TRUE;
12635
12636     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12637       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12638     else
12639       player->push_delay_value = -1;    /* get new value later */
12640
12641     /* check for element change _after_ element has been pushed */
12642     if (game.use_change_when_pushing_bug)
12643     {
12644       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12645                                  player->index_bit, dig_side);
12646       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12647                                           player->index_bit, dig_side);
12648     }
12649   }
12650   else if (IS_SWITCHABLE(element))
12651   {
12652     if (PLAYER_SWITCHING(player, x, y))
12653     {
12654       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12655                                           player->index_bit, dig_side);
12656
12657       return MP_ACTION;
12658     }
12659
12660     player->is_switching = TRUE;
12661     player->switch_x = x;
12662     player->switch_y = y;
12663
12664     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12665
12666     if (element == EL_ROBOT_WHEEL)
12667     {
12668       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12669       ZX = x;
12670       ZY = y;
12671
12672       DrawLevelField(x, y);
12673     }
12674     else if (element == EL_SP_TERMINAL)
12675     {
12676       int xx, yy;
12677
12678 #if 1
12679       SCAN_PLAYFIELD(xx, yy)
12680 #else
12681       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12682 #endif
12683       {
12684         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12685           Bang(xx, yy);
12686         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12687           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12688       }
12689     }
12690     else if (IS_BELT_SWITCH(element))
12691     {
12692       ToggleBeltSwitch(x, y);
12693     }
12694     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12695              element == EL_SWITCHGATE_SWITCH_DOWN)
12696     {
12697       ToggleSwitchgateSwitch(x, y);
12698     }
12699     else if (element == EL_LIGHT_SWITCH ||
12700              element == EL_LIGHT_SWITCH_ACTIVE)
12701     {
12702       ToggleLightSwitch(x, y);
12703     }
12704     else if (element == EL_TIMEGATE_SWITCH)
12705     {
12706       ActivateTimegateSwitch(x, y);
12707     }
12708     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12709              element == EL_BALLOON_SWITCH_RIGHT ||
12710              element == EL_BALLOON_SWITCH_UP    ||
12711              element == EL_BALLOON_SWITCH_DOWN  ||
12712              element == EL_BALLOON_SWITCH_NONE  ||
12713              element == EL_BALLOON_SWITCH_ANY)
12714     {
12715       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12716                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12717                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12718                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12719                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12720                              move_direction);
12721     }
12722     else if (element == EL_LAMP)
12723     {
12724       Feld[x][y] = EL_LAMP_ACTIVE;
12725       local_player->lights_still_needed--;
12726
12727       ResetGfxAnimation(x, y);
12728       DrawLevelField(x, y);
12729     }
12730     else if (element == EL_TIME_ORB_FULL)
12731     {
12732       Feld[x][y] = EL_TIME_ORB_EMPTY;
12733
12734       if (level.time > 0 || level.use_time_orb_bug)
12735       {
12736         TimeLeft += level.time_orb_time;
12737         DrawGameValue_Time(TimeLeft);
12738       }
12739
12740       ResetGfxAnimation(x, y);
12741       DrawLevelField(x, y);
12742     }
12743     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12744              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12745     {
12746       int xx, yy;
12747
12748       game.ball_state = !game.ball_state;
12749
12750 #if 1
12751       SCAN_PLAYFIELD(xx, yy)
12752 #else
12753       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12754 #endif
12755       {
12756         int e = Feld[xx][yy];
12757
12758         if (game.ball_state)
12759         {
12760           if (e == EL_EMC_MAGIC_BALL)
12761             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12762           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12763             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12764         }
12765         else
12766         {
12767           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12768             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12769           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12770             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12771         }
12772       }
12773     }
12774
12775     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12776                                         player->index_bit, dig_side);
12777
12778     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12779                                         player->index_bit, dig_side);
12780
12781     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12782                                         player->index_bit, dig_side);
12783
12784     return MP_ACTION;
12785   }
12786   else
12787   {
12788     if (!PLAYER_SWITCHING(player, x, y))
12789     {
12790       player->is_switching = TRUE;
12791       player->switch_x = x;
12792       player->switch_y = y;
12793
12794       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12795                                  player->index_bit, dig_side);
12796       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12797                                           player->index_bit, dig_side);
12798
12799       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12800                                  player->index_bit, dig_side);
12801       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12802                                           player->index_bit, dig_side);
12803     }
12804
12805     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12806                                player->index_bit, dig_side);
12807     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12808                                         player->index_bit, dig_side);
12809
12810     return MP_NO_ACTION;
12811   }
12812
12813   player->push_delay = -1;
12814
12815   if (is_player)                /* function can also be called by EL_PENGUIN */
12816   {
12817     if (Feld[x][y] != element)          /* really digged/collected something */
12818     {
12819       player->is_collecting = !player->is_digging;
12820       player->is_active = TRUE;
12821     }
12822   }
12823
12824   return MP_MOVING;
12825 }
12826
12827 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12828 {
12829   int jx = player->jx, jy = player->jy;
12830   int x = jx + dx, y = jy + dy;
12831   int snap_direction = (dx == -1 ? MV_LEFT  :
12832                         dx == +1 ? MV_RIGHT :
12833                         dy == -1 ? MV_UP    :
12834                         dy == +1 ? MV_DOWN  : MV_NONE);
12835   boolean can_continue_snapping = (level.continuous_snapping &&
12836                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12837
12838   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12839     return FALSE;
12840
12841   if (!player->active || !IN_LEV_FIELD(x, y))
12842     return FALSE;
12843
12844   if (dx && dy)
12845     return FALSE;
12846
12847   if (!dx && !dy)
12848   {
12849     if (player->MovPos == 0)
12850       player->is_pushing = FALSE;
12851
12852     player->is_snapping = FALSE;
12853
12854     if (player->MovPos == 0)
12855     {
12856       player->is_moving = FALSE;
12857       player->is_digging = FALSE;
12858       player->is_collecting = FALSE;
12859     }
12860
12861     return FALSE;
12862   }
12863
12864 #if USE_NEW_CONTINUOUS_SNAPPING
12865   /* prevent snapping with already pressed snap key when not allowed */
12866   if (player->is_snapping && !can_continue_snapping)
12867     return FALSE;
12868 #else
12869   if (player->is_snapping)
12870     return FALSE;
12871 #endif
12872
12873   player->MovDir = snap_direction;
12874
12875   if (player->MovPos == 0)
12876   {
12877     player->is_moving = FALSE;
12878     player->is_digging = FALSE;
12879     player->is_collecting = FALSE;
12880   }
12881
12882   player->is_dropping = FALSE;
12883   player->is_dropping_pressed = FALSE;
12884   player->drop_pressed_delay = 0;
12885
12886   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12887     return FALSE;
12888
12889   player->is_snapping = TRUE;
12890   player->is_active = TRUE;
12891
12892   if (player->MovPos == 0)
12893   {
12894     player->is_moving = FALSE;
12895     player->is_digging = FALSE;
12896     player->is_collecting = FALSE;
12897   }
12898
12899   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12900     DrawLevelField(player->last_jx, player->last_jy);
12901
12902   DrawLevelField(x, y);
12903
12904   return TRUE;
12905 }
12906
12907 boolean DropElement(struct PlayerInfo *player)
12908 {
12909   int old_element, new_element;
12910   int dropx = player->jx, dropy = player->jy;
12911   int drop_direction = player->MovDir;
12912   int drop_side = drop_direction;
12913   int drop_element = (player->inventory_size > 0 ?
12914                       player->inventory_element[player->inventory_size - 1] :
12915                       player->inventory_infinite_element != EL_UNDEFINED ?
12916                       player->inventory_infinite_element :
12917                       player->dynabombs_left > 0 ?
12918                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12919                       EL_UNDEFINED);
12920
12921   player->is_dropping_pressed = TRUE;
12922
12923   /* do not drop an element on top of another element; when holding drop key
12924      pressed without moving, dropped element must move away before the next
12925      element can be dropped (this is especially important if the next element
12926      is dynamite, which can be placed on background for historical reasons) */
12927   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12928     return MP_ACTION;
12929
12930   if (IS_THROWABLE(drop_element))
12931   {
12932     dropx += GET_DX_FROM_DIR(drop_direction);
12933     dropy += GET_DY_FROM_DIR(drop_direction);
12934
12935     if (!IN_LEV_FIELD(dropx, dropy))
12936       return FALSE;
12937   }
12938
12939   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12940   new_element = drop_element;           /* default: no change when dropping */
12941
12942   /* check if player is active, not moving and ready to drop */
12943   if (!player->active || player->MovPos || player->drop_delay > 0)
12944     return FALSE;
12945
12946   /* check if player has anything that can be dropped */
12947   if (new_element == EL_UNDEFINED)
12948     return FALSE;
12949
12950   /* check if drop key was pressed long enough for EM style dynamite */
12951   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12952     return FALSE;
12953
12954   /* check if anything can be dropped at the current position */
12955   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12956     return FALSE;
12957
12958   /* collected custom elements can only be dropped on empty fields */
12959   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12960     return FALSE;
12961
12962   if (old_element != EL_EMPTY)
12963     Back[dropx][dropy] = old_element;   /* store old element on this field */
12964
12965   ResetGfxAnimation(dropx, dropy);
12966   ResetRandomAnimationValue(dropx, dropy);
12967
12968   if (player->inventory_size > 0 ||
12969       player->inventory_infinite_element != EL_UNDEFINED)
12970   {
12971     if (player->inventory_size > 0)
12972     {
12973       player->inventory_size--;
12974
12975 #if 1
12976       DrawGameDoorValues();
12977 #else
12978       DrawGameValue_Dynamite(local_player->inventory_size);
12979 #endif
12980
12981       if (new_element == EL_DYNAMITE)
12982         new_element = EL_DYNAMITE_ACTIVE;
12983       else if (new_element == EL_EM_DYNAMITE)
12984         new_element = EL_EM_DYNAMITE_ACTIVE;
12985       else if (new_element == EL_SP_DISK_RED)
12986         new_element = EL_SP_DISK_RED_ACTIVE;
12987     }
12988
12989     Feld[dropx][dropy] = new_element;
12990
12991     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12992       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12993                           el2img(Feld[dropx][dropy]), 0);
12994
12995     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12996
12997     /* needed if previous element just changed to "empty" in the last frame */
12998     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12999
13000     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13001                                player->index_bit, drop_side);
13002     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13003                                         CE_PLAYER_DROPS_X,
13004                                         player->index_bit, drop_side);
13005
13006     TestIfElementTouchesCustomElement(dropx, dropy);
13007   }
13008   else          /* player is dropping a dyna bomb */
13009   {
13010     player->dynabombs_left--;
13011
13012     Feld[dropx][dropy] = new_element;
13013
13014     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13015       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13016                           el2img(Feld[dropx][dropy]), 0);
13017
13018     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13019   }
13020
13021   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13022     InitField_WithBug1(dropx, dropy, FALSE);
13023
13024   new_element = Feld[dropx][dropy];     /* element might have changed */
13025
13026   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13027       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13028   {
13029     int move_direction, nextx, nexty;
13030
13031     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13032       MovDir[dropx][dropy] = drop_direction;
13033
13034     move_direction = MovDir[dropx][dropy];
13035     nextx = dropx + GET_DX_FROM_DIR(move_direction);
13036     nexty = dropy + GET_DY_FROM_DIR(move_direction);
13037
13038     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
13039     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
13040   }
13041
13042   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13043   player->is_dropping = TRUE;
13044
13045   player->drop_pressed_delay = 0;
13046   player->is_dropping_pressed = FALSE;
13047
13048   player->drop_x = dropx;
13049   player->drop_y = dropy;
13050
13051   return TRUE;
13052 }
13053
13054 /* ------------------------------------------------------------------------- */
13055 /* game sound playing functions                                              */
13056 /* ------------------------------------------------------------------------- */
13057
13058 static int *loop_sound_frame = NULL;
13059 static int *loop_sound_volume = NULL;
13060
13061 void InitPlayLevelSound()
13062 {
13063   int num_sounds = getSoundListSize();
13064
13065   checked_free(loop_sound_frame);
13066   checked_free(loop_sound_volume);
13067
13068   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
13069   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13070 }
13071
13072 static void PlayLevelSound(int x, int y, int nr)
13073 {
13074   int sx = SCREENX(x), sy = SCREENY(y);
13075   int volume, stereo_position;
13076   int max_distance = 8;
13077   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13078
13079   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13080       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13081     return;
13082
13083   if (!IN_LEV_FIELD(x, y) ||
13084       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13085       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13086     return;
13087
13088   volume = SOUND_MAX_VOLUME;
13089
13090   if (!IN_SCR_FIELD(sx, sy))
13091   {
13092     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13093     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13094
13095     volume -= volume * (dx > dy ? dx : dy) / max_distance;
13096   }
13097
13098   stereo_position = (SOUND_MAX_LEFT +
13099                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13100                      (SCR_FIELDX + 2 * max_distance));
13101
13102   if (IS_LOOP_SOUND(nr))
13103   {
13104     /* This assures that quieter loop sounds do not overwrite louder ones,
13105        while restarting sound volume comparison with each new game frame. */
13106
13107     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13108       return;
13109
13110     loop_sound_volume[nr] = volume;
13111     loop_sound_frame[nr] = FrameCounter;
13112   }
13113
13114   PlaySoundExt(nr, volume, stereo_position, type);
13115 }
13116
13117 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13118 {
13119   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13120                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13121                  y < LEVELY(BY1) ? LEVELY(BY1) :
13122                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13123                  sound_action);
13124 }
13125
13126 static void PlayLevelSoundAction(int x, int y, int action)
13127 {
13128   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13129 }
13130
13131 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13132 {
13133   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13134
13135   if (sound_effect != SND_UNDEFINED)
13136     PlayLevelSound(x, y, sound_effect);
13137 }
13138
13139 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13140                                               int action)
13141 {
13142   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13143
13144   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13145     PlayLevelSound(x, y, sound_effect);
13146 }
13147
13148 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13149 {
13150   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13151
13152   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13153     PlayLevelSound(x, y, sound_effect);
13154 }
13155
13156 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13157 {
13158   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13159
13160   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13161     StopSound(sound_effect);
13162 }
13163
13164 static void PlayLevelMusic()
13165 {
13166   if (levelset.music[level_nr] != MUS_UNDEFINED)
13167     PlayMusic(levelset.music[level_nr]);        /* from config file */
13168   else
13169     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13170 }
13171
13172 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
13173 {
13174   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13175
13176   switch (sample)
13177   {
13178     case SAMPLE_blank:
13179       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13180       break;
13181
13182     case SAMPLE_roll:
13183       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13184       break;
13185
13186     case SAMPLE_stone:
13187       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13188       break;
13189
13190     case SAMPLE_nut:
13191       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13192       break;
13193
13194     case SAMPLE_crack:
13195       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13196       break;
13197
13198     case SAMPLE_bug:
13199       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13200       break;
13201
13202     case SAMPLE_tank:
13203       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13204       break;
13205
13206     case SAMPLE_android_clone:
13207       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13208       break;
13209
13210     case SAMPLE_android_move:
13211       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13212       break;
13213
13214     case SAMPLE_spring:
13215       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13216       break;
13217
13218     case SAMPLE_slurp:
13219       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
13220       break;
13221
13222     case SAMPLE_eater:
13223       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13224       break;
13225
13226     case SAMPLE_eater_eat:
13227       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13228       break;
13229
13230     case SAMPLE_alien:
13231       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13232       break;
13233
13234     case SAMPLE_collect:
13235       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13236       break;
13237
13238     case SAMPLE_diamond:
13239       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13240       break;
13241
13242     case SAMPLE_squash:
13243       /* !!! CHECK THIS !!! */
13244 #if 1
13245       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13246 #else
13247       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13248 #endif
13249       break;
13250
13251     case SAMPLE_wonderfall:
13252       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13253       break;
13254
13255     case SAMPLE_drip:
13256       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13257       break;
13258
13259     case SAMPLE_push:
13260       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13261       break;
13262
13263     case SAMPLE_dirt:
13264       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13265       break;
13266
13267     case SAMPLE_acid:
13268       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13269       break;
13270
13271     case SAMPLE_ball:
13272       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13273       break;
13274
13275     case SAMPLE_grow:
13276       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13277       break;
13278
13279     case SAMPLE_wonder:
13280       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13281       break;
13282
13283     case SAMPLE_door:
13284       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13285       break;
13286
13287     case SAMPLE_exit_open:
13288       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13289       break;
13290
13291     case SAMPLE_exit_leave:
13292       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13293       break;
13294
13295     case SAMPLE_dynamite:
13296       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13297       break;
13298
13299     case SAMPLE_tick:
13300       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13301       break;
13302
13303     case SAMPLE_press:
13304       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13305       break;
13306
13307     case SAMPLE_wheel:
13308       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13309       break;
13310
13311     case SAMPLE_boom:
13312       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13313       break;
13314
13315     case SAMPLE_die:
13316       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13317       break;
13318
13319     case SAMPLE_time:
13320       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
13321       break;
13322
13323     default:
13324       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13325       break;
13326   }
13327 }
13328
13329 void RaiseScore(int value)
13330 {
13331   local_player->score += value;
13332
13333   DrawGameValue_Score(local_player->score);
13334 }
13335
13336 void RaiseScoreElement(int element)
13337 {
13338   switch(element)
13339   {
13340     case EL_EMERALD:
13341     case EL_BD_DIAMOND:
13342     case EL_EMERALD_YELLOW:
13343     case EL_EMERALD_RED:
13344     case EL_EMERALD_PURPLE:
13345     case EL_SP_INFOTRON:
13346       RaiseScore(level.score[SC_EMERALD]);
13347       break;
13348     case EL_DIAMOND:
13349       RaiseScore(level.score[SC_DIAMOND]);
13350       break;
13351     case EL_CRYSTAL:
13352       RaiseScore(level.score[SC_CRYSTAL]);
13353       break;
13354     case EL_PEARL:
13355       RaiseScore(level.score[SC_PEARL]);
13356       break;
13357     case EL_BUG:
13358     case EL_BD_BUTTERFLY:
13359     case EL_SP_ELECTRON:
13360       RaiseScore(level.score[SC_BUG]);
13361       break;
13362     case EL_SPACESHIP:
13363     case EL_BD_FIREFLY:
13364     case EL_SP_SNIKSNAK:
13365       RaiseScore(level.score[SC_SPACESHIP]);
13366       break;
13367     case EL_YAMYAM:
13368     case EL_DARK_YAMYAM:
13369       RaiseScore(level.score[SC_YAMYAM]);
13370       break;
13371     case EL_ROBOT:
13372       RaiseScore(level.score[SC_ROBOT]);
13373       break;
13374     case EL_PACMAN:
13375       RaiseScore(level.score[SC_PACMAN]);
13376       break;
13377     case EL_NUT:
13378       RaiseScore(level.score[SC_NUT]);
13379       break;
13380     case EL_DYNAMITE:
13381     case EL_EM_DYNAMITE:
13382     case EL_SP_DISK_RED:
13383     case EL_DYNABOMB_INCREASE_NUMBER:
13384     case EL_DYNABOMB_INCREASE_SIZE:
13385     case EL_DYNABOMB_INCREASE_POWER:
13386       RaiseScore(level.score[SC_DYNAMITE]);
13387       break;
13388     case EL_SHIELD_NORMAL:
13389     case EL_SHIELD_DEADLY:
13390       RaiseScore(level.score[SC_SHIELD]);
13391       break;
13392     case EL_EXTRA_TIME:
13393       RaiseScore(level.extra_time_score);
13394       break;
13395     case EL_KEY_1:
13396     case EL_KEY_2:
13397     case EL_KEY_3:
13398     case EL_KEY_4:
13399     case EL_EM_KEY_1:
13400     case EL_EM_KEY_2:
13401     case EL_EM_KEY_3:
13402     case EL_EM_KEY_4:
13403     case EL_EMC_KEY_5:
13404     case EL_EMC_KEY_6:
13405     case EL_EMC_KEY_7:
13406     case EL_EMC_KEY_8:
13407       RaiseScore(level.score[SC_KEY]);
13408       break;
13409     default:
13410       RaiseScore(element_info[element].collect_score);
13411       break;
13412   }
13413 }
13414
13415 void RequestQuitGame(boolean ask_if_really_quit)
13416 {
13417   if (AllPlayersGone ||
13418       !ask_if_really_quit ||
13419       level_editor_test_game ||
13420       Request("Do you really want to quit the game ?",
13421               REQ_ASK | REQ_STAY_CLOSED))
13422   {
13423 #if defined(NETWORK_AVALIABLE)
13424     if (options.network)
13425       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
13426     else
13427 #endif
13428     {
13429       if (!ask_if_really_quit || level_editor_test_game)
13430       {
13431         game_status = GAME_MODE_MAIN;
13432
13433         DrawMainMenu();
13434       }
13435       else
13436       {
13437         FadeOut(REDRAW_FIELD);
13438
13439         game_status = GAME_MODE_MAIN;
13440
13441         DrawAndFadeInMainMenu(REDRAW_FIELD);
13442       }
13443     }
13444   }
13445   else
13446   {
13447     if (tape.playing && tape.deactivate_display)
13448       TapeDeactivateDisplayOff(TRUE);
13449
13450     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13451
13452     if (tape.playing && tape.deactivate_display)
13453       TapeDeactivateDisplayOn();
13454   }
13455 }
13456
13457
13458 /* ---------- new game button stuff ---------------------------------------- */
13459
13460 /* graphic position values for game buttons */
13461 #define GAME_BUTTON_XSIZE       30
13462 #define GAME_BUTTON_YSIZE       30
13463 #define GAME_BUTTON_XPOS        5
13464 #define GAME_BUTTON_YPOS        215
13465 #define SOUND_BUTTON_XPOS       5
13466 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13467
13468 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13469 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13470 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13471 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13472 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13473 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13474
13475 static struct
13476 {
13477   int x, y;
13478   int gadget_id;
13479   char *infotext;
13480 } gamebutton_info[NUM_GAME_BUTTONS] =
13481 {
13482   {
13483     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13484     GAME_CTRL_ID_STOP,
13485     "stop game"
13486   },
13487   {
13488     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13489     GAME_CTRL_ID_PAUSE,
13490     "pause game"
13491   },
13492   {
13493     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13494     GAME_CTRL_ID_PLAY,
13495     "play game"
13496   },
13497   {
13498     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13499     SOUND_CTRL_ID_MUSIC,
13500     "background music on/off"
13501   },
13502   {
13503     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13504     SOUND_CTRL_ID_LOOPS,
13505     "sound loops on/off"
13506   },
13507   {
13508     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13509     SOUND_CTRL_ID_SIMPLE,
13510     "normal sounds on/off"
13511   }
13512 };
13513
13514 void CreateGameButtons()
13515 {
13516   int i;
13517
13518   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13519   {
13520     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13521     struct GadgetInfo *gi;
13522     int button_type;
13523     boolean checked;
13524     unsigned long event_mask;
13525     int gd_xoffset, gd_yoffset;
13526     int gd_x1, gd_x2, gd_y1, gd_y2;
13527     int id = i;
13528
13529     gd_xoffset = gamebutton_info[i].x;
13530     gd_yoffset = gamebutton_info[i].y;
13531     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13532     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13533
13534     if (id == GAME_CTRL_ID_STOP ||
13535         id == GAME_CTRL_ID_PAUSE ||
13536         id == GAME_CTRL_ID_PLAY)
13537     {
13538       button_type = GD_TYPE_NORMAL_BUTTON;
13539       checked = FALSE;
13540       event_mask = GD_EVENT_RELEASED;
13541       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13542       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13543     }
13544     else
13545     {
13546       button_type = GD_TYPE_CHECK_BUTTON;
13547       checked =
13548         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13549          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13550          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13551       event_mask = GD_EVENT_PRESSED;
13552       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
13553       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13554     }
13555
13556     gi = CreateGadget(GDI_CUSTOM_ID, id,
13557                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
13558                       GDI_X, DX + gd_xoffset,
13559                       GDI_Y, DY + gd_yoffset,
13560                       GDI_WIDTH, GAME_BUTTON_XSIZE,
13561                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
13562                       GDI_TYPE, button_type,
13563                       GDI_STATE, GD_BUTTON_UNPRESSED,
13564                       GDI_CHECKED, checked,
13565                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13566                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13567                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13568                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13569                       GDI_EVENT_MASK, event_mask,
13570                       GDI_CALLBACK_ACTION, HandleGameButtons,
13571                       GDI_END);
13572
13573     if (gi == NULL)
13574       Error(ERR_EXIT, "cannot create gadget");
13575
13576     game_gadget[id] = gi;
13577   }
13578 }
13579
13580 void FreeGameButtons()
13581 {
13582   int i;
13583
13584   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13585     FreeGadget(game_gadget[i]);
13586 }
13587
13588 static void MapGameButtons()
13589 {
13590   int i;
13591
13592   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13593     MapGadget(game_gadget[i]);
13594 }
13595
13596 void UnmapGameButtons()
13597 {
13598   int i;
13599
13600   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13601     UnmapGadget(game_gadget[i]);
13602 }
13603
13604 static void HandleGameButtons(struct GadgetInfo *gi)
13605 {
13606   int id = gi->custom_id;
13607
13608   if (game_status != GAME_MODE_PLAYING)
13609     return;
13610
13611   switch (id)
13612   {
13613     case GAME_CTRL_ID_STOP:
13614       if (tape.playing)
13615         TapeStop();
13616       else
13617         RequestQuitGame(TRUE);
13618       break;
13619
13620     case GAME_CTRL_ID_PAUSE:
13621       if (options.network)
13622       {
13623 #if defined(NETWORK_AVALIABLE)
13624         if (tape.pausing)
13625           SendToServer_ContinuePlaying();
13626         else
13627           SendToServer_PausePlaying();
13628 #endif
13629       }
13630       else
13631         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13632       break;
13633
13634     case GAME_CTRL_ID_PLAY:
13635       if (tape.pausing)
13636       {
13637 #if defined(NETWORK_AVALIABLE)
13638         if (options.network)
13639           SendToServer_ContinuePlaying();
13640         else
13641 #endif
13642         {
13643           tape.pausing = FALSE;
13644           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13645         }
13646       }
13647       break;
13648
13649     case SOUND_CTRL_ID_MUSIC:
13650       if (setup.sound_music)
13651       { 
13652         setup.sound_music = FALSE;
13653         FadeMusic();
13654       }
13655       else if (audio.music_available)
13656       { 
13657         setup.sound = setup.sound_music = TRUE;
13658
13659         SetAudioMode(setup.sound);
13660
13661         PlayLevelMusic();
13662       }
13663       break;
13664
13665     case SOUND_CTRL_ID_LOOPS:
13666       if (setup.sound_loops)
13667         setup.sound_loops = FALSE;
13668       else if (audio.loops_available)
13669       {
13670         setup.sound = setup.sound_loops = TRUE;
13671         SetAudioMode(setup.sound);
13672       }
13673       break;
13674
13675     case SOUND_CTRL_ID_SIMPLE:
13676       if (setup.sound_simple)
13677         setup.sound_simple = FALSE;
13678       else if (audio.sound_available)
13679       {
13680         setup.sound = setup.sound_simple = TRUE;
13681         SetAudioMode(setup.sound);
13682       }
13683       break;
13684
13685     default:
13686       break;
13687   }
13688 }