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