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