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