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