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