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