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