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