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