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