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