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