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