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