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