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