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