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