rnd-20051214-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_NO_MOVING                         }
586 };
587
588 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
589
590 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
591 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
592 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
593                                  IS_JUST_CHANGING(x, y))
594
595 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
596
597
598 void GetPlayerConfig()
599 {
600   if (!audio.sound_available)
601     setup.sound_simple = FALSE;
602
603   if (!audio.loops_available)
604     setup.sound_loops = FALSE;
605
606   if (!audio.music_available)
607     setup.sound_music = FALSE;
608
609   if (!video.fullscreen_available)
610     setup.fullscreen = FALSE;
611
612   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
613
614   SetAudioMode(setup.sound);
615   InitJoysticks();
616 }
617
618 static int getBeltNrFromBeltElement(int element)
619 {
620   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
621           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
622           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
623 }
624
625 static int getBeltNrFromBeltActiveElement(int element)
626 {
627   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
628           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
629           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
630 }
631
632 static int getBeltNrFromBeltSwitchElement(int element)
633 {
634   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
635           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
636           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
637 }
638
639 static int getBeltDirNrFromBeltSwitchElement(int element)
640 {
641   static int belt_base_element[4] =
642   {
643     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
644     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
645     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
646     EL_CONVEYOR_BELT_4_SWITCH_LEFT
647   };
648
649   int belt_nr = getBeltNrFromBeltSwitchElement(element);
650   int belt_dir_nr = element - belt_base_element[belt_nr];
651
652   return (belt_dir_nr % 3);
653 }
654
655 static int getBeltDirFromBeltSwitchElement(int element)
656 {
657   static int belt_move_dir[3] =
658   {
659     MV_LEFT,
660     MV_NO_MOVING,
661     MV_RIGHT
662   };
663
664   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
665
666   return belt_move_dir[belt_dir_nr];
667 }
668
669 static void InitPlayerField(int x, int y, int element, boolean init_game)
670 {
671   if (element == EL_SP_MURPHY)
672   {
673     if (init_game)
674     {
675       if (stored_player[0].present)
676       {
677         Feld[x][y] = EL_SP_MURPHY_CLONE;
678
679         return;
680       }
681       else
682       {
683         stored_player[0].use_murphy_graphic = TRUE;
684       }
685
686       Feld[x][y] = EL_PLAYER_1;
687     }
688   }
689
690   if (init_game)
691   {
692     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
693     int jx = player->jx, jy = player->jy;
694
695     player->present = TRUE;
696
697     player->block_last_field = (element == EL_SP_MURPHY ?
698                                 level.sp_block_last_field :
699                                 level.block_last_field);
700
701     /* ---------- initialize player's last field block delay --------------- */
702
703     /* always start with reliable default value (no adjustment needed) */
704     player->block_delay_adjustment = 0;
705
706     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
707     if (player->block_last_field && element == EL_SP_MURPHY)
708       player->block_delay_adjustment = 1;
709
710     /* special case 2: in game engines before 3.1.1, blocking was different */
711     if (game.use_block_last_field_bug)
712       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
713
714     if (!options.network || player->connected)
715     {
716       player->active = TRUE;
717
718       /* remove potentially duplicate players */
719       if (StorePlayer[jx][jy] == Feld[x][y])
720         StorePlayer[jx][jy] = 0;
721
722       StorePlayer[x][y] = Feld[x][y];
723
724       if (options.debug)
725       {
726         printf("Player %d activated.\n", player->element_nr);
727         printf("[Local player is %d and currently %s.]\n",
728                local_player->element_nr,
729                local_player->active ? "active" : "not active");
730       }
731     }
732
733     Feld[x][y] = EL_EMPTY;
734
735     player->jx = player->last_jx = x;
736     player->jy = player->last_jy = y;
737   }
738 }
739
740 static void InitField(int x, int y, boolean init_game)
741 {
742   int element = Feld[x][y];
743
744   switch (element)
745   {
746     case EL_SP_MURPHY:
747     case EL_PLAYER_1:
748     case EL_PLAYER_2:
749     case EL_PLAYER_3:
750     case EL_PLAYER_4:
751       InitPlayerField(x, y, element, init_game);
752       break;
753
754     case EL_SOKOBAN_FIELD_PLAYER:
755       element = Feld[x][y] = EL_PLAYER_1;
756       InitField(x, y, init_game);
757
758       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
759       InitField(x, y, init_game);
760       break;
761
762     case EL_SOKOBAN_FIELD_EMPTY:
763       local_player->sokobanfields_still_needed++;
764       break;
765
766     case EL_STONEBLOCK:
767       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
768         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
769       else if (x > 0 && Feld[x-1][y] == EL_ACID)
770         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
771       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
772         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
773       else if (y > 0 && Feld[x][y-1] == EL_ACID)
774         Feld[x][y] = EL_ACID_POOL_BOTTOM;
775       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
776         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
777       break;
778
779     case EL_BUG_RIGHT:
780     case EL_BUG_UP:
781     case EL_BUG_LEFT:
782     case EL_BUG_DOWN:
783     case EL_BUG:
784     case EL_SPACESHIP_RIGHT:
785     case EL_SPACESHIP_UP:
786     case EL_SPACESHIP_LEFT:
787     case EL_SPACESHIP_DOWN:
788     case EL_SPACESHIP:
789     case EL_BD_BUTTERFLY_RIGHT:
790     case EL_BD_BUTTERFLY_UP:
791     case EL_BD_BUTTERFLY_LEFT:
792     case EL_BD_BUTTERFLY_DOWN:
793     case EL_BD_BUTTERFLY:
794     case EL_BD_FIREFLY_RIGHT:
795     case EL_BD_FIREFLY_UP:
796     case EL_BD_FIREFLY_LEFT:
797     case EL_BD_FIREFLY_DOWN:
798     case EL_BD_FIREFLY:
799     case EL_PACMAN_RIGHT:
800     case EL_PACMAN_UP:
801     case EL_PACMAN_LEFT:
802     case EL_PACMAN_DOWN:
803     case EL_YAMYAM:
804     case EL_DARK_YAMYAM:
805     case EL_ROBOT:
806     case EL_PACMAN:
807     case EL_SP_SNIKSNAK:
808     case EL_SP_ELECTRON:
809     case EL_MOLE_LEFT:
810     case EL_MOLE_RIGHT:
811     case EL_MOLE_UP:
812     case EL_MOLE_DOWN:
813     case EL_MOLE:
814       InitMovDir(x, y);
815       break;
816
817     case EL_AMOEBA_FULL:
818     case EL_BD_AMOEBA:
819       InitAmoebaNr(x, y);
820       break;
821
822     case EL_AMOEBA_DROP:
823       if (y == lev_fieldy - 1)
824       {
825         Feld[x][y] = EL_AMOEBA_GROWING;
826         Store[x][y] = EL_AMOEBA_WET;
827       }
828       break;
829
830     case EL_DYNAMITE_ACTIVE:
831     case EL_SP_DISK_RED_ACTIVE:
832     case EL_DYNABOMB_PLAYER_1_ACTIVE:
833     case EL_DYNABOMB_PLAYER_2_ACTIVE:
834     case EL_DYNABOMB_PLAYER_3_ACTIVE:
835     case EL_DYNABOMB_PLAYER_4_ACTIVE:
836       MovDelay[x][y] = 96;
837       break;
838
839     case EL_LAMP:
840       local_player->lights_still_needed++;
841       break;
842
843     case EL_PENGUIN:
844       local_player->friends_still_needed++;
845       break;
846
847     case EL_PIG:
848     case EL_DRAGON:
849       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
850       break;
851
852     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
853     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
854     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
855     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
856     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
857     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
858     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
859     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
860     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
861     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
862     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
863     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
864       if (init_game)
865       {
866         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
867         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
868         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
869
870         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
871         {
872           game.belt_dir[belt_nr] = belt_dir;
873           game.belt_dir_nr[belt_nr] = belt_dir_nr;
874         }
875         else    /* more than one switch -- set it like the first switch */
876         {
877           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
878         }
879       }
880       break;
881
882     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
883       if (init_game)
884         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
885       break;
886
887     case EL_LIGHT_SWITCH_ACTIVE:
888       if (init_game)
889         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
890       break;
891
892     default:
893       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
894         InitMovDir(x, y);
895       else if (IS_GROUP_ELEMENT(element))
896       {
897         struct ElementGroupInfo *group = element_info[element].group;
898         int last_anim_random_frame = gfx.anim_random_frame;
899         int element_pos;
900
901         if (group->choice_mode == ANIM_RANDOM)
902           gfx.anim_random_frame = RND(group->num_elements_resolved);
903
904         element_pos = getAnimationFrame(group->num_elements_resolved, 1,
905                                         group->choice_mode, 0,
906                                         group->choice_pos);
907
908         if (group->choice_mode == ANIM_RANDOM)
909           gfx.anim_random_frame = last_anim_random_frame;
910
911         group->choice_pos++;
912
913         Feld[x][y] = group->element_resolved[element_pos];
914
915         InitField(x, y, init_game);
916       }
917       break;
918   }
919
920 #if USE_NEW_COLLECT_COUNT
921   Count[x][y] = element_info[Feld[x][y]].collect_count_initial;
922 #endif
923 }
924
925 static inline void InitField_WithBug1(int x, int y, boolean init_game)
926 {
927   InitField(x, y, init_game);
928
929   /* not needed to call InitMovDir() -- already done by InitField()! */
930   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
931       CAN_MOVE(Feld[x][y]))
932     InitMovDir(x, y);
933 }
934
935 static inline void InitField_WithBug2(int x, int y, boolean init_game)
936 {
937   int old_element = Feld[x][y];
938
939   InitField(x, y, init_game);
940
941   /* not needed to call InitMovDir() -- already done by InitField()! */
942   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
943       CAN_MOVE(old_element) &&
944       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
945     InitMovDir(x, y);
946
947   /* this case is in fact a combination of not less than three bugs:
948      first, it calls InitMovDir() for elements that can move, although this is
949      already done by InitField(); then, it checks the element that was at this
950      field _before_ the call to InitField() (which can change it); lastly, it
951      was not called for "mole with direction" elements, which were treated as
952      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
953   */
954 }
955
956 inline void DrawGameValue_Emeralds(int value)
957 {
958   DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
959 }
960
961 inline void DrawGameValue_Dynamite(int value)
962 {
963   DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
964 }
965
966 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
967 {
968   int i;
969
970   /* currently only 4 of 8 possible keys are displayed */
971   for (i = 0; i < STD_NUM_KEYS; i++)
972   {
973     if (key[i])
974       DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
975                          el2edimg(EL_KEY_1 + i));
976     else
977       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
978                  DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
979                  MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
980   }
981 }
982
983 inline void DrawGameValue_Score(int value)
984 {
985   DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
986 }
987
988 inline void DrawGameValue_Time(int value)
989 {
990   if (value < 1000)
991     DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
992   else
993     DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
994 }
995
996 inline void DrawGameValue_Level(int value)
997 {
998   if (level_nr < 100)
999     DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1000   else
1001   {
1002     /* misuse area for displaying emeralds to draw bigger level number */
1003     DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1004                 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1005
1006     /* now copy it to the area for displaying level number */
1007     BlitBitmap(drawto, drawto,
1008                DX_EMERALDS, DY_EMERALDS + 1,
1009                getFontWidth(FONT_LEVEL_NUMBER) * 3,
1010                getFontHeight(FONT_LEVEL_NUMBER) - 1,
1011                DX_LEVEL - 1, DY_LEVEL + 1);
1012
1013     /* restore the area for displaying emeralds */
1014     DrawGameValue_Emeralds(local_player->gems_still_needed);
1015
1016     /* yes, this is all really ugly :-) */
1017   }
1018 }
1019
1020 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1021                        int key_bits)
1022 {
1023   int key[MAX_NUM_KEYS];
1024   int i;
1025
1026   for (i = 0; i < MAX_NUM_KEYS; i++)
1027     key[i] = key_bits & (1 << i);
1028
1029   DrawGameValue_Level(level_nr);
1030
1031   DrawGameValue_Emeralds(emeralds);
1032   DrawGameValue_Dynamite(dynamite);
1033   DrawGameValue_Score(score);
1034   DrawGameValue_Time(time);
1035
1036   DrawGameValue_Keys(key);
1037 }
1038
1039 void DrawGameDoorValues()
1040 {
1041   int i;
1042
1043   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1044   {
1045     DrawGameDoorValues_EM();
1046
1047     return;
1048   }
1049
1050   DrawGameValue_Level(level_nr);
1051
1052   DrawGameValue_Emeralds(local_player->gems_still_needed);
1053   DrawGameValue_Dynamite(local_player->inventory_size);
1054   DrawGameValue_Score(local_player->score);
1055   DrawGameValue_Time(TimeLeft);
1056
1057   for (i = 0; i < MAX_PLAYERS; i++)
1058     DrawGameValue_Keys(stored_player[i].key);
1059 }
1060
1061 static void resolve_group_element(int group_element, int recursion_depth)
1062 {
1063   static int group_nr;
1064   static struct ElementGroupInfo *group;
1065   struct ElementGroupInfo *actual_group = element_info[group_element].group;
1066   int i;
1067
1068   if (recursion_depth > NUM_GROUP_ELEMENTS)     /* recursion too deep */
1069   {
1070     Error(ERR_WARN, "recursion too deep when resolving group element %d",
1071           group_element - EL_GROUP_START + 1);
1072
1073     /* replace element which caused too deep recursion by question mark */
1074     group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1075
1076     return;
1077   }
1078
1079   if (recursion_depth == 0)                     /* initialization */
1080   {
1081     group = element_info[group_element].group;
1082     group_nr = group_element - EL_GROUP_START;
1083
1084     group->num_elements_resolved = 0;
1085     group->choice_pos = 0;
1086   }
1087
1088   for (i = 0; i < actual_group->num_elements; i++)
1089   {
1090     int element = actual_group->element[i];
1091
1092     if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1093       break;
1094
1095     if (IS_GROUP_ELEMENT(element))
1096       resolve_group_element(element, recursion_depth + 1);
1097     else
1098     {
1099       group->element_resolved[group->num_elements_resolved++] = element;
1100       element_info[element].in_group[group_nr] = TRUE;
1101     }
1102   }
1103 }
1104
1105
1106 /*
1107   =============================================================================
1108   InitGameEngine()
1109   -----------------------------------------------------------------------------
1110   initialize game engine due to level / tape version number
1111   =============================================================================
1112 */
1113
1114 static void InitGameEngine()
1115 {
1116   int i, j, k, l;
1117
1118   /* set game engine from tape file when re-playing, else from level file */
1119   game.engine_version = (tape.playing ? tape.engine_version :
1120                          level.game_version);
1121
1122   /* ---------------------------------------------------------------------- */
1123   /* set flags for bugs and changes according to active game engine version */
1124   /* ---------------------------------------------------------------------- */
1125
1126   /*
1127     Summary of bugfix/change:
1128     Fixed handling for custom elements that change when pushed by the player.
1129
1130     Fixed/changed in version:
1131     3.1.0
1132
1133     Description:
1134     Before 3.1.0, custom elements that "change when pushing" changed directly
1135     after the player started pushing them (until then handled in "DigField()").
1136     Since 3.1.0, these custom elements are not changed until the "pushing"
1137     move of the element is finished (now handled in "ContinueMoving()").
1138
1139     Affected levels/tapes:
1140     The first condition is generally needed for all levels/tapes before version
1141     3.1.0, which might use the old behaviour before it was changed; known tapes
1142     that are affected are some tapes from the level set "Walpurgis Gardens" by
1143     Jamie Cullen.
1144     The second condition is an exception from the above case and is needed for
1145     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1146     above (including some development versions of 3.1.0), but before it was
1147     known that this change would break tapes like the above and was fixed in
1148     3.1.1, so that the changed behaviour was active although the engine version
1149     while recording maybe was before 3.1.0. There is at least one tape that is
1150     affected by this exception, which is the tape for the one-level set "Bug
1151     Machine" by Juergen Bonhagen.
1152   */
1153
1154   game.use_change_when_pushing_bug =
1155     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1156      !(tape.playing &&
1157        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1158        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1159
1160   /*
1161     Summary of bugfix/change:
1162     Fixed handling for blocking the field the player leaves when moving.
1163
1164     Fixed/changed in version:
1165     3.1.1
1166
1167     Description:
1168     Before 3.1.1, when "block last field when moving" was enabled, the field
1169     the player is leaving when moving was blocked for the time of the move,
1170     and was directly unblocked afterwards. This resulted in the last field
1171     being blocked for exactly one less than the number of frames of one player
1172     move. Additionally, even when blocking was disabled, the last field was
1173     blocked for exactly one frame.
1174     Since 3.1.1, due to changes in player movement handling, the last field
1175     is not blocked at all when blocking is disabled. When blocking is enabled,
1176     the last field is blocked for exactly the number of frames of one player
1177     move. Additionally, if the player is Murphy, the hero of Supaplex, the
1178     last field is blocked for exactly one more than the number of frames of
1179     one player move.
1180
1181     Affected levels/tapes:
1182     (!!! yet to be determined -- probably many !!!)
1183   */
1184
1185   game.use_block_last_field_bug =
1186     (game.engine_version < VERSION_IDENT(3,1,1,0));
1187
1188   /* ---------------------------------------------------------------------- */
1189
1190   /* dynamically adjust element properties according to game engine version */
1191   InitElementPropertiesEngine(game.engine_version);
1192
1193 #if 0
1194   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1195   printf("          tape version == %06d [%s] [file: %06d]\n",
1196          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1197          tape.file_version);
1198   printf("       => game.engine_version == %06d\n", game.engine_version);
1199 #endif
1200
1201   /* ---------- recursively resolve group elements ------------------------- */
1202
1203   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1204     for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1205       element_info[i].in_group[j] = FALSE;
1206
1207   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1208     resolve_group_element(EL_GROUP_START + i, 0);
1209
1210   /* ---------- initialize player's initial move delay --------------------- */
1211
1212   /* dynamically adjust player properties according to level information */
1213   game.initial_move_delay_value =
1214     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1215
1216   /* dynamically adjust player properties according to game engine version */
1217   game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1218                              game.initial_move_delay_value : 0);
1219
1220   /* ---------- initialize player's initial push delay --------------------- */
1221
1222   /* dynamically adjust player properties according to game engine version */
1223   game.initial_push_delay_value =
1224     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1225
1226   /* ---------- initialize changing elements ------------------------------- */
1227
1228   /* initialize changing elements information */
1229   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1230   {
1231     struct ElementInfo *ei = &element_info[i];
1232
1233     /* this pointer might have been changed in the level editor */
1234     ei->change = &ei->change_page[0];
1235
1236     if (!IS_CUSTOM_ELEMENT(i))
1237     {
1238       ei->change->target_element = EL_EMPTY_SPACE;
1239       ei->change->delay_fixed = 0;
1240       ei->change->delay_random = 0;
1241       ei->change->delay_frames = 1;
1242     }
1243
1244     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1245     {
1246       ei->has_change_event[j] = FALSE;
1247
1248       ei->event_page_nr[j] = 0;
1249       ei->event_page[j] = &ei->change_page[0];
1250     }
1251   }
1252
1253   /* add changing elements from pre-defined list */
1254   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1255   {
1256     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1257     struct ElementInfo *ei = &element_info[ch_delay->element];
1258
1259     ei->change->target_element       = ch_delay->target_element;
1260     ei->change->delay_fixed          = ch_delay->change_delay;
1261
1262     ei->change->pre_change_function  = ch_delay->pre_change_function;
1263     ei->change->change_function      = ch_delay->change_function;
1264     ei->change->post_change_function = ch_delay->post_change_function;
1265
1266     ei->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_NO_MOVING;
1502     player->MovPos = 0;
1503     player->GfxPos = 0;
1504     player->GfxDir = MV_NO_MOVING;
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_NO_MOVING;
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_NO_MOVING);
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_NO_MOVING;
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.balloon_dir = MV_NO_MOVING;
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_NO_MOVING;
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_NO_MOVING;
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_NO_MOVING)
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_NO_MOVING)
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_NO_MOVING;
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_NO_MOVING;
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_NO_MOVING)
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_NO_MOVING,
3465     MV_RIGHT,
3466     MV_NO_MOVING,
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_NO_MOVING)
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_NO_MOVING)
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.balloon_dir;
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_NO_MOVING;
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_NO_MOVING;
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_ALONG_LEFT_SIDE)
4392   {
4393     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4394       MovDir[x][y] = left_dir;
4395     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4396       MovDir[x][y] = right_dir;
4397
4398     if (MovDir[x][y] != old_move_dir)
4399       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4400   }
4401   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4402   {
4403     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4404       MovDir[x][y] = right_dir;
4405     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4406       MovDir[x][y] = left_dir;
4407
4408     if (MovDir[x][y] != old_move_dir)
4409       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4410   }
4411   else if (move_pattern == MV_TOWARDS_PLAYER ||
4412            move_pattern == MV_AWAY_FROM_PLAYER)
4413   {
4414     int attr_x = -1, attr_y = -1;
4415     int newx, newy;
4416     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4417
4418     if (AllPlayersGone)
4419     {
4420       attr_x = ExitX;
4421       attr_y = ExitY;
4422     }
4423     else
4424     {
4425       int i;
4426
4427       for (i = 0; i < MAX_PLAYERS; i++)
4428       {
4429         struct PlayerInfo *player = &stored_player[i];
4430         int jx = player->jx, jy = player->jy;
4431
4432         if (!player->active)
4433           continue;
4434
4435         if (attr_x == -1 ||
4436             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4437         {
4438           attr_x = jx;
4439           attr_y = jy;
4440         }
4441       }
4442     }
4443
4444     MovDir[x][y] = MV_NO_MOVING;
4445     if (attr_x < x)
4446       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4447     else if (attr_x > x)
4448       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4449     if (attr_y < y)
4450       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4451     else if (attr_y > y)
4452       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4453
4454     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4455
4456     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4457     {
4458       boolean first_horiz = RND(2);
4459       int new_move_dir = MovDir[x][y];
4460
4461       if (element_info[element].move_stepsize == 0)     /* "not moving" */
4462       {
4463         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
4464         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4465
4466         return;
4467       }
4468
4469       MovDir[x][y] =
4470         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4471       Moving2Blocked(x, y, &newx, &newy);
4472
4473       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4474         return;
4475
4476       MovDir[x][y] =
4477         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4478       Moving2Blocked(x, y, &newx, &newy);
4479
4480       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4481         return;
4482
4483       MovDir[x][y] = old_move_dir;
4484     }
4485   }
4486   else if (move_pattern == MV_WHEN_PUSHED ||
4487            move_pattern == MV_WHEN_DROPPED)
4488   {
4489     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4490       MovDir[x][y] = MV_NO_MOVING;
4491
4492     MovDelay[x][y] = 0;
4493   }
4494   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4495   {
4496     static int test_xy[7][2] =
4497     {
4498       { 0, -1 },
4499       { -1, 0 },
4500       { +1, 0 },
4501       { 0, +1 },
4502       { 0, -1 },
4503       { -1, 0 },
4504       { +1, 0 },
4505     };
4506     static int test_dir[7] =
4507     {
4508       MV_UP,
4509       MV_LEFT,
4510       MV_RIGHT,
4511       MV_DOWN,
4512       MV_UP,
4513       MV_LEFT,
4514       MV_RIGHT,
4515     };
4516     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4517     int move_preference = -1000000;     /* start with very low preference */
4518     int new_move_dir = MV_NO_MOVING;
4519     int start_test = RND(4);
4520     int i;
4521
4522     for (i = 0; i < NUM_DIRECTIONS; i++)
4523     {
4524       int move_dir = test_dir[start_test + i];
4525       int move_dir_preference;
4526
4527       xx = x + test_xy[start_test + i][0];
4528       yy = y + test_xy[start_test + i][1];
4529
4530       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4531           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4532       {
4533         new_move_dir = move_dir;
4534
4535         break;
4536       }
4537
4538       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4539         continue;
4540
4541       move_dir_preference = -1 * RunnerVisit[xx][yy];
4542       if (hunter_mode && PlayerVisit[xx][yy] > 0)
4543         move_dir_preference = PlayerVisit[xx][yy];
4544
4545       if (move_dir_preference > move_preference)
4546       {
4547         /* prefer field that has not been visited for the longest time */
4548         move_preference = move_dir_preference;
4549         new_move_dir = move_dir;
4550       }
4551       else if (move_dir_preference == move_preference &&
4552                move_dir == old_move_dir)
4553       {
4554         /* prefer last direction when all directions are preferred equally */
4555         move_preference = move_dir_preference;
4556         new_move_dir = move_dir;
4557       }
4558     }
4559
4560     MovDir[x][y] = new_move_dir;
4561     if (old_move_dir != new_move_dir)
4562       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4563   }
4564 }
4565
4566 static void TurnRound(int x, int y)
4567 {
4568   int direction = MovDir[x][y];
4569
4570   TurnRoundExt(x, y);
4571
4572   GfxDir[x][y] = MovDir[x][y];
4573
4574   if (direction != MovDir[x][y])
4575     GfxFrame[x][y] = 0;
4576
4577   if (MovDelay[x][y])
4578     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4579 }
4580
4581 static boolean JustBeingPushed(int x, int y)
4582 {
4583   int i;
4584
4585   for (i = 0; i < MAX_PLAYERS; i++)
4586   {
4587     struct PlayerInfo *player = &stored_player[i];
4588
4589     if (player->active && player->is_pushing && player->MovPos)
4590     {
4591       int next_jx = player->jx + (player->jx - player->last_jx);
4592       int next_jy = player->jy + (player->jy - player->last_jy);
4593
4594       if (x == next_jx && y == next_jy)
4595         return TRUE;
4596     }
4597   }
4598
4599   return FALSE;
4600 }
4601
4602 void StartMoving(int x, int y)
4603 {
4604   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
4605   int element = Feld[x][y];
4606
4607   if (Stop[x][y])
4608     return;
4609
4610   if (MovDelay[x][y] == 0)
4611     GfxAction[x][y] = ACTION_DEFAULT;
4612
4613   if (CAN_FALL(element) && y < lev_fieldy - 1)
4614   {
4615     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
4616         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
4617       if (JustBeingPushed(x, y))
4618         return;
4619
4620     if (element == EL_QUICKSAND_FULL)
4621     {
4622       if (IS_FREE(x, y + 1))
4623       {
4624         InitMovingField(x, y, MV_DOWN);
4625         started_moving = TRUE;
4626
4627         Feld[x][y] = EL_QUICKSAND_EMPTYING;
4628         Store[x][y] = EL_ROCK;
4629
4630         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4631       }
4632       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4633       {
4634         if (!MovDelay[x][y])
4635           MovDelay[x][y] = TILEY + 1;
4636
4637         if (MovDelay[x][y])
4638         {
4639           MovDelay[x][y]--;
4640           if (MovDelay[x][y])
4641             return;
4642         }
4643
4644         Feld[x][y] = EL_QUICKSAND_EMPTY;
4645         Feld[x][y + 1] = EL_QUICKSAND_FULL;
4646         Store[x][y + 1] = Store[x][y];
4647         Store[x][y] = 0;
4648
4649         PlayLevelSoundAction(x, y, ACTION_FILLING);
4650       }
4651     }
4652     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4653              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4654     {
4655       InitMovingField(x, y, MV_DOWN);
4656       started_moving = TRUE;
4657
4658       Feld[x][y] = EL_QUICKSAND_FILLING;
4659       Store[x][y] = element;
4660
4661       PlayLevelSoundAction(x, y, ACTION_FILLING);
4662     }
4663     else if (element == EL_MAGIC_WALL_FULL)
4664     {
4665       if (IS_FREE(x, y + 1))
4666       {
4667         InitMovingField(x, y, MV_DOWN);
4668         started_moving = TRUE;
4669
4670         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4671         Store[x][y] = EL_CHANGED(Store[x][y]);
4672       }
4673       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4674       {
4675         if (!MovDelay[x][y])
4676           MovDelay[x][y] = TILEY/4 + 1;
4677
4678         if (MovDelay[x][y])
4679         {
4680           MovDelay[x][y]--;
4681           if (MovDelay[x][y])
4682             return;
4683         }
4684
4685         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4686         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4687         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4688         Store[x][y] = 0;
4689       }
4690     }
4691     else if (element == EL_BD_MAGIC_WALL_FULL)
4692     {
4693       if (IS_FREE(x, y + 1))
4694       {
4695         InitMovingField(x, y, MV_DOWN);
4696         started_moving = TRUE;
4697
4698         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4699         Store[x][y] = EL_CHANGED2(Store[x][y]);
4700       }
4701       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4702       {
4703         if (!MovDelay[x][y])
4704           MovDelay[x][y] = TILEY/4 + 1;
4705
4706         if (MovDelay[x][y])
4707         {
4708           MovDelay[x][y]--;
4709           if (MovDelay[x][y])
4710             return;
4711         }
4712
4713         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4714         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4715         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4716         Store[x][y] = 0;
4717       }
4718     }
4719     else if (CAN_PASS_MAGIC_WALL(element) &&
4720              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4721               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4722     {
4723       InitMovingField(x, y, MV_DOWN);
4724       started_moving = TRUE;
4725
4726       Feld[x][y] =
4727         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4728          EL_BD_MAGIC_WALL_FILLING);
4729       Store[x][y] = element;
4730     }
4731     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4732     {
4733       SplashAcid(x, y + 1);
4734
4735       InitMovingField(x, y, MV_DOWN);
4736       started_moving = TRUE;
4737
4738       Store[x][y] = EL_ACID;
4739     }
4740     else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4741               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
4742
4743              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4744               CAN_FALL(element) && WasJustFalling[x][y] &&
4745               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
4746
4747              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4748               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4749               (Feld[x][y + 1] == EL_BLOCKED)))
4750     {
4751       /* this is needed for a special case not covered by calling "Impact()"
4752          from "ContinueMoving()": if an element moves to a tile directly below
4753          another element which was just falling on that tile (which was empty
4754          in the previous frame), the falling element above would just stop
4755          instead of smashing the element below (in previous version, the above
4756          element was just checked for "moving" instead of "falling", resulting
4757          in incorrect smashes caused by horizontal movement of the above
4758          element; also, the case of the player being the element to smash was
4759          simply not covered here... :-/ ) */
4760
4761       CheckCollision[x][y] = 0;
4762
4763       Impact(x, y);
4764     }
4765     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
4766     {
4767       if (MovDir[x][y] == MV_NO_MOVING)
4768       {
4769         InitMovingField(x, y, MV_DOWN);
4770         started_moving = TRUE;
4771       }
4772     }
4773     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4774     {
4775       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4776         MovDir[x][y] = MV_DOWN;
4777
4778       InitMovingField(x, y, MV_DOWN);
4779       started_moving = TRUE;
4780     }
4781     else if (element == EL_AMOEBA_DROP)
4782     {
4783       Feld[x][y] = EL_AMOEBA_GROWING;
4784       Store[x][y] = EL_AMOEBA_WET;
4785     }
4786     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4787               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4788              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4789              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4790     {
4791       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
4792                                 (IS_FREE(x - 1, y + 1) ||
4793                                  Feld[x - 1][y + 1] == EL_ACID));
4794       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4795                                 (IS_FREE(x + 1, y + 1) ||
4796                                  Feld[x + 1][y + 1] == EL_ACID));
4797       boolean can_fall_any  = (can_fall_left || can_fall_right);
4798       boolean can_fall_both = (can_fall_left && can_fall_right);
4799       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4800
4801 #if USE_NEW_ALL_SLIPPERY
4802       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
4803       {
4804         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4805           can_fall_right = FALSE;
4806         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4807           can_fall_left = FALSE;
4808         else if (slippery_type == SLIPPERY_ONLY_LEFT)
4809           can_fall_right = FALSE;
4810         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4811           can_fall_left = FALSE;
4812
4813         can_fall_any  = (can_fall_left || can_fall_right);
4814         can_fall_both = FALSE;
4815       }
4816 #else
4817       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4818       {
4819         if (slippery_type == SLIPPERY_ONLY_LEFT)
4820           can_fall_right = FALSE;
4821         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4822           can_fall_left = FALSE;
4823         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4824           can_fall_right = FALSE;
4825         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4826           can_fall_left = FALSE;
4827
4828         can_fall_any  = (can_fall_left || can_fall_right);
4829         can_fall_both = (can_fall_left && can_fall_right);
4830       }
4831 #endif
4832
4833 #if USE_NEW_ALL_SLIPPERY
4834 #else
4835 #if USE_NEW_SP_SLIPPERY
4836       /* !!! better use the same properties as for custom elements here !!! */
4837       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
4838                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
4839       {
4840         can_fall_right = FALSE;         /* slip down on left side */
4841         can_fall_both = FALSE;
4842       }
4843 #endif
4844 #endif
4845
4846 #if USE_NEW_ALL_SLIPPERY
4847       if (can_fall_both)
4848       {
4849         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4850           can_fall_right = FALSE;       /* slip down on left side */
4851         else
4852           can_fall_left = !(can_fall_right = RND(2));
4853
4854         can_fall_both = FALSE;
4855       }
4856 #else
4857       if (can_fall_both)
4858       {
4859         if (game.emulation == EMU_BOULDERDASH ||
4860             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
4861           can_fall_right = FALSE;       /* slip down on left side */
4862         else
4863           can_fall_left = !(can_fall_right = RND(2));
4864
4865         can_fall_both = FALSE;
4866       }
4867 #endif
4868
4869       if (can_fall_any)
4870       {
4871         /* if not determined otherwise, prefer left side for slipping down */
4872         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4873         started_moving = TRUE;
4874       }
4875     }
4876 #if 0
4877     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
4878 #else
4879     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4880 #endif
4881     {
4882       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
4883       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4884       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4885       int belt_dir = game.belt_dir[belt_nr];
4886
4887       if ((belt_dir == MV_LEFT  && left_is_free) ||
4888           (belt_dir == MV_RIGHT && right_is_free))
4889       {
4890         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
4891
4892         InitMovingField(x, y, belt_dir);
4893         started_moving = TRUE;
4894
4895         Pushed[x][y] = TRUE;
4896         Pushed[nextx][y] = TRUE;
4897
4898         GfxAction[x][y] = ACTION_DEFAULT;
4899       }
4900       else
4901       {
4902         MovDir[x][y] = 0;       /* if element was moving, stop it */
4903       }
4904     }
4905   }
4906
4907   /* not "else if" because of elements that can fall and move (EL_SPRING) */
4908 #if 0
4909   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
4910 #else
4911   if (CAN_MOVE(element) && !started_moving)
4912 #endif
4913   {
4914     int move_pattern = element_info[element].move_pattern;
4915     int newx, newy;
4916
4917 #if 0
4918 #if DEBUG
4919     if (MovDir[x][y] == MV_NO_MOVING)
4920     {
4921       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
4922              x, y, element, element_info[element].token_name);
4923       printf("StartMoving(): This should never happen!\n");
4924     }
4925 #endif
4926 #endif
4927
4928     Moving2Blocked(x, y, &newx, &newy);
4929
4930     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4931       return;
4932
4933     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
4934         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
4935     {
4936       WasJustMoving[x][y] = 0;
4937       CheckCollision[x][y] = 0;
4938
4939       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4940
4941       if (Feld[x][y] != element)        /* element has changed */
4942         return;
4943     }
4944
4945     if (!MovDelay[x][y])        /* start new movement phase */
4946     {
4947       /* all objects that can change their move direction after each step
4948          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4949
4950       if (element != EL_YAMYAM &&
4951           element != EL_DARK_YAMYAM &&
4952           element != EL_PACMAN &&
4953           !(move_pattern & MV_ANY_DIRECTION) &&
4954           move_pattern != MV_TURNING_LEFT &&
4955           move_pattern != MV_TURNING_RIGHT &&
4956           move_pattern != MV_TURNING_LEFT_RIGHT &&
4957           move_pattern != MV_TURNING_RIGHT_LEFT &&
4958           move_pattern != MV_TURNING_RANDOM)
4959       {
4960         TurnRound(x, y);
4961
4962         if (MovDelay[x][y] && (element == EL_BUG ||
4963                                element == EL_SPACESHIP ||
4964                                element == EL_SP_SNIKSNAK ||
4965                                element == EL_SP_ELECTRON ||
4966                                element == EL_MOLE))
4967           DrawLevelField(x, y);
4968       }
4969     }
4970
4971     if (MovDelay[x][y])         /* wait some time before next movement */
4972     {
4973       MovDelay[x][y]--;
4974
4975       if (element == EL_ROBOT ||
4976           element == EL_YAMYAM ||
4977           element == EL_DARK_YAMYAM)
4978       {
4979         DrawLevelElementAnimationIfNeeded(x, y, element);
4980         PlayLevelSoundAction(x, y, ACTION_WAITING);
4981       }
4982       else if (element == EL_SP_ELECTRON)
4983         DrawLevelElementAnimationIfNeeded(x, y, element);
4984       else if (element == EL_DRAGON)
4985       {
4986         int i;
4987         int dir = MovDir[x][y];
4988         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4989         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
4990         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
4991                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
4992                        dir == MV_UP     ? IMG_FLAMES_1_UP :
4993                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4994         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4995
4996         GfxAction[x][y] = ACTION_ATTACKING;
4997
4998         if (IS_PLAYER(x, y))
4999           DrawPlayerField(x, y);
5000         else
5001           DrawLevelField(x, y);
5002
5003         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5004
5005         for (i = 1; i <= 3; i++)
5006         {
5007           int xx = x + i * dx;
5008           int yy = y + i * dy;
5009           int sx = SCREENX(xx);
5010           int sy = SCREENY(yy);
5011           int flame_graphic = graphic + (i - 1);
5012
5013           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5014             break;
5015
5016           if (MovDelay[x][y])
5017           {
5018             int flamed = MovingOrBlocked2Element(xx, yy);
5019
5020             /* !!! */
5021 #if 0
5022             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5023               Bang(xx, yy);
5024             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5025               RemoveMovingField(xx, yy);
5026             else
5027               RemoveField(xx, yy);
5028 #else
5029             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5030               Bang(xx, yy);
5031             else
5032               RemoveMovingField(xx, yy);
5033 #endif
5034
5035             ChangeDelay[xx][yy] = 0;
5036
5037             Feld[xx][yy] = EL_FLAMES;
5038
5039             if (IN_SCR_FIELD(sx, sy))
5040             {
5041               DrawLevelFieldCrumbledSand(xx, yy);
5042               DrawGraphic(sx, sy, flame_graphic, frame);
5043             }
5044           }
5045           else
5046           {
5047             if (Feld[xx][yy] == EL_FLAMES)
5048               Feld[xx][yy] = EL_EMPTY;
5049             DrawLevelField(xx, yy);
5050           }
5051         }
5052       }
5053
5054       if (MovDelay[x][y])       /* element still has to wait some time */
5055       {
5056         PlayLevelSoundAction(x, y, ACTION_WAITING);
5057
5058         return;
5059       }
5060     }
5061
5062     /* now make next step */
5063
5064     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5065
5066     if (DONT_COLLIDE_WITH(element) &&
5067         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5068         !PLAYER_ENEMY_PROTECTED(newx, newy))
5069     {
5070       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
5071
5072       return;
5073     }
5074
5075     else if (CAN_MOVE_INTO_ACID(element) &&
5076              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5077              (MovDir[x][y] == MV_DOWN ||
5078               game.engine_version >= VERSION_IDENT(3,1,0,0)))
5079     {
5080       SplashAcid(newx, newy);
5081       Store[x][y] = EL_ACID;
5082     }
5083     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5084     {
5085       if (Feld[newx][newy] == EL_EXIT_OPEN)
5086       {
5087         RemoveField(x, y);
5088         DrawLevelField(x, y);
5089
5090         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5091         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5092           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5093
5094         local_player->friends_still_needed--;
5095         if (!local_player->friends_still_needed &&
5096             !local_player->GameOver && AllPlayersGone)
5097           local_player->LevelSolved = local_player->GameOver = TRUE;
5098
5099         return;
5100       }
5101       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5102       {
5103         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5104           DrawLevelField(newx, newy);
5105         else
5106           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5107       }
5108       else if (!IS_FREE(newx, newy))
5109       {
5110         GfxAction[x][y] = ACTION_WAITING;
5111
5112         if (IS_PLAYER(x, y))
5113           DrawPlayerField(x, y);
5114         else
5115           DrawLevelField(x, y);
5116
5117         return;
5118       }
5119     }
5120     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5121     {
5122       if (IS_FOOD_PIG(Feld[newx][newy]))
5123       {
5124         if (IS_MOVING(newx, newy))
5125           RemoveMovingField(newx, newy);
5126         else
5127         {
5128           Feld[newx][newy] = EL_EMPTY;
5129           DrawLevelField(newx, newy);
5130         }
5131
5132         PlayLevelSound(x, y, SND_PIG_DIGGING);
5133       }
5134       else if (!IS_FREE(newx, newy))
5135       {
5136         if (IS_PLAYER(x, y))
5137           DrawPlayerField(x, y);
5138         else
5139           DrawLevelField(x, y);
5140
5141         return;
5142       }
5143     }
5144     else if (IS_CUSTOM_ELEMENT(element) &&
5145              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5146     {
5147       int new_element = Feld[newx][newy];
5148
5149       if (!IS_FREE(newx, newy))
5150       {
5151         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5152                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5153                       ACTION_BREAKING);
5154
5155         /* no element can dig solid indestructible elements */
5156         if (IS_INDESTRUCTIBLE(new_element) &&
5157             !IS_DIGGABLE(new_element) &&
5158             !IS_COLLECTIBLE(new_element))
5159           return;
5160
5161         if (AmoebaNr[newx][newy] &&
5162             (new_element == EL_AMOEBA_FULL ||
5163              new_element == EL_BD_AMOEBA ||
5164              new_element == EL_AMOEBA_GROWING))
5165         {
5166           AmoebaCnt[AmoebaNr[newx][newy]]--;
5167           AmoebaCnt2[AmoebaNr[newx][newy]]--;
5168         }
5169
5170         if (IS_MOVING(newx, newy))
5171           RemoveMovingField(newx, newy);
5172         else
5173         {
5174           RemoveField(newx, newy);
5175           DrawLevelField(newx, newy);
5176         }
5177
5178         /* if digged element was about to explode, prevent the explosion */
5179         ExplodeField[newx][newy] = EX_TYPE_NONE;
5180
5181         PlayLevelSoundAction(x, y, action);
5182       }
5183
5184       Store[newx][newy] = EL_EMPTY;
5185       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5186       {
5187         int move_leave_element = element_info[element].move_leave_element;
5188
5189         /* this makes it possible to leave the removed element again */
5190         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
5191                              new_element : move_leave_element);
5192       }
5193
5194       if (move_pattern & MV_MAZE_RUNNER_STYLE)
5195       {
5196         RunnerVisit[x][y] = FrameCounter;
5197         PlayerVisit[x][y] /= 8;         /* expire player visit path */
5198       }
5199     }
5200     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5201     {
5202       if (!IS_FREE(newx, newy))
5203       {
5204         if (IS_PLAYER(x, y))
5205           DrawPlayerField(x, y);
5206         else
5207           DrawLevelField(x, y);
5208
5209         return;
5210       }
5211       else
5212       {
5213         boolean wanna_flame = !RND(10);
5214         int dx = newx - x, dy = newy - y;
5215         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5216         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5217         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5218                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5219         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5220                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5221
5222         if ((wanna_flame ||
5223              IS_CLASSIC_ENEMY(element1) ||
5224              IS_CLASSIC_ENEMY(element2)) &&
5225             element1 != EL_DRAGON && element2 != EL_DRAGON &&
5226             element1 != EL_FLAMES && element2 != EL_FLAMES)
5227         {
5228           ResetGfxAnimation(x, y);
5229           GfxAction[x][y] = ACTION_ATTACKING;
5230
5231           if (IS_PLAYER(x, y))
5232             DrawPlayerField(x, y);
5233           else
5234             DrawLevelField(x, y);
5235
5236           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5237
5238           MovDelay[x][y] = 50;
5239
5240           /* !!! */
5241 #if 0
5242           RemoveField(newx, newy);
5243 #endif
5244           Feld[newx][newy] = EL_FLAMES;
5245           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5246           {
5247 #if 0
5248             RemoveField(newx1, newy1);
5249 #endif
5250             Feld[newx1][newy1] = EL_FLAMES;
5251           }
5252           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5253           {
5254 #if 0
5255             RemoveField(newx2, newy2);
5256 #endif
5257             Feld[newx2][newy2] = EL_FLAMES;
5258           }
5259
5260           return;
5261         }
5262       }
5263     }
5264     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5265              Feld[newx][newy] == EL_DIAMOND)
5266     {
5267       if (IS_MOVING(newx, newy))
5268         RemoveMovingField(newx, newy);
5269       else
5270       {
5271         Feld[newx][newy] = EL_EMPTY;
5272         DrawLevelField(newx, newy);
5273       }
5274
5275       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5276     }
5277     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5278              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5279     {
5280       if (AmoebaNr[newx][newy])
5281       {
5282         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5283         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5284             Feld[newx][newy] == EL_BD_AMOEBA)
5285           AmoebaCnt[AmoebaNr[newx][newy]]--;
5286       }
5287
5288 #if 0
5289       /* !!! test !!! */
5290       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5291       {
5292         RemoveMovingField(newx, newy);
5293       }
5294 #else
5295       if (IS_MOVING(newx, newy))
5296       {
5297         RemoveMovingField(newx, newy);
5298       }
5299 #endif
5300       else
5301       {
5302         Feld[newx][newy] = EL_EMPTY;
5303         DrawLevelField(newx, newy);
5304       }
5305
5306       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5307     }
5308     else if ((element == EL_PACMAN || element == EL_MOLE)
5309              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5310     {
5311       if (AmoebaNr[newx][newy])
5312       {
5313         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5314         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5315             Feld[newx][newy] == EL_BD_AMOEBA)
5316           AmoebaCnt[AmoebaNr[newx][newy]]--;
5317       }
5318
5319       if (element == EL_MOLE)
5320       {
5321         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5322         PlayLevelSound(x, y, SND_MOLE_DIGGING);
5323
5324         ResetGfxAnimation(x, y);
5325         GfxAction[x][y] = ACTION_DIGGING;
5326         DrawLevelField(x, y);
5327
5328         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
5329
5330         return;                         /* wait for shrinking amoeba */
5331       }
5332       else      /* element == EL_PACMAN */
5333       {
5334         Feld[newx][newy] = EL_EMPTY;
5335         DrawLevelField(newx, newy);
5336         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5337       }
5338     }
5339     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5340              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5341               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5342     {
5343       /* wait for shrinking amoeba to completely disappear */
5344       return;
5345     }
5346     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5347     {
5348       /* object was running against a wall */
5349
5350       TurnRound(x, y);
5351
5352 #if 0
5353       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
5354       if (move_pattern & MV_ANY_DIRECTION &&
5355           move_pattern == MovDir[x][y])
5356       {
5357         int blocking_element =
5358           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5359
5360         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5361                                  MovDir[x][y]);
5362
5363         element = Feld[x][y];   /* element might have changed */
5364       }
5365 #endif
5366
5367       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
5368         DrawLevelElementAnimation(x, y, element);
5369
5370       if (DONT_TOUCH(element))
5371         TestIfBadThingTouchesPlayer(x, y);
5372
5373       return;
5374     }
5375
5376     InitMovingField(x, y, MovDir[x][y]);
5377
5378     PlayLevelSoundAction(x, y, ACTION_MOVING);
5379   }
5380
5381   if (MovDir[x][y])
5382     ContinueMoving(x, y);
5383 }
5384
5385 void ContinueMoving(int x, int y)
5386 {
5387   int element = Feld[x][y];
5388   int stored = Store[x][y];
5389   struct ElementInfo *ei = &element_info[element];
5390   int direction = MovDir[x][y];
5391   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5392   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5393   int newx = x + dx, newy = y + dy;
5394   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
5395   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5396   boolean last_line = (newy == lev_fieldy - 1);
5397
5398   MovPos[x][y] += getElementMoveStepsize(x, y);
5399
5400   if (pushed_by_player) /* special case: moving object pushed by player */
5401     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5402
5403   if (ABS(MovPos[x][y]) < TILEX)
5404   {
5405     DrawLevelField(x, y);
5406
5407     return;     /* element is still moving */
5408   }
5409
5410   /* element reached destination field */
5411
5412   Feld[x][y] = EL_EMPTY;
5413   Feld[newx][newy] = element;
5414   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
5415
5416   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
5417   {
5418     element = Feld[newx][newy] = EL_ACID;
5419   }
5420   else if (element == EL_MOLE)
5421   {
5422     Feld[x][y] = EL_SAND;
5423
5424     DrawLevelFieldCrumbledSandNeighbours(x, y);
5425   }
5426   else if (element == EL_QUICKSAND_FILLING)
5427   {
5428     element = Feld[newx][newy] = get_next_element(element);
5429     Store[newx][newy] = Store[x][y];
5430   }
5431   else if (element == EL_QUICKSAND_EMPTYING)
5432   {
5433     Feld[x][y] = get_next_element(element);
5434     element = Feld[newx][newy] = Store[x][y];
5435   }
5436   else if (element == EL_MAGIC_WALL_FILLING)
5437   {
5438     element = Feld[newx][newy] = get_next_element(element);
5439     if (!game.magic_wall_active)
5440       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5441     Store[newx][newy] = Store[x][y];
5442   }
5443   else if (element == EL_MAGIC_WALL_EMPTYING)
5444   {
5445     Feld[x][y] = get_next_element(element);
5446     if (!game.magic_wall_active)
5447       Feld[x][y] = EL_MAGIC_WALL_DEAD;
5448     element = Feld[newx][newy] = Store[x][y];
5449
5450 #if USE_NEW_COLLECT_COUNT
5451     InitField(newx, newy, FALSE);
5452 #endif
5453   }
5454   else if (element == EL_BD_MAGIC_WALL_FILLING)
5455   {
5456     element = Feld[newx][newy] = get_next_element(element);
5457     if (!game.magic_wall_active)
5458       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5459     Store[newx][newy] = Store[x][y];
5460   }
5461   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5462   {
5463     Feld[x][y] = get_next_element(element);
5464     if (!game.magic_wall_active)
5465       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5466     element = Feld[newx][newy] = Store[x][y];
5467
5468 #if USE_NEW_COLLECT_COUNT
5469     InitField(newx, newy, FALSE);
5470 #endif
5471   }
5472   else if (element == EL_AMOEBA_DROPPING)
5473   {
5474     Feld[x][y] = get_next_element(element);
5475     element = Feld[newx][newy] = Store[x][y];
5476   }
5477   else if (element == EL_SOKOBAN_OBJECT)
5478   {
5479     if (Back[x][y])
5480       Feld[x][y] = Back[x][y];
5481
5482     if (Back[newx][newy])
5483       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5484
5485     Back[x][y] = Back[newx][newy] = 0;
5486   }
5487
5488   Store[x][y] = EL_EMPTY;
5489   MovPos[x][y] = 0;
5490   MovDir[x][y] = 0;
5491   MovDelay[x][y] = 0;
5492
5493   MovDelay[newx][newy] = 0;
5494
5495   if (CAN_CHANGE(element))
5496   {
5497     /* copy element change control values to new field */
5498     ChangeDelay[newx][newy] = ChangeDelay[x][y];
5499     ChangePage[newx][newy]  = ChangePage[x][y];
5500     Changed[newx][newy]     = Changed[x][y];
5501     ChangeEvent[newx][newy] = ChangeEvent[x][y];
5502
5503 #if USE_NEW_COLLECT_COUNT
5504     Count[newx][newy] = Count[x][y];
5505 #endif
5506   }
5507
5508   ChangeDelay[x][y] = 0;
5509   ChangePage[x][y] = -1;
5510   Changed[x][y] = FALSE;
5511   ChangeEvent[x][y] = -1;
5512
5513 #if USE_NEW_COLLECT_COUNT
5514   Count[x][y] = 0;
5515 #endif
5516
5517   /* copy animation control values to new field */
5518   GfxFrame[newx][newy]  = GfxFrame[x][y];
5519   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
5520   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
5521   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
5522
5523   Pushed[x][y] = Pushed[newx][newy] = FALSE;
5524
5525   /* some elements can leave other elements behind after moving */
5526   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
5527       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
5528       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
5529   {
5530     int move_leave_element = ei->move_leave_element;
5531
5532     /* this makes it possible to leave the removed element again */
5533     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
5534         ei->move_leave_element == EL_TRIGGER_ELEMENT)
5535       move_leave_element = stored;
5536
5537     Feld[x][y] = move_leave_element;
5538
5539     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
5540       MovDir[x][y] = direction;
5541
5542     InitField(x, y, FALSE);
5543
5544     if (GFX_CRUMBLED(Feld[x][y]))
5545       DrawLevelFieldCrumbledSandNeighbours(x, y);
5546
5547     if (ELEM_IS_PLAYER(move_leave_element))
5548       RelocatePlayer(x, y, move_leave_element);
5549   }
5550
5551   /* do this after checking for left-behind element */
5552   ResetGfxAnimation(x, y);      /* reset animation values for old field */
5553
5554   if (!CAN_MOVE(element) ||
5555       (CAN_FALL(element) && direction == MV_DOWN &&
5556        (element == EL_SPRING ||
5557         element_info[element].move_pattern == MV_WHEN_PUSHED ||
5558         element_info[element].move_pattern == MV_WHEN_DROPPED)))
5559     GfxDir[x][y] = MovDir[newx][newy] = 0;
5560
5561   DrawLevelField(x, y);
5562   DrawLevelField(newx, newy);
5563
5564   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
5565
5566   /* prevent pushed element from moving on in pushed direction */
5567   if (pushed_by_player && CAN_MOVE(element) &&
5568       element_info[element].move_pattern & MV_ANY_DIRECTION &&
5569       !(element_info[element].move_pattern & direction))
5570     TurnRound(newx, newy);
5571
5572   /* prevent elements on conveyor belt from moving on in last direction */
5573   if (pushed_by_conveyor && CAN_FALL(element) &&
5574       direction & MV_HORIZONTAL)
5575     MovDir[newx][newy] = 0;
5576
5577   if (!pushed_by_player)
5578   {
5579     int nextx = newx + dx, nexty = newy + dy;
5580     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
5581
5582     WasJustMoving[newx][newy] = 3;
5583
5584     if (CAN_FALL(element) && direction == MV_DOWN)
5585       WasJustFalling[newx][newy] = 3;
5586
5587     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
5588       CheckCollision[newx][newy] = 2;
5589   }
5590
5591   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
5592   {
5593     TestIfBadThingTouchesPlayer(newx, newy);
5594     TestIfBadThingTouchesFriend(newx, newy);
5595
5596     if (!IS_CUSTOM_ELEMENT(element))
5597       TestIfBadThingTouchesOtherBadThing(newx, newy);
5598   }
5599   else if (element == EL_PENGUIN)
5600     TestIfFriendTouchesBadThing(newx, newy);
5601
5602   /* give the player one last chance (one more frame) to move away */
5603   if (CAN_FALL(element) && direction == MV_DOWN &&
5604       (last_line || (!IS_FREE(x, newy + 1) &&
5605                      (!IS_PLAYER(x, newy + 1) ||
5606                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
5607     Impact(x, newy);
5608
5609   if (pushed_by_player && !game.use_change_when_pushing_bug)
5610   {
5611     int dig_side = MV_DIR_OPPOSITE(direction);
5612     struct PlayerInfo *player = PLAYERINFO(x, y);
5613
5614     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
5615                                player->index_bit, dig_side);
5616     CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
5617                                         player->index_bit, dig_side);
5618   }
5619
5620   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
5621
5622   TestIfElementHitsCustomElement(newx, newy, direction);
5623   TestIfPlayerTouchesCustomElement(newx, newy);
5624   TestIfElementTouchesCustomElement(newx, newy);
5625 }
5626
5627 int AmoebeNachbarNr(int ax, int ay)
5628 {
5629   int i;
5630   int element = Feld[ax][ay];
5631   int group_nr = 0;
5632   static int xy[4][2] =
5633   {
5634     { 0, -1 },
5635     { -1, 0 },
5636     { +1, 0 },
5637     { 0, +1 }
5638   };
5639
5640   for (i = 0; i < NUM_DIRECTIONS; i++)
5641   {
5642     int x = ax + xy[i][0];
5643     int y = ay + xy[i][1];
5644
5645     if (!IN_LEV_FIELD(x, y))
5646       continue;
5647
5648     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5649       group_nr = AmoebaNr[x][y];
5650   }
5651
5652   return group_nr;
5653 }
5654
5655 void AmoebenVereinigen(int ax, int ay)
5656 {
5657   int i, x, y, xx, yy;
5658   int new_group_nr = AmoebaNr[ax][ay];
5659   static int xy[4][2] =
5660   {
5661     { 0, -1 },
5662     { -1, 0 },
5663     { +1, 0 },
5664     { 0, +1 }
5665   };
5666
5667   if (new_group_nr == 0)
5668     return;
5669
5670   for (i = 0; i < NUM_DIRECTIONS; i++)
5671   {
5672     x = ax + xy[i][0];
5673     y = ay + xy[i][1];
5674
5675     if (!IN_LEV_FIELD(x, y))
5676       continue;
5677
5678     if ((Feld[x][y] == EL_AMOEBA_FULL ||
5679          Feld[x][y] == EL_BD_AMOEBA ||
5680          Feld[x][y] == EL_AMOEBA_DEAD) &&
5681         AmoebaNr[x][y] != new_group_nr)
5682     {
5683       int old_group_nr = AmoebaNr[x][y];
5684
5685       if (old_group_nr == 0)
5686         return;
5687
5688       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5689       AmoebaCnt[old_group_nr] = 0;
5690       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5691       AmoebaCnt2[old_group_nr] = 0;
5692
5693       for (yy = 0; yy < lev_fieldy; yy++)
5694       {
5695         for (xx = 0; xx < lev_fieldx; xx++)
5696         {
5697           if (AmoebaNr[xx][yy] == old_group_nr)
5698             AmoebaNr[xx][yy] = new_group_nr;
5699         }
5700       }
5701     }
5702   }
5703 }
5704
5705 void AmoebeUmwandeln(int ax, int ay)
5706 {
5707   int i, x, y;
5708
5709   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5710   {
5711     int group_nr = AmoebaNr[ax][ay];
5712
5713 #ifdef DEBUG
5714     if (group_nr == 0)
5715     {
5716       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5717       printf("AmoebeUmwandeln(): This should never happen!\n");
5718       return;
5719     }
5720 #endif
5721
5722     for (y = 0; y < lev_fieldy; y++)
5723     {
5724       for (x = 0; x < lev_fieldx; x++)
5725       {
5726         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5727         {
5728           AmoebaNr[x][y] = 0;
5729           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5730         }
5731       }
5732     }
5733     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5734                             SND_AMOEBA_TURNING_TO_GEM :
5735                             SND_AMOEBA_TURNING_TO_ROCK));
5736     Bang(ax, ay);
5737   }
5738   else
5739   {
5740     static int xy[4][2] =
5741     {
5742       { 0, -1 },
5743       { -1, 0 },
5744       { +1, 0 },
5745       { 0, +1 }
5746     };
5747
5748     for (i = 0; i < NUM_DIRECTIONS; i++)
5749     {
5750       x = ax + xy[i][0];
5751       y = ay + xy[i][1];
5752
5753       if (!IN_LEV_FIELD(x, y))
5754         continue;
5755
5756       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5757       {
5758         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5759                               SND_AMOEBA_TURNING_TO_GEM :
5760                               SND_AMOEBA_TURNING_TO_ROCK));
5761         Bang(x, y);
5762       }
5763     }
5764   }
5765 }
5766
5767 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5768 {
5769   int x, y;
5770   int group_nr = AmoebaNr[ax][ay];
5771   boolean done = FALSE;
5772
5773 #ifdef DEBUG
5774   if (group_nr == 0)
5775   {
5776     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5777     printf("AmoebeUmwandelnBD(): This should never happen!\n");
5778     return;
5779   }
5780 #endif
5781
5782   for (y = 0; y < lev_fieldy; y++)
5783   {
5784     for (x = 0; x < lev_fieldx; x++)
5785     {
5786       if (AmoebaNr[x][y] == group_nr &&
5787           (Feld[x][y] == EL_AMOEBA_DEAD ||
5788            Feld[x][y] == EL_BD_AMOEBA ||
5789            Feld[x][y] == EL_AMOEBA_GROWING))
5790       {
5791         AmoebaNr[x][y] = 0;
5792         Feld[x][y] = new_element;
5793         InitField(x, y, FALSE);
5794         DrawLevelField(x, y);
5795         done = TRUE;
5796       }
5797     }
5798   }
5799
5800   if (done)
5801     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5802                             SND_BD_AMOEBA_TURNING_TO_ROCK :
5803                             SND_BD_AMOEBA_TURNING_TO_GEM));
5804 }
5805
5806 void AmoebeWaechst(int x, int y)
5807 {
5808   static unsigned long sound_delay = 0;
5809   static unsigned long sound_delay_value = 0;
5810
5811   if (!MovDelay[x][y])          /* start new growing cycle */
5812   {
5813     MovDelay[x][y] = 7;
5814
5815     if (DelayReached(&sound_delay, sound_delay_value))
5816     {
5817       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5818       sound_delay_value = 30;
5819     }
5820   }
5821
5822   if (MovDelay[x][y])           /* wait some time before growing bigger */
5823   {
5824     MovDelay[x][y]--;
5825     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5826     {
5827       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5828                                            6 - MovDelay[x][y]);
5829
5830       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5831     }
5832
5833     if (!MovDelay[x][y])
5834     {
5835       Feld[x][y] = Store[x][y];
5836       Store[x][y] = 0;
5837       DrawLevelField(x, y);
5838     }
5839   }
5840 }
5841
5842 void AmoebaDisappearing(int x, int y)
5843 {
5844   static unsigned long sound_delay = 0;
5845   static unsigned long sound_delay_value = 0;
5846
5847   if (!MovDelay[x][y])          /* start new shrinking cycle */
5848   {
5849     MovDelay[x][y] = 7;
5850
5851     if (DelayReached(&sound_delay, sound_delay_value))
5852       sound_delay_value = 30;
5853   }
5854
5855   if (MovDelay[x][y])           /* wait some time before shrinking */
5856   {
5857     MovDelay[x][y]--;
5858     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5859     {
5860       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5861                                            6 - MovDelay[x][y]);
5862
5863       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5864     }
5865
5866     if (!MovDelay[x][y])
5867     {
5868       Feld[x][y] = EL_EMPTY;
5869       DrawLevelField(x, y);
5870
5871       /* don't let mole enter this field in this cycle;
5872          (give priority to objects falling to this field from above) */
5873       Stop[x][y] = TRUE;
5874     }
5875   }
5876 }
5877
5878 void AmoebeAbleger(int ax, int ay)
5879 {
5880   int i;
5881   int element = Feld[ax][ay];
5882   int graphic = el2img(element);
5883   int newax = ax, neway = ay;
5884   static int xy[4][2] =
5885   {
5886     { 0, -1 },
5887     { -1, 0 },
5888     { +1, 0 },
5889     { 0, +1 }
5890   };
5891
5892   if (!level.amoeba_speed)
5893   {
5894     Feld[ax][ay] = EL_AMOEBA_DEAD;
5895     DrawLevelField(ax, ay);
5896     return;
5897   }
5898
5899   if (IS_ANIMATED(graphic))
5900     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5901
5902   if (!MovDelay[ax][ay])        /* start making new amoeba field */
5903     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5904
5905   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
5906   {
5907     MovDelay[ax][ay]--;
5908     if (MovDelay[ax][ay])
5909       return;
5910   }
5911
5912   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5913   {
5914     int start = RND(4);
5915     int x = ax + xy[start][0];
5916     int y = ay + xy[start][1];
5917
5918     if (!IN_LEV_FIELD(x, y))
5919       return;
5920
5921     if (IS_FREE(x, y) ||
5922         CAN_GROW_INTO(Feld[x][y]) ||
5923         Feld[x][y] == EL_QUICKSAND_EMPTY)
5924     {
5925       newax = x;
5926       neway = y;
5927     }
5928
5929     if (newax == ax && neway == ay)
5930       return;
5931   }
5932   else                          /* normal or "filled" (BD style) amoeba */
5933   {
5934     int start = RND(4);
5935     boolean waiting_for_player = FALSE;
5936
5937     for (i = 0; i < NUM_DIRECTIONS; i++)
5938     {
5939       int j = (start + i) % 4;
5940       int x = ax + xy[j][0];
5941       int y = ay + xy[j][1];
5942
5943       if (!IN_LEV_FIELD(x, y))
5944         continue;
5945
5946       if (IS_FREE(x, y) ||
5947           CAN_GROW_INTO(Feld[x][y]) ||
5948           Feld[x][y] == EL_QUICKSAND_EMPTY)
5949       {
5950         newax = x;
5951         neway = y;
5952         break;
5953       }
5954       else if (IS_PLAYER(x, y))
5955         waiting_for_player = TRUE;
5956     }
5957
5958     if (newax == ax && neway == ay)             /* amoeba cannot grow */
5959     {
5960       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
5961       {
5962         Feld[ax][ay] = EL_AMOEBA_DEAD;
5963         DrawLevelField(ax, ay);
5964         AmoebaCnt[AmoebaNr[ax][ay]]--;
5965
5966         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
5967         {
5968           if (element == EL_AMOEBA_FULL)
5969             AmoebeUmwandeln(ax, ay);
5970           else if (element == EL_BD_AMOEBA)
5971             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5972         }
5973       }
5974       return;
5975     }
5976     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5977     {
5978       /* amoeba gets larger by growing in some direction */
5979
5980       int new_group_nr = AmoebaNr[ax][ay];
5981
5982 #ifdef DEBUG
5983   if (new_group_nr == 0)
5984   {
5985     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5986     printf("AmoebeAbleger(): This should never happen!\n");
5987     return;
5988   }
5989 #endif
5990
5991       AmoebaNr[newax][neway] = new_group_nr;
5992       AmoebaCnt[new_group_nr]++;
5993       AmoebaCnt2[new_group_nr]++;
5994
5995       /* if amoeba touches other amoeba(s) after growing, unify them */
5996       AmoebenVereinigen(newax, neway);
5997
5998       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5999       {
6000         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6001         return;
6002       }
6003     }
6004   }
6005
6006   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6007       (neway == lev_fieldy - 1 && newax != ax))
6008   {
6009     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
6010     Store[newax][neway] = element;
6011   }
6012   else if (neway == ay)
6013   {
6014     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
6015
6016     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6017   }
6018   else
6019   {
6020     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
6021     Feld[ax][ay] = EL_AMOEBA_DROPPING;
6022     Store[ax][ay] = EL_AMOEBA_DROP;
6023     ContinueMoving(ax, ay);
6024     return;
6025   }
6026
6027   DrawLevelField(newax, neway);
6028 }
6029
6030 void Life(int ax, int ay)
6031 {
6032   int x1, y1, x2, y2;
6033 #if 0
6034   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
6035 #endif
6036   int life_time = 40;
6037   int element = Feld[ax][ay];
6038   int graphic = el2img(element);
6039   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
6040                          level.biomaze);
6041   boolean changed = FALSE;
6042
6043   if (IS_ANIMATED(graphic))
6044     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6045
6046   if (Stop[ax][ay])
6047     return;
6048
6049   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
6050     MovDelay[ax][ay] = life_time;
6051
6052   if (MovDelay[ax][ay])         /* wait some time before next cycle */
6053   {
6054     MovDelay[ax][ay]--;
6055     if (MovDelay[ax][ay])
6056       return;
6057   }
6058
6059   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6060   {
6061     int xx = ax+x1, yy = ay+y1;
6062     int nachbarn = 0;
6063
6064     if (!IN_LEV_FIELD(xx, yy))
6065       continue;
6066
6067     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6068     {
6069       int x = xx+x2, y = yy+y2;
6070
6071       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6072         continue;
6073
6074       if (((Feld[x][y] == element ||
6075             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6076            !Stop[x][y]) ||
6077           (IS_FREE(x, y) && Stop[x][y]))
6078         nachbarn++;
6079     }
6080
6081     if (xx == ax && yy == ay)           /* field in the middle */
6082     {
6083       if (nachbarn < life_parameter[0] ||
6084           nachbarn > life_parameter[1])
6085       {
6086         Feld[xx][yy] = EL_EMPTY;
6087         if (!Stop[xx][yy])
6088           DrawLevelField(xx, yy);
6089         Stop[xx][yy] = TRUE;
6090         changed = TRUE;
6091       }
6092     }
6093     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6094     {                                   /* free border field */
6095       if (nachbarn >= life_parameter[2] &&
6096           nachbarn <= life_parameter[3])
6097       {
6098         Feld[xx][yy] = element;
6099         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6100         if (!Stop[xx][yy])
6101           DrawLevelField(xx, yy);
6102         Stop[xx][yy] = TRUE;
6103         changed = TRUE;
6104       }
6105     }
6106   }
6107
6108   if (changed)
6109     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6110                    SND_GAME_OF_LIFE_GROWING);
6111 }
6112
6113 static void InitRobotWheel(int x, int y)
6114 {
6115   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6116 }
6117
6118 static void RunRobotWheel(int x, int y)
6119 {
6120   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6121 }
6122
6123 static void StopRobotWheel(int x, int y)
6124 {
6125   if (ZX == x && ZY == y)
6126     ZX = ZY = -1;
6127 }
6128
6129 static void InitTimegateWheel(int x, int y)
6130 {
6131   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6132 }
6133
6134 static void RunTimegateWheel(int x, int y)
6135 {
6136   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6137 }
6138
6139 void CheckExit(int x, int y)
6140 {
6141   if (local_player->gems_still_needed > 0 ||
6142       local_player->sokobanfields_still_needed > 0 ||
6143       local_player->lights_still_needed > 0)
6144   {
6145     int element = Feld[x][y];
6146     int graphic = el2img(element);
6147
6148     if (IS_ANIMATED(graphic))
6149       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6150
6151     return;
6152   }
6153
6154   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6155     return;
6156
6157   Feld[x][y] = EL_EXIT_OPENING;
6158
6159   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6160 }
6161
6162 void CheckExitSP(int x, int y)
6163 {
6164   if (local_player->gems_still_needed > 0)
6165   {
6166     int element = Feld[x][y];
6167     int graphic = el2img(element);
6168
6169     if (IS_ANIMATED(graphic))
6170       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6171
6172     return;
6173   }
6174
6175   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6176     return;
6177
6178   Feld[x][y] = EL_SP_EXIT_OPENING;
6179
6180   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6181 }
6182
6183 static void CloseAllOpenTimegates()
6184 {
6185   int x, y;
6186
6187   for (y = 0; y < lev_fieldy; y++)
6188   {
6189     for (x = 0; x < lev_fieldx; x++)
6190     {
6191       int element = Feld[x][y];
6192
6193       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6194       {
6195         Feld[x][y] = EL_TIMEGATE_CLOSING;
6196
6197         PlayLevelSoundAction(x, y, ACTION_CLOSING);
6198       }
6199     }
6200   }
6201 }
6202
6203 void EdelsteinFunkeln(int x, int y)
6204 {
6205   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6206     return;
6207
6208   if (Feld[x][y] == EL_BD_DIAMOND)
6209     return;
6210
6211   if (MovDelay[x][y] == 0)      /* next animation frame */
6212     MovDelay[x][y] = 11 * !SimpleRND(500);
6213
6214   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
6215   {
6216     MovDelay[x][y]--;
6217
6218     if (setup.direct_draw && MovDelay[x][y])
6219       SetDrawtoField(DRAW_BUFFERED);
6220
6221     DrawLevelElementAnimation(x, y, Feld[x][y]);
6222
6223     if (MovDelay[x][y] != 0)
6224     {
6225       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6226                                            10 - MovDelay[x][y]);
6227
6228       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6229
6230       if (setup.direct_draw)
6231       {
6232         int dest_x, dest_y;
6233
6234         dest_x = FX + SCREENX(x) * TILEX;
6235         dest_y = FY + SCREENY(y) * TILEY;
6236
6237         BlitBitmap(drawto_field, window,
6238                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6239         SetDrawtoField(DRAW_DIRECT);
6240       }
6241     }
6242   }
6243 }
6244
6245 void MauerWaechst(int x, int y)
6246 {
6247   int delay = 6;
6248
6249   if (!MovDelay[x][y])          /* next animation frame */
6250     MovDelay[x][y] = 3 * delay;
6251
6252   if (MovDelay[x][y])           /* wait some time before next frame */
6253   {
6254     MovDelay[x][y]--;
6255
6256     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6257     {
6258       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6259       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6260
6261       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6262     }
6263
6264     if (!MovDelay[x][y])
6265     {
6266       if (MovDir[x][y] == MV_LEFT)
6267       {
6268         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6269           DrawLevelField(x - 1, y);
6270       }
6271       else if (MovDir[x][y] == MV_RIGHT)
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_UP)
6277       {
6278         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6279           DrawLevelField(x, y - 1);
6280       }
6281       else
6282       {
6283         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6284           DrawLevelField(x, y + 1);
6285       }
6286
6287       Feld[x][y] = Store[x][y];
6288       Store[x][y] = 0;
6289       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6290       DrawLevelField(x, y);
6291     }
6292   }
6293 }
6294
6295 void MauerAbleger(int ax, int ay)
6296 {
6297   int element = Feld[ax][ay];
6298   int graphic = el2img(element);
6299   boolean oben_frei = FALSE, unten_frei = FALSE;
6300   boolean links_frei = FALSE, rechts_frei = FALSE;
6301   boolean oben_massiv = FALSE, unten_massiv = FALSE;
6302   boolean links_massiv = FALSE, rechts_massiv = FALSE;
6303   boolean new_wall = FALSE;
6304
6305   if (IS_ANIMATED(graphic))
6306     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6307
6308   if (!MovDelay[ax][ay])        /* start building new wall */
6309     MovDelay[ax][ay] = 6;
6310
6311   if (MovDelay[ax][ay])         /* wait some time before building new wall */
6312   {
6313     MovDelay[ax][ay]--;
6314     if (MovDelay[ax][ay])
6315       return;
6316   }
6317
6318   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6319     oben_frei = TRUE;
6320   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6321     unten_frei = TRUE;
6322   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6323     links_frei = TRUE;
6324   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6325     rechts_frei = TRUE;
6326
6327   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6328       element == EL_EXPANDABLE_WALL_ANY)
6329   {
6330     if (oben_frei)
6331     {
6332       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6333       Store[ax][ay-1] = element;
6334       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6335       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6336         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6337                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6338       new_wall = TRUE;
6339     }
6340     if (unten_frei)
6341     {
6342       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6343       Store[ax][ay+1] = element;
6344       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6345       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6346         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6347                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6348       new_wall = TRUE;
6349     }
6350   }
6351
6352   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6353       element == EL_EXPANDABLE_WALL_ANY ||
6354       element == EL_EXPANDABLE_WALL)
6355   {
6356     if (links_frei)
6357     {
6358       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6359       Store[ax-1][ay] = element;
6360       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6361       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6362         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6363                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6364       new_wall = TRUE;
6365     }
6366
6367     if (rechts_frei)
6368     {
6369       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6370       Store[ax+1][ay] = element;
6371       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6372       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6373         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6374                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6375       new_wall = TRUE;
6376     }
6377   }
6378
6379   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6380     DrawLevelField(ax, ay);
6381
6382   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6383     oben_massiv = TRUE;
6384   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6385     unten_massiv = TRUE;
6386   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6387     links_massiv = TRUE;
6388   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6389     rechts_massiv = TRUE;
6390
6391   if (((oben_massiv && unten_massiv) ||
6392        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6393        element == EL_EXPANDABLE_WALL) &&
6394       ((links_massiv && rechts_massiv) ||
6395        element == EL_EXPANDABLE_WALL_VERTICAL))
6396     Feld[ax][ay] = EL_WALL;
6397
6398   if (new_wall)
6399     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6400 }
6401
6402 void CheckForDragon(int x, int y)
6403 {
6404   int i, j;
6405   boolean dragon_found = FALSE;
6406   static int xy[4][2] =
6407   {
6408     { 0, -1 },
6409     { -1, 0 },
6410     { +1, 0 },
6411     { 0, +1 }
6412   };
6413
6414   for (i = 0; i < NUM_DIRECTIONS; i++)
6415   {
6416     for (j = 0; j < 4; j++)
6417     {
6418       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6419
6420       if (IN_LEV_FIELD(xx, yy) &&
6421           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6422       {
6423         if (Feld[xx][yy] == EL_DRAGON)
6424           dragon_found = TRUE;
6425       }
6426       else
6427         break;
6428     }
6429   }
6430
6431   if (!dragon_found)
6432   {
6433     for (i = 0; i < NUM_DIRECTIONS; i++)
6434     {
6435       for (j = 0; j < 3; j++)
6436       {
6437         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
6438   
6439         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6440         {
6441           Feld[xx][yy] = EL_EMPTY;
6442           DrawLevelField(xx, yy);
6443         }
6444         else
6445           break;
6446       }
6447     }
6448   }
6449 }
6450
6451 static void InitBuggyBase(int x, int y)
6452 {
6453   int element = Feld[x][y];
6454   int activating_delay = FRAMES_PER_SECOND / 4;
6455
6456   ChangeDelay[x][y] =
6457     (element == EL_SP_BUGGY_BASE ?
6458      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6459      element == EL_SP_BUGGY_BASE_ACTIVATING ?
6460      activating_delay :
6461      element == EL_SP_BUGGY_BASE_ACTIVE ?
6462      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6463 }
6464
6465 static void WarnBuggyBase(int x, int y)
6466 {
6467   int i;
6468   static int xy[4][2] =
6469   {
6470     { 0, -1 },
6471     { -1, 0 },
6472     { +1, 0 },
6473     { 0, +1 }
6474   };
6475
6476   for (i = 0; i < NUM_DIRECTIONS; i++)
6477   {
6478     int xx = x + xy[i][0], yy = y + xy[i][1];
6479
6480     if (IS_PLAYER(xx, yy))
6481     {
6482       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6483
6484       break;
6485     }
6486   }
6487 }
6488
6489 static void InitTrap(int x, int y)
6490 {
6491   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6492 }
6493
6494 static void ActivateTrap(int x, int y)
6495 {
6496   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6497 }
6498
6499 static void ChangeActiveTrap(int x, int y)
6500 {
6501   int graphic = IMG_TRAP_ACTIVE;
6502
6503   /* if new animation frame was drawn, correct crumbled sand border */
6504   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6505     DrawLevelFieldCrumbledSand(x, y);
6506 }
6507
6508 static int getSpecialActionElement(int element, int number, int base_element)
6509 {
6510   return (element != EL_EMPTY ? element :
6511           number != -1 ? base_element + number - 1 :
6512           EL_EMPTY);
6513 }
6514
6515 static int getModifiedActionNumber(int value_old, int operator, int operand,
6516                                    int value_min, int value_max)
6517 {
6518   int value_new = (operator == CA_MODE_SET      ? operand :
6519                    operator == CA_MODE_ADD      ? value_old + operand :
6520                    operator == CA_MODE_SUBTRACT ? value_old - operand :
6521                    operator == CA_MODE_MULTIPLY ? value_old * operand :
6522                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
6523                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
6524                    value_old);
6525
6526   return (value_new < value_min ? value_min :
6527           value_new > value_max ? value_max :
6528           value_new);
6529 }
6530
6531 static void ExecuteCustomElementAction(int x, int y, int element, int page)
6532 {
6533   struct ElementInfo *ei = &element_info[element];
6534   struct ElementChangeInfo *change = &ei->change_page[page];
6535   int action_type = change->action_type;
6536   int action_mode = change->action_mode;
6537   int action_arg = change->action_arg;
6538   int i;
6539
6540   if (!change->has_action)
6541     return;
6542
6543   /* ---------- determine action paramater values ---------- */
6544
6545   int action_arg_element =
6546     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
6547      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
6548      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
6549      EL_EMPTY);
6550
6551   int action_arg_number_min =
6552     (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
6553      CA_ARG_MIN);
6554
6555   int action_arg_number_max =
6556     (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
6557      action_type == CA_SET_GEMS ? 999 :
6558      action_type == CA_SET_TIME ? 9999 :
6559      action_type == CA_SET_SCORE ? 99999 :
6560      action_type == CA_SET_CE_SCORE ? 9999 :
6561      action_type == CA_SET_CE_COUNT ? 9999 :
6562      CA_ARG_MAX);
6563
6564   int action_arg_number_reset =
6565     (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
6566      action_type == CA_SET_GEMS ? level.gems_needed :
6567      action_type == CA_SET_TIME ? level.time :
6568      action_type == CA_SET_SCORE ? 0 :
6569      action_type == CA_SET_CE_SCORE ? 0 :
6570      action_type == CA_SET_CE_COUNT ? ei->collect_count_initial :
6571      0);
6572
6573   int action_arg_number_normal =
6574     (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_NORMAL :
6575      action_arg_number_reset);
6576
6577   int action_arg_number =
6578     (action_arg <= CA_ARG_MAX ? action_arg :
6579      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
6580      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
6581      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
6582      action_arg == CA_ARG_NUMBER_NORMAL ? action_arg_number_normal :
6583      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
6584 #if USE_NEW_COLLECT_COUNT
6585      action_arg == CA_ARG_NUMBER_CE_COUNT ? Count[x][y] :
6586 #else
6587      action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count_initial :
6588 #endif
6589      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
6590      -1);
6591
6592   int action_arg_number_old =
6593     (action_type == CA_SET_GEMS ? local_player->gems_still_needed :
6594      action_type == CA_SET_TIME ? TimeLeft :
6595      action_type == CA_SET_SCORE ? local_player->score :
6596      action_type == CA_SET_CE_SCORE ? ei->collect_score :
6597      action_type == CA_SET_CE_COUNT ? Count[x][y] :
6598      0);
6599
6600   int action_arg_number_new =
6601     getModifiedActionNumber(action_arg_number_old,
6602                             action_mode, action_arg_number,
6603                             action_arg_number_min, action_arg_number_max);
6604
6605   int action_arg_player_bits =
6606     (action_arg == CA_ARG_PLAYER_ANY ? PLAYER_BITS_ANY :
6607      action_arg >= CA_ARG_PLAYER_1 &&
6608      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
6609      action_arg >= CA_ARG_1 &&
6610      action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - CA_ARG_1)) :
6611      action_arg_element >= EL_PLAYER_1 &&
6612      action_arg_element <= EL_PLAYER_4 ?
6613      (1 << (action_arg_element - EL_PLAYER_1)) :
6614      PLAYER_BITS_ANY);
6615
6616   int trigger_player_bits =
6617     (change->actual_trigger_player >= EL_PLAYER_1 &&
6618      change->actual_trigger_player <= EL_PLAYER_4 ?
6619      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
6620      PLAYER_BITS_ANY);
6621
6622   /* ---------- execute action  ---------- */
6623
6624   switch(action_type)
6625   {
6626     case CA_NO_ACTION:
6627     {
6628       return;
6629     }
6630
6631     case CA_EXIT_PLAYER:
6632     {
6633       for (i = 0; i < MAX_PLAYERS; i++)
6634         if (action_arg_player_bits & (1 << i))
6635           stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
6636
6637       break;
6638     }
6639
6640     case CA_KILL_PLAYER:
6641     {
6642       for (i = 0; i < MAX_PLAYERS; i++)
6643         if (action_arg_player_bits & (1 << i))
6644           KillPlayer(&stored_player[i]);
6645
6646       break;
6647     }
6648
6649     case CA_RESTART_LEVEL:
6650     {
6651       game.restart_level = TRUE;
6652
6653       break;
6654     }
6655
6656     case CA_SHOW_ENVELOPE:
6657     {
6658       int element = getSpecialActionElement(action_arg_element,
6659                                             action_arg_number, EL_ENVELOPE_1);
6660
6661       if (IS_ENVELOPE(element))
6662         local_player->show_envelope = element;
6663
6664       break;
6665     }
6666
6667     case CA_ADD_KEY:
6668     {
6669       int element = getSpecialActionElement(action_arg_element,
6670                                             action_arg_number, EL_KEY_1);
6671
6672       if (IS_KEY(element))
6673       {
6674         for (i = 0; i < MAX_PLAYERS; i++)
6675         {
6676           if (trigger_player_bits & (1 << i))
6677           {
6678             stored_player[i].key[KEY_NR(element)] = TRUE;
6679
6680             DrawGameValue_Keys(stored_player[i].key);
6681
6682             redraw_mask |= REDRAW_DOOR_1;
6683           }
6684         }
6685       }
6686
6687       break;
6688     }
6689
6690     case CA_DEL_KEY:
6691     {
6692       int element = getSpecialActionElement(action_arg_element,
6693                                             action_arg_number, EL_KEY_1);
6694
6695       if (IS_KEY(element))
6696       {
6697         for (i = 0; i < MAX_PLAYERS; i++)
6698         {
6699           if (trigger_player_bits & (1 << i))
6700           {
6701             stored_player[i].key[KEY_NR(element)] = FALSE;
6702
6703             DrawGameValue_Keys(stored_player[i].key);
6704
6705             redraw_mask |= REDRAW_DOOR_1;
6706           }
6707         }
6708       }
6709
6710       break;
6711     }
6712
6713     case CA_SET_PLAYER_SPEED:
6714     {
6715       for (i = 0; i < MAX_PLAYERS; i++)
6716       {
6717         if (trigger_player_bits & (1 << i))
6718         {
6719           int move_stepsize = TILEX / stored_player[i].move_delay_value;
6720
6721           if (action_mode == CA_MODE_ADD || action_mode == CA_MODE_SUBTRACT)
6722           {
6723             /* translate "+" and "-" to "*" and "/" with powers of two */
6724             action_arg_number = 1 << action_arg_number;
6725             action_mode = (action_mode == CA_MODE_ADD ? CA_MODE_MULTIPLY :
6726                            CA_MODE_DIVIDE);
6727           }
6728
6729           move_stepsize =
6730             getModifiedActionNumber(move_stepsize,
6731                                     action_mode,
6732                                     action_arg_number,
6733                                     action_arg_number_min,
6734                                     action_arg_number_max);
6735
6736           /* make sure that value is power of 2 */
6737           move_stepsize = (1 << log_2(move_stepsize));
6738
6739           /* do no immediately change -- the player might just be moving */
6740           stored_player[i].move_delay_value_next = TILEX / move_stepsize;
6741
6742 #if 0
6743           printf("::: move_delay_value == %d [%d]\n",
6744                  stored_player[i].move_delay_value_next, action_arg_number);
6745 #endif
6746         }
6747       }
6748
6749       break;
6750     }
6751
6752     case CA_SET_GEMS:
6753     {
6754       local_player->gems_still_needed = action_arg_number_new;
6755
6756       DrawGameValue_Emeralds(local_player->gems_still_needed);
6757
6758       break;
6759     }
6760
6761     case CA_SET_TIME:
6762     {
6763       if (level.time > 0)       /* only modify limited time value */
6764       {
6765         TimeLeft = action_arg_number_new;
6766
6767         DrawGameValue_Time(TimeLeft);
6768
6769         if (!TimeLeft && setup.time_limit)
6770           for (i = 0; i < MAX_PLAYERS; i++)
6771             KillPlayer(&stored_player[i]);
6772       }
6773
6774       break;
6775     }
6776
6777     case CA_SET_SCORE:
6778     {
6779       local_player->score = action_arg_number_new;
6780
6781       DrawGameValue_Score(local_player->score);
6782
6783       break;
6784     }
6785
6786     case CA_SET_CE_SCORE:
6787     {
6788       ei->collect_score = action_arg_number_new;
6789
6790       break;
6791     }
6792
6793     case CA_SET_CE_COUNT:
6794     {
6795 #if USE_NEW_COLLECT_COUNT
6796       int count_last = Count[x][y];
6797
6798       Count[x][y] = action_arg_number_new;
6799
6800 #if 0
6801       printf("::: Count == %d\n", Count[x][y]);
6802 #endif
6803
6804       if (Count[x][y] == 0 && count_last > 0)
6805       {
6806 #if 0
6807         printf("::: CE_COUNT_AT_ZERO\n");
6808 #endif
6809
6810         CheckElementChange(x, y, element, EL_UNDEFINED, CE_COUNT_AT_ZERO);
6811         CheckTriggeredElementChange(element, CE_COUNT_AT_ZERO_OF_X);
6812       }
6813 #endif
6814
6815       break;
6816     }
6817
6818     case CA_SET_DYNABOMB_NUMBER:
6819     {
6820       printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
6821
6822       break;
6823     }
6824
6825     case CA_SET_DYNABOMB_SIZE:
6826     {
6827       printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
6828
6829       break;
6830     }
6831
6832     case CA_SET_DYNABOMB_POWER:
6833     {
6834       printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
6835
6836       break;
6837     }
6838
6839     case CA_TOGGLE_PLAYER_GRAVITY:
6840     {
6841       game.gravity = !game.gravity;
6842
6843       break;
6844     }
6845
6846     case CA_ENABLE_PLAYER_GRAVITY:
6847     {
6848       game.gravity = TRUE;
6849
6850       break;
6851     }
6852
6853     case CA_DISABLE_PLAYER_GRAVITY:
6854     {
6855       game.gravity = FALSE;
6856
6857       break;
6858     }
6859
6860     default:
6861       break;
6862   }
6863 }
6864
6865 static void ChangeElementNowExt(struct ElementChangeInfo *change,
6866                                 int x, int y, int target_element)
6867 {
6868   int previous_move_direction = MovDir[x][y];
6869   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
6870                         IS_WALKABLE(Feld[x][y]));
6871
6872   /* check if element under player changes from accessible to unaccessible
6873      (needed for special case of dropping element which then changes) */
6874   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6875       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6876   {
6877     Bang(x, y);
6878     return;
6879   }
6880
6881   if (!add_player)
6882   {
6883     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6884       RemoveMovingField(x, y);
6885     else
6886       RemoveField(x, y);
6887
6888     Feld[x][y] = target_element;
6889
6890     ResetGfxAnimation(x, y);
6891     ResetRandomAnimationValue(x, y);
6892
6893     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6894       MovDir[x][y] = previous_move_direction;
6895
6896     InitField_WithBug1(x, y, FALSE);
6897
6898     DrawLevelField(x, y);
6899
6900     if (GFX_CRUMBLED(Feld[x][y]))
6901       DrawLevelFieldCrumbledSandNeighbours(x, y);
6902   }
6903
6904   /* "Changed[][]" not set yet to allow "entered by player" change one time */
6905   if (ELEM_IS_PLAYER(target_element))
6906     RelocatePlayer(x, y, target_element);
6907
6908 #if 1
6909   Changed[x][y] = TRUE;         /* ignore all further changes in this frame */
6910 #else
6911   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6912 #endif
6913
6914   TestIfBadThingTouchesPlayer(x, y);
6915   TestIfPlayerTouchesCustomElement(x, y);
6916   TestIfElementTouchesCustomElement(x, y);
6917 }
6918
6919 static boolean ChangeElementNow(int x, int y, int element, int page)
6920 {
6921   struct ElementChangeInfo *change = &element_info[element].change_page[page];
6922   int target_element;
6923   int old_element = Feld[x][y];
6924
6925   /* always use default change event to prevent running into a loop */
6926   if (ChangeEvent[x][y] == -1)
6927     ChangeEvent[x][y] = CE_DELAY;
6928
6929   if (ChangeEvent[x][y] == CE_DELAY)
6930   {
6931     /* reset actual trigger element, trigger player and action element */
6932     change->actual_trigger_element = EL_EMPTY;
6933     change->actual_trigger_player = EL_PLAYER_1;
6934   }
6935
6936 #if 1
6937   /* do not change any elements that have already changed in this frame */
6938   if (Changed[x][y])
6939     return FALSE;
6940 #else
6941   /* do not change already changed elements with same change event */
6942   if (Changed[x][y] & ChangeEvent[x][y])
6943     return FALSE;
6944 #endif
6945
6946 #if 1
6947   Changed[x][y] = TRUE;         /* ignore all further changes in this frame */
6948 #else
6949   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6950 #endif
6951
6952   if (change->explode)
6953   {
6954     Bang(x, y);
6955
6956     return TRUE;
6957   }
6958
6959   if (change->use_target_content)
6960   {
6961     boolean complete_replace = TRUE;
6962     boolean can_replace[3][3];
6963     int xx, yy;
6964
6965     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6966     {
6967       boolean is_empty;
6968       boolean is_walkable;
6969       boolean is_diggable;
6970       boolean is_collectible;
6971       boolean is_removable;
6972       boolean is_destructible;
6973       int ex = x + xx - 1;
6974       int ey = y + yy - 1;
6975       int content_element = change->target_content.e[xx][yy];
6976       int e;
6977
6978       can_replace[xx][yy] = TRUE;
6979
6980       if (ex == x && ey == y)   /* do not check changing element itself */
6981         continue;
6982
6983       if (content_element == EL_EMPTY_SPACE)
6984       {
6985         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
6986
6987         continue;
6988       }
6989
6990       if (!IN_LEV_FIELD(ex, ey))
6991       {
6992         can_replace[xx][yy] = FALSE;
6993         complete_replace = FALSE;
6994
6995         continue;
6996       }
6997
6998       e = Feld[ex][ey];
6999
7000       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7001         e = MovingOrBlocked2Element(ex, ey);
7002
7003       is_empty = (IS_FREE(ex, ey) ||
7004                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7005
7006       is_walkable     = (is_empty || IS_WALKABLE(e));
7007       is_diggable     = (is_empty || IS_DIGGABLE(e));
7008       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
7009       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7010       is_removable    = (is_diggable || is_collectible);
7011
7012       can_replace[xx][yy] =
7013         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
7014           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
7015           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
7016           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
7017           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
7018           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7019          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7020
7021       if (!can_replace[xx][yy])
7022         complete_replace = FALSE;
7023     }
7024
7025     if (!change->only_if_complete || complete_replace)
7026     {
7027       boolean something_has_changed = FALSE;
7028
7029       if (change->only_if_complete && change->use_random_replace &&
7030           RND(100) < change->random_percentage)
7031         return FALSE;
7032
7033       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7034       {
7035         int ex = x + xx - 1;
7036         int ey = y + yy - 1;
7037         int content_element;
7038
7039         if (can_replace[xx][yy] && (!change->use_random_replace ||
7040                                     RND(100) < change->random_percentage))
7041         {
7042           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7043             RemoveMovingField(ex, ey);
7044
7045           ChangeEvent[ex][ey] = ChangeEvent[x][y];
7046
7047           content_element = change->target_content.e[xx][yy];
7048           target_element = GET_TARGET_ELEMENT(content_element, change);
7049
7050           ChangeElementNowExt(change, ex, ey, target_element);
7051
7052           something_has_changed = TRUE;
7053
7054           /* for symmetry reasons, freeze newly created border elements */
7055           if (ex != x || ey != y)
7056             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
7057         }
7058       }
7059
7060       if (something_has_changed)
7061       {
7062         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7063         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7064       }
7065     }
7066   }
7067   else
7068   {
7069     target_element = GET_TARGET_ELEMENT(change->target_element, change);
7070
7071     ChangeElementNowExt(change, x, y, target_element);
7072
7073     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7074     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
7075   }
7076
7077   /* this uses direct change before indirect change */
7078   CheckTriggeredElementChangeByPage(old_element, CE_CHANGE_OF_X, page);
7079
7080   return TRUE;
7081 }
7082
7083 #if USE_NEW_DELAYED_ACTION
7084
7085 static void ChangeElement(int x, int y, int page)
7086 {
7087   int element = MovingOrBlocked2Element(x, y);
7088   struct ElementInfo *ei = &element_info[element];
7089   struct ElementChangeInfo *change = &ei->change_page[page];
7090
7091 #ifdef DEBUG
7092   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
7093       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
7094   {
7095     printf("\n\n");
7096     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7097            x, y, element, element_info[element].token_name);
7098     printf("ChangeElement(): This should never happen!\n");
7099     printf("\n\n");
7100   }
7101 #endif
7102
7103   /* this can happen with classic bombs on walkable, changing elements */
7104   if (!CAN_CHANGE_OR_HAS_ACTION(element))
7105   {
7106 #if 0
7107     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
7108       ChangeDelay[x][y] = 0;
7109 #endif
7110
7111     return;
7112   }
7113
7114   if (ChangeDelay[x][y] == 0)           /* initialize element change */
7115   {
7116     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7117
7118     if (change->can_change)
7119     {
7120       ResetGfxAnimation(x, y);
7121       ResetRandomAnimationValue(x, y);
7122
7123       if (change->pre_change_function)
7124         change->pre_change_function(x, y);
7125     }
7126   }
7127
7128   ChangeDelay[x][y]--;
7129
7130   if (ChangeDelay[x][y] != 0)           /* continue element change */
7131   {
7132     if (change->can_change)
7133     {
7134       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7135
7136       if (IS_ANIMATED(graphic))
7137         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7138
7139       if (change->change_function)
7140         change->change_function(x, y);
7141     }
7142   }
7143   else                                  /* finish element change */
7144   {
7145     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
7146     {
7147       page = ChangePage[x][y];
7148       ChangePage[x][y] = -1;
7149
7150       change = &ei->change_page[page];
7151     }
7152
7153     if (IS_MOVING(x, y))                /* never change a running system ;-) */
7154     {
7155       ChangeDelay[x][y] = 1;            /* try change after next move step */
7156       ChangePage[x][y] = page;          /* remember page to use for change */
7157
7158       return;
7159     }
7160
7161     if (change->can_change)
7162     {
7163       if (ChangeElementNow(x, y, element, page))
7164       {
7165         if (change->post_change_function)
7166           change->post_change_function(x, y);
7167       }
7168     }
7169
7170     if (change->has_action)
7171       ExecuteCustomElementAction(x, y, element, page);
7172   }
7173 }
7174
7175 #else
7176
7177 static void ChangeElement(int x, int y, int page)
7178 {
7179   int element = MovingOrBlocked2Element(x, y);
7180   struct ElementInfo *ei = &element_info[element];
7181   struct ElementChangeInfo *change = &ei->change_page[page];
7182
7183 #ifdef DEBUG
7184   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7185   {
7186     printf("\n\n");
7187     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7188            x, y, element, element_info[element].token_name);
7189     printf("ChangeElement(): This should never happen!\n");
7190     printf("\n\n");
7191   }
7192 #endif
7193
7194   /* this can happen with classic bombs on walkable, changing elements */
7195   if (!CAN_CHANGE(element))
7196   {
7197 #if 0
7198     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
7199       ChangeDelay[x][y] = 0;
7200 #endif
7201
7202     return;
7203   }
7204
7205   if (ChangeDelay[x][y] == 0)           /* initialize element change */
7206   {
7207     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
7208
7209     ResetGfxAnimation(x, y);
7210     ResetRandomAnimationValue(x, y);
7211
7212     if (change->pre_change_function)
7213       change->pre_change_function(x, y);
7214   }
7215
7216   ChangeDelay[x][y]--;
7217
7218   if (ChangeDelay[x][y] != 0)           /* continue element change */
7219   {
7220     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7221
7222     if (IS_ANIMATED(graphic))
7223       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7224
7225     if (change->change_function)
7226       change->change_function(x, y);
7227   }
7228   else                                  /* finish element change */
7229   {
7230     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
7231     {
7232       page = ChangePage[x][y];
7233       ChangePage[x][y] = -1;
7234
7235       change = &ei->change_page[page];
7236     }
7237
7238     if (IS_MOVING(x, y))                /* never change a running system ;-) */
7239     {
7240       ChangeDelay[x][y] = 1;            /* try change after next move step */
7241       ChangePage[x][y] = page;          /* remember page to use for change */
7242
7243       return;
7244     }
7245
7246     if (ChangeElementNow(x, y, element, page))
7247     {
7248       if (change->post_change_function)
7249         change->post_change_function(x, y);
7250     }
7251   }
7252 }
7253
7254 #endif
7255
7256 static boolean CheckTriggeredElementChangeExt(int trigger_element,
7257                                               int trigger_event,
7258                                               int trigger_player,
7259                                               int trigger_side,
7260                                               int trigger_page)
7261 {
7262   boolean change_done_any = FALSE;
7263   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7264   int i;
7265
7266   if (!(trigger_events[trigger_element][trigger_event]))
7267     return FALSE;
7268
7269   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7270   {
7271     int element = EL_CUSTOM_START + i;
7272     boolean change_done = FALSE;
7273     int p;
7274
7275     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7276         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7277       continue;
7278
7279     for (p = 0; p < element_info[element].num_change_pages; p++)
7280     {
7281       struct ElementChangeInfo *change = &element_info[element].change_page[p];
7282
7283       if (change->can_change_or_has_action &&
7284           change->has_event[trigger_event] &&
7285           change->trigger_side & trigger_side &&
7286           change->trigger_player & trigger_player &&
7287           change->trigger_page & trigger_page_bits &&
7288           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7289       {
7290         change->actual_trigger_element = trigger_element;
7291         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7292
7293         if ((change->can_change && !change_done) || change->has_action)
7294         {
7295           int x, y;
7296
7297           for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7298           {
7299             if (Feld[x][y] == element)
7300             {
7301               if (change->can_change && !change_done)
7302               {
7303                 ChangeDelay[x][y] = 1;
7304                 ChangeEvent[x][y] = trigger_event;
7305                 ChangeElement(x, y, p);
7306               }
7307 #if USE_NEW_DELAYED_ACTION
7308               else if (change->has_action)
7309               {
7310                 ExecuteCustomElementAction(x, y, element, p);
7311                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7312               }
7313 #else
7314               if (change->has_action)
7315               {
7316                 ExecuteCustomElementAction(x, y, element, p);
7317                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7318               }
7319 #endif
7320             }
7321           }
7322
7323           if (change->can_change)
7324           {
7325             change_done = TRUE;
7326             change_done_any = TRUE;
7327           }
7328         }
7329       }
7330     }
7331   }
7332
7333   return change_done_any;
7334 }
7335
7336 static boolean CheckElementChangeExt(int x, int y,
7337                                      int element,
7338                                      int trigger_element,
7339                                      int trigger_event,
7340                                      int trigger_player,
7341                                      int trigger_side)
7342 {
7343   boolean change_done = FALSE;
7344   int p;
7345
7346   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
7347       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7348     return FALSE;
7349
7350   if (Feld[x][y] == EL_BLOCKED)
7351   {
7352     Blocked2Moving(x, y, &x, &y);
7353     element = Feld[x][y];
7354   }
7355
7356   if (Feld[x][y] != element)    /* check if element has already changed */
7357     return FALSE;
7358
7359   for (p = 0; p < element_info[element].num_change_pages; p++)
7360   {
7361     struct ElementChangeInfo *change = &element_info[element].change_page[p];
7362
7363     boolean check_trigger_element =
7364       (trigger_event == CE_TOUCHING_X ||
7365        trigger_event == CE_HITTING_X ||
7366        trigger_event == CE_HIT_BY_X);
7367
7368     if (change->can_change_or_has_action &&
7369         change->has_event[trigger_event] &&
7370         change->trigger_side & trigger_side &&
7371         change->trigger_player & trigger_player &&
7372         (!check_trigger_element ||
7373          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
7374     {
7375       change->actual_trigger_element = trigger_element;
7376       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7377
7378       if (change->can_change && !change_done)
7379       {
7380         ChangeDelay[x][y] = 1;
7381         ChangeEvent[x][y] = trigger_event;
7382         ChangeElement(x, y, p);
7383
7384         change_done = TRUE;
7385       }
7386 #if USE_NEW_DELAYED_ACTION
7387       else if (change->has_action)
7388       {
7389         ExecuteCustomElementAction(x, y, element, p);
7390         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7391       }
7392 #else
7393       if (change->has_action)
7394       {
7395         ExecuteCustomElementAction(x, y, element, p);
7396         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
7397       }
7398 #endif
7399     }
7400   }
7401
7402   return change_done;
7403 }
7404
7405 static void PlayPlayerSound(struct PlayerInfo *player)
7406 {
7407   int jx = player->jx, jy = player->jy;
7408   int element = player->element_nr;
7409   int last_action = player->last_action_waiting;
7410   int action = player->action_waiting;
7411
7412   if (player->is_waiting)
7413   {
7414     if (action != last_action)
7415       PlayLevelSoundElementAction(jx, jy, element, action);
7416     else
7417       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7418   }
7419   else
7420   {
7421     if (action != last_action)
7422       StopSound(element_info[element].sound[last_action]);
7423
7424     if (last_action == ACTION_SLEEPING)
7425       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7426   }
7427 }
7428
7429 static void PlayAllPlayersSound()
7430 {
7431   int i;
7432
7433   for (i = 0; i < MAX_PLAYERS; i++)
7434     if (stored_player[i].active)
7435       PlayPlayerSound(&stored_player[i]);
7436 }
7437
7438 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7439 {
7440   boolean last_waiting = player->is_waiting;
7441   int move_dir = player->MovDir;
7442
7443   player->last_action_waiting = player->action_waiting;
7444
7445   if (is_waiting)
7446   {
7447     if (!last_waiting)          /* not waiting -> waiting */
7448     {
7449       player->is_waiting = TRUE;
7450
7451       player->frame_counter_bored =
7452         FrameCounter +
7453         game.player_boring_delay_fixed +
7454         SimpleRND(game.player_boring_delay_random);
7455       player->frame_counter_sleeping =
7456         FrameCounter +
7457         game.player_sleeping_delay_fixed +
7458         SimpleRND(game.player_sleeping_delay_random);
7459
7460       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7461     }
7462
7463     if (game.player_sleeping_delay_fixed +
7464         game.player_sleeping_delay_random > 0 &&
7465         player->anim_delay_counter == 0 &&
7466         player->post_delay_counter == 0 &&
7467         FrameCounter >= player->frame_counter_sleeping)
7468       player->is_sleeping = TRUE;
7469     else if (game.player_boring_delay_fixed +
7470              game.player_boring_delay_random > 0 &&
7471              FrameCounter >= player->frame_counter_bored)
7472       player->is_bored = TRUE;
7473
7474     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7475                               player->is_bored ? ACTION_BORING :
7476                               ACTION_WAITING);
7477
7478     if (player->is_sleeping)
7479     {
7480       if (player->num_special_action_sleeping > 0)
7481       {
7482         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7483         {
7484           int last_special_action = player->special_action_sleeping;
7485           int num_special_action = player->num_special_action_sleeping;
7486           int special_action =
7487             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7488              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7489              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7490              last_special_action + 1 : ACTION_SLEEPING);
7491           int special_graphic =
7492             el_act_dir2img(player->element_nr, special_action, move_dir);
7493
7494           player->anim_delay_counter =
7495             graphic_info[special_graphic].anim_delay_fixed +
7496             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7497           player->post_delay_counter =
7498             graphic_info[special_graphic].post_delay_fixed +
7499             SimpleRND(graphic_info[special_graphic].post_delay_random);
7500
7501           player->special_action_sleeping = special_action;
7502         }
7503
7504         if (player->anim_delay_counter > 0)
7505         {
7506           player->action_waiting = player->special_action_sleeping;
7507           player->anim_delay_counter--;
7508         }
7509         else if (player->post_delay_counter > 0)
7510         {
7511           player->post_delay_counter--;
7512         }
7513       }
7514     }
7515     else if (player->is_bored)
7516     {
7517       if (player->num_special_action_bored > 0)
7518       {
7519         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7520         {
7521           int special_action =
7522             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7523           int special_graphic =
7524             el_act_dir2img(player->element_nr, special_action, move_dir);
7525
7526           player->anim_delay_counter =
7527             graphic_info[special_graphic].anim_delay_fixed +
7528             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7529           player->post_delay_counter =
7530             graphic_info[special_graphic].post_delay_fixed +
7531             SimpleRND(graphic_info[special_graphic].post_delay_random);
7532
7533           player->special_action_bored = special_action;
7534         }
7535
7536         if (player->anim_delay_counter > 0)
7537         {
7538           player->action_waiting = player->special_action_bored;
7539           player->anim_delay_counter--;
7540         }
7541         else if (player->post_delay_counter > 0)
7542         {
7543           player->post_delay_counter--;
7544         }
7545       }
7546     }
7547   }
7548   else if (last_waiting)        /* waiting -> not waiting */
7549   {
7550     player->is_waiting = FALSE;
7551     player->is_bored = FALSE;
7552     player->is_sleeping = FALSE;
7553
7554     player->frame_counter_bored = -1;
7555     player->frame_counter_sleeping = -1;
7556
7557     player->anim_delay_counter = 0;
7558     player->post_delay_counter = 0;
7559
7560     player->action_waiting = ACTION_DEFAULT;
7561
7562     player->special_action_bored = ACTION_DEFAULT;
7563     player->special_action_sleeping = ACTION_DEFAULT;
7564   }
7565 }
7566
7567 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7568 {
7569   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7570   int left      = player_action & JOY_LEFT;
7571   int right     = player_action & JOY_RIGHT;
7572   int up        = player_action & JOY_UP;
7573   int down      = player_action & JOY_DOWN;
7574   int button1   = player_action & JOY_BUTTON_1;
7575   int button2   = player_action & JOY_BUTTON_2;
7576   int dx        = (left ? -1    : right ? 1     : 0);
7577   int dy        = (up   ? -1    : down  ? 1     : 0);
7578
7579   if (!player->active || tape.pausing)
7580     return 0;
7581
7582   if (player_action)
7583   {
7584     if (button1)
7585       snapped = SnapField(player, dx, dy);
7586     else
7587     {
7588       if (button2)
7589         dropped = DropElement(player);
7590
7591       moved = MovePlayer(player, dx, dy);
7592     }
7593
7594     if (tape.single_step && tape.recording && !tape.pausing)
7595     {
7596       if (button1 || (dropped && !moved))
7597       {
7598         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7599         SnapField(player, 0, 0);                /* stop snapping */
7600       }
7601     }
7602
7603     SetPlayerWaiting(player, FALSE);
7604
7605     return player_action;
7606   }
7607   else
7608   {
7609     /* no actions for this player (no input at player's configured device) */
7610
7611     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
7612     SnapField(player, 0, 0);
7613     CheckGravityMovementWhenNotMoving(player);
7614
7615     if (player->MovPos == 0)
7616       SetPlayerWaiting(player, TRUE);
7617
7618     if (player->MovPos == 0)    /* needed for tape.playing */
7619       player->is_moving = FALSE;
7620
7621     player->is_dropping = FALSE;
7622
7623     return 0;
7624   }
7625 }
7626
7627 void AdvanceFrameAndPlayerCounters(int player_nr)
7628 {
7629   int i;
7630
7631   /* advance frame counters (global frame counter and time frame counter) */
7632   FrameCounter++;
7633   TimeFrames++;
7634
7635   /* advance player counters (counters for move delay, move animation etc.) */
7636   for (i = 0; i < MAX_PLAYERS; i++)
7637   {
7638     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
7639     int move_delay_value = stored_player[i].move_delay_value;
7640     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
7641
7642     if (!advance_player_counters)       /* not all players may be affected */
7643       continue;
7644
7645 #if USE_NEW_PLAYER_ANIM
7646     if (move_frames == 0)       /* less than one move per game frame */
7647     {
7648       int stepsize = TILEX / move_delay_value;
7649       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
7650       int count = (stored_player[i].is_moving ?
7651                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
7652
7653       if (count % delay == 0)
7654         move_frames = 1;
7655     }
7656 #endif
7657
7658     stored_player[i].Frame += move_frames;
7659
7660     if (stored_player[i].MovPos != 0)
7661       stored_player[i].StepFrame += move_frames;
7662
7663     if (stored_player[i].move_delay > 0)
7664       stored_player[i].move_delay--;
7665
7666     /* due to bugs in previous versions, counter must count up, not down */
7667     if (stored_player[i].push_delay != -1)
7668       stored_player[i].push_delay++;
7669
7670     if (stored_player[i].drop_delay > 0)
7671       stored_player[i].drop_delay--;
7672   }
7673 }
7674
7675 void GameActions()
7676 {
7677   static unsigned long game_frame_delay = 0;
7678   unsigned long game_frame_delay_value;
7679   int magic_wall_x = 0, magic_wall_y = 0;
7680   int i, x, y, element, graphic;
7681   byte *recorded_player_action;
7682   byte summarized_player_action = 0;
7683   byte tape_action[MAX_PLAYERS];
7684
7685   if (game_status != GAME_MODE_PLAYING)
7686     return;
7687
7688   game_frame_delay_value =
7689     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7690
7691   if (tape.playing && tape.warp_forward && !tape.pausing)
7692     game_frame_delay_value = 0;
7693
7694   /* ---------- main game synchronization point ---------- */
7695
7696   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
7697
7698   if (network_playing && !network_player_action_received)
7699   {
7700     /* try to get network player actions in time */
7701
7702 #if defined(NETWORK_AVALIABLE)
7703     /* last chance to get network player actions without main loop delay */
7704     HandleNetworking();
7705 #endif
7706
7707     /* game was quit by network peer */
7708     if (game_status != GAME_MODE_PLAYING)
7709       return;
7710
7711     if (!network_player_action_received)
7712       return;           /* failed to get network player actions in time */
7713   }
7714
7715   if (tape.pausing)
7716     return;
7717
7718   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7719
7720 #if 1
7721   /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
7722   if (recorded_player_action == NULL && tape.pausing)
7723     return;
7724 #endif
7725
7726   for (i = 0; i < MAX_PLAYERS; i++)
7727   {
7728     summarized_player_action |= stored_player[i].action;
7729
7730     if (!network_playing)
7731       stored_player[i].effective_action = stored_player[i].action;
7732   }
7733
7734 #if defined(NETWORK_AVALIABLE)
7735   if (network_playing)
7736     SendToServer_MovePlayer(summarized_player_action);
7737 #endif
7738
7739   if (!options.network && !setup.team_mode)
7740     local_player->effective_action = summarized_player_action;
7741
7742   if (recorded_player_action != NULL)
7743     for (i = 0; i < MAX_PLAYERS; i++)
7744       stored_player[i].effective_action = recorded_player_action[i];
7745
7746   for (i = 0; i < MAX_PLAYERS; i++)
7747   {
7748     tape_action[i] = stored_player[i].effective_action;
7749
7750     if (tape.recording && tape_action[i] && !tape.player_participates[i])
7751       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
7752   }
7753
7754   /* only save actions from input devices, but not programmed actions */
7755   if (tape.recording)
7756     TapeRecordAction(tape_action);
7757
7758   for (i = 0; i < MAX_PLAYERS; i++)
7759   {
7760     int actual_player_action = stored_player[i].effective_action;
7761
7762 #if 1
7763     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
7764        - rnd_equinox_tetrachloride 048
7765        - rnd_equinox_tetrachloride_ii 096
7766        - rnd_emanuel_schmieg 002
7767        - doctor_sloan_ww 001, 020
7768     */
7769     if (stored_player[i].MovPos == 0)
7770       CheckGravityMovement(&stored_player[i]);
7771 #endif
7772
7773     /* overwrite programmed action with tape action */
7774     if (stored_player[i].programmed_action)
7775       actual_player_action = stored_player[i].programmed_action;
7776
7777 #if 1
7778     PlayerActions(&stored_player[i], actual_player_action);
7779 #else
7780     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7781
7782     if (tape.recording && tape_action[i] && !tape.player_participates[i])
7783       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
7784 #endif
7785
7786     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7787   }
7788
7789   network_player_action_received = FALSE;
7790
7791   ScrollScreen(NULL, SCROLL_GO_ON);
7792
7793   /* for backwards compatibility, the following code emulates a fixed bug that
7794      occured when pushing elements (causing elements that just made their last
7795      pushing step to already (if possible) make their first falling step in the
7796      same game frame, which is bad); this code is also needed to use the famous
7797      "spring push bug" which is used in older levels and might be wanted to be
7798      used also in newer levels, but in this case the buggy pushing code is only
7799      affecting the "spring" element and no other elements */
7800
7801   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
7802   {
7803     for (i = 0; i < MAX_PLAYERS; i++)
7804     {
7805       struct PlayerInfo *player = &stored_player[i];
7806       int x = player->jx;
7807       int y = player->jy;
7808
7809       if (player->active && player->is_pushing && player->is_moving &&
7810           IS_MOVING(x, y) &&
7811           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
7812            Feld[x][y] == EL_SPRING))
7813       {
7814         ContinueMoving(x, y);
7815
7816         /* continue moving after pushing (this is actually a bug) */
7817         if (!IS_MOVING(x, y))
7818         {
7819           Stop[x][y] = FALSE;
7820         }
7821       }
7822     }
7823   }
7824
7825   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7826   {
7827     Changed[x][y] = FALSE;
7828     ChangeEvent[x][y] = -1;
7829
7830     /* this must be handled before main playfield loop */
7831     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
7832     {
7833       MovDelay[x][y]--;
7834       if (MovDelay[x][y] <= 0)
7835         RemoveField(x, y);
7836     }
7837
7838 #if DEBUG
7839     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7840     {
7841       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7842       printf("GameActions(): This should never happen!\n");
7843
7844       ChangePage[x][y] = -1;
7845     }
7846 #endif
7847
7848     Stop[x][y] = FALSE;
7849     if (WasJustMoving[x][y] > 0)
7850       WasJustMoving[x][y]--;
7851     if (WasJustFalling[x][y] > 0)
7852       WasJustFalling[x][y]--;
7853     if (CheckCollision[x][y] > 0)
7854       CheckCollision[x][y]--;
7855
7856     GfxFrame[x][y]++;
7857
7858     /* reset finished pushing action (not done in ContinueMoving() to allow
7859        continous pushing animation for elements with zero push delay) */
7860     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7861     {
7862       ResetGfxAnimation(x, y);
7863       DrawLevelField(x, y);
7864     }
7865
7866 #if DEBUG
7867     if (IS_BLOCKED(x, y))
7868     {
7869       int oldx, oldy;
7870
7871       Blocked2Moving(x, y, &oldx, &oldy);
7872       if (!IS_MOVING(oldx, oldy))
7873       {
7874         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7875         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7876         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7877         printf("GameActions(): This should never happen!\n");
7878       }
7879     }
7880 #endif
7881   }
7882
7883   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7884   {
7885     element = Feld[x][y];
7886     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7887
7888     if (graphic_info[graphic].anim_global_sync)
7889       GfxFrame[x][y] = FrameCounter;
7890
7891     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7892         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7893       ResetRandomAnimationValue(x, y);
7894
7895     SetRandomAnimationValue(x, y);
7896
7897     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7898
7899     if (IS_INACTIVE(element))
7900     {
7901       if (IS_ANIMATED(graphic))
7902         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7903
7904       continue;
7905     }
7906
7907     /* this may take place after moving, so 'element' may have changed */
7908     if (IS_CHANGING(x, y) &&
7909         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7910     {
7911       int page = element_info[element].event_page_nr[CE_DELAY];
7912 #if 0
7913       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
7914 #else
7915
7916 #if 0
7917       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
7918 #endif
7919
7920 #if 1
7921       ChangeElement(x, y, page);
7922 #else
7923       if (CAN_CHANGE(element))
7924         ChangeElement(x, y, page);
7925
7926       if (HAS_ACTION(element))
7927         ExecuteCustomElementAction(x, y, element, page);
7928 #endif
7929
7930 #endif
7931
7932       element = Feld[x][y];
7933       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7934     }
7935
7936     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7937     {
7938       StartMoving(x, y);
7939
7940       element = Feld[x][y];
7941       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7942
7943       if (IS_ANIMATED(graphic) &&
7944           !IS_MOVING(x, y) &&
7945           !Stop[x][y])
7946         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7947
7948       if (IS_GEM(element) || element == EL_SP_INFOTRON)
7949         EdelsteinFunkeln(x, y);
7950     }
7951     else if ((element == EL_ACID ||
7952               element == EL_EXIT_OPEN ||
7953               element == EL_SP_EXIT_OPEN ||
7954               element == EL_SP_TERMINAL ||
7955               element == EL_SP_TERMINAL_ACTIVE ||
7956               element == EL_EXTRA_TIME ||
7957               element == EL_SHIELD_NORMAL ||
7958               element == EL_SHIELD_DEADLY) &&
7959              IS_ANIMATED(graphic))
7960       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7961     else if (IS_MOVING(x, y))
7962       ContinueMoving(x, y);
7963     else if (IS_ACTIVE_BOMB(element))
7964       CheckDynamite(x, y);
7965     else if (element == EL_AMOEBA_GROWING)
7966       AmoebeWaechst(x, y);
7967     else if (element == EL_AMOEBA_SHRINKING)
7968       AmoebaDisappearing(x, y);
7969
7970 #if !USE_NEW_AMOEBA_CODE
7971     else if (IS_AMOEBALIVE(element))
7972       AmoebeAbleger(x, y);
7973 #endif
7974
7975     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7976       Life(x, y);
7977     else if (element == EL_EXIT_CLOSED)
7978       CheckExit(x, y);
7979     else if (element == EL_SP_EXIT_CLOSED)
7980       CheckExitSP(x, y);
7981     else if (element == EL_EXPANDABLE_WALL_GROWING)
7982       MauerWaechst(x, y);
7983     else if (element == EL_EXPANDABLE_WALL ||
7984              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7985              element == EL_EXPANDABLE_WALL_VERTICAL ||
7986              element == EL_EXPANDABLE_WALL_ANY)
7987       MauerAbleger(x, y);
7988     else if (element == EL_FLAMES)
7989       CheckForDragon(x, y);
7990     else if (element == EL_EXPLOSION)
7991       ; /* drawing of correct explosion animation is handled separately */
7992     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7993       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7994
7995     if (IS_BELT_ACTIVE(element))
7996       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7997
7998     if (game.magic_wall_active)
7999     {
8000       int jx = local_player->jx, jy = local_player->jy;
8001
8002       /* play the element sound at the position nearest to the player */
8003       if ((element == EL_MAGIC_WALL_FULL ||
8004            element == EL_MAGIC_WALL_ACTIVE ||
8005            element == EL_MAGIC_WALL_EMPTYING ||
8006            element == EL_BD_MAGIC_WALL_FULL ||
8007            element == EL_BD_MAGIC_WALL_ACTIVE ||
8008            element == EL_BD_MAGIC_WALL_EMPTYING) &&
8009           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8010       {
8011         magic_wall_x = x;
8012         magic_wall_y = y;
8013       }
8014     }
8015   }
8016
8017 #if USE_NEW_AMOEBA_CODE
8018   /* new experimental amoeba growth stuff */
8019   if (!(FrameCounter % 8))
8020   {
8021     static unsigned long random = 1684108901;
8022
8023     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8024     {
8025       x = RND(lev_fieldx);
8026       y = RND(lev_fieldy);
8027       element = Feld[x][y];
8028
8029       if (!IS_PLAYER(x,y) &&
8030           (element == EL_EMPTY ||
8031            CAN_GROW_INTO(element) ||
8032            element == EL_QUICKSAND_EMPTY ||
8033            element == EL_ACID_SPLASH_LEFT ||
8034            element == EL_ACID_SPLASH_RIGHT))
8035       {
8036         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8037             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8038             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8039             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8040           Feld[x][y] = EL_AMOEBA_DROP;
8041       }
8042
8043       random = random * 129 + 1;
8044     }
8045   }
8046 #endif
8047
8048 #if 0
8049   if (game.explosions_delayed)
8050 #endif
8051   {
8052     game.explosions_delayed = FALSE;
8053
8054     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8055     {
8056       element = Feld[x][y];
8057
8058       if (ExplodeField[x][y])
8059         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8060       else if (element == EL_EXPLOSION)
8061         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8062
8063       ExplodeField[x][y] = EX_TYPE_NONE;
8064     }
8065
8066     game.explosions_delayed = TRUE;
8067   }
8068
8069   if (game.magic_wall_active)
8070   {
8071     if (!(game.magic_wall_time_left % 4))
8072     {
8073       int element = Feld[magic_wall_x][magic_wall_y];
8074
8075       if (element == EL_BD_MAGIC_WALL_FULL ||
8076           element == EL_BD_MAGIC_WALL_ACTIVE ||
8077           element == EL_BD_MAGIC_WALL_EMPTYING)
8078         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8079       else
8080         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8081     }
8082
8083     if (game.magic_wall_time_left > 0)
8084     {
8085       game.magic_wall_time_left--;
8086       if (!game.magic_wall_time_left)
8087       {
8088         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8089         {
8090           element = Feld[x][y];
8091
8092           if (element == EL_MAGIC_WALL_ACTIVE ||
8093               element == EL_MAGIC_WALL_FULL)
8094           {
8095             Feld[x][y] = EL_MAGIC_WALL_DEAD;
8096             DrawLevelField(x, y);
8097           }
8098           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8099                    element == EL_BD_MAGIC_WALL_FULL)
8100           {
8101             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8102             DrawLevelField(x, y);
8103           }
8104         }
8105
8106         game.magic_wall_active = FALSE;
8107       }
8108     }
8109   }
8110
8111   if (game.light_time_left > 0)
8112   {
8113     game.light_time_left--;
8114
8115     if (game.light_time_left == 0)
8116       RedrawAllLightSwitchesAndInvisibleElements();
8117   }
8118
8119   if (game.timegate_time_left > 0)
8120   {
8121     game.timegate_time_left--;
8122
8123     if (game.timegate_time_left == 0)
8124       CloseAllOpenTimegates();
8125   }
8126
8127   for (i = 0; i < MAX_PLAYERS; i++)
8128   {
8129     struct PlayerInfo *player = &stored_player[i];
8130
8131     if (SHIELD_ON(player))
8132     {
8133       if (player->shield_deadly_time_left)
8134         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8135       else if (player->shield_normal_time_left)
8136         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8137     }
8138   }
8139
8140   if (TimeFrames >= FRAMES_PER_SECOND)
8141   {
8142     TimeFrames = 0;
8143     TapeTime++;
8144
8145     for (i = 0; i < MAX_PLAYERS; i++)
8146     {
8147       struct PlayerInfo *player = &stored_player[i];
8148
8149       if (SHIELD_ON(player))
8150       {
8151         player->shield_normal_time_left--;
8152
8153         if (player->shield_deadly_time_left > 0)
8154           player->shield_deadly_time_left--;
8155       }
8156     }
8157
8158     if (!level.use_step_counter)
8159     {
8160       TimePlayed++;
8161
8162       if (TimeLeft > 0)
8163       {
8164         TimeLeft--;
8165
8166         if (TimeLeft <= 10 && setup.time_limit)
8167           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8168
8169         DrawGameValue_Time(TimeLeft);
8170
8171         if (!TimeLeft && setup.time_limit)
8172           for (i = 0; i < MAX_PLAYERS; i++)
8173             KillPlayer(&stored_player[i]);
8174       }
8175       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8176         DrawGameValue_Time(TimePlayed);
8177     }
8178
8179     if (tape.recording || tape.playing)
8180       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8181   }
8182
8183   DrawAllPlayers();
8184   PlayAllPlayersSound();
8185
8186   if (options.debug)                    /* calculate frames per second */
8187   {
8188     static unsigned long fps_counter = 0;
8189     static int fps_frames = 0;
8190     unsigned long fps_delay_ms = Counter() - fps_counter;
8191
8192     fps_frames++;
8193
8194     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
8195     {
8196       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8197
8198       fps_frames = 0;
8199       fps_counter = Counter();
8200     }
8201
8202     redraw_mask |= REDRAW_FPS;
8203   }
8204
8205   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
8206
8207   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8208   {
8209     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8210
8211     local_player->show_envelope = 0;
8212   }
8213
8214   /* use random number generator in every frame to make it less predictable */
8215   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8216     RND(1);
8217 }
8218
8219 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8220 {
8221   int min_x = x, min_y = y, max_x = x, max_y = y;
8222   int i;
8223
8224   for (i = 0; i < MAX_PLAYERS; i++)
8225   {
8226     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8227
8228     if (!stored_player[i].active || &stored_player[i] == player)
8229       continue;
8230
8231     min_x = MIN(min_x, jx);
8232     min_y = MIN(min_y, jy);
8233     max_x = MAX(max_x, jx);
8234     max_y = MAX(max_y, jy);
8235   }
8236
8237   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8238 }
8239
8240 static boolean AllPlayersInVisibleScreen()
8241 {
8242   int i;
8243
8244   for (i = 0; i < MAX_PLAYERS; i++)
8245   {
8246     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8247
8248     if (!stored_player[i].active)
8249       continue;
8250
8251     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8252       return FALSE;
8253   }
8254
8255   return TRUE;
8256 }
8257
8258 void ScrollLevel(int dx, int dy)
8259 {
8260   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8261   int x, y;
8262
8263   BlitBitmap(drawto_field, drawto_field,
8264              FX + TILEX * (dx == -1) - softscroll_offset,
8265              FY + TILEY * (dy == -1) - softscroll_offset,
8266              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8267              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8268              FX + TILEX * (dx == 1) - softscroll_offset,
8269              FY + TILEY * (dy == 1) - softscroll_offset);
8270
8271   if (dx)
8272   {
8273     x = (dx == 1 ? BX1 : BX2);
8274     for (y = BY1; y <= BY2; y++)
8275       DrawScreenField(x, y);
8276   }
8277
8278   if (dy)
8279   {
8280     y = (dy == 1 ? BY1 : BY2);
8281     for (x = BX1; x <= BX2; x++)
8282       DrawScreenField(x, y);
8283   }
8284
8285   redraw_mask |= REDRAW_FIELD;
8286 }
8287
8288 static boolean canFallDown(struct PlayerInfo *player)
8289 {
8290   int jx = player->jx, jy = player->jy;
8291
8292   return (IN_LEV_FIELD(jx, jy + 1) &&
8293           (IS_FREE(jx, jy + 1) ||
8294            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8295           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8296           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8297 }
8298
8299 static boolean canPassField(int x, int y, int move_dir)
8300 {
8301   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8302   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8303   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
8304   int nextx = x + dx;
8305   int nexty = y + dy;
8306   int element = Feld[x][y];
8307
8308   return (IS_PASSABLE_FROM(element, opposite_dir) &&
8309           !CAN_MOVE(element) &&
8310           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
8311           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
8312           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
8313 }
8314
8315 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
8316 {
8317   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8318   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8319   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
8320   int newx = x + dx;
8321   int newy = y + dy;
8322
8323   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
8324           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
8325           (IS_DIGGABLE(Feld[newx][newy]) ||
8326            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
8327            canPassField(newx, newy, move_dir)));
8328 }
8329
8330 static void CheckGravityMovement(struct PlayerInfo *player)
8331 {
8332   if (game.gravity && !player->programmed_action)
8333   {
8334     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
8335     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
8336     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
8337     int jx = player->jx, jy = player->jy;
8338     boolean player_is_moving_to_valid_field =
8339       (!player_is_snapping &&
8340        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
8341         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
8342     boolean player_can_fall_down = canFallDown(player);
8343
8344     if (player_can_fall_down &&
8345         !player_is_moving_to_valid_field)
8346       player->programmed_action = MV_DOWN;
8347   }
8348 }
8349
8350 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
8351 {
8352   return CheckGravityMovement(player);
8353
8354   if (game.gravity && !player->programmed_action)
8355   {
8356     int jx = player->jx, jy = player->jy;
8357     boolean field_under_player_is_free =
8358       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
8359     boolean player_is_standing_on_valid_field =
8360       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
8361        (IS_WALKABLE(Feld[jx][jy]) &&
8362         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
8363
8364     if (field_under_player_is_free && !player_is_standing_on_valid_field)
8365       player->programmed_action = MV_DOWN;
8366   }
8367 }
8368
8369 /*
8370   MovePlayerOneStep()
8371   -----------------------------------------------------------------------------
8372   dx, dy:               direction (non-diagonal) to try to move the player to
8373   real_dx, real_dy:     direction as read from input device (can be diagonal)
8374 */
8375
8376 boolean MovePlayerOneStep(struct PlayerInfo *player,
8377                           int dx, int dy, int real_dx, int real_dy)
8378 {
8379   int jx = player->jx, jy = player->jy;
8380   int new_jx = jx + dx, new_jy = jy + dy;
8381   int element;
8382   int can_move;
8383
8384   if (!player->active || (!dx && !dy))
8385     return MF_NO_ACTION;
8386
8387   player->MovDir = (dx < 0 ? MV_LEFT :
8388                     dx > 0 ? MV_RIGHT :
8389                     dy < 0 ? MV_UP :
8390                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
8391
8392   if (!IN_LEV_FIELD(new_jx, new_jy))
8393     return MF_NO_ACTION;
8394
8395   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
8396     return MF_NO_ACTION;
8397
8398   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
8399
8400   if (DONT_RUN_INTO(element))
8401   {
8402     if (element == EL_ACID && dx == 0 && dy == 1)
8403     {
8404       SplashAcid(new_jx, new_jy);
8405       Feld[jx][jy] = EL_PLAYER_1;
8406       InitMovingField(jx, jy, MV_DOWN);
8407       Store[jx][jy] = EL_ACID;
8408       ContinueMoving(jx, jy);
8409       BuryPlayer(player);
8410     }
8411     else
8412       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
8413
8414     return MF_MOVING;
8415   }
8416
8417   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
8418   if (can_move != MF_MOVING)
8419     return can_move;
8420
8421   /* check if DigField() has caused relocation of the player */
8422   if (player->jx != jx || player->jy != jy)
8423     return MF_NO_ACTION;        /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
8424
8425   StorePlayer[jx][jy] = 0;
8426   player->last_jx = jx;
8427   player->last_jy = jy;
8428   player->jx = new_jx;
8429   player->jy = new_jy;
8430   StorePlayer[new_jx][new_jy] = player->element_nr;
8431
8432   if (player->move_delay_value_next != -1)
8433   {
8434     player->move_delay_value = player->move_delay_value_next;
8435     player->move_delay_value_next = -1;
8436   }
8437
8438   player->MovPos =
8439     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
8440
8441   player->step_counter++;
8442
8443   PlayerVisit[jx][jy] = FrameCounter;
8444
8445   ScrollPlayer(player, SCROLL_INIT);
8446
8447   return MF_MOVING;
8448 }
8449
8450 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
8451 {
8452   int jx = player->jx, jy = player->jy;
8453   int old_jx = jx, old_jy = jy;
8454   int moved = MF_NO_ACTION;
8455
8456   if (!player->active)
8457     return FALSE;
8458
8459   if (!dx && !dy)
8460   {
8461     if (player->MovPos == 0)
8462     {
8463       player->is_moving = FALSE;
8464       player->is_digging = FALSE;
8465       player->is_collecting = FALSE;
8466       player->is_snapping = FALSE;
8467       player->is_pushing = FALSE;
8468     }
8469
8470     return FALSE;
8471   }
8472
8473   if (player->move_delay > 0)
8474     return FALSE;
8475
8476   player->move_delay = -1;              /* set to "uninitialized" value */
8477
8478   /* store if player is automatically moved to next field */
8479   player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
8480
8481   /* remove the last programmed player action */
8482   player->programmed_action = 0;
8483
8484   if (player->MovPos)
8485   {
8486     /* should only happen if pre-1.2 tape recordings are played */
8487     /* this is only for backward compatibility */
8488
8489     int original_move_delay_value = player->move_delay_value;
8490
8491 #if DEBUG
8492     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
8493            tape.counter);
8494 #endif
8495
8496     /* scroll remaining steps with finest movement resolution */
8497     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8498
8499     while (player->MovPos)
8500     {
8501       ScrollPlayer(player, SCROLL_GO_ON);
8502       ScrollScreen(NULL, SCROLL_GO_ON);
8503
8504       AdvanceFrameAndPlayerCounters(player->index_nr);
8505
8506       DrawAllPlayers();
8507       BackToFront();
8508     }
8509
8510     player->move_delay_value = original_move_delay_value;
8511   }
8512
8513   if (player->last_move_dir & MV_HORIZONTAL)
8514   {
8515     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
8516       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
8517   }
8518   else
8519   {
8520     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
8521       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
8522   }
8523
8524   jx = player->jx;
8525   jy = player->jy;
8526
8527   if (moved & MF_MOVING && !ScreenMovPos &&
8528       (player == local_player || !options.network))
8529   {
8530     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
8531     int offset = (setup.scroll_delay ? 3 : 0);
8532
8533     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8534     {
8535       /* actual player has left the screen -- scroll in that direction */
8536       if (jx != old_jx)         /* player has moved horizontally */
8537         scroll_x += (jx - old_jx);
8538       else                      /* player has moved vertically */
8539         scroll_y += (jy - old_jy);
8540     }
8541     else
8542     {
8543       if (jx != old_jx)         /* player has moved horizontally */
8544       {
8545         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
8546             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8547           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8548
8549         /* don't scroll over playfield boundaries */
8550         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8551           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8552
8553         /* don't scroll more than one field at a time */
8554         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8555
8556         /* don't scroll against the player's moving direction */
8557         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
8558             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8559           scroll_x = old_scroll_x;
8560       }
8561       else                      /* player has moved vertically */
8562       {
8563         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
8564             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8565           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8566
8567         /* don't scroll over playfield boundaries */
8568         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8569           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8570
8571         /* don't scroll more than one field at a time */
8572         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8573
8574         /* don't scroll against the player's moving direction */
8575         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
8576             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8577           scroll_y = old_scroll_y;
8578       }
8579     }
8580
8581     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8582     {
8583       if (!options.network && !AllPlayersInVisibleScreen())
8584       {
8585         scroll_x = old_scroll_x;
8586         scroll_y = old_scroll_y;
8587       }
8588       else
8589       {
8590         ScrollScreen(player, SCROLL_INIT);
8591         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8592       }
8593     }
8594   }
8595
8596   player->StepFrame = 0;
8597
8598   if (moved & MF_MOVING)
8599   {
8600     if (old_jx != jx && old_jy == jy)
8601       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8602     else if (old_jx == jx && old_jy != jy)
8603       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8604
8605     DrawLevelField(jx, jy);     /* for "crumbled sand" */
8606
8607     player->last_move_dir = player->MovDir;
8608     player->is_moving = TRUE;
8609     player->is_snapping = FALSE;
8610     player->is_switching = FALSE;
8611     player->is_dropping = FALSE;
8612   }
8613   else
8614   {
8615     CheckGravityMovementWhenNotMoving(player);
8616
8617     player->is_moving = FALSE;
8618
8619     /* at this point, the player is allowed to move, but cannot move right now
8620        (e.g. because of something blocking the way) -- ensure that the player
8621        is also allowed to move in the next frame (in old versions before 3.1.1,
8622        the player was forced to wait again for eight frames before next try) */
8623
8624     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
8625       player->move_delay = 0;   /* allow direct movement in the next frame */
8626   }
8627
8628   if (player->move_delay == -1)         /* not yet initialized by DigField() */
8629     player->move_delay = player->move_delay_value;
8630
8631   if (game.engine_version < VERSION_IDENT(3,0,7,0))
8632   {
8633     TestIfPlayerTouchesBadThing(jx, jy);
8634     TestIfPlayerTouchesCustomElement(jx, jy);
8635   }
8636
8637   if (!player->active)
8638     RemovePlayer(player);
8639
8640   return moved;
8641 }
8642
8643 void ScrollPlayer(struct PlayerInfo *player, int mode)
8644 {
8645   int jx = player->jx, jy = player->jy;
8646   int last_jx = player->last_jx, last_jy = player->last_jy;
8647   int move_stepsize = TILEX / player->move_delay_value;
8648
8649 #if USE_NEW_PLAYER_SPEED
8650   if (!player->active)
8651     return;
8652
8653   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
8654     return;
8655 #else
8656   if (!player->active || player->MovPos == 0)
8657     return;
8658 #endif
8659
8660   if (mode == SCROLL_INIT)
8661   {
8662     player->actual_frame_counter = FrameCounter;
8663     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8664
8665     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
8666         Feld[last_jx][last_jy] == EL_EMPTY)
8667     {
8668       int last_field_block_delay = 0;   /* start with no blocking at all */
8669       int block_delay_adjustment = player->block_delay_adjustment;
8670
8671       /* if player blocks last field, add delay for exactly one move */
8672       if (player->block_last_field)
8673       {
8674         last_field_block_delay += player->move_delay_value;
8675
8676         /* when blocking enabled, prevent moving up despite gravity */
8677         if (game.gravity && player->MovDir == MV_UP)
8678           block_delay_adjustment = -1;
8679       }
8680
8681       /* add block delay adjustment (also possible when not blocking) */
8682       last_field_block_delay += block_delay_adjustment;
8683
8684       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8685       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
8686     }
8687
8688 #if USE_NEW_PLAYER_SPEED
8689     if (player->MovPos != 0)    /* player has not yet reached destination */
8690       return;
8691 #else
8692     return;
8693 #endif
8694   }
8695   else if (!FrameReached(&player->actual_frame_counter, 1))
8696     return;
8697
8698 #if 0
8699   printf("::: player->MovPos: %d -> %d\n",
8700          player->MovPos,
8701          player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
8702 #endif
8703
8704 #if USE_NEW_PLAYER_SPEED
8705   if (player->MovPos != 0)
8706   {
8707     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8708     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8709
8710     /* before DrawPlayer() to draw correct player graphic for this case */
8711     if (player->MovPos == 0)
8712       CheckGravityMovement(player);
8713   }
8714 #else
8715   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8716   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8717
8718   /* before DrawPlayer() to draw correct player graphic for this case */
8719   if (player->MovPos == 0)
8720     CheckGravityMovement(player);
8721 #endif
8722
8723   if (player->MovPos == 0)      /* player reached destination field */
8724   {
8725 #if 0
8726     printf("::: player reached destination field\n");
8727 #endif
8728
8729     if (player->move_delay_reset_counter > 0)
8730     {
8731       player->move_delay_reset_counter--;
8732
8733       if (player->move_delay_reset_counter == 0)
8734       {
8735         /* continue with normal speed after quickly moving through gate */
8736         HALVE_PLAYER_SPEED(player);
8737
8738         /* be able to make the next move without delay */
8739         player->move_delay = 0;
8740       }
8741     }
8742
8743     player->last_jx = jx;
8744     player->last_jy = jy;
8745
8746     if (Feld[jx][jy] == EL_EXIT_OPEN ||
8747         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8748         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
8749     {
8750       DrawPlayer(player);       /* needed here only to cleanup last field */
8751       RemovePlayer(player);
8752
8753       if (local_player->friends_still_needed == 0 ||
8754           IS_SP_ELEMENT(Feld[jx][jy]))
8755         player->LevelSolved = player->GameOver = TRUE;
8756     }
8757
8758     /* this breaks one level: "machine", level 000 */
8759     {
8760       int move_direction = player->MovDir;
8761       int enter_side = MV_DIR_OPPOSITE(move_direction);
8762       int leave_side = move_direction;
8763       int old_jx = last_jx;
8764       int old_jy = last_jy;
8765       int old_element = Feld[old_jx][old_jy];
8766       int new_element = Feld[jx][jy];
8767
8768       if (IS_CUSTOM_ELEMENT(old_element))
8769         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
8770                                    CE_LEFT_BY_PLAYER,
8771                                    player->index_bit, leave_side);
8772
8773       CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X,
8774                                           player->index_bit, leave_side);
8775
8776       if (IS_CUSTOM_ELEMENT(new_element))
8777         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
8778                                    player->index_bit, enter_side);
8779
8780       CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_ENTERS_X,
8781                                           player->index_bit, enter_side);
8782     }
8783
8784     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8785     {
8786       TestIfPlayerTouchesBadThing(jx, jy);
8787       TestIfPlayerTouchesCustomElement(jx, jy);
8788
8789       /* needed because pushed element has not yet reached its destination,
8790          so it would trigger a change event at its previous field location */
8791       if (!player->is_pushing)
8792         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
8793
8794       if (!player->active)
8795         RemovePlayer(player);
8796     }
8797
8798     if (level.use_step_counter)
8799     {
8800       int i;
8801
8802       TimePlayed++;
8803
8804       if (TimeLeft > 0)
8805       {
8806         TimeLeft--;
8807
8808         if (TimeLeft <= 10 && setup.time_limit)
8809           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8810
8811         DrawGameValue_Time(TimeLeft);
8812
8813         if (!TimeLeft && setup.time_limit)
8814           for (i = 0; i < MAX_PLAYERS; i++)
8815             KillPlayer(&stored_player[i]);
8816       }
8817       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8818         DrawGameValue_Time(TimePlayed);
8819     }
8820
8821     if (tape.single_step && tape.recording && !tape.pausing &&
8822         !player->programmed_action)
8823       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8824   }
8825 }
8826
8827 void ScrollScreen(struct PlayerInfo *player, int mode)
8828 {
8829   static unsigned long screen_frame_counter = 0;
8830
8831   if (mode == SCROLL_INIT)
8832   {
8833     /* set scrolling step size according to actual player's moving speed */
8834     ScrollStepSize = TILEX / player->move_delay_value;
8835
8836     screen_frame_counter = FrameCounter;
8837     ScreenMovDir = player->MovDir;
8838     ScreenMovPos = player->MovPos;
8839     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8840     return;
8841   }
8842   else if (!FrameReached(&screen_frame_counter, 1))
8843     return;
8844
8845   if (ScreenMovPos)
8846   {
8847     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8848     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8849     redraw_mask |= REDRAW_FIELD;
8850   }
8851   else
8852     ScreenMovDir = MV_NO_MOVING;
8853 }
8854
8855 void TestIfPlayerTouchesCustomElement(int x, int y)
8856 {
8857   static int xy[4][2] =
8858   {
8859     { 0, -1 },
8860     { -1, 0 },
8861     { +1, 0 },
8862     { 0, +1 }
8863   };
8864   static int trigger_sides[4][2] =
8865   {
8866     /* center side       border side */
8867     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8868     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8869     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8870     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8871   };
8872   static int touch_dir[4] =
8873   {
8874     MV_LEFT | MV_RIGHT,
8875     MV_UP   | MV_DOWN,
8876     MV_UP   | MV_DOWN,
8877     MV_LEFT | MV_RIGHT
8878   };
8879   int center_element = Feld[x][y];      /* should always be non-moving! */
8880   int i;
8881
8882   for (i = 0; i < NUM_DIRECTIONS; i++)
8883   {
8884     int xx = x + xy[i][0];
8885     int yy = y + xy[i][1];
8886     int center_side = trigger_sides[i][0];
8887     int border_side = trigger_sides[i][1];
8888     int border_element;
8889
8890     if (!IN_LEV_FIELD(xx, yy))
8891       continue;
8892
8893     if (IS_PLAYER(x, y))
8894     {
8895       struct PlayerInfo *player = PLAYERINFO(x, y);
8896
8897       if (game.engine_version < VERSION_IDENT(3,0,7,0))
8898         border_element = Feld[xx][yy];          /* may be moving! */
8899       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8900         border_element = Feld[xx][yy];
8901       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
8902         border_element = MovingOrBlocked2Element(xx, yy);
8903       else
8904         continue;               /* center and border element do not touch */
8905
8906       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
8907                                  player->index_bit, border_side);
8908       CheckTriggeredElementChangeByPlayer(border_element, CE_PLAYER_TOUCHES_X,
8909                                           player->index_bit, border_side);
8910     }
8911     else if (IS_PLAYER(xx, yy))
8912     {
8913       struct PlayerInfo *player = PLAYERINFO(xx, yy);
8914
8915       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8916       {
8917         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8918           continue;             /* center and border element do not touch */
8919       }
8920
8921       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
8922                                  player->index_bit, center_side);
8923       CheckTriggeredElementChangeByPlayer(center_element, CE_PLAYER_TOUCHES_X,
8924                                           player->index_bit, center_side);
8925       break;
8926     }
8927   }
8928 }
8929
8930 void TestIfElementTouchesCustomElement(int x, int y)
8931 {
8932   static int xy[4][2] =
8933   {
8934     { 0, -1 },
8935     { -1, 0 },
8936     { +1, 0 },
8937     { 0, +1 }
8938   };
8939   static int trigger_sides[4][2] =
8940   {
8941     /* center side      border side */
8942     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8943     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8944     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8945     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8946   };
8947   static int touch_dir[4] =
8948   {
8949     MV_LEFT | MV_RIGHT,
8950     MV_UP   | MV_DOWN,
8951     MV_UP   | MV_DOWN,
8952     MV_LEFT | MV_RIGHT
8953   };
8954   boolean change_center_element = FALSE;
8955   int center_element = Feld[x][y];      /* should always be non-moving! */
8956   int i;
8957
8958   for (i = 0; i < NUM_DIRECTIONS; i++)
8959   {
8960     int xx = x + xy[i][0];
8961     int yy = y + xy[i][1];
8962     int center_side = trigger_sides[i][0];
8963     int border_side = trigger_sides[i][1];
8964     int border_element;
8965
8966     if (!IN_LEV_FIELD(xx, yy))
8967       continue;
8968
8969     if (game.engine_version < VERSION_IDENT(3,0,7,0))
8970       border_element = Feld[xx][yy];    /* may be moving! */
8971     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8972       border_element = Feld[xx][yy];
8973     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
8974       border_element = MovingOrBlocked2Element(xx, yy);
8975     else
8976       continue;                 /* center and border element do not touch */
8977
8978     /* check for change of center element (but change it only once) */
8979     if (!change_center_element)
8980       change_center_element =
8981         CheckElementChangeBySide(x, y, center_element, border_element,
8982                                  CE_TOUCHING_X, border_side);
8983
8984     /* check for change of border element */
8985     CheckElementChangeBySide(xx, yy, border_element, center_element,
8986                              CE_TOUCHING_X, center_side);
8987   }
8988 }
8989
8990 void TestIfElementHitsCustomElement(int x, int y, int direction)
8991 {
8992   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8993   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8994   int hitx = x + dx, hity = y + dy;
8995   int hitting_element = Feld[x][y];
8996   int touched_element;
8997
8998   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8999     return;
9000
9001   touched_element = (IN_LEV_FIELD(hitx, hity) ?
9002                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9003
9004   if (IN_LEV_FIELD(hitx, hity))
9005   {
9006     int opposite_direction = MV_DIR_OPPOSITE(direction);
9007     int hitting_side = direction;
9008     int touched_side = opposite_direction;
9009     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9010                           MovDir[hitx][hity] != direction ||
9011                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9012
9013     object_hit = TRUE;
9014
9015     if (object_hit)
9016     {
9017       CheckElementChangeBySide(x, y, hitting_element, touched_element,
9018                                CE_HITTING_X, touched_side);
9019
9020       CheckElementChangeBySide(hitx, hity, touched_element,
9021                                hitting_element, CE_HIT_BY_X, hitting_side);
9022
9023       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9024                                CE_HIT_BY_SOMETHING, opposite_direction);
9025     }
9026   }
9027
9028   /* "hitting something" is also true when hitting the playfield border */
9029   CheckElementChangeBySide(x, y, hitting_element, touched_element,
9030                            CE_HITTING_SOMETHING, direction);
9031 }
9032
9033 #if 0
9034 void TestIfElementSmashesCustomElement(int x, int y, int direction)
9035 {
9036   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9037   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9038   int hitx = x + dx, hity = y + dy;
9039   int hitting_element = Feld[x][y];
9040   int touched_element;
9041 #if 0
9042   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9043                         !IS_FREE(hitx, hity) &&
9044                         (!IS_MOVING(hitx, hity) ||
9045                          MovDir[hitx][hity] != direction ||
9046                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
9047 #endif
9048
9049   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9050     return;
9051
9052 #if 0
9053   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9054     return;
9055 #endif
9056
9057   touched_element = (IN_LEV_FIELD(hitx, hity) ?
9058                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
9059
9060   CheckElementChangeBySide(x, y, hitting_element, touched_element,
9061                            EP_CAN_SMASH_EVERYTHING, direction);
9062
9063   if (IN_LEV_FIELD(hitx, hity))
9064   {
9065     int opposite_direction = MV_DIR_OPPOSITE(direction);
9066     int hitting_side = direction;
9067     int touched_side = opposite_direction;
9068 #if 0
9069     int touched_element = MovingOrBlocked2Element(hitx, hity);
9070 #endif
9071 #if 1
9072     boolean object_hit = (!IS_MOVING(hitx, hity) ||
9073                           MovDir[hitx][hity] != direction ||
9074                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
9075
9076     object_hit = TRUE;
9077 #endif
9078
9079     if (object_hit)
9080     {
9081       int i;
9082
9083       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9084                                CE_SMASHED_BY_SOMETHING, opposite_direction);
9085
9086       CheckElementChangeBySide(x, y, hitting_element, touched_element,
9087                                CE_OTHER_IS_SMASHING, touched_side);
9088
9089       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
9090                                CE_OTHER_GETS_SMASHED, hitting_side);
9091     }
9092   }
9093 }
9094 #endif
9095
9096 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
9097 {
9098   int i, kill_x = -1, kill_y = -1;
9099   int bad_element = -1;
9100   static int test_xy[4][2] =
9101   {
9102     { 0, -1 },
9103     { -1, 0 },
9104     { +1, 0 },
9105     { 0, +1 }
9106   };
9107   static int test_dir[4] =
9108   {
9109     MV_UP,
9110     MV_LEFT,
9111     MV_RIGHT,
9112     MV_DOWN
9113   };
9114
9115   for (i = 0; i < NUM_DIRECTIONS; i++)
9116   {
9117     int test_x, test_y, test_move_dir, test_element;
9118
9119     test_x = good_x + test_xy[i][0];
9120     test_y = good_y + test_xy[i][1];
9121
9122     if (!IN_LEV_FIELD(test_x, test_y))
9123       continue;
9124
9125     test_move_dir =
9126       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9127
9128     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
9129
9130     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9131        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9132     */
9133     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
9134         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
9135     {
9136       kill_x = test_x;
9137       kill_y = test_y;
9138       bad_element = test_element;
9139
9140       break;
9141     }
9142   }
9143
9144   if (kill_x != -1 || kill_y != -1)
9145   {
9146     if (IS_PLAYER(good_x, good_y))
9147     {
9148       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
9149
9150       if (player->shield_deadly_time_left > 0 &&
9151           !IS_INDESTRUCTIBLE(bad_element))
9152         Bang(kill_x, kill_y);
9153       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
9154         KillPlayer(player);
9155     }
9156     else
9157       Bang(good_x, good_y);
9158   }
9159 }
9160
9161 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
9162 {
9163   int i, kill_x = -1, kill_y = -1;
9164   int bad_element = Feld[bad_x][bad_y];
9165   static int test_xy[4][2] =
9166   {
9167     { 0, -1 },
9168     { -1, 0 },
9169     { +1, 0 },
9170     { 0, +1 }
9171   };
9172   static int touch_dir[4] =
9173   {
9174     MV_LEFT | MV_RIGHT,
9175     MV_UP   | MV_DOWN,
9176     MV_UP   | MV_DOWN,
9177     MV_LEFT | MV_RIGHT
9178   };
9179   static int test_dir[4] =
9180   {
9181     MV_UP,
9182     MV_LEFT,
9183     MV_RIGHT,
9184     MV_DOWN
9185   };
9186
9187   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
9188     return;
9189
9190   for (i = 0; i < NUM_DIRECTIONS; i++)
9191   {
9192     int test_x, test_y, test_move_dir, test_element;
9193
9194     test_x = bad_x + test_xy[i][0];
9195     test_y = bad_y + test_xy[i][1];
9196     if (!IN_LEV_FIELD(test_x, test_y))
9197       continue;
9198
9199     test_move_dir =
9200       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
9201
9202     test_element = Feld[test_x][test_y];
9203
9204     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
9205        2nd case: DONT_TOUCH style bad thing does not move away from good thing
9206     */
9207     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
9208         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
9209     {
9210       /* good thing is player or penguin that does not move away */
9211       if (IS_PLAYER(test_x, test_y))
9212       {
9213         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
9214
9215         if (bad_element == EL_ROBOT && player->is_moving)
9216           continue;     /* robot does not kill player if he is moving */
9217
9218         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9219         {
9220           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9221             continue;           /* center and border element do not touch */
9222         }
9223
9224         kill_x = test_x;
9225         kill_y = test_y;
9226         break;
9227       }
9228       else if (test_element == EL_PENGUIN)
9229       {
9230         kill_x = test_x;
9231         kill_y = test_y;
9232         break;
9233       }
9234     }
9235   }
9236
9237   if (kill_x != -1 || kill_y != -1)
9238   {
9239     if (IS_PLAYER(kill_x, kill_y))
9240     {
9241       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
9242
9243       if (player->shield_deadly_time_left > 0 &&
9244           !IS_INDESTRUCTIBLE(bad_element))
9245         Bang(bad_x, bad_y);
9246       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
9247         KillPlayer(player);
9248     }
9249     else
9250       Bang(kill_x, kill_y);
9251   }
9252 }
9253
9254 void TestIfPlayerTouchesBadThing(int x, int y)
9255 {
9256   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9257 }
9258
9259 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
9260 {
9261   TestIfGoodThingHitsBadThing(x, y, move_dir);
9262 }
9263
9264 void TestIfBadThingTouchesPlayer(int x, int y)
9265 {
9266   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9267 }
9268
9269 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
9270 {
9271   TestIfBadThingHitsGoodThing(x, y, move_dir);
9272 }
9273
9274 void TestIfFriendTouchesBadThing(int x, int y)
9275 {
9276   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
9277 }
9278
9279 void TestIfBadThingTouchesFriend(int x, int y)
9280 {
9281   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
9282 }
9283
9284 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
9285 {
9286   int i, kill_x = bad_x, kill_y = bad_y;
9287   static int xy[4][2] =
9288   {
9289     { 0, -1 },
9290     { -1, 0 },
9291     { +1, 0 },
9292     { 0, +1 }
9293   };
9294
9295   for (i = 0; i < NUM_DIRECTIONS; i++)
9296   {
9297     int x, y, element;
9298
9299     x = bad_x + xy[i][0];
9300     y = bad_y + xy[i][1];
9301     if (!IN_LEV_FIELD(x, y))
9302       continue;
9303
9304     element = Feld[x][y];
9305     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
9306         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
9307     {
9308       kill_x = x;
9309       kill_y = y;
9310       break;
9311     }
9312   }
9313
9314   if (kill_x != bad_x || kill_y != bad_y)
9315     Bang(bad_x, bad_y);
9316 }
9317
9318 void KillPlayer(struct PlayerInfo *player)
9319 {
9320   int jx = player->jx, jy = player->jy;
9321
9322   if (!player->active)
9323     return;
9324
9325   /* remove accessible field at the player's position */
9326   Feld[jx][jy] = EL_EMPTY;
9327
9328   /* deactivate shield (else Bang()/Explode() would not work right) */
9329   player->shield_normal_time_left = 0;
9330   player->shield_deadly_time_left = 0;
9331
9332   Bang(jx, jy);
9333   BuryPlayer(player);
9334 }
9335
9336 static void KillPlayerUnlessEnemyProtected(int x, int y)
9337 {
9338   if (!PLAYER_ENEMY_PROTECTED(x, y))
9339     KillPlayer(PLAYERINFO(x, y));
9340 }
9341
9342 static void KillPlayerUnlessExplosionProtected(int x, int y)
9343 {
9344   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
9345     KillPlayer(PLAYERINFO(x, y));
9346 }
9347
9348 void BuryPlayer(struct PlayerInfo *player)
9349 {
9350   int jx = player->jx, jy = player->jy;
9351
9352   if (!player->active)
9353     return;
9354
9355   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
9356   PlayLevelSound(jx, jy, SND_GAME_LOSING);
9357
9358   player->GameOver = TRUE;
9359   RemovePlayer(player);
9360 }
9361
9362 void RemovePlayer(struct PlayerInfo *player)
9363 {
9364   int jx = player->jx, jy = player->jy;
9365   int i, found = FALSE;
9366
9367   player->present = FALSE;
9368   player->active = FALSE;
9369
9370   if (!ExplodeField[jx][jy])
9371     StorePlayer[jx][jy] = 0;
9372
9373   if (player->is_moving)
9374     DrawLevelField(player->last_jx, player->last_jy);
9375
9376   for (i = 0; i < MAX_PLAYERS; i++)
9377     if (stored_player[i].active)
9378       found = TRUE;
9379
9380   if (!found)
9381     AllPlayersGone = TRUE;
9382
9383   ExitX = ZX = jx;
9384   ExitY = ZY = jy;
9385 }
9386
9387 /*
9388   =============================================================================
9389   checkDiagonalPushing()
9390   -----------------------------------------------------------------------------
9391   check if diagonal input device direction results in pushing of object
9392   (by checking if the alternative direction is walkable, diggable, ...)
9393   =============================================================================
9394 */
9395
9396 static boolean checkDiagonalPushing(struct PlayerInfo *player,
9397                                     int x, int y, int real_dx, int real_dy)
9398 {
9399   int jx, jy, dx, dy, xx, yy;
9400
9401   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
9402     return TRUE;
9403
9404   /* diagonal direction: check alternative direction */
9405   jx = player->jx;
9406   jy = player->jy;
9407   dx = x - jx;
9408   dy = y - jy;
9409   xx = jx + (dx == 0 ? real_dx : 0);
9410   yy = jy + (dy == 0 ? real_dy : 0);
9411
9412   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
9413 }
9414
9415 /*
9416   =============================================================================
9417   DigField()
9418   -----------------------------------------------------------------------------
9419   x, y:                 field next to player (non-diagonal) to try to dig to
9420   real_dx, real_dy:     direction as read from input device (can be diagonal)
9421   =============================================================================
9422 */
9423
9424 int DigField(struct PlayerInfo *player,
9425              int oldx, int oldy, int x, int y,
9426              int real_dx, int real_dy, int mode)
9427 {
9428   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
9429   boolean player_was_pushing = player->is_pushing;
9430   int jx = oldx, jy = oldy;
9431   int dx = x - jx, dy = y - jy;
9432   int nextx = x + dx, nexty = y + dy;
9433   int move_direction = (dx == -1 ? MV_LEFT :
9434                         dx == +1 ? MV_RIGHT :
9435                         dy == -1 ? MV_UP :
9436                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9437   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
9438   int dig_side = MV_DIR_OPPOSITE(move_direction);
9439   int old_element = Feld[jx][jy];
9440   int element;
9441   int collect_count;
9442
9443   if (is_player)                /* function can also be called by EL_PENGUIN */
9444   {
9445     if (player->MovPos == 0)
9446     {
9447       player->is_digging = FALSE;
9448       player->is_collecting = FALSE;
9449     }
9450
9451     if (player->MovPos == 0)    /* last pushing move finished */
9452       player->is_pushing = FALSE;
9453
9454     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
9455     {
9456       player->is_switching = FALSE;
9457       player->push_delay = -1;
9458
9459       return MF_NO_ACTION;
9460     }
9461   }
9462
9463   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
9464     return MF_NO_ACTION;
9465
9466   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
9467     old_element = Back[jx][jy];
9468
9469   /* in case of element dropped at player position, check background */
9470   else if (Back[jx][jy] != EL_EMPTY &&
9471            game.engine_version >= VERSION_IDENT(2,2,0,0))
9472     old_element = Back[jx][jy];
9473
9474   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
9475     return MF_NO_ACTION;        /* field has no opening in this direction */
9476
9477   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
9478     return MF_NO_ACTION;        /* field has no opening in this direction */
9479
9480   element = Feld[x][y];
9481 #if USE_NEW_COLLECT_COUNT
9482   collect_count = Count[x][y];
9483 #else
9484   collect_count = element_info[element].collect_count_initial;
9485 #endif
9486
9487 #if 0
9488   if (element != EL_BLOCKED &&
9489       Count[x][y] != element_info[element].collect_count_initial)
9490     printf("::: %d: %d != %d\n",
9491            element,
9492            Count[x][y],
9493            element_info[element].collect_count_initial);
9494 #endif
9495
9496   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
9497     return MF_NO_ACTION;
9498
9499   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
9500       game.engine_version >= VERSION_IDENT(2,2,0,0))
9501     return MF_NO_ACTION;
9502
9503   if (game.gravity && is_player && !player->is_auto_moving &&
9504       canFallDown(player) && move_direction != MV_DOWN &&
9505       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
9506     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
9507
9508   if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
9509   {
9510     int sound_element = SND_ELEMENT(element);
9511     int sound_action = ACTION_WALKING;
9512
9513     if (IS_RND_GATE(element))
9514     {
9515       if (!player->key[RND_GATE_NR(element)])
9516         return MF_NO_ACTION;
9517     }
9518     else if (IS_RND_GATE_GRAY(element))
9519     {
9520       if (!player->key[RND_GATE_GRAY_NR(element)])
9521         return MF_NO_ACTION;
9522     }
9523     else if (element == EL_EXIT_OPEN ||
9524              element == EL_SP_EXIT_OPEN ||
9525              element == EL_SP_EXIT_OPENING)
9526     {
9527       sound_action = ACTION_PASSING;    /* player is passing exit */
9528     }
9529     else if (element == EL_EMPTY)
9530     {
9531       sound_action = ACTION_MOVING;             /* nothing to walk on */
9532     }
9533
9534     /* play sound from background or player, whatever is available */
9535     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
9536       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
9537     else
9538       PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9539   }
9540   else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
9541   {
9542     if (!ACCESS_FROM(element, opposite_direction))
9543       return MF_NO_ACTION;      /* field not accessible from this direction */
9544
9545     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
9546       return MF_NO_ACTION;
9547
9548     if (IS_EM_GATE(element))
9549     {
9550       if (!player->key[EM_GATE_NR(element)])
9551         return MF_NO_ACTION;
9552     }
9553     else if (IS_EM_GATE_GRAY(element))
9554     {
9555       if (!player->key[EM_GATE_GRAY_NR(element)])
9556         return MF_NO_ACTION;
9557     }
9558     else if (IS_SP_PORT(element))
9559     {
9560       if (element == EL_SP_GRAVITY_PORT_LEFT ||
9561           element == EL_SP_GRAVITY_PORT_RIGHT ||
9562           element == EL_SP_GRAVITY_PORT_UP ||
9563           element == EL_SP_GRAVITY_PORT_DOWN)
9564         game.gravity = !game.gravity;
9565       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
9566                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
9567                element == EL_SP_GRAVITY_ON_PORT_UP ||
9568                element == EL_SP_GRAVITY_ON_PORT_DOWN)
9569         game.gravity = TRUE;
9570       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
9571                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
9572                element == EL_SP_GRAVITY_OFF_PORT_UP ||
9573                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
9574         game.gravity = FALSE;
9575     }
9576
9577     /* automatically move to the next field with double speed */
9578     player->programmed_action = move_direction;
9579
9580     if (player->move_delay_reset_counter == 0)
9581     {
9582       player->move_delay_reset_counter = 2;     /* two double speed steps */
9583
9584       DOUBLE_PLAYER_SPEED(player);
9585     }
9586
9587     PlayLevelSoundAction(x, y, ACTION_PASSING);
9588   }
9589   else if (IS_DIGGABLE(element))
9590   {
9591     RemoveField(x, y);
9592
9593     if (mode != DF_SNAP)
9594     {
9595       GfxElement[x][y] = GFX_ELEMENT(element);
9596       player->is_digging = TRUE;
9597     }
9598
9599     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9600
9601     CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_DIGS_X,
9602                                         player->index_bit, dig_side);
9603
9604     if (mode == DF_SNAP)
9605       TestIfElementTouchesCustomElement(x, y);  /* for empty space */
9606   }
9607   else if (IS_COLLECTIBLE(element))
9608   {
9609     RemoveField(x, y);
9610
9611     if (is_player && mode != DF_SNAP)
9612     {
9613       GfxElement[x][y] = element;
9614       player->is_collecting = TRUE;
9615     }
9616
9617     if (element == EL_SPEED_PILL)
9618     {
9619       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9620     }
9621     else if (element == EL_EXTRA_TIME && level.time > 0)
9622     {
9623       TimeLeft += 10;
9624       DrawGameValue_Time(TimeLeft);
9625     }
9626     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9627     {
9628       player->shield_normal_time_left += 10;
9629       if (element == EL_SHIELD_DEADLY)
9630         player->shield_deadly_time_left += 10;
9631     }
9632     else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9633     {
9634       if (player->inventory_size < MAX_INVENTORY_SIZE)
9635         player->inventory_element[player->inventory_size++] = element;
9636
9637       DrawGameValue_Dynamite(local_player->inventory_size);
9638     }
9639     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9640     {
9641       player->dynabomb_count++;
9642       player->dynabombs_left++;
9643     }
9644     else if (element == EL_DYNABOMB_INCREASE_SIZE)
9645     {
9646       player->dynabomb_size++;
9647     }
9648     else if (element == EL_DYNABOMB_INCREASE_POWER)
9649     {
9650       player->dynabomb_xl = TRUE;
9651     }
9652     else if (IS_KEY(element))
9653     {
9654       player->key[KEY_NR(element)] = TRUE;
9655
9656       DrawGameValue_Keys(player->key);
9657
9658       redraw_mask |= REDRAW_DOOR_1;
9659     }
9660     else if (IS_ENVELOPE(element))
9661     {
9662       player->show_envelope = element;
9663     }
9664     else if (IS_DROPPABLE(element) ||
9665              IS_THROWABLE(element))     /* can be collected and dropped */
9666     {
9667       int i;
9668
9669       if (collect_count == 0)
9670         player->inventory_infinite_element = element;
9671       else
9672         for (i = 0; i < collect_count; i++)
9673           if (player->inventory_size < MAX_INVENTORY_SIZE)
9674             player->inventory_element[player->inventory_size++] = element;
9675
9676       DrawGameValue_Dynamite(local_player->inventory_size);
9677     }
9678     else if (collect_count > 0)
9679     {
9680       local_player->gems_still_needed -= collect_count;
9681       if (local_player->gems_still_needed < 0)
9682         local_player->gems_still_needed = 0;
9683
9684       DrawGameValue_Emeralds(local_player->gems_still_needed);
9685     }
9686
9687     RaiseScoreElement(element);
9688     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9689
9690     if (is_player)
9691       CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_COLLECTS_X,
9692                                           player->index_bit, dig_side);
9693
9694     if (mode == DF_SNAP)
9695       TestIfElementTouchesCustomElement(x, y);  /* for empty space */
9696   }
9697   else if (IS_PUSHABLE(element))
9698   {
9699     if (mode == DF_SNAP && element != EL_BD_ROCK)
9700       return MF_NO_ACTION;
9701
9702     if (CAN_FALL(element) && dy)
9703       return MF_NO_ACTION;
9704
9705     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9706         !(element == EL_SPRING && level.use_spring_bug))
9707       return MF_NO_ACTION;
9708
9709     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9710         ((move_direction & MV_VERTICAL &&
9711           ((element_info[element].move_pattern & MV_LEFT &&
9712             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9713            (element_info[element].move_pattern & MV_RIGHT &&
9714             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9715          (move_direction & MV_HORIZONTAL &&
9716           ((element_info[element].move_pattern & MV_UP &&
9717             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9718            (element_info[element].move_pattern & MV_DOWN &&
9719             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9720       return MF_NO_ACTION;
9721
9722     /* do not push elements already moving away faster than player */
9723     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9724         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9725       return MF_NO_ACTION;
9726
9727     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9728     {
9729       if (player->push_delay_value == -1 || !player_was_pushing)
9730         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9731     }
9732     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9733     {
9734       if (player->push_delay_value == -1)
9735         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9736     }
9737     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9738     {
9739       if (!player->is_pushing)
9740         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9741     }
9742
9743     player->is_pushing = TRUE;
9744
9745     if (!(IN_LEV_FIELD(nextx, nexty) &&
9746           (IS_FREE(nextx, nexty) ||
9747            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9748             IS_SB_ELEMENT(element)))))
9749       return MF_NO_ACTION;
9750
9751     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9752       return MF_NO_ACTION;
9753
9754     if (player->push_delay == -1)       /* new pushing; restart delay */
9755       player->push_delay = 0;
9756
9757     if (player->push_delay < player->push_delay_value &&
9758         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9759         element != EL_SPRING && element != EL_BALLOON)
9760     {
9761       /* make sure that there is no move delay before next try to push */
9762       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9763         player->move_delay = 0;
9764
9765       return MF_NO_ACTION;
9766     }
9767
9768     if (IS_SB_ELEMENT(element))
9769     {
9770       if (element == EL_SOKOBAN_FIELD_FULL)
9771       {
9772         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9773         local_player->sokobanfields_still_needed++;
9774       }
9775
9776       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9777       {
9778         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9779         local_player->sokobanfields_still_needed--;
9780       }
9781
9782       Feld[x][y] = EL_SOKOBAN_OBJECT;
9783
9784       if (Back[x][y] == Back[nextx][nexty])
9785         PlayLevelSoundAction(x, y, ACTION_PUSHING);
9786       else if (Back[x][y] != 0)
9787         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9788                                     ACTION_EMPTYING);
9789       else
9790         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9791                                     ACTION_FILLING);
9792
9793       if (local_player->sokobanfields_still_needed == 0 &&
9794           game.emulation == EMU_SOKOBAN)
9795       {
9796         player->LevelSolved = player->GameOver = TRUE;
9797         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9798       }
9799     }
9800     else
9801       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9802
9803     InitMovingField(x, y, move_direction);
9804     GfxAction[x][y] = ACTION_PUSHING;
9805
9806     if (mode == DF_SNAP)
9807       ContinueMoving(x, y);
9808     else
9809       MovPos[x][y] = (dx != 0 ? dx : dy);
9810
9811     Pushed[x][y] = TRUE;
9812     Pushed[nextx][nexty] = TRUE;
9813
9814     if (game.engine_version < VERSION_IDENT(2,2,0,7))
9815       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9816     else
9817       player->push_delay_value = -1;    /* get new value later */
9818
9819     /* check for element change _after_ element has been pushed */
9820     if (game.use_change_when_pushing_bug)
9821     {
9822       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
9823                                  player->index_bit, dig_side);
9824       CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
9825                                           player->index_bit, dig_side);
9826     }
9827   }
9828   else if (IS_SWITCHABLE(element))
9829   {
9830     if (PLAYER_SWITCHING(player, x, y))
9831     {
9832       CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
9833                                           player->index_bit, dig_side);
9834
9835       return MF_ACTION;
9836     }
9837
9838     player->is_switching = TRUE;
9839     player->switch_x = x;
9840     player->switch_y = y;
9841
9842     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9843
9844     if (element == EL_ROBOT_WHEEL)
9845     {
9846       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9847       ZX = x;
9848       ZY = y;
9849
9850       DrawLevelField(x, y);
9851     }
9852     else if (element == EL_SP_TERMINAL)
9853     {
9854       int xx, yy;
9855
9856       for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9857       {
9858         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9859           Bang(xx, yy);
9860         else if (Feld[xx][yy] == EL_SP_TERMINAL)
9861           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9862       }
9863     }
9864     else if (IS_BELT_SWITCH(element))
9865     {
9866       ToggleBeltSwitch(x, y);
9867     }
9868     else if (element == EL_SWITCHGATE_SWITCH_UP ||
9869              element == EL_SWITCHGATE_SWITCH_DOWN)
9870     {
9871       ToggleSwitchgateSwitch(x, y);
9872     }
9873     else if (element == EL_LIGHT_SWITCH ||
9874              element == EL_LIGHT_SWITCH_ACTIVE)
9875     {
9876       ToggleLightSwitch(x, y);
9877     }
9878     else if (element == EL_TIMEGATE_SWITCH)
9879     {
9880       ActivateTimegateSwitch(x, y);
9881     }
9882     else if (element == EL_BALLOON_SWITCH_LEFT ||
9883              element == EL_BALLOON_SWITCH_RIGHT ||
9884              element == EL_BALLOON_SWITCH_UP ||
9885              element == EL_BALLOON_SWITCH_DOWN ||
9886              element == EL_BALLOON_SWITCH_ANY)
9887     {
9888       if (element == EL_BALLOON_SWITCH_ANY)
9889         game.balloon_dir = move_direction;
9890       else
9891         game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
9892                             element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9893                             element == EL_BALLOON_SWITCH_UP    ? MV_UP :
9894                             element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
9895                             MV_NO_MOVING);
9896     }
9897     else if (element == EL_LAMP)
9898     {
9899       Feld[x][y] = EL_LAMP_ACTIVE;
9900       local_player->lights_still_needed--;
9901
9902       ResetGfxAnimation(x, y);
9903       DrawLevelField(x, y);
9904     }
9905     else if (element == EL_TIME_ORB_FULL)
9906     {
9907       Feld[x][y] = EL_TIME_ORB_EMPTY;
9908       TimeLeft += 10;
9909       DrawGameValue_Time(TimeLeft);
9910
9911       ResetGfxAnimation(x, y);
9912       DrawLevelField(x, y);
9913     }
9914
9915     CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X,
9916                                         player->index_bit, dig_side);
9917
9918     CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
9919                                         player->index_bit, dig_side);
9920
9921     return MF_ACTION;
9922   }
9923   else
9924   {
9925     if (!PLAYER_SWITCHING(player, x, y))
9926     {
9927       player->is_switching = TRUE;
9928       player->switch_x = x;
9929       player->switch_y = y;
9930
9931       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
9932                                  player->index_bit, dig_side);
9933       CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X,
9934                                           player->index_bit, dig_side);
9935     }
9936
9937     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
9938                                player->index_bit, dig_side);
9939     CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
9940                                         player->index_bit, dig_side);
9941
9942     return MF_NO_ACTION;
9943   }
9944
9945   player->push_delay = -1;
9946
9947   if (is_player)                /* function can also be called by EL_PENGUIN */
9948   {
9949     if (Feld[x][y] != element)          /* really digged/collected something */
9950       player->is_collecting = !player->is_digging;
9951   }
9952
9953   return MF_MOVING;
9954 }
9955
9956 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9957 {
9958   int jx = player->jx, jy = player->jy;
9959   int x = jx + dx, y = jy + dy;
9960   int snap_direction = (dx == -1 ? MV_LEFT :
9961                         dx == +1 ? MV_RIGHT :
9962                         dy == -1 ? MV_UP :
9963                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9964
9965   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
9966     return FALSE;
9967
9968   if (!player->active || !IN_LEV_FIELD(x, y))
9969     return FALSE;
9970
9971   if (dx && dy)
9972     return FALSE;
9973
9974   if (!dx && !dy)
9975   {
9976     if (player->MovPos == 0)
9977       player->is_pushing = FALSE;
9978
9979     player->is_snapping = FALSE;
9980
9981     if (player->MovPos == 0)
9982     {
9983       player->is_moving = FALSE;
9984       player->is_digging = FALSE;
9985       player->is_collecting = FALSE;
9986     }
9987
9988     return FALSE;
9989   }
9990
9991   if (player->is_snapping)
9992     return FALSE;
9993
9994   player->MovDir = snap_direction;
9995
9996   if (player->MovPos == 0)
9997   {
9998     player->is_moving = FALSE;
9999     player->is_digging = FALSE;
10000     player->is_collecting = FALSE;
10001   }
10002
10003   player->is_dropping = FALSE;
10004
10005   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
10006     return FALSE;
10007
10008   player->is_snapping = TRUE;
10009
10010   if (player->MovPos == 0)
10011   {
10012     player->is_moving = FALSE;
10013     player->is_digging = FALSE;
10014     player->is_collecting = FALSE;
10015   }
10016
10017   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
10018     DrawLevelField(player->last_jx, player->last_jy);
10019
10020   DrawLevelField(x, y);
10021
10022   return TRUE;
10023 }
10024
10025 boolean DropElement(struct PlayerInfo *player)
10026 {
10027   int old_element, new_element;
10028   int dropx = player->jx, dropy = player->jy;
10029   int drop_direction = player->MovDir;
10030   int drop_side = drop_direction;
10031   int drop_element = (player->inventory_size > 0 ?
10032                       player->inventory_element[player->inventory_size - 1] :
10033                       player->inventory_infinite_element != EL_UNDEFINED ?
10034                       player->inventory_infinite_element :
10035                       player->dynabombs_left > 0 ?
10036                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
10037                       EL_UNDEFINED);
10038
10039   /* do not drop an element on top of another element; when holding drop key
10040      pressed without moving, dropped element must move away before the next
10041      element can be dropped (this is especially important if the next element
10042      is dynamite, which can be placed on background for historical reasons) */
10043   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
10044     return MF_ACTION;
10045
10046   if (IS_THROWABLE(drop_element))
10047   {
10048     dropx += GET_DX_FROM_DIR(drop_direction);
10049     dropy += GET_DY_FROM_DIR(drop_direction);
10050
10051     if (!IN_LEV_FIELD(dropx, dropy))
10052       return FALSE;
10053   }
10054
10055   old_element = Feld[dropx][dropy];     /* old element at dropping position */
10056   new_element = drop_element;           /* default: no change when dropping */
10057
10058   /* check if player is active, not moving and ready to drop */
10059   if (!player->active || player->MovPos || player->drop_delay > 0)
10060     return FALSE;
10061
10062   /* check if player has anything that can be dropped */
10063   if (new_element == EL_UNDEFINED)
10064     return FALSE;
10065
10066   /* check if anything can be dropped at the current position */
10067   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
10068     return FALSE;
10069
10070   /* collected custom elements can only be dropped on empty fields */
10071   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
10072     return FALSE;
10073
10074   if (old_element != EL_EMPTY)
10075     Back[dropx][dropy] = old_element;   /* store old element on this field */
10076
10077   ResetGfxAnimation(dropx, dropy);
10078   ResetRandomAnimationValue(dropx, dropy);
10079
10080   if (player->inventory_size > 0 ||
10081       player->inventory_infinite_element != EL_UNDEFINED)
10082   {
10083     if (player->inventory_size > 0)
10084     {
10085       player->inventory_size--;
10086
10087       DrawGameValue_Dynamite(local_player->inventory_size);
10088
10089       if (new_element == EL_DYNAMITE)
10090         new_element = EL_DYNAMITE_ACTIVE;
10091       else if (new_element == EL_SP_DISK_RED)
10092         new_element = EL_SP_DISK_RED_ACTIVE;
10093     }
10094
10095     Feld[dropx][dropy] = new_element;
10096
10097     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10098       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10099                           el2img(Feld[dropx][dropy]), 0);
10100
10101     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10102
10103     /* needed if previous element just changed to "empty" in the last frame */
10104     Changed[dropx][dropy] = FALSE;              /* allow another change */
10105
10106     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
10107                                player->index_bit, drop_side);
10108     CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_DROPS_X,
10109                                         player->index_bit, drop_side);
10110
10111     TestIfElementTouchesCustomElement(dropx, dropy);
10112   }
10113   else          /* player is dropping a dyna bomb */
10114   {
10115     player->dynabombs_left--;
10116
10117     Feld[dropx][dropy] = new_element;
10118
10119     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
10120       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
10121                           el2img(Feld[dropx][dropy]), 0);
10122
10123     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
10124   }
10125
10126   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
10127     InitField_WithBug1(dropx, dropy, FALSE);
10128
10129   new_element = Feld[dropx][dropy];     /* element might have changed */
10130
10131   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
10132       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
10133   {
10134     int move_direction, nextx, nexty;
10135
10136     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
10137       MovDir[dropx][dropy] = drop_direction;
10138
10139     move_direction = MovDir[dropx][dropy];
10140     nextx = dropx + GET_DX_FROM_DIR(move_direction);
10141     nexty = dropy + GET_DY_FROM_DIR(move_direction);
10142
10143     Changed[dropx][dropy] = FALSE;              /* allow another change */
10144     CheckCollision[dropx][dropy] = 2;
10145   }
10146
10147   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
10148   player->is_dropping = TRUE;
10149
10150   player->drop_x = dropx;
10151   player->drop_y = dropy;
10152
10153   return TRUE;
10154 }
10155
10156 /* ------------------------------------------------------------------------- */
10157 /* game sound playing functions                                              */
10158 /* ------------------------------------------------------------------------- */
10159
10160 static int *loop_sound_frame = NULL;
10161 static int *loop_sound_volume = NULL;
10162
10163 void InitPlayLevelSound()
10164 {
10165   int num_sounds = getSoundListSize();
10166
10167   checked_free(loop_sound_frame);
10168   checked_free(loop_sound_volume);
10169
10170   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
10171   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
10172 }
10173
10174 static void PlayLevelSound(int x, int y, int nr)
10175 {
10176   int sx = SCREENX(x), sy = SCREENY(y);
10177   int volume, stereo_position;
10178   int max_distance = 8;
10179   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
10180
10181   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
10182       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
10183     return;
10184
10185   if (!IN_LEV_FIELD(x, y) ||
10186       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
10187       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
10188     return;
10189
10190   volume = SOUND_MAX_VOLUME;
10191
10192   if (!IN_SCR_FIELD(sx, sy))
10193   {
10194     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
10195     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
10196
10197     volume -= volume * (dx > dy ? dx : dy) / max_distance;
10198   }
10199
10200   stereo_position = (SOUND_MAX_LEFT +
10201                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
10202                      (SCR_FIELDX + 2 * max_distance));
10203
10204   if (IS_LOOP_SOUND(nr))
10205   {
10206     /* This assures that quieter loop sounds do not overwrite louder ones,
10207        while restarting sound volume comparison with each new game frame. */
10208
10209     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
10210       return;
10211
10212     loop_sound_volume[nr] = volume;
10213     loop_sound_frame[nr] = FrameCounter;
10214   }
10215
10216   PlaySoundExt(nr, volume, stereo_position, type);
10217 }
10218
10219 static void PlayLevelSoundNearest(int x, int y, int sound_action)
10220 {
10221   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
10222                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
10223                  y < LEVELY(BY1) ? LEVELY(BY1) :
10224                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
10225                  sound_action);
10226 }
10227
10228 static void PlayLevelSoundAction(int x, int y, int action)
10229 {
10230   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
10231 }
10232
10233 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
10234 {
10235   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10236
10237   if (sound_effect != SND_UNDEFINED)
10238     PlayLevelSound(x, y, sound_effect);
10239 }
10240
10241 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
10242                                               int action)
10243 {
10244   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
10245
10246   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10247     PlayLevelSound(x, y, sound_effect);
10248 }
10249
10250 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
10251 {
10252   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10253
10254   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10255     PlayLevelSound(x, y, sound_effect);
10256 }
10257
10258 static void StopLevelSoundActionIfLoop(int x, int y, int action)
10259 {
10260   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
10261
10262   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
10263     StopSound(sound_effect);
10264 }
10265
10266 static void PlayLevelMusic()
10267 {
10268   if (levelset.music[level_nr] != MUS_UNDEFINED)
10269     PlayMusic(levelset.music[level_nr]);        /* from config file */
10270   else
10271     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
10272 }
10273
10274 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
10275 {
10276   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
10277
10278   switch (sample)
10279   {
10280     case SAMPLE_blank:
10281       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
10282       break;
10283
10284     case SAMPLE_roll:
10285       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10286       break;
10287
10288     case SAMPLE_stone:
10289       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10290       break;
10291
10292     case SAMPLE_nut:
10293       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10294       break;
10295
10296     case SAMPLE_crack:
10297       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10298       break;
10299
10300     case SAMPLE_bug:
10301       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10302       break;
10303
10304     case SAMPLE_tank:
10305       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10306       break;
10307
10308     case SAMPLE_android_clone:
10309       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10310       break;
10311
10312     case SAMPLE_android_move:
10313       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10314       break;
10315
10316     case SAMPLE_spring:
10317       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10318       break;
10319
10320     case SAMPLE_slurp:
10321       PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
10322       break;
10323
10324     case SAMPLE_eater:
10325       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
10326       break;
10327
10328     case SAMPLE_eater_eat:
10329       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10330       break;
10331
10332     case SAMPLE_alien:
10333       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
10334       break;
10335
10336     case SAMPLE_collect:
10337       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
10338       break;
10339
10340     case SAMPLE_diamond:
10341       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10342       break;
10343
10344     case SAMPLE_squash:
10345       /* !!! CHECK THIS !!! */
10346 #if 1
10347       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
10348 #else
10349       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
10350 #endif
10351       break;
10352
10353     case SAMPLE_wonderfall:
10354       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
10355       break;
10356
10357     case SAMPLE_drip:
10358       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
10359       break;
10360
10361     case SAMPLE_push:
10362       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
10363       break;
10364
10365     case SAMPLE_dirt:
10366       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10367       break;
10368
10369     case SAMPLE_acid:
10370       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
10371       break;
10372
10373     case SAMPLE_ball:
10374       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10375       break;
10376
10377     case SAMPLE_grow:
10378       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
10379       break;
10380
10381     case SAMPLE_wonder:
10382       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10383       break;
10384
10385     case SAMPLE_door:
10386       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10387       break;
10388
10389     case SAMPLE_exit_open:
10390       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
10391       break;
10392
10393     case SAMPLE_exit_leave:
10394       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
10395       break;
10396
10397     case SAMPLE_dynamite:
10398       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
10399       break;
10400
10401     case SAMPLE_tick:
10402       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10403       break;
10404
10405     case SAMPLE_press:
10406       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
10407       break;
10408
10409     case SAMPLE_wheel:
10410       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
10411       break;
10412
10413     case SAMPLE_boom:
10414       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
10415       break;
10416
10417     case SAMPLE_die:
10418       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
10419       break;
10420
10421     case SAMPLE_time:
10422       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10423       break;
10424
10425     default:
10426       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
10427       break;
10428   }
10429 }
10430
10431 void RaiseScore(int value)
10432 {
10433   local_player->score += value;
10434
10435   DrawGameValue_Score(local_player->score);
10436 }
10437
10438 void RaiseScoreElement(int element)
10439 {
10440   switch(element)
10441   {
10442     case EL_EMERALD:
10443     case EL_BD_DIAMOND:
10444     case EL_EMERALD_YELLOW:
10445     case EL_EMERALD_RED:
10446     case EL_EMERALD_PURPLE:
10447     case EL_SP_INFOTRON:
10448       RaiseScore(level.score[SC_EMERALD]);
10449       break;
10450     case EL_DIAMOND:
10451       RaiseScore(level.score[SC_DIAMOND]);
10452       break;
10453     case EL_CRYSTAL:
10454       RaiseScore(level.score[SC_CRYSTAL]);
10455       break;
10456     case EL_PEARL:
10457       RaiseScore(level.score[SC_PEARL]);
10458       break;
10459     case EL_BUG:
10460     case EL_BD_BUTTERFLY:
10461     case EL_SP_ELECTRON:
10462       RaiseScore(level.score[SC_BUG]);
10463       break;
10464     case EL_SPACESHIP:
10465     case EL_BD_FIREFLY:
10466     case EL_SP_SNIKSNAK:
10467       RaiseScore(level.score[SC_SPACESHIP]);
10468       break;
10469     case EL_YAMYAM:
10470     case EL_DARK_YAMYAM:
10471       RaiseScore(level.score[SC_YAMYAM]);
10472       break;
10473     case EL_ROBOT:
10474       RaiseScore(level.score[SC_ROBOT]);
10475       break;
10476     case EL_PACMAN:
10477       RaiseScore(level.score[SC_PACMAN]);
10478       break;
10479     case EL_NUT:
10480       RaiseScore(level.score[SC_NUT]);
10481       break;
10482     case EL_DYNAMITE:
10483     case EL_SP_DISK_RED:
10484     case EL_DYNABOMB_INCREASE_NUMBER:
10485     case EL_DYNABOMB_INCREASE_SIZE:
10486     case EL_DYNABOMB_INCREASE_POWER:
10487       RaiseScore(level.score[SC_DYNAMITE]);
10488       break;
10489     case EL_SHIELD_NORMAL:
10490     case EL_SHIELD_DEADLY:
10491       RaiseScore(level.score[SC_SHIELD]);
10492       break;
10493     case EL_EXTRA_TIME:
10494       RaiseScore(level.score[SC_TIME_BONUS]);
10495       break;
10496     case EL_KEY_1:
10497     case EL_KEY_2:
10498     case EL_KEY_3:
10499     case EL_KEY_4:
10500     case EL_EM_KEY_1:
10501     case EL_EM_KEY_2:
10502     case EL_EM_KEY_3:
10503     case EL_EM_KEY_4:
10504     case EL_EMC_KEY_5:
10505     case EL_EMC_KEY_6:
10506     case EL_EMC_KEY_7:
10507     case EL_EMC_KEY_8:
10508       RaiseScore(level.score[SC_KEY]);
10509       break;
10510     default:
10511       RaiseScore(element_info[element].collect_score);
10512       break;
10513   }
10514 }
10515
10516 void RequestQuitGame(boolean ask_if_really_quit)
10517 {
10518   if (AllPlayersGone ||
10519       !ask_if_really_quit ||
10520       level_editor_test_game ||
10521       Request("Do you really want to quit the game ?",
10522               REQ_ASK | REQ_STAY_CLOSED))
10523   {
10524 #if defined(NETWORK_AVALIABLE)
10525     if (options.network)
10526       SendToServer_StopPlaying();
10527     else
10528 #endif
10529     {
10530       game_status = GAME_MODE_MAIN;
10531       DrawMainMenu();
10532     }
10533   }
10534   else
10535   {
10536     if (tape.playing && tape.deactivate_display)
10537       TapeDeactivateDisplayOff(TRUE);
10538
10539     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
10540
10541     if (tape.playing && tape.deactivate_display)
10542       TapeDeactivateDisplayOn();
10543   }
10544 }
10545
10546
10547 /* ---------- new game button stuff ---------------------------------------- */
10548
10549 /* graphic position values for game buttons */
10550 #define GAME_BUTTON_XSIZE       30
10551 #define GAME_BUTTON_YSIZE       30
10552 #define GAME_BUTTON_XPOS        5
10553 #define GAME_BUTTON_YPOS        215
10554 #define SOUND_BUTTON_XPOS       5
10555 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
10556
10557 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10558 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10559 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10560 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10561 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10562 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10563
10564 static struct
10565 {
10566   int x, y;
10567   int gadget_id;
10568   char *infotext;
10569 } gamebutton_info[NUM_GAME_BUTTONS] =
10570 {
10571   {
10572     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
10573     GAME_CTRL_ID_STOP,
10574     "stop game"
10575   },
10576   {
10577     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
10578     GAME_CTRL_ID_PAUSE,
10579     "pause game"
10580   },
10581   {
10582     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
10583     GAME_CTRL_ID_PLAY,
10584     "play game"
10585   },
10586   {
10587     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
10588     SOUND_CTRL_ID_MUSIC,
10589     "background music on/off"
10590   },
10591   {
10592     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
10593     SOUND_CTRL_ID_LOOPS,
10594     "sound loops on/off"
10595   },
10596   {
10597     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
10598     SOUND_CTRL_ID_SIMPLE,
10599     "normal sounds on/off"
10600   }
10601 };
10602
10603 void CreateGameButtons()
10604 {
10605   int i;
10606
10607   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10608   {
10609     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10610     struct GadgetInfo *gi;
10611     int button_type;
10612     boolean checked;
10613     unsigned long event_mask;
10614     int gd_xoffset, gd_yoffset;
10615     int gd_x1, gd_x2, gd_y1, gd_y2;
10616     int id = i;
10617
10618     gd_xoffset = gamebutton_info[i].x;
10619     gd_yoffset = gamebutton_info[i].y;
10620     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10621     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10622
10623     if (id == GAME_CTRL_ID_STOP ||
10624         id == GAME_CTRL_ID_PAUSE ||
10625         id == GAME_CTRL_ID_PLAY)
10626     {
10627       button_type = GD_TYPE_NORMAL_BUTTON;
10628       checked = FALSE;
10629       event_mask = GD_EVENT_RELEASED;
10630       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10631       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10632     }
10633     else
10634     {
10635       button_type = GD_TYPE_CHECK_BUTTON;
10636       checked =
10637         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10638          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10639          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10640       event_mask = GD_EVENT_PRESSED;
10641       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
10642       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10643     }
10644
10645     gi = CreateGadget(GDI_CUSTOM_ID, id,
10646                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
10647                       GDI_X, DX + gd_xoffset,
10648                       GDI_Y, DY + gd_yoffset,
10649                       GDI_WIDTH, GAME_BUTTON_XSIZE,
10650                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
10651                       GDI_TYPE, button_type,
10652                       GDI_STATE, GD_BUTTON_UNPRESSED,
10653                       GDI_CHECKED, checked,
10654                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10655                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10656                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10657                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10658                       GDI_EVENT_MASK, event_mask,
10659                       GDI_CALLBACK_ACTION, HandleGameButtons,
10660                       GDI_END);
10661
10662     if (gi == NULL)
10663       Error(ERR_EXIT, "cannot create gadget");
10664
10665     game_gadget[id] = gi;
10666   }
10667 }
10668
10669 void FreeGameButtons()
10670 {
10671   int i;
10672
10673   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10674     FreeGadget(game_gadget[i]);
10675 }
10676
10677 static void MapGameButtons()
10678 {
10679   int i;
10680
10681   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10682     MapGadget(game_gadget[i]);
10683 }
10684
10685 void UnmapGameButtons()
10686 {
10687   int i;
10688
10689   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10690     UnmapGadget(game_gadget[i]);
10691 }
10692
10693 static void HandleGameButtons(struct GadgetInfo *gi)
10694 {
10695   int id = gi->custom_id;
10696
10697   if (game_status != GAME_MODE_PLAYING)
10698     return;
10699
10700   switch (id)
10701   {
10702     case GAME_CTRL_ID_STOP:
10703       RequestQuitGame(TRUE);
10704       break;
10705
10706     case GAME_CTRL_ID_PAUSE:
10707       if (options.network)
10708       {
10709 #if defined(NETWORK_AVALIABLE)
10710         if (tape.pausing)
10711           SendToServer_ContinuePlaying();
10712         else
10713           SendToServer_PausePlaying();
10714 #endif
10715       }
10716       else
10717         TapeTogglePause(TAPE_TOGGLE_MANUAL);
10718       break;
10719
10720     case GAME_CTRL_ID_PLAY:
10721       if (tape.pausing)
10722       {
10723 #if defined(NETWORK_AVALIABLE)
10724         if (options.network)
10725           SendToServer_ContinuePlaying();
10726         else
10727 #endif
10728         {
10729           tape.pausing = FALSE;
10730           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10731         }
10732       }
10733       break;
10734
10735     case SOUND_CTRL_ID_MUSIC:
10736       if (setup.sound_music)
10737       { 
10738         setup.sound_music = FALSE;
10739         FadeMusic();
10740       }
10741       else if (audio.music_available)
10742       { 
10743         setup.sound = setup.sound_music = TRUE;
10744
10745         SetAudioMode(setup.sound);
10746
10747         PlayLevelMusic();
10748       }
10749       break;
10750
10751     case SOUND_CTRL_ID_LOOPS:
10752       if (setup.sound_loops)
10753         setup.sound_loops = FALSE;
10754       else if (audio.loops_available)
10755       {
10756         setup.sound = setup.sound_loops = TRUE;
10757         SetAudioMode(setup.sound);
10758       }
10759       break;
10760
10761     case SOUND_CTRL_ID_SIMPLE:
10762       if (setup.sound_simple)
10763         setup.sound_simple = FALSE;
10764       else if (audio.sound_available)
10765       {
10766         setup.sound = setup.sound_simple = TRUE;
10767         SetAudioMode(setup.sound);
10768       }
10769       break;
10770
10771     default:
10772       break;
10773   }
10774 }