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