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