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