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