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