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