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