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