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