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