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