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