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