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