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