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