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