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