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