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