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