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