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