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