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