rnd-20040423-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 1
3507       if (element != EL_EMPTY && element != EL_EXPLOSION &&
3508           !IS_DIGGABLE(element) && !dynabomb_xl)
3509         break;
3510 #else
3511       if (element != EL_EMPTY && element != EL_EXPLOSION &&
3512           !CAN_GROW_INTO(element) && !dynabomb_xl)
3513         break;
3514 #endif
3515 #else
3516       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3517       if (element != EL_EMPTY && element != EL_EXPLOSION &&
3518           element != EL_SAND && !dynabomb_xl)
3519         break;
3520 #endif
3521     }
3522   }
3523 }
3524
3525 void Bang(int x, int y)
3526 {
3527 #if 1
3528   int element = MovingOrBlocked2Element(x, y);
3529 #else
3530   int element = Feld[x][y];
3531 #endif
3532
3533 #if 1
3534   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3535 #else
3536   if (IS_PLAYER(x, y))
3537 #endif
3538   {
3539     struct PlayerInfo *player = PLAYERINFO(x, y);
3540
3541     element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3542                             player->element_nr);
3543   }
3544
3545 #if 0
3546 #if 1
3547   PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3548 #else
3549   if (game.emulation == EMU_SUPAPLEX)
3550     PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3551   else
3552     PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3553 #endif
3554 #endif
3555
3556 #if 0
3557   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
3558     element = EL_EMPTY;
3559 #endif
3560
3561   switch(element)
3562   {
3563     case EL_BUG:
3564     case EL_SPACESHIP:
3565     case EL_BD_BUTTERFLY:
3566     case EL_BD_FIREFLY:
3567     case EL_YAMYAM:
3568     case EL_DARK_YAMYAM:
3569     case EL_ROBOT:
3570     case EL_PACMAN:
3571     case EL_MOLE:
3572       RaiseScoreElement(element);
3573       Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3574       break;
3575     case EL_DYNABOMB_PLAYER_1_ACTIVE:
3576     case EL_DYNABOMB_PLAYER_2_ACTIVE:
3577     case EL_DYNABOMB_PLAYER_3_ACTIVE:
3578     case EL_DYNABOMB_PLAYER_4_ACTIVE:
3579     case EL_DYNABOMB_INCREASE_NUMBER:
3580     case EL_DYNABOMB_INCREASE_SIZE:
3581     case EL_DYNABOMB_INCREASE_POWER:
3582       DynaExplode(x, y);
3583       break;
3584     case EL_PENGUIN:
3585     case EL_LAMP:
3586     case EL_LAMP_ACTIVE:
3587 #if 1
3588     case EL_AMOEBA_TO_DIAMOND:
3589 #endif
3590       if (IS_PLAYER(x, y))
3591         Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3592       else
3593         Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3594       break;
3595     default:
3596       if (CAN_EXPLODE_CROSS(element))
3597 #if 1
3598         Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3599 #else
3600         DynaExplode(x, y);
3601 #endif
3602       else if (CAN_EXPLODE_1X1(element))
3603         Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3604       else
3605         Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3606       break;
3607   }
3608
3609   CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3610 }
3611
3612 void SplashAcid(int x, int y)
3613 {
3614 #if 1
3615   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3616       (!IN_LEV_FIELD(x - 1, y - 2) ||
3617        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3618     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3619
3620   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3621       (!IN_LEV_FIELD(x + 1, y - 2) ||
3622        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3623     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3624
3625   PlayLevelSound(x, y, SND_ACID_SPLASHING);
3626 #else
3627   /* input: position of element entering acid (obsolete) */
3628
3629   int element = Feld[x][y];
3630
3631   if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3632     return;
3633
3634   if (element != EL_ACID_SPLASH_LEFT &&
3635       element != EL_ACID_SPLASH_RIGHT)
3636   {
3637     PlayLevelSound(x, y, SND_ACID_SPLASHING);
3638
3639     if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3640         (!IN_LEV_FIELD(x - 1, y - 1) ||
3641          !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3642       Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3643
3644     if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3645         (!IN_LEV_FIELD(x + 1, y - 1) ||
3646          !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3647       Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3648   }
3649 #endif
3650 }
3651
3652 static void InitBeltMovement()
3653 {
3654   static int belt_base_element[4] =
3655   {
3656     EL_CONVEYOR_BELT_1_LEFT,
3657     EL_CONVEYOR_BELT_2_LEFT,
3658     EL_CONVEYOR_BELT_3_LEFT,
3659     EL_CONVEYOR_BELT_4_LEFT
3660   };
3661   static int belt_base_active_element[4] =
3662   {
3663     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3664     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3665     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3666     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3667   };
3668
3669   int x, y, i, j;
3670
3671   /* set frame order for belt animation graphic according to belt direction */
3672   for (i = 0; i < NUM_BELTS; i++)
3673   {
3674     int belt_nr = i;
3675
3676     for (j = 0; j < NUM_BELT_PARTS; j++)
3677     {
3678       int element = belt_base_active_element[belt_nr] + j;
3679       int graphic = el2img(element);
3680
3681       if (game.belt_dir[i] == MV_LEFT)
3682         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3683       else
3684         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3685     }
3686   }
3687
3688   for (y = 0; y < lev_fieldy; y++)
3689   {
3690     for (x = 0; x < lev_fieldx; x++)
3691     {
3692       int element = Feld[x][y];
3693
3694       for (i = 0; i < NUM_BELTS; i++)
3695       {
3696         if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3697         {
3698           int e_belt_nr = getBeltNrFromBeltElement(element);
3699           int belt_nr = i;
3700
3701           if (e_belt_nr == belt_nr)
3702           {
3703             int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3704
3705             Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3706           }
3707         }
3708       }
3709     }
3710   }
3711 }
3712
3713 static void ToggleBeltSwitch(int x, int y)
3714 {
3715   static int belt_base_element[4] =
3716   {
3717     EL_CONVEYOR_BELT_1_LEFT,
3718     EL_CONVEYOR_BELT_2_LEFT,
3719     EL_CONVEYOR_BELT_3_LEFT,
3720     EL_CONVEYOR_BELT_4_LEFT
3721   };
3722   static int belt_base_active_element[4] =
3723   {
3724     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3725     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3726     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3727     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3728   };
3729   static int belt_base_switch_element[4] =
3730   {
3731     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3732     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3733     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3734     EL_CONVEYOR_BELT_4_SWITCH_LEFT
3735   };
3736   static int belt_move_dir[4] =
3737   {
3738     MV_LEFT,
3739     MV_NO_MOVING,
3740     MV_RIGHT,
3741     MV_NO_MOVING,
3742   };
3743
3744   int element = Feld[x][y];
3745   int belt_nr = getBeltNrFromBeltSwitchElement(element);
3746   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3747   int belt_dir = belt_move_dir[belt_dir_nr];
3748   int xx, yy, i;
3749
3750   if (!IS_BELT_SWITCH(element))
3751     return;
3752
3753   game.belt_dir_nr[belt_nr] = belt_dir_nr;
3754   game.belt_dir[belt_nr] = belt_dir;
3755
3756   if (belt_dir_nr == 3)
3757     belt_dir_nr = 1;
3758
3759   /* set frame order for belt animation graphic according to belt direction */
3760   for (i = 0; i < NUM_BELT_PARTS; i++)
3761   {
3762     int element = belt_base_active_element[belt_nr] + i;
3763     int graphic = el2img(element);
3764
3765     if (belt_dir == MV_LEFT)
3766       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3767     else
3768       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3769   }
3770
3771   for (yy = 0; yy < lev_fieldy; yy++)
3772   {
3773     for (xx = 0; xx < lev_fieldx; xx++)
3774     {
3775       int element = Feld[xx][yy];
3776
3777       if (IS_BELT_SWITCH(element))
3778       {
3779         int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3780
3781         if (e_belt_nr == belt_nr)
3782         {
3783           Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3784           DrawLevelField(xx, yy);
3785         }
3786       }
3787       else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3788       {
3789         int e_belt_nr = getBeltNrFromBeltElement(element);
3790
3791         if (e_belt_nr == belt_nr)
3792         {
3793           int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3794
3795           Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3796           DrawLevelField(xx, yy);
3797         }
3798       }
3799       else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3800       {
3801         int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3802
3803         if (e_belt_nr == belt_nr)
3804         {
3805           int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3806
3807           Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3808           DrawLevelField(xx, yy);
3809         }
3810       }
3811     }
3812   }
3813 }
3814
3815 static void ToggleSwitchgateSwitch(int x, int y)
3816 {
3817   int xx, yy;
3818
3819   game.switchgate_pos = !game.switchgate_pos;
3820
3821   for (yy = 0; yy < lev_fieldy; yy++)
3822   {
3823     for (xx = 0; xx < lev_fieldx; xx++)
3824     {
3825       int element = Feld[xx][yy];
3826
3827       if (element == EL_SWITCHGATE_SWITCH_UP ||
3828           element == EL_SWITCHGATE_SWITCH_DOWN)
3829       {
3830         Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3831         DrawLevelField(xx, yy);
3832       }
3833       else if (element == EL_SWITCHGATE_OPEN ||
3834                element == EL_SWITCHGATE_OPENING)
3835       {
3836         Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3837 #if 1
3838         PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3839 #else
3840         PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3841 #endif
3842       }
3843       else if (element == EL_SWITCHGATE_CLOSED ||
3844                element == EL_SWITCHGATE_CLOSING)
3845       {
3846         Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3847 #if 1
3848         PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3849 #else
3850         PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3851 #endif
3852       }
3853     }
3854   }
3855 }
3856
3857 static int getInvisibleActiveFromInvisibleElement(int element)
3858 {
3859   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3860           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
3861           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
3862           element);
3863 }
3864
3865 static int getInvisibleFromInvisibleActiveElement(int element)
3866 {
3867   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3868           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
3869           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
3870           element);
3871 }
3872
3873 static void RedrawAllLightSwitchesAndInvisibleElements()
3874 {
3875   int x, y;
3876
3877   for (y = 0; y < lev_fieldy; y++)
3878   {
3879     for (x = 0; x < lev_fieldx; x++)
3880     {
3881       int element = Feld[x][y];
3882
3883       if (element == EL_LIGHT_SWITCH &&
3884           game.light_time_left > 0)
3885       {
3886         Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3887         DrawLevelField(x, y);
3888       }
3889       else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3890                game.light_time_left == 0)
3891       {
3892         Feld[x][y] = EL_LIGHT_SWITCH;
3893         DrawLevelField(x, y);
3894       }
3895       else if (element == EL_INVISIBLE_STEELWALL ||
3896                element == EL_INVISIBLE_WALL ||
3897                element == EL_INVISIBLE_SAND)
3898       {
3899         if (game.light_time_left > 0)
3900           Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3901
3902         DrawLevelField(x, y);
3903       }
3904       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3905                element == EL_INVISIBLE_WALL_ACTIVE ||
3906                element == EL_INVISIBLE_SAND_ACTIVE)
3907       {
3908         if (game.light_time_left == 0)
3909           Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3910
3911         DrawLevelField(x, y);
3912       }
3913     }
3914   }
3915 }
3916
3917 static void ToggleLightSwitch(int x, int y)
3918 {
3919   int element = Feld[x][y];
3920
3921   game.light_time_left =
3922     (element == EL_LIGHT_SWITCH ?
3923      level.time_light * FRAMES_PER_SECOND : 0);
3924
3925   RedrawAllLightSwitchesAndInvisibleElements();
3926 }
3927
3928 static void ActivateTimegateSwitch(int x, int y)
3929 {
3930   int xx, yy;
3931
3932   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3933
3934   for (yy = 0; yy < lev_fieldy; yy++)
3935   {
3936     for (xx = 0; xx < lev_fieldx; xx++)
3937     {
3938       int element = Feld[xx][yy];
3939
3940       if (element == EL_TIMEGATE_CLOSED ||
3941           element == EL_TIMEGATE_CLOSING)
3942       {
3943         Feld[xx][yy] = EL_TIMEGATE_OPENING;
3944         PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3945       }
3946
3947       /*
3948       else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3949       {
3950         Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3951         DrawLevelField(xx, yy);
3952       }
3953       */
3954
3955     }
3956   }
3957
3958   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3959 }
3960
3961 inline static int getElementMoveStepsize(int x, int y)
3962 {
3963   int element = Feld[x][y];
3964   int direction = MovDir[x][y];
3965   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3966   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3967   int horiz_move = (dx != 0);
3968   int sign = (horiz_move ? dx : dy);
3969   int step = sign * element_info[element].move_stepsize;
3970
3971   /* special values for move stepsize for spring and things on conveyor belt */
3972   if (horiz_move)
3973   {
3974 #if 0
3975     if (element == EL_SPRING)
3976       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3977     else if (CAN_FALL(element) && !CAN_MOVE(element) &&
3978              y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3979       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3980 #else
3981     if (CAN_FALL(element) &&
3982         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3983       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3984     else if (element == EL_SPRING)
3985       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3986 #endif
3987   }
3988
3989   return step;
3990 }
3991
3992 void Impact(int x, int y)
3993 {
3994   boolean lastline = (y == lev_fieldy-1);
3995   boolean object_hit = FALSE;
3996   boolean impact = (lastline || object_hit);
3997   int element = Feld[x][y];
3998   int smashed = EL_STEELWALL;
3999
4000   if (!lastline)        /* check if element below was hit */
4001   {
4002     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4003       return;
4004
4005     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4006                                          MovDir[x][y + 1] != MV_DOWN ||
4007                                          MovPos[x][y + 1] <= TILEY / 2));
4008
4009 #if 0
4010     object_hit = !IS_FREE(x, y + 1);
4011 #endif
4012
4013     /* do not smash moving elements that left the smashed field in time */
4014     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4015         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4016       object_hit = FALSE;
4017
4018     if (object_hit)
4019       smashed = MovingOrBlocked2Element(x, y + 1);
4020
4021     impact = (lastline || object_hit);
4022   }
4023
4024   if (!lastline && smashed == EL_ACID)  /* element falls into acid */
4025   {
4026     SplashAcid(x, y + 1);
4027     return;
4028   }
4029
4030   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4031   /* only reset graphic animation if graphic really changes after impact */
4032   if (impact &&
4033       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4034   {
4035     ResetGfxAnimation(x, y);
4036     DrawLevelField(x, y);
4037   }
4038
4039   if (impact && CAN_EXPLODE_IMPACT(element))
4040   {
4041     Bang(x, y);
4042     return;
4043   }
4044   else if (impact && element == EL_PEARL)
4045   {
4046     ResetGfxAnimation(x, y);
4047
4048     Feld[x][y] = EL_PEARL_BREAKING;
4049     PlayLevelSound(x, y, SND_PEARL_BREAKING);
4050     return;
4051   }
4052   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4053   {
4054     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4055
4056     return;
4057   }
4058
4059   if (impact && element == EL_AMOEBA_DROP)
4060   {
4061     if (object_hit && IS_PLAYER(x, y + 1))
4062       KillHeroUnlessEnemyProtected(x, y + 1);
4063     else if (object_hit && smashed == EL_PENGUIN)
4064       Bang(x, y + 1);
4065     else
4066     {
4067       Feld[x][y] = EL_AMOEBA_GROWING;
4068       Store[x][y] = EL_AMOEBA_WET;
4069
4070       ResetRandomAnimationValue(x, y);
4071     }
4072     return;
4073   }
4074
4075   if (object_hit)               /* check which object was hit */
4076   {
4077     if (CAN_PASS_MAGIC_WALL(element) && 
4078         (smashed == EL_MAGIC_WALL ||
4079          smashed == EL_BD_MAGIC_WALL))
4080     {
4081       int xx, yy;
4082       int activated_magic_wall =
4083         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4084          EL_BD_MAGIC_WALL_ACTIVE);
4085
4086       /* activate magic wall / mill */
4087       for (yy = 0; yy < lev_fieldy; yy++)
4088         for (xx = 0; xx < lev_fieldx; xx++)
4089           if (Feld[xx][yy] == smashed)
4090             Feld[xx][yy] = activated_magic_wall;
4091
4092       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4093       game.magic_wall_active = TRUE;
4094
4095       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4096                             SND_MAGIC_WALL_ACTIVATING :
4097                             SND_BD_MAGIC_WALL_ACTIVATING));
4098     }
4099
4100     if (IS_PLAYER(x, y + 1))
4101     {
4102       if (CAN_SMASH_PLAYER(element))
4103       {
4104         KillHeroUnlessEnemyProtected(x, y + 1);
4105         return;
4106       }
4107     }
4108     else if (smashed == EL_PENGUIN)
4109     {
4110       if (CAN_SMASH_PLAYER(element))
4111       {
4112         Bang(x, y + 1);
4113         return;
4114       }
4115     }
4116     else if (element == EL_BD_DIAMOND)
4117     {
4118       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4119       {
4120         Bang(x, y + 1);
4121         return;
4122       }
4123     }
4124     else if (((element == EL_SP_INFOTRON ||
4125                element == EL_SP_ZONK) &&
4126               (smashed == EL_SP_SNIKSNAK ||
4127                smashed == EL_SP_ELECTRON ||
4128                smashed == EL_SP_DISK_ORANGE)) ||
4129              (element == EL_SP_INFOTRON &&
4130               smashed == EL_SP_DISK_YELLOW))
4131     {
4132       Bang(x, y + 1);
4133       return;
4134     }
4135 #if 0
4136     else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4137     {
4138       Bang(x, y + 1);
4139       return;
4140     }
4141 #endif
4142     else if (CAN_SMASH_EVERYTHING(element))
4143     {
4144       if (IS_CLASSIC_ENEMY(smashed) ||
4145           CAN_EXPLODE_SMASHED(smashed))
4146       {
4147         Bang(x, y + 1);
4148         return;
4149       }
4150       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4151       {
4152         if (smashed == EL_LAMP ||
4153             smashed == EL_LAMP_ACTIVE)
4154         {
4155           Bang(x, y + 1);
4156           return;
4157         }
4158         else if (smashed == EL_NUT)
4159         {
4160           Feld[x][y + 1] = EL_NUT_BREAKING;
4161           PlayLevelSound(x, y, SND_NUT_BREAKING);
4162           RaiseScoreElement(EL_NUT);
4163           return;
4164         }
4165         else if (smashed == EL_PEARL)
4166         {
4167           ResetGfxAnimation(x, y);
4168
4169           Feld[x][y + 1] = EL_PEARL_BREAKING;
4170           PlayLevelSound(x, y, SND_PEARL_BREAKING);
4171           return;
4172         }
4173         else if (smashed == EL_DIAMOND)
4174         {
4175           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4176           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4177           return;
4178         }
4179         else if (IS_BELT_SWITCH(smashed))
4180         {
4181           ToggleBeltSwitch(x, y + 1);
4182         }
4183         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4184                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
4185         {
4186           ToggleSwitchgateSwitch(x, y + 1);
4187         }
4188         else if (smashed == EL_LIGHT_SWITCH ||
4189                  smashed == EL_LIGHT_SWITCH_ACTIVE)
4190         {
4191           ToggleLightSwitch(x, y + 1);
4192         }
4193         else
4194         {
4195 #if 0
4196           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4197 #endif
4198
4199           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4200
4201 #if 1
4202           /* !!! TEST ONLY !!! */
4203           CheckElementChangeBySide(x, y + 1, smashed, element,
4204                                    CE_SWITCHED, CH_SIDE_TOP);
4205           CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4206                                             CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4207 #else
4208           CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4209                                             CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4210           CheckElementChangeBySide(x, y + 1, smashed, element,
4211                                    CE_SWITCHED, CH_SIDE_TOP);
4212 #endif
4213         }
4214       }
4215       else
4216       {
4217         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4218       }
4219     }
4220   }
4221
4222   /* play sound of magic wall / mill */
4223   if (!lastline &&
4224       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4225        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4226   {
4227     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4228       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4229     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4230       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4231
4232     return;
4233   }
4234
4235   /* play sound of object that hits the ground */
4236   if (lastline || object_hit)
4237     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4238 }
4239
4240 inline static void TurnRoundExt(int x, int y)
4241 {
4242   static struct
4243   {
4244     int x, y;
4245   } move_xy[] =
4246   {
4247     {  0,  0 },
4248     { -1,  0 },
4249     { +1,  0 },
4250     {  0,  0 },
4251     {  0, -1 },
4252     {  0,  0 }, { 0, 0 }, { 0, 0 },
4253     {  0, +1 }
4254   };
4255   static struct
4256   {
4257     int left, right, back;
4258   } turn[] =
4259   {
4260     { 0,        0,              0        },
4261     { MV_DOWN,  MV_UP,          MV_RIGHT },
4262     { MV_UP,    MV_DOWN,        MV_LEFT  },
4263     { 0,        0,              0        },
4264     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
4265     { 0,        0,              0        },
4266     { 0,        0,              0        },
4267     { 0,        0,              0        },
4268     { MV_RIGHT, MV_LEFT,        MV_UP    }
4269   };
4270
4271   int element = Feld[x][y];
4272   int move_pattern = element_info[element].move_pattern;
4273
4274   int old_move_dir = MovDir[x][y];
4275   int left_dir  = turn[old_move_dir].left;
4276   int right_dir = turn[old_move_dir].right;
4277   int back_dir  = turn[old_move_dir].back;
4278
4279   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
4280   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
4281   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
4282   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
4283
4284   int left_x  = x + left_dx,  left_y  = y + left_dy;
4285   int right_x = x + right_dx, right_y = y + right_dy;
4286   int move_x  = x + move_dx,  move_y  = y + move_dy;
4287
4288   int xx, yy;
4289
4290   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4291   {
4292     TestIfBadThingTouchesOtherBadThing(x, y);
4293
4294     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4295       MovDir[x][y] = right_dir;
4296     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4297       MovDir[x][y] = left_dir;
4298
4299     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4300       MovDelay[x][y] = 9;
4301     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
4302       MovDelay[x][y] = 1;
4303   }
4304 #if 0
4305   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4306            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4307   {
4308     TestIfBadThingTouchesOtherBadThing(x, y);
4309
4310     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4311       MovDir[x][y] = left_dir;
4312     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4313       MovDir[x][y] = right_dir;
4314
4315     if ((element == EL_SPACESHIP ||
4316          element == EL_SP_SNIKSNAK ||
4317          element == EL_SP_ELECTRON)
4318         && MovDir[x][y] != old_move_dir)
4319       MovDelay[x][y] = 9;
4320     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
4321       MovDelay[x][y] = 1;
4322   }
4323 #else
4324   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4325   {
4326     TestIfBadThingTouchesOtherBadThing(x, y);
4327
4328     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4329       MovDir[x][y] = left_dir;
4330     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4331       MovDir[x][y] = right_dir;
4332
4333     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4334       MovDelay[x][y] = 9;
4335     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
4336       MovDelay[x][y] = 1;
4337   }
4338   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4339   {
4340     TestIfBadThingTouchesOtherBadThing(x, y);
4341
4342     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4343       MovDir[x][y] = left_dir;
4344     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4345       MovDir[x][y] = right_dir;
4346
4347     if (MovDir[x][y] != old_move_dir)
4348       MovDelay[x][y] = 9;
4349   }
4350 #endif
4351   else if (element == EL_YAMYAM)
4352   {
4353     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4354     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4355
4356     if (can_turn_left && can_turn_right)
4357       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4358     else if (can_turn_left)
4359       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4360     else if (can_turn_right)
4361       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4362     else
4363       MovDir[x][y] = back_dir;
4364
4365     MovDelay[x][y] = 16 + 16 * RND(3);
4366   }
4367   else if (element == EL_DARK_YAMYAM)
4368   {
4369     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4370                                                          left_x, left_y);
4371     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4372                                                          right_x, right_y);
4373
4374     if (can_turn_left && can_turn_right)
4375       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4376     else if (can_turn_left)
4377       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4378     else if (can_turn_right)
4379       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4380     else
4381       MovDir[x][y] = back_dir;
4382
4383     MovDelay[x][y] = 16 + 16 * RND(3);
4384   }
4385   else if (element == EL_PACMAN)
4386   {
4387     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4388     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4389
4390     if (can_turn_left && can_turn_right)
4391       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4392     else if (can_turn_left)
4393       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4394     else if (can_turn_right)
4395       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4396     else
4397       MovDir[x][y] = back_dir;
4398
4399     MovDelay[x][y] = 6 + RND(40);
4400   }
4401   else if (element == EL_PIG)
4402   {
4403     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4404     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4405     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4406     boolean should_turn_left, should_turn_right, should_move_on;
4407     int rnd_value = 24;
4408     int rnd = RND(rnd_value);
4409
4410     should_turn_left = (can_turn_left &&
4411                         (!can_move_on ||
4412                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4413                                                    y + back_dy + left_dy)));
4414     should_turn_right = (can_turn_right &&
4415                          (!can_move_on ||
4416                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4417                                                     y + back_dy + right_dy)));
4418     should_move_on = (can_move_on &&
4419                       (!can_turn_left ||
4420                        !can_turn_right ||
4421                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4422                                                  y + move_dy + left_dy) ||
4423                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4424                                                  y + move_dy + right_dy)));
4425
4426     if (should_turn_left || should_turn_right || should_move_on)
4427     {
4428       if (should_turn_left && should_turn_right && should_move_on)
4429         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
4430                         rnd < 2 * rnd_value / 3 ? right_dir :
4431                         old_move_dir);
4432       else if (should_turn_left && should_turn_right)
4433         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4434       else if (should_turn_left && should_move_on)
4435         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4436       else if (should_turn_right && should_move_on)
4437         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4438       else if (should_turn_left)
4439         MovDir[x][y] = left_dir;
4440       else if (should_turn_right)
4441         MovDir[x][y] = right_dir;
4442       else if (should_move_on)
4443         MovDir[x][y] = old_move_dir;
4444     }
4445     else if (can_move_on && rnd > rnd_value / 8)
4446       MovDir[x][y] = old_move_dir;
4447     else if (can_turn_left && can_turn_right)
4448       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4449     else if (can_turn_left && rnd > rnd_value / 8)
4450       MovDir[x][y] = left_dir;
4451     else if (can_turn_right && rnd > rnd_value/8)
4452       MovDir[x][y] = right_dir;
4453     else
4454       MovDir[x][y] = back_dir;
4455
4456     xx = x + move_xy[MovDir[x][y]].x;
4457     yy = y + move_xy[MovDir[x][y]].y;
4458
4459     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4460       MovDir[x][y] = old_move_dir;
4461
4462     MovDelay[x][y] = 0;
4463   }
4464   else if (element == EL_DRAGON)
4465   {
4466     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4467     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4468     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4469     int rnd_value = 24;
4470     int rnd = RND(rnd_value);
4471
4472 #if 0
4473     if (FrameCounter < 1 && x == 0 && y == 29)
4474       printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4475 #endif
4476
4477     if (can_move_on && rnd > rnd_value / 8)
4478       MovDir[x][y] = old_move_dir;
4479     else if (can_turn_left && can_turn_right)
4480       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4481     else if (can_turn_left && rnd > rnd_value / 8)
4482       MovDir[x][y] = left_dir;
4483     else if (can_turn_right && rnd > rnd_value / 8)
4484       MovDir[x][y] = right_dir;
4485     else
4486       MovDir[x][y] = back_dir;
4487
4488     xx = x + move_xy[MovDir[x][y]].x;
4489     yy = y + move_xy[MovDir[x][y]].y;
4490
4491 #if 0
4492     if (FrameCounter < 1 && x == 0 && y == 29)
4493       printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4494              xx, yy, Feld[xx][yy],
4495              FrameCounter);
4496 #endif
4497
4498 #if 1
4499     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4500       MovDir[x][y] = old_move_dir;
4501 #else
4502     if (!IS_FREE(xx, yy))
4503       MovDir[x][y] = old_move_dir;
4504 #endif
4505
4506 #if 0
4507     if (FrameCounter < 1 && x == 0 && y == 29)
4508       printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4509 #endif
4510
4511     MovDelay[x][y] = 0;
4512   }
4513   else if (element == EL_MOLE)
4514   {
4515     boolean can_move_on =
4516       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4517                             IS_AMOEBOID(Feld[move_x][move_y]) ||
4518                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4519     if (!can_move_on)
4520     {
4521       boolean can_turn_left =
4522         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4523                               IS_AMOEBOID(Feld[left_x][left_y])));
4524
4525       boolean can_turn_right =
4526         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4527                               IS_AMOEBOID(Feld[right_x][right_y])));
4528
4529       if (can_turn_left && can_turn_right)
4530         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4531       else if (can_turn_left)
4532         MovDir[x][y] = left_dir;
4533       else
4534         MovDir[x][y] = right_dir;
4535     }
4536
4537     if (MovDir[x][y] != old_move_dir)
4538       MovDelay[x][y] = 9;
4539   }
4540   else if (element == EL_BALLOON)
4541   {
4542     MovDir[x][y] = game.balloon_dir;
4543     MovDelay[x][y] = 0;
4544   }
4545   else if (element == EL_SPRING)
4546   {
4547 #if 0
4548     if (MovDir[x][y] & MV_HORIZONTAL &&
4549         !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4550       MovDir[x][y] = MV_NO_MOVING;
4551 #else
4552     if (MovDir[x][y] & MV_HORIZONTAL &&
4553         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4554          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4555       MovDir[x][y] = MV_NO_MOVING;
4556 #endif
4557
4558     MovDelay[x][y] = 0;
4559   }
4560   else if (element == EL_ROBOT ||
4561            element == EL_SATELLITE ||
4562            element == EL_PENGUIN)
4563   {
4564     int attr_x = -1, attr_y = -1;
4565
4566     if (AllPlayersGone)
4567     {
4568       attr_x = ExitX;
4569       attr_y = ExitY;
4570     }
4571     else
4572     {
4573       int i;
4574
4575       for (i = 0; i < MAX_PLAYERS; i++)
4576       {
4577         struct PlayerInfo *player = &stored_player[i];
4578         int jx = player->jx, jy = player->jy;
4579
4580         if (!player->active)
4581           continue;
4582
4583         if (attr_x == -1 ||
4584             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4585         {
4586           attr_x = jx;
4587           attr_y = jy;
4588         }
4589       }
4590     }
4591
4592 #if 1
4593     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4594         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4595          game.engine_version < VERSION_IDENT(3,1,0,0)))
4596 #else
4597     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4598 #endif
4599     {
4600       attr_x = ZX;
4601       attr_y = ZY;
4602     }
4603
4604     if (element == EL_PENGUIN)
4605     {
4606       int i;
4607       static int xy[4][2] =
4608       {
4609         { 0, -1 },
4610         { -1, 0 },
4611         { +1, 0 },
4612         { 0, +1 }
4613       };
4614
4615       for (i = 0; i < NUM_DIRECTIONS; i++)
4616       {
4617         int ex = x + xy[i][0];
4618         int ey = y + xy[i][1];
4619
4620         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4621         {
4622           attr_x = ex;
4623           attr_y = ey;
4624           break;
4625         }
4626       }
4627     }
4628
4629     MovDir[x][y] = MV_NO_MOVING;
4630     if (attr_x < x)
4631       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4632     else if (attr_x > x)
4633       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4634     if (attr_y < y)
4635       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4636     else if (attr_y > y)
4637       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4638
4639     if (element == EL_ROBOT)
4640     {
4641       int newx, newy;
4642
4643       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4644         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4645       Moving2Blocked(x, y, &newx, &newy);
4646
4647       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4648         MovDelay[x][y] = 8 + 8 * !RND(3);
4649       else
4650         MovDelay[x][y] = 16;
4651     }
4652     else if (element == EL_PENGUIN)
4653     {
4654       int newx, newy;
4655
4656       MovDelay[x][y] = 1;
4657
4658       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4659       {
4660         boolean first_horiz = RND(2);
4661         int new_move_dir = MovDir[x][y];
4662
4663         MovDir[x][y] =
4664           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4665         Moving2Blocked(x, y, &newx, &newy);
4666
4667         if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4668           return;
4669
4670         MovDir[x][y] =
4671           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4672         Moving2Blocked(x, y, &newx, &newy);
4673
4674         if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4675           return;
4676
4677         MovDir[x][y] = old_move_dir;
4678         return;
4679       }
4680     }
4681     else        /* (element == EL_SATELLITE) */
4682     {
4683       int newx, newy;
4684
4685       MovDelay[x][y] = 1;
4686
4687       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4688       {
4689         boolean first_horiz = RND(2);
4690         int new_move_dir = MovDir[x][y];
4691
4692         MovDir[x][y] =
4693           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4694         Moving2Blocked(x, y, &newx, &newy);
4695
4696         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4697           return;
4698
4699         MovDir[x][y] =
4700           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4701         Moving2Blocked(x, y, &newx, &newy);
4702
4703         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4704           return;
4705
4706         MovDir[x][y] = old_move_dir;
4707         return;
4708       }
4709     }
4710   }
4711   else if (move_pattern == MV_TURNING_LEFT ||
4712            move_pattern == MV_TURNING_RIGHT ||
4713            move_pattern == MV_TURNING_LEFT_RIGHT ||
4714            move_pattern == MV_TURNING_RIGHT_LEFT ||
4715            move_pattern == MV_TURNING_RANDOM ||
4716            move_pattern == MV_ALL_DIRECTIONS)
4717   {
4718     boolean can_turn_left =
4719       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4720     boolean can_turn_right =
4721       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4722
4723     if (move_pattern == MV_TURNING_LEFT)
4724       MovDir[x][y] = left_dir;
4725     else if (move_pattern == MV_TURNING_RIGHT)
4726       MovDir[x][y] = right_dir;
4727     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4728       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4729     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4730       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4731     else if (move_pattern == MV_TURNING_RANDOM)
4732       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4733                       can_turn_right && !can_turn_left ? right_dir :
4734                       RND(2) ? left_dir : right_dir);
4735     else if (can_turn_left && can_turn_right)
4736       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4737     else if (can_turn_left)
4738       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4739     else if (can_turn_right)
4740       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4741     else
4742       MovDir[x][y] = back_dir;
4743
4744     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4745   }
4746   else if (move_pattern == MV_HORIZONTAL ||
4747            move_pattern == MV_VERTICAL)
4748   {
4749     if (move_pattern & old_move_dir)
4750       MovDir[x][y] = back_dir;
4751     else if (move_pattern == MV_HORIZONTAL)
4752       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4753     else if (move_pattern == MV_VERTICAL)
4754       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4755
4756     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4757   }
4758   else if (move_pattern & MV_ANY_DIRECTION)
4759   {
4760     MovDir[x][y] = move_pattern;
4761     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4762   }
4763   else if (move_pattern == MV_ALONG_LEFT_SIDE)
4764   {
4765     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4766       MovDir[x][y] = left_dir;
4767     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4768       MovDir[x][y] = right_dir;
4769
4770     if (MovDir[x][y] != old_move_dir)
4771       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4772   }
4773   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4774   {
4775     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4776       MovDir[x][y] = right_dir;
4777     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4778       MovDir[x][y] = left_dir;
4779
4780     if (MovDir[x][y] != old_move_dir)
4781       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4782   }
4783   else if (move_pattern == MV_TOWARDS_PLAYER ||
4784            move_pattern == MV_AWAY_FROM_PLAYER)
4785   {
4786     int attr_x = -1, attr_y = -1;
4787     int newx, newy;
4788     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4789
4790     if (AllPlayersGone)
4791     {
4792       attr_x = ExitX;
4793       attr_y = ExitY;
4794     }
4795     else
4796     {
4797       int i;
4798
4799       for (i = 0; i < MAX_PLAYERS; i++)
4800       {
4801         struct PlayerInfo *player = &stored_player[i];
4802         int jx = player->jx, jy = player->jy;
4803
4804         if (!player->active)
4805           continue;
4806
4807         if (attr_x == -1 ||
4808             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4809         {
4810           attr_x = jx;
4811           attr_y = jy;
4812         }
4813       }
4814     }
4815
4816     MovDir[x][y] = MV_NO_MOVING;
4817     if (attr_x < x)
4818       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4819     else if (attr_x > x)
4820       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4821     if (attr_y < y)
4822       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4823     else if (attr_y > y)
4824       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4825
4826     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4827
4828     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4829     {
4830       boolean first_horiz = RND(2);
4831       int new_move_dir = MovDir[x][y];
4832
4833       MovDir[x][y] =
4834         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4835       Moving2Blocked(x, y, &newx, &newy);
4836
4837       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4838         return;
4839
4840       MovDir[x][y] =
4841         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4842       Moving2Blocked(x, y, &newx, &newy);
4843
4844       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4845         return;
4846
4847       MovDir[x][y] = old_move_dir;
4848     }
4849   }
4850   else if (move_pattern == MV_WHEN_PUSHED ||
4851            move_pattern == MV_WHEN_DROPPED)
4852   {
4853     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4854       MovDir[x][y] = MV_NO_MOVING;
4855
4856     MovDelay[x][y] = 0;
4857   }
4858   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4859   {
4860     static int test_xy[7][2] =
4861     {
4862       { 0, -1 },
4863       { -1, 0 },
4864       { +1, 0 },
4865       { 0, +1 },
4866       { 0, -1 },
4867       { -1, 0 },
4868       { +1, 0 },
4869     };
4870     static int test_dir[7] =
4871     {
4872       MV_UP,
4873       MV_LEFT,
4874       MV_RIGHT,
4875       MV_DOWN,
4876       MV_UP,
4877       MV_LEFT,
4878       MV_RIGHT,
4879     };
4880     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4881     int move_preference = -1000000;     /* start with very low preference */
4882     int new_move_dir = MV_NO_MOVING;
4883     int start_test = RND(4);
4884     int i;
4885
4886     for (i = 0; i < NUM_DIRECTIONS; i++)
4887     {
4888       int move_dir = test_dir[start_test + i];
4889       int move_dir_preference;
4890
4891       xx = x + test_xy[start_test + i][0];
4892       yy = y + test_xy[start_test + i][1];
4893
4894       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4895           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4896       {
4897         new_move_dir = move_dir;
4898
4899         break;
4900       }
4901
4902       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4903         continue;
4904
4905       move_dir_preference = -1 * RunnerVisit[xx][yy];
4906       if (hunter_mode && PlayerVisit[xx][yy] > 0)
4907         move_dir_preference = PlayerVisit[xx][yy];
4908
4909       if (move_dir_preference > move_preference)
4910       {
4911         /* prefer field that has not been visited for the longest time */
4912         move_preference = move_dir_preference;
4913         new_move_dir = move_dir;
4914       }
4915       else if (move_dir_preference == move_preference &&
4916                move_dir == old_move_dir)
4917       {
4918         /* prefer last direction when all directions are preferred equally */
4919         move_preference = move_dir_preference;
4920         new_move_dir = move_dir;
4921       }
4922     }
4923
4924     MovDir[x][y] = new_move_dir;
4925     if (old_move_dir != new_move_dir)
4926       MovDelay[x][y] = 9;
4927   }
4928 }
4929
4930 static void TurnRound(int x, int y)
4931 {
4932   int direction = MovDir[x][y];
4933
4934 #if 0
4935   GfxDir[x][y] = MovDir[x][y];
4936 #endif
4937
4938   TurnRoundExt(x, y);
4939
4940 #if 1
4941   GfxDir[x][y] = MovDir[x][y];
4942 #endif
4943
4944   if (direction != MovDir[x][y])
4945     GfxFrame[x][y] = 0;
4946
4947 #if 1
4948   if (MovDelay[x][y])
4949     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4950 #else
4951   if (MovDelay[x][y])
4952     GfxAction[x][y] = ACTION_WAITING;
4953 #endif
4954 }
4955
4956 static boolean JustBeingPushed(int x, int y)
4957 {
4958   int i;
4959
4960   for (i = 0; i < MAX_PLAYERS; i++)
4961   {
4962     struct PlayerInfo *player = &stored_player[i];
4963
4964     if (player->active && player->is_pushing && player->MovPos)
4965     {
4966       int next_jx = player->jx + (player->jx - player->last_jx);
4967       int next_jy = player->jy + (player->jy - player->last_jy);
4968
4969       if (x == next_jx && y == next_jy)
4970         return TRUE;
4971     }
4972   }
4973
4974   return FALSE;
4975 }
4976
4977 void StartMoving(int x, int y)
4978 {
4979 #if 0
4980   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4981 #endif
4982   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
4983   int element = Feld[x][y];
4984
4985   if (Stop[x][y])
4986     return;
4987
4988 #if 1
4989   if (MovDelay[x][y] == 0)
4990     GfxAction[x][y] = ACTION_DEFAULT;
4991 #else
4992   /* !!! this should be handled more generic (not only for mole) !!! */
4993   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4994     GfxAction[x][y] = ACTION_DEFAULT;
4995 #endif
4996
4997   if (CAN_FALL(element) && y < lev_fieldy - 1)
4998   {
4999     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5000         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5001       if (JustBeingPushed(x, y))
5002         return;
5003
5004     if (element == EL_QUICKSAND_FULL)
5005     {
5006       if (IS_FREE(x, y + 1))
5007       {
5008         InitMovingField(x, y, MV_DOWN);
5009         started_moving = TRUE;
5010
5011         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5012         Store[x][y] = EL_ROCK;
5013 #if 1
5014         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5015 #else
5016         PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5017 #endif
5018       }
5019       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5020       {
5021         if (!MovDelay[x][y])
5022           MovDelay[x][y] = TILEY + 1;
5023
5024         if (MovDelay[x][y])
5025         {
5026           MovDelay[x][y]--;
5027           if (MovDelay[x][y])
5028             return;
5029         }
5030
5031         Feld[x][y] = EL_QUICKSAND_EMPTY;
5032         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5033         Store[x][y + 1] = Store[x][y];
5034         Store[x][y] = 0;
5035 #if 1
5036         PlayLevelSoundAction(x, y, ACTION_FILLING);
5037 #else
5038         PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5039 #endif
5040       }
5041     }
5042     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5043              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5044     {
5045       InitMovingField(x, y, MV_DOWN);
5046       started_moving = TRUE;
5047
5048       Feld[x][y] = EL_QUICKSAND_FILLING;
5049       Store[x][y] = element;
5050 #if 1
5051       PlayLevelSoundAction(x, y, ACTION_FILLING);
5052 #else
5053       PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5054 #endif
5055     }
5056     else if (element == EL_MAGIC_WALL_FULL)
5057     {
5058       if (IS_FREE(x, y + 1))
5059       {
5060         InitMovingField(x, y, MV_DOWN);
5061         started_moving = TRUE;
5062
5063         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5064         Store[x][y] = EL_CHANGED(Store[x][y]);
5065       }
5066       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5067       {
5068         if (!MovDelay[x][y])
5069           MovDelay[x][y] = TILEY/4 + 1;
5070
5071         if (MovDelay[x][y])
5072         {
5073           MovDelay[x][y]--;
5074           if (MovDelay[x][y])
5075             return;
5076         }
5077
5078         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5079         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5080         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5081         Store[x][y] = 0;
5082       }
5083     }
5084     else if (element == EL_BD_MAGIC_WALL_FULL)
5085     {
5086       if (IS_FREE(x, y + 1))
5087       {
5088         InitMovingField(x, y, MV_DOWN);
5089         started_moving = TRUE;
5090
5091         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5092         Store[x][y] = EL_CHANGED2(Store[x][y]);
5093       }
5094       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5095       {
5096         if (!MovDelay[x][y])
5097           MovDelay[x][y] = TILEY/4 + 1;
5098
5099         if (MovDelay[x][y])
5100         {
5101           MovDelay[x][y]--;
5102           if (MovDelay[x][y])
5103             return;
5104         }
5105
5106         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5107         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5108         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5109         Store[x][y] = 0;
5110       }
5111     }
5112     else if (CAN_PASS_MAGIC_WALL(element) &&
5113              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5114               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5115     {
5116       InitMovingField(x, y, MV_DOWN);
5117       started_moving = TRUE;
5118
5119       Feld[x][y] =
5120         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5121          EL_BD_MAGIC_WALL_FILLING);
5122       Store[x][y] = element;
5123     }
5124 #if 0
5125     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5126 #else
5127     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5128 #endif
5129     {
5130       SplashAcid(x, y + 1);
5131
5132       InitMovingField(x, y, MV_DOWN);
5133       started_moving = TRUE;
5134
5135       Store[x][y] = EL_ACID;
5136 #if 0
5137       /* !!! TEST !!! better use "_FALLING" etc. !!! */
5138       GfxAction[x][y + 1] = ACTION_ACTIVE;
5139 #endif
5140     }
5141 #if 1
5142     else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5143               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5144
5145              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5146               CAN_SMASH(element) && WasJustFalling[x][y] &&
5147               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5148
5149              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5150               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5151               (Feld[x][y + 1] == EL_BLOCKED)))
5152
5153 #else
5154 #if 1
5155     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5156              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5157              WasJustMoving[x][y] && !Pushed[x][y + 1])
5158 #else
5159     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5160              WasJustMoving[x][y])
5161 #endif
5162 #endif
5163
5164     {
5165       /* this is needed for a special case not covered by calling "Impact()"
5166          from "ContinueMoving()": if an element moves to a tile directly below
5167          another element which was just falling on that tile (which was empty
5168          in the previous frame), the falling element above would just stop
5169          instead of smashing the element below (in previous version, the above
5170          element was just checked for "moving" instead of "falling", resulting
5171          in incorrect smashes caused by horizontal movement of the above
5172          element; also, the case of the player being the element to smash was
5173          simply not covered here... :-/ ) */
5174
5175 #if 0
5176       WasJustMoving[x][y] = 0;
5177       WasJustFalling[x][y] = 0;
5178 #endif
5179
5180       CheckCollision[x][y] = 0;
5181
5182       Impact(x, y);
5183     }
5184     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5185     {
5186       if (MovDir[x][y] == MV_NO_MOVING)
5187       {
5188         InitMovingField(x, y, MV_DOWN);
5189         started_moving = TRUE;
5190       }
5191     }
5192     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5193     {
5194       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5195         MovDir[x][y] = MV_DOWN;
5196
5197       InitMovingField(x, y, MV_DOWN);
5198       started_moving = TRUE;
5199     }
5200     else if (element == EL_AMOEBA_DROP)
5201     {
5202       Feld[x][y] = EL_AMOEBA_GROWING;
5203       Store[x][y] = EL_AMOEBA_WET;
5204     }
5205     /* Store[x][y + 1] must be zero, because:
5206        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5207     */
5208 #if 0
5209 #if OLD_GAME_BEHAVIOUR
5210     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5211 #else
5212     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5213              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5214              element != EL_DX_SUPABOMB)
5215 #endif
5216 #else
5217     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5218               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5219              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5220              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5221 #endif
5222     {
5223       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
5224                                 (IS_FREE(x - 1, y + 1) ||
5225                                  Feld[x - 1][y + 1] == EL_ACID));
5226       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5227                                 (IS_FREE(x + 1, y + 1) ||
5228                                  Feld[x + 1][y + 1] == EL_ACID));
5229       boolean can_fall_any  = (can_fall_left || can_fall_right);
5230       boolean can_fall_both = (can_fall_left && can_fall_right);
5231
5232       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5233       {
5234         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5235
5236         if (slippery_type == SLIPPERY_ONLY_LEFT)
5237           can_fall_right = FALSE;
5238         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5239           can_fall_left = FALSE;
5240         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5241           can_fall_right = FALSE;
5242         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5243           can_fall_left = FALSE;
5244
5245         can_fall_any  = (can_fall_left || can_fall_right);
5246         can_fall_both = (can_fall_left && can_fall_right);
5247       }
5248
5249       if (can_fall_any)
5250       {
5251         if (can_fall_both &&
5252             (game.emulation != EMU_BOULDERDASH &&
5253              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5254           can_fall_left = !(can_fall_right = RND(2));
5255
5256         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5257         started_moving = TRUE;
5258       }
5259     }
5260 #if 0
5261     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5262 #else
5263     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5264 #endif
5265     {
5266       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
5267       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5268       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5269       int belt_dir = game.belt_dir[belt_nr];
5270
5271       if ((belt_dir == MV_LEFT  && left_is_free) ||
5272           (belt_dir == MV_RIGHT && right_is_free))
5273       {
5274 #if 1
5275         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5276 #endif
5277
5278         InitMovingField(x, y, belt_dir);
5279         started_moving = TRUE;
5280
5281 #if 1
5282         Pushed[x][y] = TRUE;
5283         Pushed[nextx][y] = TRUE;
5284 #endif
5285
5286         GfxAction[x][y] = ACTION_DEFAULT;
5287       }
5288       else
5289       {
5290         MovDir[x][y] = 0;       /* if element was moving, stop it */
5291       }
5292     }
5293   }
5294
5295   /* not "else if" because of elements that can fall and move (EL_SPRING) */
5296 #if 0
5297   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5298 #else
5299   if (CAN_MOVE(element) && !started_moving)
5300 #endif
5301   {
5302     int move_pattern = element_info[element].move_pattern;
5303     int newx, newy;
5304
5305 #if 0
5306 #if DEBUG
5307     if (MovDir[x][y] == MV_NO_MOVING)
5308     {
5309       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5310              x, y, element, element_info[element].token_name);
5311       printf("StartMoving(): This should never happen!\n");
5312     }
5313 #endif
5314 #endif
5315
5316     Moving2Blocked(x, y, &newx, &newy);
5317
5318 #if 1
5319     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5320       return;
5321 #else
5322     if ((element == EL_SATELLITE ||
5323          element == EL_BALLOON ||
5324          element == EL_SPRING)
5325         && JustBeingPushed(x, y))
5326       return;
5327 #endif
5328
5329 #if 1
5330
5331 #if 1
5332     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5333         CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5334 #else
5335     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5336         WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5337         (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5338 #endif
5339     {
5340 #if 0
5341       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5342              element, element_info[element].token_name,
5343              WasJustMoving[x][y],
5344              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5345              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5346              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5347              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5348 #endif
5349
5350 #if 1
5351       WasJustMoving[x][y] = 0;
5352 #endif
5353
5354       CheckCollision[x][y] = 0;
5355
5356       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5357
5358 #if 0
5359       if (Feld[x][y] != element)        /* element has changed */
5360       {
5361         element = Feld[x][y];
5362         move_pattern = element_info[element].move_pattern;
5363
5364         if (!CAN_MOVE(element))
5365           return;
5366       }
5367 #else
5368       if (Feld[x][y] != element)        /* element has changed */
5369         return;
5370 #endif
5371     }
5372 #endif
5373
5374 #if 0
5375 #if 0
5376     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5377       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
5378 #else
5379     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5380     {
5381       Moving2Blocked(x, y, &newx, &newy);
5382       if (Feld[newx][newy] == EL_BLOCKED)
5383         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
5384     }
5385 #endif
5386 #endif
5387
5388 #if 0
5389     if (FrameCounter < 1 && x == 0 && y == 29)
5390       printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5391 #endif
5392
5393     if (!MovDelay[x][y])        /* start new movement phase */
5394     {
5395       /* all objects that can change their move direction after each step
5396          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5397
5398       if (element != EL_YAMYAM &&
5399           element != EL_DARK_YAMYAM &&
5400           element != EL_PACMAN &&
5401           !(move_pattern & MV_ANY_DIRECTION) &&
5402           move_pattern != MV_TURNING_LEFT &&
5403           move_pattern != MV_TURNING_RIGHT &&
5404           move_pattern != MV_TURNING_LEFT_RIGHT &&
5405           move_pattern != MV_TURNING_RIGHT_LEFT &&
5406           move_pattern != MV_TURNING_RANDOM)
5407       {
5408         TurnRound(x, y);
5409
5410 #if 0
5411         if (FrameCounter < 1 && x == 0 && y == 29)
5412           printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5413 #endif
5414
5415         if (MovDelay[x][y] && (element == EL_BUG ||
5416                                element == EL_SPACESHIP ||
5417                                element == EL_SP_SNIKSNAK ||
5418                                element == EL_SP_ELECTRON ||
5419                                element == EL_MOLE))
5420           DrawLevelField(x, y);
5421       }
5422     }
5423
5424     if (MovDelay[x][y])         /* wait some time before next movement */
5425     {
5426       MovDelay[x][y]--;
5427
5428 #if 0
5429       if (element == EL_YAMYAM)
5430       {
5431         printf("::: %d\n",
5432                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5433         DrawLevelElementAnimation(x, y, element);
5434       }
5435 #endif
5436
5437       if (MovDelay[x][y])       /* element still has to wait some time */
5438       {
5439 #if 0
5440         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5441         ResetGfxAnimation(x, y);
5442 #endif
5443
5444 #if 0
5445         if (GfxAction[x][y] != ACTION_WAITING)
5446           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5447
5448         GfxAction[x][y] = ACTION_WAITING;
5449 #endif
5450       }
5451
5452       if (element == EL_ROBOT ||
5453 #if 0
5454           element == EL_PACMAN ||
5455 #endif
5456           element == EL_YAMYAM ||
5457           element == EL_DARK_YAMYAM)
5458       {
5459 #if 0
5460         DrawLevelElementAnimation(x, y, element);
5461 #else
5462         DrawLevelElementAnimationIfNeeded(x, y, element);
5463 #endif
5464         PlayLevelSoundAction(x, y, ACTION_WAITING);
5465       }
5466       else if (element == EL_SP_ELECTRON)
5467         DrawLevelElementAnimationIfNeeded(x, y, element);
5468       else if (element == EL_DRAGON)
5469       {
5470         int i;
5471         int dir = MovDir[x][y];
5472         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5473         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
5474         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
5475                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
5476                        dir == MV_UP     ? IMG_FLAMES_1_UP :
5477                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5478         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5479
5480 #if 0
5481         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5482 #endif
5483
5484         GfxAction[x][y] = ACTION_ATTACKING;
5485
5486         if (IS_PLAYER(x, y))
5487           DrawPlayerField(x, y);
5488         else
5489           DrawLevelField(x, y);
5490
5491         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5492
5493         for (i = 1; i <= 3; i++)
5494         {
5495           int xx = x + i * dx;
5496           int yy = y + i * dy;
5497           int sx = SCREENX(xx);
5498           int sy = SCREENY(yy);
5499           int flame_graphic = graphic + (i - 1);
5500
5501           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5502             break;
5503
5504           if (MovDelay[x][y])
5505           {
5506             int flamed = MovingOrBlocked2Element(xx, yy);
5507
5508             /* !!! */
5509 #if 0
5510             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5511               Bang(xx, yy);
5512             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5513               RemoveMovingField(xx, yy);
5514             else
5515               RemoveField(xx, yy);
5516 #else
5517             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5518               Bang(xx, yy);
5519             else
5520               RemoveMovingField(xx, yy);
5521 #endif
5522
5523 #if 0
5524             if (ChangeDelay[xx][yy])
5525               printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5526                                         Feld[xx][yy] == EL_BLOCKED));
5527 #endif
5528
5529 #if 1
5530             ChangeDelay[xx][yy] = 0;
5531 #endif
5532             Feld[xx][yy] = EL_FLAMES;
5533             if (IN_SCR_FIELD(sx, sy))
5534             {
5535               DrawLevelFieldCrumbledSand(xx, yy);
5536               DrawGraphic(sx, sy, flame_graphic, frame);
5537             }
5538           }
5539           else
5540           {
5541             if (Feld[xx][yy] == EL_FLAMES)
5542               Feld[xx][yy] = EL_EMPTY;
5543             DrawLevelField(xx, yy);
5544           }
5545         }
5546       }
5547
5548       if (MovDelay[x][y])       /* element still has to wait some time */
5549       {
5550         PlayLevelSoundAction(x, y, ACTION_WAITING);
5551
5552         return;
5553       }
5554
5555 #if 0
5556       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5557          for all other elements GfxAction will be set by InitMovingField() */
5558       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5559         GfxAction[x][y] = ACTION_MOVING;
5560 #endif
5561     }
5562
5563     /* now make next step */
5564
5565     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5566
5567     if (DONT_COLLIDE_WITH(element) &&
5568         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5569         !PLAYER_ENEMY_PROTECTED(newx, newy))
5570     {
5571 #if 1
5572       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5573
5574       return;
5575 #else
5576       /* player killed by element which is deadly when colliding with */
5577       MovDir[x][y] = 0;
5578       KillHero(PLAYERINFO(newx, newy));
5579       return;
5580 #endif
5581
5582     }
5583 #if 1
5584 #if 1
5585     else if (CAN_MOVE_INTO_ACID(element) &&
5586              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5587              (MovDir[x][y] == MV_DOWN ||
5588               game.engine_version >= VERSION_IDENT(3,1,0,0)))
5589 #else
5590     else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5591              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5592 #endif
5593 #else
5594
5595     else if ((element == EL_PENGUIN ||
5596               element == EL_ROBOT ||
5597               element == EL_SATELLITE ||
5598               element == EL_BALLOON ||
5599               IS_CUSTOM_ELEMENT(element)) &&
5600              IN_LEV_FIELD(newx, newy) &&
5601              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5602 #endif
5603     {
5604       SplashAcid(newx, newy);
5605       Store[x][y] = EL_ACID;
5606     }
5607     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5608     {
5609       if (Feld[newx][newy] == EL_EXIT_OPEN)
5610       {
5611 #if 1
5612         RemoveField(x, y);
5613         DrawLevelField(x, y);
5614 #else
5615         Feld[x][y] = EL_EMPTY;
5616         DrawLevelField(x, y);
5617 #endif
5618
5619         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5620         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5621           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5622
5623         local_player->friends_still_needed--;
5624         if (!local_player->friends_still_needed &&
5625             !local_player->GameOver && AllPlayersGone)
5626           local_player->LevelSolved = local_player->GameOver = TRUE;
5627
5628         return;
5629       }
5630       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5631       {
5632         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5633           DrawLevelField(newx, newy);
5634         else
5635           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5636       }
5637       else if (!IS_FREE(newx, newy))
5638       {
5639         GfxAction[x][y] = ACTION_WAITING;
5640
5641         if (IS_PLAYER(x, y))
5642           DrawPlayerField(x, y);
5643         else
5644           DrawLevelField(x, y);
5645
5646         return;
5647       }
5648     }
5649     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5650     {
5651       if (IS_FOOD_PIG(Feld[newx][newy]))
5652       {
5653         if (IS_MOVING(newx, newy))
5654           RemoveMovingField(newx, newy);
5655         else
5656         {
5657           Feld[newx][newy] = EL_EMPTY;
5658           DrawLevelField(newx, newy);
5659         }
5660
5661         PlayLevelSound(x, y, SND_PIG_DIGGING);
5662       }
5663       else if (!IS_FREE(newx, newy))
5664       {
5665         if (IS_PLAYER(x, y))
5666           DrawPlayerField(x, y);
5667         else
5668           DrawLevelField(x, y);
5669
5670         return;
5671       }
5672     }
5673
5674 #if 1
5675
5676     /*
5677     else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5678     */
5679
5680     else if (IS_CUSTOM_ELEMENT(element) &&
5681              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5682
5683 #if 0
5684  &&
5685              !IS_FREE(newx, newy)
5686 #endif
5687
5688 )
5689     {
5690       int new_element = Feld[newx][newy];
5691
5692 #if 0
5693       printf("::: '%s' digs '%s' [%d]\n",
5694              element_info[element].token_name,
5695              element_info[Feld[newx][newy]].token_name,
5696              StorePlayer[newx][newy]);
5697 #endif
5698
5699       if (!IS_FREE(newx, newy))
5700       {
5701         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5702                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5703                       ACTION_BREAKING);
5704
5705         /* no element can dig solid indestructible elements */
5706         if (IS_INDESTRUCTIBLE(new_element) &&
5707             !IS_DIGGABLE(new_element) &&
5708             !IS_COLLECTIBLE(new_element))
5709           return;
5710
5711         if (AmoebaNr[newx][newy] &&
5712             (new_element == EL_AMOEBA_FULL ||
5713              new_element == EL_BD_AMOEBA ||
5714              new_element == EL_AMOEBA_GROWING))
5715         {
5716           AmoebaCnt[AmoebaNr[newx][newy]]--;
5717           AmoebaCnt2[AmoebaNr[newx][newy]]--;
5718         }
5719
5720         if (IS_MOVING(newx, newy))
5721           RemoveMovingField(newx, newy);
5722         else
5723         {
5724           RemoveField(newx, newy);
5725           DrawLevelField(newx, newy);
5726         }
5727
5728         /* if digged element was about to explode, prevent the explosion */
5729         ExplodeField[newx][newy] = EX_TYPE_NONE;
5730
5731         PlayLevelSoundAction(x, y, action);
5732       }
5733
5734 #if 1
5735 #if 1
5736       Store[newx][newy] = EL_EMPTY;
5737       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5738         Store[newx][newy] = element_info[element].move_leave_element;
5739 #else
5740       Store[newx][newy] = EL_EMPTY;
5741       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5742           element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5743         Store[newx][newy] = element_info[element].move_leave_element;
5744 #endif
5745 #else
5746       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5747         element_info[element].can_leave_element = TRUE;
5748 #endif
5749
5750       if (move_pattern & MV_MAZE_RUNNER_STYLE)
5751       {
5752         RunnerVisit[x][y] = FrameCounter;
5753         PlayerVisit[x][y] /= 8;         /* expire player visit path */
5754       }
5755     }
5756
5757 #endif
5758
5759     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5760     {
5761       if (!IS_FREE(newx, newy))
5762       {
5763         if (IS_PLAYER(x, y))
5764           DrawPlayerField(x, y);
5765         else
5766           DrawLevelField(x, y);
5767
5768         return;
5769       }
5770       else
5771       {
5772         boolean wanna_flame = !RND(10);
5773         int dx = newx - x, dy = newy - y;
5774         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5775         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5776         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5777                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5778         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5779                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5780
5781         if ((wanna_flame ||
5782              IS_CLASSIC_ENEMY(element1) ||
5783              IS_CLASSIC_ENEMY(element2)) &&
5784             element1 != EL_DRAGON && element2 != EL_DRAGON &&
5785             element1 != EL_FLAMES && element2 != EL_FLAMES)
5786         {
5787 #if 1
5788           ResetGfxAnimation(x, y);
5789           GfxAction[x][y] = ACTION_ATTACKING;
5790 #endif
5791
5792           if (IS_PLAYER(x, y))
5793             DrawPlayerField(x, y);
5794           else
5795             DrawLevelField(x, y);
5796
5797           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5798
5799           MovDelay[x][y] = 50;
5800
5801           /* !!! */
5802 #if 0
5803           RemoveField(newx, newy);
5804 #endif
5805           Feld[newx][newy] = EL_FLAMES;
5806           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5807           {
5808 #if 0
5809             RemoveField(newx1, newy1);
5810 #endif
5811             Feld[newx1][newy1] = EL_FLAMES;
5812           }
5813           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5814           {
5815 #if 0
5816             RemoveField(newx2, newy2);
5817 #endif
5818             Feld[newx2][newy2] = EL_FLAMES;
5819           }
5820
5821           return;
5822         }
5823       }
5824     }
5825     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5826              Feld[newx][newy] == EL_DIAMOND)
5827     {
5828       if (IS_MOVING(newx, newy))
5829         RemoveMovingField(newx, newy);
5830       else
5831       {
5832         Feld[newx][newy] = EL_EMPTY;
5833         DrawLevelField(newx, newy);
5834       }
5835
5836       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5837     }
5838     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5839              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5840     {
5841       if (AmoebaNr[newx][newy])
5842       {
5843         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5844         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5845             Feld[newx][newy] == EL_BD_AMOEBA)
5846           AmoebaCnt[AmoebaNr[newx][newy]]--;
5847       }
5848
5849 #if 0
5850       /* !!! test !!! */
5851       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5852 #else
5853       if (IS_MOVING(newx, newy))
5854 #endif
5855       {
5856         RemoveMovingField(newx, newy);
5857       }
5858       else
5859       {
5860         Feld[newx][newy] = EL_EMPTY;
5861         DrawLevelField(newx, newy);
5862       }
5863
5864       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5865     }
5866     else if ((element == EL_PACMAN || element == EL_MOLE)
5867              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5868     {
5869       if (AmoebaNr[newx][newy])
5870       {
5871         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5872         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5873             Feld[newx][newy] == EL_BD_AMOEBA)
5874           AmoebaCnt[AmoebaNr[newx][newy]]--;
5875       }
5876
5877       if (element == EL_MOLE)
5878       {
5879         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5880         PlayLevelSound(x, y, SND_MOLE_DIGGING);
5881
5882         ResetGfxAnimation(x, y);
5883         GfxAction[x][y] = ACTION_DIGGING;
5884         DrawLevelField(x, y);
5885
5886         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
5887
5888         return;                         /* wait for shrinking amoeba */
5889       }
5890       else      /* element == EL_PACMAN */
5891       {
5892         Feld[newx][newy] = EL_EMPTY;
5893         DrawLevelField(newx, newy);
5894         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5895       }
5896     }
5897     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5898              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5899               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5900     {
5901       /* wait for shrinking amoeba to completely disappear */
5902       return;
5903     }
5904     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5905     {
5906       /* object was running against a wall */
5907
5908       TurnRound(x, y);
5909
5910 #if 0
5911       if (move_pattern & MV_ANY_DIRECTION &&
5912           move_pattern == MovDir[x][y])
5913       {
5914         int blocking_element =
5915           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5916
5917 #if 0
5918         printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
5919                element_info[element].token_name,
5920                element_info[blocking_element].token_name,
5921                x, y, newx, newy);
5922 #endif
5923
5924         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5925                                  MovDir[x][y]);
5926
5927         element = Feld[x][y];   /* element might have changed */
5928       }
5929 #endif
5930
5931 #if 1
5932       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
5933         DrawLevelElementAnimation(x, y, element);
5934 #else
5935       if (element == EL_BUG ||
5936           element == EL_SPACESHIP ||
5937           element == EL_SP_SNIKSNAK)
5938         DrawLevelField(x, y);
5939       else if (element == EL_MOLE)
5940         DrawLevelField(x, y);
5941       else if (element == EL_BD_BUTTERFLY ||
5942                element == EL_BD_FIREFLY)
5943         DrawLevelElementAnimationIfNeeded(x, y, element);
5944       else if (element == EL_SATELLITE)
5945         DrawLevelElementAnimationIfNeeded(x, y, element);
5946       else if (element == EL_SP_ELECTRON)
5947         DrawLevelElementAnimationIfNeeded(x, y, element);
5948 #endif
5949
5950       if (DONT_TOUCH(element))
5951         TestIfBadThingTouchesHero(x, y);
5952
5953 #if 0
5954       PlayLevelSoundAction(x, y, ACTION_WAITING);
5955 #endif
5956
5957       return;
5958     }
5959
5960     InitMovingField(x, y, MovDir[x][y]);
5961
5962     PlayLevelSoundAction(x, y, ACTION_MOVING);
5963   }
5964
5965   if (MovDir[x][y])
5966     ContinueMoving(x, y);
5967 }
5968
5969 void ContinueMoving(int x, int y)
5970 {
5971   int element = Feld[x][y];
5972   int stored = Store[x][y];
5973   struct ElementInfo *ei = &element_info[element];
5974   int direction = MovDir[x][y];
5975   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5976   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5977   int newx = x + dx, newy = y + dy;
5978 #if 0
5979   int nextx = newx + dx, nexty = newy + dy;
5980 #endif
5981 #if 1
5982   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
5983   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
5984 #else
5985   boolean pushed_by_player = Pushed[x][y];
5986 #endif
5987
5988   MovPos[x][y] += getElementMoveStepsize(x, y);
5989
5990 #if 0
5991   if (pushed_by_player && IS_PLAYER(x, y))
5992   {
5993     /* special case: moving object pushed by player */
5994     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5995   }
5996 #else
5997   if (pushed_by_player) /* special case: moving object pushed by player */
5998     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5999 #endif
6000
6001   if (ABS(MovPos[x][y]) < TILEX)
6002   {
6003     DrawLevelField(x, y);
6004
6005     return;     /* element is still moving */
6006   }
6007
6008   /* element reached destination field */
6009
6010   Feld[x][y] = EL_EMPTY;
6011   Feld[newx][newy] = element;
6012   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6013
6014 #if 1
6015   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6016   {
6017     element = Feld[newx][newy] = EL_ACID;
6018   }
6019 #endif
6020   else if (element == EL_MOLE)
6021   {
6022     Feld[x][y] = EL_SAND;
6023
6024     DrawLevelFieldCrumbledSandNeighbours(x, y);
6025   }
6026   else if (element == EL_QUICKSAND_FILLING)
6027   {
6028     element = Feld[newx][newy] = get_next_element(element);
6029     Store[newx][newy] = Store[x][y];
6030   }
6031   else if (element == EL_QUICKSAND_EMPTYING)
6032   {
6033     Feld[x][y] = get_next_element(element);
6034     element = Feld[newx][newy] = Store[x][y];
6035   }
6036   else if (element == EL_MAGIC_WALL_FILLING)
6037   {
6038     element = Feld[newx][newy] = get_next_element(element);
6039     if (!game.magic_wall_active)
6040       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6041     Store[newx][newy] = Store[x][y];
6042   }
6043   else if (element == EL_MAGIC_WALL_EMPTYING)
6044   {
6045     Feld[x][y] = get_next_element(element);
6046     if (!game.magic_wall_active)
6047       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6048     element = Feld[newx][newy] = Store[x][y];
6049   }
6050   else if (element == EL_BD_MAGIC_WALL_FILLING)
6051   {
6052     element = Feld[newx][newy] = get_next_element(element);
6053     if (!game.magic_wall_active)
6054       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6055     Store[newx][newy] = Store[x][y];
6056   }
6057   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6058   {
6059     Feld[x][y] = get_next_element(element);
6060     if (!game.magic_wall_active)
6061       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6062     element = Feld[newx][newy] = Store[x][y];
6063   }
6064   else if (element == EL_AMOEBA_DROPPING)
6065   {
6066     Feld[x][y] = get_next_element(element);
6067     element = Feld[newx][newy] = Store[x][y];
6068   }
6069   else if (element == EL_SOKOBAN_OBJECT)
6070   {
6071     if (Back[x][y])
6072       Feld[x][y] = Back[x][y];
6073
6074     if (Back[newx][newy])
6075       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6076
6077     Back[x][y] = Back[newx][newy] = 0;
6078   }
6079 #if 0
6080   else if (Store[x][y] == EL_ACID)
6081   {
6082     element = Feld[newx][newy] = EL_ACID;
6083   }
6084 #endif
6085 #if 0
6086   else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6087            ei->move_leave_element != EL_EMPTY &&
6088            (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6089             Store[x][y] != EL_EMPTY))
6090   {
6091     /* some elements can leave other elements behind after moving */
6092
6093     Feld[x][y] = ei->move_leave_element;
6094     InitField(x, y, FALSE);
6095
6096     if (GFX_CRUMBLED(Feld[x][y]))
6097       DrawLevelFieldCrumbledSandNeighbours(x, y);
6098   }
6099 #endif
6100
6101   Store[x][y] = EL_EMPTY;
6102   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6103   MovDelay[newx][newy] = 0;
6104
6105   if (CAN_CHANGE(element))
6106   {
6107     /* copy element change control values to new field */
6108     ChangeDelay[newx][newy] = ChangeDelay[x][y];
6109     ChangePage[newx][newy]  = ChangePage[x][y];
6110     Changed[newx][newy]     = Changed[x][y];
6111     ChangeEvent[newx][newy] = ChangeEvent[x][y];
6112   }
6113
6114   ChangeDelay[x][y] = 0;
6115   ChangePage[x][y] = -1;
6116   Changed[x][y] = CE_BITMASK_DEFAULT;
6117   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6118
6119   /* copy animation control values to new field */
6120   GfxFrame[newx][newy]  = GfxFrame[x][y];
6121   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
6122   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
6123   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
6124
6125   Pushed[x][y] = Pushed[newx][newy] = FALSE;
6126
6127   ResetGfxAnimation(x, y);      /* reset animation values for old field */
6128
6129 #if 1
6130   /* some elements can leave other elements behind after moving */
6131 #if 1
6132   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6133       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6134       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6135 #else
6136   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6137       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6138       !IS_PLAYER(x, y))
6139 #endif
6140   {
6141     int move_leave_element = ei->move_leave_element;
6142
6143     Feld[x][y] = move_leave_element;
6144     InitField(x, y, FALSE);
6145
6146     if (GFX_CRUMBLED(Feld[x][y]))
6147       DrawLevelFieldCrumbledSandNeighbours(x, y);
6148
6149     if (ELEM_IS_PLAYER(move_leave_element))
6150       RelocatePlayer(x, y, move_leave_element);
6151   }
6152 #endif
6153
6154 #if 0
6155   /* some elements can leave other elements behind after moving */
6156   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6157       ei->move_leave_element != EL_EMPTY &&
6158       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6159        ei->can_leave_element_last))
6160   {
6161     Feld[x][y] = ei->move_leave_element;
6162     InitField(x, y, FALSE);
6163
6164     if (GFX_CRUMBLED(Feld[x][y]))
6165       DrawLevelFieldCrumbledSandNeighbours(x, y);
6166   }
6167
6168   ei->can_leave_element_last = ei->can_leave_element;
6169   ei->can_leave_element = FALSE;
6170 #endif
6171
6172 #if 0
6173   /* 2.1.1 (does not work correctly for spring) */
6174   if (!CAN_MOVE(element))
6175     MovDir[newx][newy] = 0;
6176 #else
6177
6178 #if 0
6179   /* (does not work for falling objects that slide horizontally) */
6180   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6181     MovDir[newx][newy] = 0;
6182 #else
6183   /*
6184   if (!CAN_MOVE(element) ||
6185       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6186     MovDir[newx][newy] = 0;
6187   */
6188
6189 #if 0
6190   if (!CAN_MOVE(element) ||
6191       (CAN_FALL(element) && direction == MV_DOWN))
6192     GfxDir[x][y] = MovDir[newx][newy] = 0;
6193 #else
6194   if (!CAN_MOVE(element) ||
6195       (CAN_FALL(element) && direction == MV_DOWN &&
6196        (element == EL_SPRING ||
6197         element_info[element].move_pattern == MV_WHEN_PUSHED ||
6198         element_info[element].move_pattern == MV_WHEN_DROPPED)))
6199     GfxDir[x][y] = MovDir[newx][newy] = 0;
6200 #endif
6201
6202 #endif
6203 #endif
6204
6205   DrawLevelField(x, y);
6206   DrawLevelField(newx, newy);
6207
6208   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
6209
6210   /* prevent pushed element from moving on in pushed direction */
6211   if (pushed_by_player && CAN_MOVE(element) &&
6212       element_info[element].move_pattern & MV_ANY_DIRECTION &&
6213       !(element_info[element].move_pattern & direction))
6214     TurnRound(newx, newy);
6215
6216 #if 1
6217   /* prevent elements on conveyor belt from moving on in last direction */
6218   if (pushed_by_conveyor && CAN_FALL(element) &&
6219       direction & MV_HORIZONTAL)
6220   {
6221 #if 0
6222     if (CAN_MOVE(element))
6223       InitMovDir(newx, newy);
6224     else
6225       MovDir[newx][newy] = 0;
6226 #else
6227     MovDir[newx][newy] = 0;
6228 #endif
6229   }
6230 #endif
6231
6232   if (!pushed_by_player)
6233   {
6234     int nextx = newx + dx, nexty = newy + dy;
6235     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6236
6237     WasJustMoving[newx][newy] = 3;
6238
6239     if (CAN_FALL(element) && direction == MV_DOWN)
6240       WasJustFalling[newx][newy] = 3;
6241
6242     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6243       CheckCollision[newx][newy] = 2;
6244   }
6245
6246   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
6247   {
6248     TestIfBadThingTouchesHero(newx, newy);
6249     TestIfBadThingTouchesFriend(newx, newy);
6250
6251     if (!IS_CUSTOM_ELEMENT(element))
6252       TestIfBadThingTouchesOtherBadThing(newx, newy);
6253   }
6254   else if (element == EL_PENGUIN)
6255     TestIfFriendTouchesBadThing(newx, newy);
6256
6257   if (CAN_FALL(element) && direction == MV_DOWN &&
6258       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6259     Impact(x, newy);
6260
6261 #if 1
6262   if (pushed_by_player)
6263   {
6264     static int trigger_sides[4] =
6265     {
6266       CH_SIDE_RIGHT,    /* moving left  */
6267       CH_SIDE_LEFT,     /* moving right */
6268       CH_SIDE_BOTTOM,   /* moving up    */
6269       CH_SIDE_TOP,      /* moving down  */
6270     };
6271     int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6272     struct PlayerInfo *player = PLAYERINFO(x, y);
6273
6274     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6275                                player->index_bit, dig_side);
6276     CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6277                                         player->index_bit, dig_side);
6278   }
6279 #endif
6280
6281 #if 1
6282   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
6283 #endif
6284
6285 #if 0
6286   if (ChangePage[newx][newy] != -1)                     /* delayed change */
6287     ChangeElement(newx, newy, ChangePage[newx][newy]);
6288 #endif
6289
6290 #if 1
6291
6292   TestIfElementHitsCustomElement(newx, newy, direction);
6293
6294 #else
6295
6296   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6297   {
6298     int hitting_element = Feld[newx][newy];
6299
6300     /* !!! fix side (direction) orientation here and elsewhere !!! */
6301     CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6302                              direction);
6303
6304 #if 0
6305     if (IN_LEV_FIELD(nextx, nexty))
6306     {
6307       int opposite_direction = MV_DIR_OPPOSITE(direction);
6308       int hitting_side = direction;
6309       int touched_side = opposite_direction;
6310       int touched_element = MovingOrBlocked2Element(nextx, nexty);
6311       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6312                             MovDir[nextx][nexty] != direction ||
6313                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6314
6315       if (object_hit)
6316       {
6317         int i;
6318
6319         CheckElementChangeBySide(nextx, nexty, touched_element,
6320                                  CE_HIT_BY_SOMETHING, opposite_direction);
6321
6322         if (IS_CUSTOM_ELEMENT(hitting_element) &&
6323             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6324         {
6325           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6326           {
6327             struct ElementChangeInfo *change =
6328               &element_info[hitting_element].change_page[i];
6329
6330             if (change->can_change &&
6331                 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6332                 change->trigger_side & touched_side &&
6333                 change->trigger_element == touched_element)
6334             {
6335               CheckElementChangeByPage(newx, newy, hitting_element,
6336                                        touched_element, CE_OTHER_IS_HITTING,i);
6337               break;
6338             }
6339           }
6340         }
6341
6342         if (IS_CUSTOM_ELEMENT(touched_element) &&
6343             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6344         {
6345           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6346           {
6347             struct ElementChangeInfo *change =
6348               &element_info[touched_element].change_page[i];
6349
6350             if (change->can_change &&
6351                 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6352                 change->trigger_side & hitting_side &&
6353                 change->trigger_element == hitting_element)
6354             {
6355               CheckElementChangeByPage(nextx, nexty, touched_element,
6356                                        hitting_element, CE_OTHER_GETS_HIT, i);
6357               break;
6358             }
6359           }
6360         }
6361       }
6362     }
6363 #endif
6364   }
6365 #endif
6366
6367   TestIfPlayerTouchesCustomElement(newx, newy);
6368   TestIfElementTouchesCustomElement(newx, newy);
6369 }
6370
6371 int AmoebeNachbarNr(int ax, int ay)
6372 {
6373   int i;
6374   int element = Feld[ax][ay];
6375   int group_nr = 0;
6376   static int xy[4][2] =
6377   {
6378     { 0, -1 },
6379     { -1, 0 },
6380     { +1, 0 },
6381     { 0, +1 }
6382   };
6383
6384   for (i = 0; i < NUM_DIRECTIONS; i++)
6385   {
6386     int x = ax + xy[i][0];
6387     int y = ay + xy[i][1];
6388
6389     if (!IN_LEV_FIELD(x, y))
6390       continue;
6391
6392     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6393       group_nr = AmoebaNr[x][y];
6394   }
6395
6396   return group_nr;
6397 }
6398
6399 void AmoebenVereinigen(int ax, int ay)
6400 {
6401   int i, x, y, xx, yy;
6402   int new_group_nr = AmoebaNr[ax][ay];
6403   static int xy[4][2] =
6404   {
6405     { 0, -1 },
6406     { -1, 0 },
6407     { +1, 0 },
6408     { 0, +1 }
6409   };
6410
6411   if (new_group_nr == 0)
6412     return;
6413
6414   for (i = 0; i < NUM_DIRECTIONS; i++)
6415   {
6416     x = ax + xy[i][0];
6417     y = ay + xy[i][1];
6418
6419     if (!IN_LEV_FIELD(x, y))
6420       continue;
6421
6422     if ((Feld[x][y] == EL_AMOEBA_FULL ||
6423          Feld[x][y] == EL_BD_AMOEBA ||
6424          Feld[x][y] == EL_AMOEBA_DEAD) &&
6425         AmoebaNr[x][y] != new_group_nr)
6426     {
6427       int old_group_nr = AmoebaNr[x][y];
6428
6429       if (old_group_nr == 0)
6430         return;
6431
6432       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6433       AmoebaCnt[old_group_nr] = 0;
6434       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6435       AmoebaCnt2[old_group_nr] = 0;
6436
6437       for (yy = 0; yy < lev_fieldy; yy++)
6438       {
6439         for (xx = 0; xx < lev_fieldx; xx++)
6440         {
6441           if (AmoebaNr[xx][yy] == old_group_nr)
6442             AmoebaNr[xx][yy] = new_group_nr;
6443         }
6444       }
6445     }
6446   }
6447 }
6448
6449 void AmoebeUmwandeln(int ax, int ay)
6450 {
6451   int i, x, y;
6452
6453   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6454   {
6455     int group_nr = AmoebaNr[ax][ay];
6456
6457 #ifdef DEBUG
6458     if (group_nr == 0)
6459     {
6460       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6461       printf("AmoebeUmwandeln(): This should never happen!\n");
6462       return;
6463     }
6464 #endif
6465
6466     for (y = 0; y < lev_fieldy; y++)
6467     {
6468       for (x = 0; x < lev_fieldx; x++)
6469       {
6470         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6471         {
6472           AmoebaNr[x][y] = 0;
6473           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6474         }
6475       }
6476     }
6477     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6478                             SND_AMOEBA_TURNING_TO_GEM :
6479                             SND_AMOEBA_TURNING_TO_ROCK));
6480     Bang(ax, ay);
6481   }
6482   else
6483   {
6484     static int xy[4][2] =
6485     {
6486       { 0, -1 },
6487       { -1, 0 },
6488       { +1, 0 },
6489       { 0, +1 }
6490     };
6491
6492     for (i = 0; i < NUM_DIRECTIONS; i++)
6493     {
6494       x = ax + xy[i][0];
6495       y = ay + xy[i][1];
6496
6497       if (!IN_LEV_FIELD(x, y))
6498         continue;
6499
6500       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6501       {
6502         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6503                               SND_AMOEBA_TURNING_TO_GEM :
6504                               SND_AMOEBA_TURNING_TO_ROCK));
6505         Bang(x, y);
6506       }
6507     }
6508   }
6509 }
6510
6511 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6512 {
6513   int x, y;
6514   int group_nr = AmoebaNr[ax][ay];
6515   boolean done = FALSE;
6516
6517 #ifdef DEBUG
6518   if (group_nr == 0)
6519   {
6520     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6521     printf("AmoebeUmwandelnBD(): This should never happen!\n");
6522     return;
6523   }
6524 #endif
6525
6526   for (y = 0; y < lev_fieldy; y++)
6527   {
6528     for (x = 0; x < lev_fieldx; x++)
6529     {
6530       if (AmoebaNr[x][y] == group_nr &&
6531           (Feld[x][y] == EL_AMOEBA_DEAD ||
6532            Feld[x][y] == EL_BD_AMOEBA ||
6533            Feld[x][y] == EL_AMOEBA_GROWING))
6534       {
6535         AmoebaNr[x][y] = 0;
6536         Feld[x][y] = new_element;
6537         InitField(x, y, FALSE);
6538         DrawLevelField(x, y);
6539         done = TRUE;
6540       }
6541     }
6542   }
6543
6544   if (done)
6545     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6546                             SND_BD_AMOEBA_TURNING_TO_ROCK :
6547                             SND_BD_AMOEBA_TURNING_TO_GEM));
6548 }
6549
6550 void AmoebeWaechst(int x, int y)
6551 {
6552   static unsigned long sound_delay = 0;
6553   static unsigned long sound_delay_value = 0;
6554
6555   if (!MovDelay[x][y])          /* start new growing cycle */
6556   {
6557     MovDelay[x][y] = 7;
6558
6559     if (DelayReached(&sound_delay, sound_delay_value))
6560     {
6561 #if 1
6562       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6563 #else
6564       if (Store[x][y] == EL_BD_AMOEBA)
6565         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6566       else
6567         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6568 #endif
6569       sound_delay_value = 30;
6570     }
6571   }
6572
6573   if (MovDelay[x][y])           /* wait some time before growing bigger */
6574   {
6575     MovDelay[x][y]--;
6576     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6577     {
6578       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6579                                            6 - MovDelay[x][y]);
6580
6581       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6582     }
6583
6584     if (!MovDelay[x][y])
6585     {
6586       Feld[x][y] = Store[x][y];
6587       Store[x][y] = 0;
6588       DrawLevelField(x, y);
6589     }
6590   }
6591 }
6592
6593 void AmoebaDisappearing(int x, int y)
6594 {
6595   static unsigned long sound_delay = 0;
6596   static unsigned long sound_delay_value = 0;
6597
6598   if (!MovDelay[x][y])          /* start new shrinking cycle */
6599   {
6600     MovDelay[x][y] = 7;
6601
6602     if (DelayReached(&sound_delay, sound_delay_value))
6603       sound_delay_value = 30;
6604   }
6605
6606   if (MovDelay[x][y])           /* wait some time before shrinking */
6607   {
6608     MovDelay[x][y]--;
6609     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6610     {
6611       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6612                                            6 - MovDelay[x][y]);
6613
6614       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6615     }
6616
6617     if (!MovDelay[x][y])
6618     {
6619       Feld[x][y] = EL_EMPTY;
6620       DrawLevelField(x, y);
6621
6622       /* don't let mole enter this field in this cycle;
6623          (give priority to objects falling to this field from above) */
6624       Stop[x][y] = TRUE;
6625     }
6626   }
6627 }
6628
6629 void AmoebeAbleger(int ax, int ay)
6630 {
6631   int i;
6632   int element = Feld[ax][ay];
6633   int graphic = el2img(element);
6634   int newax = ax, neway = ay;
6635   static int xy[4][2] =
6636   {
6637     { 0, -1 },
6638     { -1, 0 },
6639     { +1, 0 },
6640     { 0, +1 }
6641   };
6642
6643   if (!level.amoeba_speed)
6644   {
6645     Feld[ax][ay] = EL_AMOEBA_DEAD;
6646     DrawLevelField(ax, ay);
6647     return;
6648   }
6649
6650   if (IS_ANIMATED(graphic))
6651     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6652
6653   if (!MovDelay[ax][ay])        /* start making new amoeba field */
6654     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6655
6656   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
6657   {
6658     MovDelay[ax][ay]--;
6659     if (MovDelay[ax][ay])
6660       return;
6661   }
6662
6663   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6664   {
6665     int start = RND(4);
6666     int x = ax + xy[start][0];
6667     int y = ay + xy[start][1];
6668
6669     if (!IN_LEV_FIELD(x, y))
6670       return;
6671
6672 #if 1
6673     if (IS_FREE(x, y) ||
6674         CAN_GROW_INTO(Feld[x][y]) ||
6675         Feld[x][y] == EL_QUICKSAND_EMPTY)
6676     {
6677       newax = x;
6678       neway = y;
6679     }
6680 #else
6681     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6682     if (IS_FREE(x, y) ||
6683         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6684     {
6685       newax = x;
6686       neway = y;
6687     }
6688 #endif
6689
6690     if (newax == ax && neway == ay)
6691       return;
6692   }
6693   else                          /* normal or "filled" (BD style) amoeba */
6694   {
6695     int start = RND(4);
6696     boolean waiting_for_player = FALSE;
6697
6698     for (i = 0; i < NUM_DIRECTIONS; i++)
6699     {
6700       int j = (start + i) % 4;
6701       int x = ax + xy[j][0];
6702       int y = ay + xy[j][1];
6703
6704       if (!IN_LEV_FIELD(x, y))
6705         continue;
6706
6707 #if 1
6708       if (IS_FREE(x, y) ||
6709           CAN_GROW_INTO(Feld[x][y]) ||
6710           Feld[x][y] == EL_QUICKSAND_EMPTY)
6711       {
6712         newax = x;
6713         neway = y;
6714         break;
6715       }
6716 #else
6717       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6718       if (IS_FREE(x, y) ||
6719           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6720       {
6721         newax = x;
6722         neway = y;
6723         break;
6724       }
6725 #endif
6726       else if (IS_PLAYER(x, y))
6727         waiting_for_player = TRUE;
6728     }
6729
6730     if (newax == ax && neway == ay)             /* amoeba cannot grow */
6731     {
6732 #if 1
6733       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6734 #else
6735       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6736 #endif
6737       {
6738         Feld[ax][ay] = EL_AMOEBA_DEAD;
6739         DrawLevelField(ax, ay);
6740         AmoebaCnt[AmoebaNr[ax][ay]]--;
6741
6742         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
6743         {
6744           if (element == EL_AMOEBA_FULL)
6745             AmoebeUmwandeln(ax, ay);
6746           else if (element == EL_BD_AMOEBA)
6747             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6748         }
6749       }
6750       return;
6751     }
6752     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6753     {
6754       /* amoeba gets larger by growing in some direction */
6755
6756       int new_group_nr = AmoebaNr[ax][ay];
6757
6758 #ifdef DEBUG
6759   if (new_group_nr == 0)
6760   {
6761     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6762     printf("AmoebeAbleger(): This should never happen!\n");
6763     return;
6764   }
6765 #endif
6766
6767       AmoebaNr[newax][neway] = new_group_nr;
6768       AmoebaCnt[new_group_nr]++;
6769       AmoebaCnt2[new_group_nr]++;
6770
6771       /* if amoeba touches other amoeba(s) after growing, unify them */
6772       AmoebenVereinigen(newax, neway);
6773
6774       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6775       {
6776         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6777         return;
6778       }
6779     }
6780   }
6781
6782   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6783       (neway == lev_fieldy - 1 && newax != ax))
6784   {
6785     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
6786     Store[newax][neway] = element;
6787   }
6788   else if (neway == ay)
6789   {
6790     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
6791 #if 1
6792     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6793 #else
6794     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6795 #endif
6796   }
6797   else
6798   {
6799     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
6800     Feld[ax][ay] = EL_AMOEBA_DROPPING;
6801     Store[ax][ay] = EL_AMOEBA_DROP;
6802     ContinueMoving(ax, ay);
6803     return;
6804   }
6805
6806   DrawLevelField(newax, neway);
6807 }
6808
6809 void Life(int ax, int ay)
6810 {
6811   int x1, y1, x2, y2;
6812   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
6813   int life_time = 40;
6814   int element = Feld[ax][ay];
6815   int graphic = el2img(element);
6816   boolean changed = FALSE;
6817
6818   if (IS_ANIMATED(graphic))
6819     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6820
6821   if (Stop[ax][ay])
6822     return;
6823
6824   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
6825     MovDelay[ax][ay] = life_time;
6826
6827   if (MovDelay[ax][ay])         /* wait some time before next cycle */
6828   {
6829     MovDelay[ax][ay]--;
6830     if (MovDelay[ax][ay])
6831       return;
6832   }
6833
6834   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6835   {
6836     int xx = ax+x1, yy = ay+y1;
6837     int nachbarn = 0;
6838
6839     if (!IN_LEV_FIELD(xx, yy))
6840       continue;
6841
6842     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6843     {
6844       int x = xx+x2, y = yy+y2;
6845
6846       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6847         continue;
6848
6849       if (((Feld[x][y] == element ||
6850             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6851            !Stop[x][y]) ||
6852           (IS_FREE(x, y) && Stop[x][y]))
6853         nachbarn++;
6854     }
6855
6856     if (xx == ax && yy == ay)           /* field in the middle */
6857     {
6858       if (nachbarn < life[0] || nachbarn > life[1])
6859       {
6860         Feld[xx][yy] = EL_EMPTY;
6861         if (!Stop[xx][yy])
6862           DrawLevelField(xx, yy);
6863         Stop[xx][yy] = TRUE;
6864         changed = TRUE;
6865       }
6866     }
6867 #if 1
6868     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6869     {                                   /* free border field */
6870       if (nachbarn >= life[2] && nachbarn <= life[3])
6871       {
6872         Feld[xx][yy] = element;
6873         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6874         if (!Stop[xx][yy])
6875           DrawLevelField(xx, yy);
6876         Stop[xx][yy] = TRUE;
6877         changed = TRUE;
6878       }
6879     }
6880 #else
6881     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6882     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6883     {                                   /* free border field */
6884       if (nachbarn >= life[2] && nachbarn <= life[3])
6885       {
6886         Feld[xx][yy] = element;
6887         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6888         if (!Stop[xx][yy])
6889           DrawLevelField(xx, yy);
6890         Stop[xx][yy] = TRUE;
6891         changed = TRUE;
6892       }
6893     }
6894 #endif
6895   }
6896
6897   if (changed)
6898     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6899                    SND_GAME_OF_LIFE_GROWING);
6900 }
6901
6902 static void InitRobotWheel(int x, int y)
6903 {
6904   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6905 }
6906
6907 static void RunRobotWheel(int x, int y)
6908 {
6909   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6910 }
6911
6912 static void StopRobotWheel(int x, int y)
6913 {
6914   if (ZX == x && ZY == y)
6915     ZX = ZY = -1;
6916 }
6917
6918 static void InitTimegateWheel(int x, int y)
6919 {
6920 #if 1
6921   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6922 #else
6923   /* another brainless, "type style" bug ... :-( */
6924   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6925 #endif
6926 }
6927
6928 static void RunTimegateWheel(int x, int y)
6929 {
6930   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6931 }
6932
6933 void CheckExit(int x, int y)
6934 {
6935   if (local_player->gems_still_needed > 0 ||
6936       local_player->sokobanfields_still_needed > 0 ||
6937       local_player->lights_still_needed > 0)
6938   {
6939     int element = Feld[x][y];
6940     int graphic = el2img(element);
6941
6942     if (IS_ANIMATED(graphic))
6943       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6944
6945     return;
6946   }
6947
6948   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6949     return;
6950
6951   Feld[x][y] = EL_EXIT_OPENING;
6952
6953   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6954 }
6955
6956 void CheckExitSP(int x, int y)
6957 {
6958   if (local_player->gems_still_needed > 0)
6959   {
6960     int element = Feld[x][y];
6961     int graphic = el2img(element);
6962
6963     if (IS_ANIMATED(graphic))
6964       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6965
6966     return;
6967   }
6968
6969   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6970     return;
6971
6972   Feld[x][y] = EL_SP_EXIT_OPENING;
6973
6974   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6975 }
6976
6977 static void CloseAllOpenTimegates()
6978 {
6979   int x, y;
6980
6981   for (y = 0; y < lev_fieldy; y++)
6982   {
6983     for (x = 0; x < lev_fieldx; x++)
6984     {
6985       int element = Feld[x][y];
6986
6987       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6988       {
6989         Feld[x][y] = EL_TIMEGATE_CLOSING;
6990 #if 1
6991         PlayLevelSoundAction(x, y, ACTION_CLOSING);
6992 #else
6993         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6994 #endif
6995       }
6996     }
6997   }
6998 }
6999
7000 void EdelsteinFunkeln(int x, int y)
7001 {
7002   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7003     return;
7004
7005   if (Feld[x][y] == EL_BD_DIAMOND)
7006     return;
7007
7008   if (MovDelay[x][y] == 0)      /* next animation frame */
7009     MovDelay[x][y] = 11 * !SimpleRND(500);
7010
7011   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7012   {
7013     MovDelay[x][y]--;
7014
7015     if (setup.direct_draw && MovDelay[x][y])
7016       SetDrawtoField(DRAW_BUFFERED);
7017
7018     DrawLevelElementAnimation(x, y, Feld[x][y]);
7019
7020     if (MovDelay[x][y] != 0)
7021     {
7022       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7023                                            10 - MovDelay[x][y]);
7024
7025       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7026
7027       if (setup.direct_draw)
7028       {
7029         int dest_x, dest_y;
7030
7031         dest_x = FX + SCREENX(x) * TILEX;
7032         dest_y = FY + SCREENY(y) * TILEY;
7033
7034         BlitBitmap(drawto_field, window,
7035                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7036         SetDrawtoField(DRAW_DIRECT);
7037       }
7038     }
7039   }
7040 }
7041
7042 void MauerWaechst(int x, int y)
7043 {
7044   int delay = 6;
7045
7046   if (!MovDelay[x][y])          /* next animation frame */
7047     MovDelay[x][y] = 3 * delay;
7048
7049   if (MovDelay[x][y])           /* wait some time before next frame */
7050   {
7051     MovDelay[x][y]--;
7052
7053     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7054     {
7055       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7056       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7057
7058       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7059     }
7060
7061     if (!MovDelay[x][y])
7062     {
7063       if (MovDir[x][y] == MV_LEFT)
7064       {
7065         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7066           DrawLevelField(x - 1, y);
7067       }
7068       else if (MovDir[x][y] == MV_RIGHT)
7069       {
7070         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7071           DrawLevelField(x + 1, y);
7072       }
7073       else if (MovDir[x][y] == MV_UP)
7074       {
7075         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7076           DrawLevelField(x, y - 1);
7077       }
7078       else
7079       {
7080         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7081           DrawLevelField(x, y + 1);
7082       }
7083
7084       Feld[x][y] = Store[x][y];
7085       Store[x][y] = 0;
7086       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7087       DrawLevelField(x, y);
7088     }
7089   }
7090 }
7091
7092 void MauerAbleger(int ax, int ay)
7093 {
7094   int element = Feld[ax][ay];
7095   int graphic = el2img(element);
7096   boolean oben_frei = FALSE, unten_frei = FALSE;
7097   boolean links_frei = FALSE, rechts_frei = FALSE;
7098   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7099   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7100   boolean new_wall = FALSE;
7101
7102   if (IS_ANIMATED(graphic))
7103     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7104
7105   if (!MovDelay[ax][ay])        /* start building new wall */
7106     MovDelay[ax][ay] = 6;
7107
7108   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7109   {
7110     MovDelay[ax][ay]--;
7111     if (MovDelay[ax][ay])
7112       return;
7113   }
7114
7115   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7116     oben_frei = TRUE;
7117   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7118     unten_frei = TRUE;
7119   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7120     links_frei = TRUE;
7121   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7122     rechts_frei = TRUE;
7123
7124   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7125       element == EL_EXPANDABLE_WALL_ANY)
7126   {
7127     if (oben_frei)
7128     {
7129       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7130       Store[ax][ay-1] = element;
7131       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7132       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7133         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7134                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7135       new_wall = TRUE;
7136     }
7137     if (unten_frei)
7138     {
7139       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7140       Store[ax][ay+1] = element;
7141       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7142       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7143         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7144                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7145       new_wall = TRUE;
7146     }
7147   }
7148
7149   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7150       element == EL_EXPANDABLE_WALL_ANY ||
7151       element == EL_EXPANDABLE_WALL)
7152   {
7153     if (links_frei)
7154     {
7155       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7156       Store[ax-1][ay] = element;
7157       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7158       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7159         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7160                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7161       new_wall = TRUE;
7162     }
7163
7164     if (rechts_frei)
7165     {
7166       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7167       Store[ax+1][ay] = element;
7168       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7169       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7170         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7171                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7172       new_wall = TRUE;
7173     }
7174   }
7175
7176   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7177     DrawLevelField(ax, ay);
7178
7179   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7180     oben_massiv = TRUE;
7181   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7182     unten_massiv = TRUE;
7183   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7184     links_massiv = TRUE;
7185   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7186     rechts_massiv = TRUE;
7187
7188   if (((oben_massiv && unten_massiv) ||
7189        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7190        element == EL_EXPANDABLE_WALL) &&
7191       ((links_massiv && rechts_massiv) ||
7192        element == EL_EXPANDABLE_WALL_VERTICAL))
7193     Feld[ax][ay] = EL_WALL;
7194
7195   if (new_wall)
7196 #if 1
7197     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7198 #else
7199     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7200 #endif
7201 }
7202
7203 void CheckForDragon(int x, int y)
7204 {
7205   int i, j;
7206   boolean dragon_found = FALSE;
7207   static int xy[4][2] =
7208   {
7209     { 0, -1 },
7210     { -1, 0 },
7211     { +1, 0 },
7212     { 0, +1 }
7213   };
7214
7215   for (i = 0; i < NUM_DIRECTIONS; i++)
7216   {
7217     for (j = 0; j < 4; j++)
7218     {
7219       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7220
7221       if (IN_LEV_FIELD(xx, yy) &&
7222           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7223       {
7224         if (Feld[xx][yy] == EL_DRAGON)
7225           dragon_found = TRUE;
7226       }
7227       else
7228         break;
7229     }
7230   }
7231
7232   if (!dragon_found)
7233   {
7234     for (i = 0; i < NUM_DIRECTIONS; i++)
7235     {
7236       for (j = 0; j < 3; j++)
7237       {
7238         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7239   
7240         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7241         {
7242           Feld[xx][yy] = EL_EMPTY;
7243           DrawLevelField(xx, yy);
7244         }
7245         else
7246           break;
7247       }
7248     }
7249   }
7250 }
7251
7252 static void InitBuggyBase(int x, int y)
7253 {
7254   int element = Feld[x][y];
7255   int activating_delay = FRAMES_PER_SECOND / 4;
7256
7257   ChangeDelay[x][y] =
7258     (element == EL_SP_BUGGY_BASE ?
7259      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7260      element == EL_SP_BUGGY_BASE_ACTIVATING ?
7261      activating_delay :
7262      element == EL_SP_BUGGY_BASE_ACTIVE ?
7263      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7264 }
7265
7266 static void WarnBuggyBase(int x, int y)
7267 {
7268   int i;
7269   static int xy[4][2] =
7270   {
7271     { 0, -1 },
7272     { -1, 0 },
7273     { +1, 0 },
7274     { 0, +1 }
7275   };
7276
7277   for (i = 0; i < NUM_DIRECTIONS; i++)
7278   {
7279     int xx = x + xy[i][0], yy = y + xy[i][1];
7280
7281     if (IS_PLAYER(xx, yy))
7282     {
7283       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7284
7285       break;
7286     }
7287   }
7288 }
7289
7290 static void InitTrap(int x, int y)
7291 {
7292   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7293 }
7294
7295 static void ActivateTrap(int x, int y)
7296 {
7297   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7298 }
7299
7300 static void ChangeActiveTrap(int x, int y)
7301 {
7302   int graphic = IMG_TRAP_ACTIVE;
7303
7304   /* if new animation frame was drawn, correct crumbled sand border */
7305   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7306     DrawLevelFieldCrumbledSand(x, y);
7307 }
7308
7309 static void ChangeElementNowExt(int x, int y, int target_element)
7310 {
7311   int previous_move_direction = MovDir[x][y];
7312 #if 1
7313   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7314                         IS_WALKABLE(Feld[x][y]));
7315 #else
7316   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7317                         IS_WALKABLE(Feld[x][y]) &&
7318                         !IS_MOVING(x, y));
7319 #endif
7320
7321   /* check if element under player changes from accessible to unaccessible
7322      (needed for special case of dropping element which then changes) */
7323   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7324       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7325   {
7326     Bang(x, y);
7327     return;
7328   }
7329
7330 #if 1
7331   if (!add_player)
7332 #endif
7333   {
7334 #if 1
7335     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7336       RemoveMovingField(x, y);
7337     else
7338       RemoveField(x, y);
7339
7340     Feld[x][y] = target_element;
7341 #else
7342     RemoveField(x, y);
7343     Feld[x][y] = target_element;
7344 #endif
7345
7346     ResetGfxAnimation(x, y);
7347     ResetRandomAnimationValue(x, y);
7348
7349     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7350       MovDir[x][y] = previous_move_direction;
7351
7352 #if 1
7353     InitField_WithBug1(x, y, FALSE);
7354 #else
7355     InitField(x, y, FALSE);
7356     if (CAN_MOVE(Feld[x][y]))
7357       InitMovDir(x, y);
7358 #endif
7359
7360     DrawLevelField(x, y);
7361
7362     if (GFX_CRUMBLED(Feld[x][y]))
7363       DrawLevelFieldCrumbledSandNeighbours(x, y);
7364   }
7365
7366   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7367
7368 #if 0
7369   TestIfBadThingTouchesHero(x, y);
7370   TestIfPlayerTouchesCustomElement(x, y);
7371   TestIfElementTouchesCustomElement(x, y);
7372 #endif
7373
7374   if (ELEM_IS_PLAYER(target_element))
7375     RelocatePlayer(x, y, target_element);
7376
7377 #if 1
7378   TestIfBadThingTouchesHero(x, y);
7379   TestIfPlayerTouchesCustomElement(x, y);
7380   TestIfElementTouchesCustomElement(x, y);
7381 #endif
7382 }
7383
7384 static boolean ChangeElementNow(int x, int y, int element, int page)
7385 {
7386   struct ElementChangeInfo *change = &element_info[element].change_page[page];
7387   int target_element;
7388   int old_element = Feld[x][y];
7389
7390   /* always use default change event to prevent running into a loop */
7391   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7392     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7393
7394   if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7395   {
7396     /* reset actual trigger element and player */
7397     change->actual_trigger_element = EL_EMPTY;
7398     change->actual_trigger_player = EL_PLAYER_1;
7399   }
7400
7401   /* do not change already changed elements with same change event */
7402 #if 0
7403   if (Changed[x][y] & ChangeEvent[x][y])
7404     return FALSE;
7405 #else
7406   if (Changed[x][y])
7407     return FALSE;
7408 #endif
7409
7410   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7411
7412 #if 0
7413   /* !!! indirect change before direct change !!! */
7414   CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7415 #endif
7416
7417   if (change->explode)
7418   {
7419     Bang(x, y);
7420
7421     return TRUE;
7422   }
7423
7424   if (change->use_target_content)
7425   {
7426     boolean complete_replace = TRUE;
7427     boolean can_replace[3][3];
7428     int xx, yy;
7429
7430     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7431     {
7432       boolean is_empty;
7433       boolean is_walkable;
7434       boolean is_diggable;
7435       boolean is_collectible;
7436       boolean is_removable;
7437       boolean is_destructible;
7438       int ex = x + xx - 1;
7439       int ey = y + yy - 1;
7440       int content_element = change->target_content[xx][yy];
7441       int e;
7442
7443       can_replace[xx][yy] = TRUE;
7444
7445       if (ex == x && ey == y)   /* do not check changing element itself */
7446         continue;
7447
7448       if (content_element == EL_EMPTY_SPACE)
7449       {
7450         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
7451
7452         continue;
7453       }
7454
7455       if (!IN_LEV_FIELD(ex, ey))
7456       {
7457         can_replace[xx][yy] = FALSE;
7458         complete_replace = FALSE;
7459
7460         continue;
7461       }
7462
7463       e = Feld[ex][ey];
7464
7465       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7466         e = MovingOrBlocked2Element(ex, ey);
7467
7468 #if 1
7469
7470 #if 0
7471       is_empty = (IS_FREE(ex, ey) ||
7472                   (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7473                   (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7474                    !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7475 #else
7476       is_empty = (IS_FREE(ex, ey) ||
7477                   (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7478 #endif
7479       is_walkable     = (is_empty || IS_WALKABLE(e));
7480       is_diggable     = (is_empty || IS_DIGGABLE(e));
7481       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
7482       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7483       is_removable    = (is_diggable || is_collectible);
7484
7485       can_replace[xx][yy] =
7486         ((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
7487          (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
7488          (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
7489          (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
7490          (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
7491          (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7492
7493       if (!can_replace[xx][yy])
7494         complete_replace = FALSE;
7495 #else
7496       empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7497                                                IS_WALKABLE(content_element)));
7498 #if 1
7499       half_destructible = (empty_for_element || IS_DIGGABLE(e));
7500 #else
7501       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7502 #endif
7503
7504       if ((change->replace_when <= CP_WHEN_EMPTY  && !empty_for_element) ||
7505           (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7506           (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7507       {
7508         can_replace[xx][yy] = FALSE;
7509         complete_replace = FALSE;
7510       }
7511 #endif
7512     }
7513
7514     if (!change->only_if_complete || complete_replace)
7515     {
7516       boolean something_has_changed = FALSE;
7517
7518       if (change->only_if_complete && change->use_random_replace &&
7519           RND(100) < change->random_percentage)
7520         return FALSE;
7521
7522       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7523       {
7524         int ex = x + xx - 1;
7525         int ey = y + yy - 1;
7526         int content_element;
7527
7528         if (can_replace[xx][yy] && (!change->use_random_replace ||
7529                                     RND(100) < change->random_percentage))
7530         {
7531           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7532             RemoveMovingField(ex, ey);
7533
7534           ChangeEvent[ex][ey] = ChangeEvent[x][y];
7535
7536           content_element = change->target_content[xx][yy];
7537           target_element = GET_TARGET_ELEMENT(content_element, change);
7538
7539           ChangeElementNowExt(ex, ey, target_element);
7540
7541           something_has_changed = TRUE;
7542
7543           /* for symmetry reasons, freeze newly created border elements */
7544           if (ex != x || ey != y)
7545             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
7546         }
7547       }
7548
7549       if (something_has_changed)
7550         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7551     }
7552   }
7553   else
7554   {
7555     target_element = GET_TARGET_ELEMENT(change->target_element, change);
7556
7557     ChangeElementNowExt(x, y, target_element);
7558
7559     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7560   }
7561
7562 #if 1
7563   /* this uses direct change before indirect change */
7564   CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7565 #endif
7566
7567   return TRUE;
7568 }
7569
7570 static void ChangeElement(int x, int y, int page)
7571 {
7572   int element = MovingOrBlocked2Element(x, y);
7573   struct ElementInfo *ei = &element_info[element];
7574   struct ElementChangeInfo *change = &ei->change_page[page];
7575
7576 #ifdef DEBUG
7577   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7578   {
7579     printf("\n\n");
7580     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7581            x, y, element, element_info[element].token_name);
7582     printf("ChangeElement(): This should never happen!\n");
7583     printf("\n\n");
7584   }
7585 #endif
7586
7587   /* this can happen with classic bombs on walkable, changing elements */
7588   if (!CAN_CHANGE(element))
7589   {
7590 #if 0
7591     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
7592       ChangeDelay[x][y] = 0;
7593 #endif
7594
7595     return;
7596   }
7597
7598   if (ChangeDelay[x][y] == 0)           /* initialize element change */
7599   {
7600     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
7601                          RND(change->delay_random * change->delay_frames)) + 1;
7602
7603     ResetGfxAnimation(x, y);
7604     ResetRandomAnimationValue(x, y);
7605
7606     if (change->pre_change_function)
7607       change->pre_change_function(x, y);
7608   }
7609
7610   ChangeDelay[x][y]--;
7611
7612   if (ChangeDelay[x][y] != 0)           /* continue element change */
7613   {
7614     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7615
7616     if (IS_ANIMATED(graphic))
7617       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7618
7619     if (change->change_function)
7620       change->change_function(x, y);
7621   }
7622   else                                  /* finish element change */
7623   {
7624     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
7625     {
7626       page = ChangePage[x][y];
7627       ChangePage[x][y] = -1;
7628
7629       change = &ei->change_page[page];
7630     }
7631
7632 #if 0
7633     if (IS_MOVING(x, y) && !change->explode)
7634 #else
7635     if (IS_MOVING(x, y))                /* never change a running system ;-) */
7636 #endif
7637     {
7638       ChangeDelay[x][y] = 1;            /* try change after next move step */
7639       ChangePage[x][y] = page;          /* remember page to use for change */
7640
7641       return;
7642     }
7643
7644     if (ChangeElementNow(x, y, element, page))
7645     {
7646       if (change->post_change_function)
7647         change->post_change_function(x, y);
7648     }
7649   }
7650 }
7651
7652 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7653                                               int trigger_element,
7654                                               int trigger_event,
7655                                               int trigger_player,
7656                                               int trigger_side,
7657                                               int trigger_page)
7658 {
7659   int i, j, x, y;
7660   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7661
7662   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7663     return FALSE;
7664
7665   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7666   {
7667     int element = EL_CUSTOM_START + i;
7668
7669     boolean change_element = FALSE;
7670     int page = 0;
7671
7672     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7673       continue;
7674
7675     for (j = 0; j < element_info[element].num_change_pages; j++)
7676     {
7677       struct ElementChangeInfo *change = &element_info[element].change_page[j];
7678
7679       if (change->can_change &&
7680           change->events & CH_EVENT_BIT(trigger_event) &&
7681           change->trigger_side & trigger_side &&
7682           change->trigger_player & trigger_player &&
7683           change->trigger_page & trigger_page_bits &&
7684           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7685       {
7686 #if 0
7687         if (!(change->events & CH_EVENT_BIT(trigger_event)))
7688           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7689                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7690 #endif
7691
7692         change_element = TRUE;
7693         page = j;
7694
7695         change->actual_trigger_element = trigger_element;
7696         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7697
7698         break;
7699       }
7700     }
7701
7702     if (!change_element)
7703       continue;
7704
7705     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7706     {
7707 #if 0
7708       if (x == lx && y == ly)   /* do not change trigger element itself */
7709         continue;
7710 #endif
7711
7712       if (Feld[x][y] == element)
7713       {
7714         ChangeDelay[x][y] = 1;
7715         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7716         ChangeElement(x, y, page);
7717       }
7718     }
7719   }
7720
7721   return TRUE;
7722 }
7723
7724 static boolean CheckElementChangeExt(int x, int y,
7725                                      int element,
7726                                      int trigger_element,
7727                                      int trigger_event,
7728                                      int trigger_player,
7729                                      int trigger_side,
7730                                      int trigger_page)
7731 {
7732   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7733     return FALSE;
7734
7735   if (Feld[x][y] == EL_BLOCKED)
7736   {
7737     Blocked2Moving(x, y, &x, &y);
7738     element = Feld[x][y];
7739   }
7740
7741 #if 1
7742   if (Feld[x][y] != element)    /* check if element has already changed */
7743   {
7744 #if 0
7745     printf("::: %d ('%s') != %d ('%s') [%d]\n",
7746            Feld[x][y], element_info[Feld[x][y]].token_name,
7747            element, element_info[element].token_name,
7748            trigger_event);
7749 #endif
7750
7751     return FALSE;
7752   }
7753 #endif
7754
7755 #if 1
7756   if (trigger_page < 0)
7757   {
7758     boolean change_element = FALSE;
7759     int i;
7760
7761     for (i = 0; i < element_info[element].num_change_pages; i++)
7762     {
7763       struct ElementChangeInfo *change = &element_info[element].change_page[i];
7764
7765       if (change->can_change &&
7766           change->events & CH_EVENT_BIT(trigger_event) &&
7767           change->trigger_side & trigger_side &&
7768           change->trigger_player & trigger_player)
7769       {
7770         change_element = TRUE;
7771         trigger_page = i;
7772
7773         change->actual_trigger_element = trigger_element;
7774         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7775
7776         break;
7777       }
7778     }
7779
7780     if (!change_element)
7781       return FALSE;
7782   }
7783   else
7784   {
7785     struct ElementInfo *ei = &element_info[element];
7786     struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7787
7788     change->actual_trigger_element = trigger_element;
7789     change->actual_trigger_player = EL_PLAYER_1;        /* unused */
7790   }
7791
7792 #else
7793
7794   /* !!! this check misses pages with same event, but different side !!! */
7795
7796   if (trigger_page < 0)
7797     trigger_page = element_info[element].event_page_nr[trigger_event];
7798
7799   if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7800     return FALSE;
7801 #endif
7802
7803   ChangeDelay[x][y] = 1;
7804   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7805   ChangeElement(x, y, trigger_page);
7806
7807   return TRUE;
7808 }
7809
7810 static void PlayPlayerSound(struct PlayerInfo *player)
7811 {
7812   int jx = player->jx, jy = player->jy;
7813   int element = player->element_nr;
7814   int last_action = player->last_action_waiting;
7815   int action = player->action_waiting;
7816
7817   if (player->is_waiting)
7818   {
7819     if (action != last_action)
7820       PlayLevelSoundElementAction(jx, jy, element, action);
7821     else
7822       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7823   }
7824   else
7825   {
7826     if (action != last_action)
7827       StopSound(element_info[element].sound[last_action]);
7828
7829     if (last_action == ACTION_SLEEPING)
7830       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7831   }
7832 }
7833
7834 static void PlayAllPlayersSound()
7835 {
7836   int i;
7837
7838   for (i = 0; i < MAX_PLAYERS; i++)
7839     if (stored_player[i].active)
7840       PlayPlayerSound(&stored_player[i]);
7841 }
7842
7843 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7844 {
7845   boolean last_waiting = player->is_waiting;
7846   int move_dir = player->MovDir;
7847
7848   player->last_action_waiting = player->action_waiting;
7849
7850   if (is_waiting)
7851   {
7852     if (!last_waiting)          /* not waiting -> waiting */
7853     {
7854       player->is_waiting = TRUE;
7855
7856       player->frame_counter_bored =
7857         FrameCounter +
7858         game.player_boring_delay_fixed +
7859         SimpleRND(game.player_boring_delay_random);
7860       player->frame_counter_sleeping =
7861         FrameCounter +
7862         game.player_sleeping_delay_fixed +
7863         SimpleRND(game.player_sleeping_delay_random);
7864
7865       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7866     }
7867
7868     if (game.player_sleeping_delay_fixed +
7869         game.player_sleeping_delay_random > 0 &&
7870         player->anim_delay_counter == 0 &&
7871         player->post_delay_counter == 0 &&
7872         FrameCounter >= player->frame_counter_sleeping)
7873       player->is_sleeping = TRUE;
7874     else if (game.player_boring_delay_fixed +
7875              game.player_boring_delay_random > 0 &&
7876              FrameCounter >= player->frame_counter_bored)
7877       player->is_bored = TRUE;
7878
7879     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7880                               player->is_bored ? ACTION_BORING :
7881                               ACTION_WAITING);
7882
7883     if (player->is_sleeping)
7884     {
7885       if (player->num_special_action_sleeping > 0)
7886       {
7887         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7888         {
7889           int last_special_action = player->special_action_sleeping;
7890           int num_special_action = player->num_special_action_sleeping;
7891           int special_action =
7892             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7893              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7894              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7895              last_special_action + 1 : ACTION_SLEEPING);
7896           int special_graphic =
7897             el_act_dir2img(player->element_nr, special_action, move_dir);
7898
7899           player->anim_delay_counter =
7900             graphic_info[special_graphic].anim_delay_fixed +
7901             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7902           player->post_delay_counter =
7903             graphic_info[special_graphic].post_delay_fixed +
7904             SimpleRND(graphic_info[special_graphic].post_delay_random);
7905
7906           player->special_action_sleeping = special_action;
7907         }
7908
7909         if (player->anim_delay_counter > 0)
7910         {
7911           player->action_waiting = player->special_action_sleeping;
7912           player->anim_delay_counter--;
7913         }
7914         else if (player->post_delay_counter > 0)
7915         {
7916           player->post_delay_counter--;
7917         }
7918       }
7919     }
7920     else if (player->is_bored)
7921     {
7922       if (player->num_special_action_bored > 0)
7923       {
7924         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7925         {
7926           int special_action =
7927             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7928           int special_graphic =
7929             el_act_dir2img(player->element_nr, special_action, move_dir);
7930
7931           player->anim_delay_counter =
7932             graphic_info[special_graphic].anim_delay_fixed +
7933             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7934           player->post_delay_counter =
7935             graphic_info[special_graphic].post_delay_fixed +
7936             SimpleRND(graphic_info[special_graphic].post_delay_random);
7937
7938           player->special_action_bored = special_action;
7939         }
7940
7941         if (player->anim_delay_counter > 0)
7942         {
7943           player->action_waiting = player->special_action_bored;
7944           player->anim_delay_counter--;
7945         }
7946         else if (player->post_delay_counter > 0)
7947         {
7948           player->post_delay_counter--;
7949         }
7950       }
7951     }
7952   }
7953   else if (last_waiting)        /* waiting -> not waiting */
7954   {
7955     player->is_waiting = FALSE;
7956     player->is_bored = FALSE;
7957     player->is_sleeping = FALSE;
7958
7959     player->frame_counter_bored = -1;
7960     player->frame_counter_sleeping = -1;
7961
7962     player->anim_delay_counter = 0;
7963     player->post_delay_counter = 0;
7964
7965     player->action_waiting = ACTION_DEFAULT;
7966
7967     player->special_action_bored = ACTION_DEFAULT;
7968     player->special_action_sleeping = ACTION_DEFAULT;
7969   }
7970 }
7971
7972 #if 1
7973 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
7974 {
7975 #if 0
7976   static byte stored_player_action[MAX_PLAYERS];
7977   static int num_stored_actions = 0;
7978 #endif
7979   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
7980   int left      = player_action & JOY_LEFT;
7981   int right     = player_action & JOY_RIGHT;
7982   int up        = player_action & JOY_UP;
7983   int down      = player_action & JOY_DOWN;
7984   int button1   = player_action & JOY_BUTTON_1;
7985   int button2   = player_action & JOY_BUTTON_2;
7986   int dx        = (left ? -1    : right ? 1     : 0);
7987   int dy        = (up   ? -1    : down  ? 1     : 0);
7988
7989 #if 0
7990   stored_player_action[player->index_nr] = 0;
7991   num_stored_actions++;
7992 #endif
7993
7994 #if 0
7995   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7996 #endif
7997
7998   if (!player->active || tape.pausing)
7999     return 0;
8000
8001 #if 0
8002   printf("::: [%d %d %d %d] [%d %d]\n",
8003          left, right, up, down, button1, button2);
8004 #endif
8005
8006   if (player_action)
8007   {
8008 #if 0
8009     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8010 #endif
8011
8012 #if 0
8013     /* !!! TEST !!! */
8014     if (player->MovPos == 0)
8015       CheckGravityMovement(player);
8016 #endif
8017     if (button1)
8018       snapped = SnapField(player, dx, dy);
8019     else
8020     {
8021       if (button2)
8022         dropped = DropElement(player);
8023
8024       moved = MovePlayer(player, dx, dy);
8025     }
8026
8027     if (tape.single_step && tape.recording && !tape.pausing)
8028     {
8029       if (button1 || (dropped && !moved))
8030       {
8031         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8032         SnapField(player, 0, 0);                /* stop snapping */
8033       }
8034     }
8035
8036     SetPlayerWaiting(player, FALSE);
8037
8038 #if 1
8039     return player_action;
8040 #else
8041     stored_player_action[player->index_nr] = player_action;
8042 #endif
8043   }
8044   else
8045   {
8046 #if 0
8047     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8048 #endif
8049
8050     /* no actions for this player (no input at player's configured device) */
8051
8052     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8053     SnapField(player, 0, 0);
8054     CheckGravityMovementWhenNotMoving(player);
8055
8056     if (player->MovPos == 0)
8057       SetPlayerWaiting(player, TRUE);
8058
8059     if (player->MovPos == 0)    /* needed for tape.playing */
8060       player->is_moving = FALSE;
8061
8062     player->is_dropping = FALSE;
8063
8064     return 0;
8065   }
8066
8067 #if 0
8068   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8069   {
8070     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8071
8072     TapeRecordAction(stored_player_action);
8073     num_stored_actions = 0;
8074   }
8075 #endif
8076 }
8077
8078 #else
8079
8080 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8081 {
8082   static byte stored_player_action[MAX_PLAYERS];
8083   static int num_stored_actions = 0;
8084   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8085   int left      = player_action & JOY_LEFT;
8086   int right     = player_action & JOY_RIGHT;
8087   int up        = player_action & JOY_UP;
8088   int down      = player_action & JOY_DOWN;
8089   int button1   = player_action & JOY_BUTTON_1;
8090   int button2   = player_action & JOY_BUTTON_2;
8091   int dx        = (left ? -1    : right ? 1     : 0);
8092   int dy        = (up   ? -1    : down  ? 1     : 0);
8093
8094   stored_player_action[player->index_nr] = 0;
8095   num_stored_actions++;
8096
8097   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8098
8099   if (!player->active || tape.pausing)
8100     return;
8101
8102   if (player_action)
8103   {
8104     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8105
8106     if (button1)
8107       snapped = SnapField(player, dx, dy);
8108     else
8109     {
8110       if (button2)
8111         dropped = DropElement(player);
8112
8113       moved = MovePlayer(player, dx, dy);
8114     }
8115
8116     if (tape.single_step && tape.recording && !tape.pausing)
8117     {
8118       if (button1 || (dropped && !moved))
8119       {
8120         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8121         SnapField(player, 0, 0);                /* stop snapping */
8122       }
8123     }
8124
8125     stored_player_action[player->index_nr] = player_action;
8126   }
8127   else
8128   {
8129     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8130
8131     /* no actions for this player (no input at player's configured device) */
8132
8133     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8134     SnapField(player, 0, 0);
8135     CheckGravityMovementWhenNotMoving(player);
8136
8137     if (player->MovPos == 0)
8138       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8139
8140     if (player->MovPos == 0)    /* needed for tape.playing */
8141       player->is_moving = FALSE;
8142   }
8143
8144   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8145   {
8146     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8147
8148     TapeRecordAction(stored_player_action);
8149     num_stored_actions = 0;
8150   }
8151 }
8152 #endif
8153
8154 void GameActions()
8155 {
8156   static unsigned long action_delay = 0;
8157   unsigned long action_delay_value;
8158   int magic_wall_x = 0, magic_wall_y = 0;
8159   int i, x, y, element, graphic;
8160   byte *recorded_player_action;
8161   byte summarized_player_action = 0;
8162 #if 1
8163   byte tape_action[MAX_PLAYERS];
8164 #endif
8165
8166   if (game_status != GAME_MODE_PLAYING)
8167     return;
8168
8169   action_delay_value =
8170     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8171
8172   if (tape.playing && tape.warp_forward && !tape.pausing)
8173     action_delay_value = 0;
8174
8175   /* ---------- main game synchronization point ---------- */
8176
8177   WaitUntilDelayReached(&action_delay, action_delay_value);
8178
8179   if (network_playing && !network_player_action_received)
8180   {
8181     /*
8182 #ifdef DEBUG
8183     printf("DEBUG: try to get network player actions in time\n");
8184 #endif
8185     */
8186
8187 #if defined(PLATFORM_UNIX)
8188     /* last chance to get network player actions without main loop delay */
8189     HandleNetworking();
8190 #endif
8191
8192     if (game_status != GAME_MODE_PLAYING)
8193       return;
8194
8195     if (!network_player_action_received)
8196     {
8197       /*
8198 #ifdef DEBUG
8199       printf("DEBUG: failed to get network player actions in time\n");
8200 #endif
8201       */
8202       return;
8203     }
8204   }
8205
8206   if (tape.pausing)
8207     return;
8208
8209 #if 0
8210   printf("::: getting new tape action [%d]\n", FrameCounter);
8211 #endif
8212
8213   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8214
8215 #if 1
8216   if (recorded_player_action == NULL && tape.pausing)
8217     return;
8218 #endif
8219
8220 #if 0
8221   printf("::: %d\n", stored_player[0].action);
8222 #endif
8223
8224 #if 0
8225   if (recorded_player_action != NULL)
8226     for (i = 0; i < MAX_PLAYERS; i++)
8227       stored_player[i].action = recorded_player_action[i];
8228 #endif
8229
8230   for (i = 0; i < MAX_PLAYERS; i++)
8231   {
8232     summarized_player_action |= stored_player[i].action;
8233
8234     if (!network_playing)
8235       stored_player[i].effective_action = stored_player[i].action;
8236   }
8237
8238 #if defined(PLATFORM_UNIX)
8239   if (network_playing)
8240     SendToServer_MovePlayer(summarized_player_action);
8241 #endif
8242
8243   if (!options.network && !setup.team_mode)
8244     local_player->effective_action = summarized_player_action;
8245
8246 #if 1
8247   if (recorded_player_action != NULL)
8248     for (i = 0; i < MAX_PLAYERS; i++)
8249       stored_player[i].effective_action = recorded_player_action[i];
8250 #endif
8251
8252 #if 1
8253   for (i = 0; i < MAX_PLAYERS; i++)
8254   {
8255     tape_action[i] = stored_player[i].effective_action;
8256
8257     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8258       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8259   }
8260
8261   /* only save actions from input devices, but not programmed actions */
8262   if (tape.recording)
8263     TapeRecordAction(tape_action);
8264 #endif
8265
8266   for (i = 0; i < MAX_PLAYERS; i++)
8267   {
8268     int actual_player_action = stored_player[i].effective_action;
8269
8270 #if 1
8271     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8272        - rnd_equinox_tetrachloride 048
8273        - rnd_equinox_tetrachloride_ii 096
8274        - rnd_emanuel_schmieg 002
8275        - doctor_sloan_ww 001, 020
8276     */
8277     if (stored_player[i].MovPos == 0)
8278       CheckGravityMovement(&stored_player[i]);
8279 #endif
8280
8281 #if 1
8282     /* overwrite programmed action with tape action */
8283     if (stored_player[i].programmed_action)
8284       actual_player_action = stored_player[i].programmed_action;
8285 #endif
8286
8287 #if 0
8288     if (stored_player[i].programmed_action)
8289       printf("::: %d\n", stored_player[i].programmed_action);
8290 #endif
8291
8292     if (recorded_player_action)
8293     {
8294 #if 0
8295       if (stored_player[i].programmed_action &&
8296           stored_player[i].programmed_action != recorded_player_action[i])
8297         printf("::: %d: %d <-> %d\n", i,
8298                stored_player[i].programmed_action, recorded_player_action[i]);
8299 #endif
8300
8301 #if 0
8302       actual_player_action = recorded_player_action[i];
8303 #endif
8304     }
8305
8306 #if 0
8307     /* overwrite tape action with programmed action */
8308     if (stored_player[i].programmed_action)
8309       actual_player_action = stored_player[i].programmed_action;
8310 #endif
8311
8312 #if 0
8313     if (i == 0)
8314       printf("::: action: %d: %x [%d]\n",
8315              stored_player[i].MovPos, actual_player_action, FrameCounter);
8316 #endif
8317
8318 #if 1
8319     PlayerActions(&stored_player[i], actual_player_action);
8320 #else
8321     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8322
8323     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8324       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8325 #endif
8326
8327     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8328   }
8329
8330 #if 0
8331   if (tape.recording)
8332     TapeRecordAction(tape_action);
8333 #endif
8334
8335   network_player_action_received = FALSE;
8336
8337   ScrollScreen(NULL, SCROLL_GO_ON);
8338
8339 #if 0
8340   FrameCounter++;
8341   TimeFrames++;
8342
8343   for (i = 0; i < MAX_PLAYERS; i++)
8344     stored_player[i].Frame++;
8345 #endif
8346
8347 #if 1
8348   /* for downwards compatibility, the following code emulates a fixed bug that
8349      occured when pushing elements (causing elements that just made their last
8350      pushing step to already (if possible) make their first falling step in the
8351      same game frame, which is bad); this code is also needed to use the famous
8352      "spring push bug" which is used in older levels and might be wanted to be
8353      used also in newer levels, but in this case the buggy pushing code is only
8354      affecting the "spring" element and no other elements */
8355
8356 #if 1
8357   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8358 #else
8359   if (game.engine_version < VERSION_IDENT(2,2,0,7))
8360 #endif
8361   {
8362     for (i = 0; i < MAX_PLAYERS; i++)
8363     {
8364       struct PlayerInfo *player = &stored_player[i];
8365       int x = player->jx;
8366       int y = player->jy;
8367
8368 #if 1
8369       if (player->active && player->is_pushing && player->is_moving &&
8370           IS_MOVING(x, y) &&
8371           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8372            Feld[x][y] == EL_SPRING))
8373 #else
8374       if (player->active && player->is_pushing && player->is_moving &&
8375           IS_MOVING(x, y))
8376 #endif
8377       {
8378         ContinueMoving(x, y);
8379
8380         /* continue moving after pushing (this is actually a bug) */
8381         if (!IS_MOVING(x, y))
8382         {
8383           Stop[x][y] = FALSE;
8384         }
8385       }
8386     }
8387   }
8388 #endif
8389
8390   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8391   {
8392     Changed[x][y] = CE_BITMASK_DEFAULT;
8393     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8394
8395 #if DEBUG
8396     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8397     {
8398       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8399       printf("GameActions(): This should never happen!\n");
8400
8401       ChangePage[x][y] = -1;
8402     }
8403 #endif
8404
8405     Stop[x][y] = FALSE;
8406     if (WasJustMoving[x][y] > 0)
8407       WasJustMoving[x][y]--;
8408     if (WasJustFalling[x][y] > 0)
8409       WasJustFalling[x][y]--;
8410     if (CheckCollision[x][y] > 0)
8411       CheckCollision[x][y]--;
8412
8413     GfxFrame[x][y]++;
8414
8415 #if 1
8416     /* reset finished pushing action (not done in ContinueMoving() to allow
8417        continous pushing animation for elements with zero push delay) */
8418     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8419     {
8420       ResetGfxAnimation(x, y);
8421       DrawLevelField(x, y);
8422     }
8423 #endif
8424
8425 #if DEBUG
8426     if (IS_BLOCKED(x, y))
8427     {
8428       int oldx, oldy;
8429
8430       Blocked2Moving(x, y, &oldx, &oldy);
8431       if (!IS_MOVING(oldx, oldy))
8432       {
8433         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8434         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8435         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8436         printf("GameActions(): This should never happen!\n");
8437       }
8438     }
8439 #endif
8440   }
8441
8442   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8443   {
8444     element = Feld[x][y];
8445 #if 1
8446     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8447 #else
8448     graphic = el2img(element);
8449 #endif
8450
8451 #if 0
8452     if (element == -1)
8453     {
8454       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8455
8456       element = graphic = 0;
8457     }
8458 #endif
8459
8460     if (graphic_info[graphic].anim_global_sync)
8461       GfxFrame[x][y] = FrameCounter;
8462
8463     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8464         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8465       ResetRandomAnimationValue(x, y);
8466
8467     SetRandomAnimationValue(x, y);
8468
8469 #if 1
8470     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8471 #endif
8472
8473     if (IS_INACTIVE(element))
8474     {
8475       if (IS_ANIMATED(graphic))
8476         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8477
8478       continue;
8479     }
8480
8481 #if 1
8482     /* this may take place after moving, so 'element' may have changed */
8483 #if 0
8484     if (IS_CHANGING(x, y))
8485 #else
8486     if (IS_CHANGING(x, y) &&
8487         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8488 #endif
8489     {
8490 #if 0
8491       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8492                     element_info[element].event_page_nr[CE_DELAY]);
8493 #else
8494       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8495 #endif
8496
8497       element = Feld[x][y];
8498       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8499     }
8500 #endif
8501
8502     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8503     {
8504       StartMoving(x, y);
8505
8506 #if 1
8507       element = Feld[x][y];
8508       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8509 #if 0
8510       if (element == EL_MOLE)
8511         printf("::: %d, %d, %d [%d]\n",
8512                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8513                GfxAction[x][y]);
8514 #endif
8515 #if 0
8516       if (element == EL_YAMYAM)
8517         printf("::: %d, %d, %d\n",
8518                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8519 #endif
8520 #endif
8521
8522       if (IS_ANIMATED(graphic) &&
8523           !IS_MOVING(x, y) &&
8524           !Stop[x][y])
8525       {
8526         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8527
8528 #if 0
8529         if (element == EL_BUG)
8530           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8531 #endif
8532
8533 #if 0
8534         if (element == EL_MOLE)
8535           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8536 #endif
8537       }
8538
8539       if (IS_GEM(element) || element == EL_SP_INFOTRON)
8540         EdelsteinFunkeln(x, y);
8541     }
8542     else if ((element == EL_ACID ||
8543               element == EL_EXIT_OPEN ||
8544               element == EL_SP_EXIT_OPEN ||
8545               element == EL_SP_TERMINAL ||
8546               element == EL_SP_TERMINAL_ACTIVE ||
8547               element == EL_EXTRA_TIME ||
8548               element == EL_SHIELD_NORMAL ||
8549               element == EL_SHIELD_DEADLY) &&
8550              IS_ANIMATED(graphic))
8551       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8552     else if (IS_MOVING(x, y))
8553       ContinueMoving(x, y);
8554     else if (IS_ACTIVE_BOMB(element))
8555       CheckDynamite(x, y);
8556 #if 0
8557     else if (element == EL_EXPLOSION && !game.explosions_delayed)
8558       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8559 #endif
8560     else if (element == EL_AMOEBA_GROWING)
8561       AmoebeWaechst(x, y);
8562     else if (element == EL_AMOEBA_SHRINKING)
8563       AmoebaDisappearing(x, y);
8564
8565 #if !USE_NEW_AMOEBA_CODE
8566     else if (IS_AMOEBALIVE(element))
8567       AmoebeAbleger(x, y);
8568 #endif
8569
8570     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8571       Life(x, y);
8572     else if (element == EL_EXIT_CLOSED)
8573       CheckExit(x, y);
8574     else if (element == EL_SP_EXIT_CLOSED)
8575       CheckExitSP(x, y);
8576     else if (element == EL_EXPANDABLE_WALL_GROWING)
8577       MauerWaechst(x, y);
8578     else if (element == EL_EXPANDABLE_WALL ||
8579              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8580              element == EL_EXPANDABLE_WALL_VERTICAL ||
8581              element == EL_EXPANDABLE_WALL_ANY)
8582       MauerAbleger(x, y);
8583     else if (element == EL_FLAMES)
8584       CheckForDragon(x, y);
8585 #if 0
8586     else if (IS_AUTO_CHANGING(element))
8587       ChangeElement(x, y);
8588 #endif
8589     else if (element == EL_EXPLOSION)
8590       ; /* drawing of correct explosion animation is handled separately */
8591     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8592       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8593
8594 #if 0
8595     /* this may take place after moving, so 'element' may have changed */
8596     if (IS_AUTO_CHANGING(Feld[x][y]))
8597       ChangeElement(x, y);
8598 #endif
8599
8600     if (IS_BELT_ACTIVE(element))
8601       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8602
8603     if (game.magic_wall_active)
8604     {
8605       int jx = local_player->jx, jy = local_player->jy;
8606
8607       /* play the element sound at the position nearest to the player */
8608       if ((element == EL_MAGIC_WALL_FULL ||
8609            element == EL_MAGIC_WALL_ACTIVE ||
8610            element == EL_MAGIC_WALL_EMPTYING ||
8611            element == EL_BD_MAGIC_WALL_FULL ||
8612            element == EL_BD_MAGIC_WALL_ACTIVE ||
8613            element == EL_BD_MAGIC_WALL_EMPTYING) &&
8614           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8615       {
8616         magic_wall_x = x;
8617         magic_wall_y = y;
8618       }
8619     }
8620   }
8621
8622 #if USE_NEW_AMOEBA_CODE
8623   /* new experimental amoeba growth stuff */
8624 #if 1
8625   if (!(FrameCounter % 8))
8626 #endif
8627   {
8628     static unsigned long random = 1684108901;
8629
8630     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8631     {
8632 #if 0
8633       x = (random >> 10) % lev_fieldx;
8634       y = (random >> 20) % lev_fieldy;
8635 #else
8636       x = RND(lev_fieldx);
8637       y = RND(lev_fieldy);
8638 #endif
8639       element = Feld[x][y];
8640
8641 #if 1
8642       if (!IS_PLAYER(x,y) &&
8643           (element == EL_EMPTY ||
8644            CAN_GROW_INTO(element) ||
8645            element == EL_QUICKSAND_EMPTY ||
8646            element == EL_ACID_SPLASH_LEFT ||
8647            element == EL_ACID_SPLASH_RIGHT))
8648       {
8649         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8650             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8651             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8652             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8653           Feld[x][y] = EL_AMOEBA_DROP;
8654       }
8655 #else
8656       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8657       if (!IS_PLAYER(x,y) &&
8658           (element == EL_EMPTY ||
8659            element == EL_SAND ||
8660            element == EL_QUICKSAND_EMPTY ||
8661            element == EL_ACID_SPLASH_LEFT ||
8662            element == EL_ACID_SPLASH_RIGHT))
8663       {
8664         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8665             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8666             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8667             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8668           Feld[x][y] = EL_AMOEBA_DROP;
8669       }
8670 #endif
8671
8672       random = random * 129 + 1;
8673     }
8674   }
8675 #endif
8676
8677 #if 0
8678   if (game.explosions_delayed)
8679 #endif
8680   {
8681     game.explosions_delayed = FALSE;
8682
8683     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8684     {
8685       element = Feld[x][y];
8686
8687       if (ExplodeField[x][y])
8688         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8689       else if (element == EL_EXPLOSION)
8690         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8691
8692       ExplodeField[x][y] = EX_TYPE_NONE;
8693     }
8694
8695     game.explosions_delayed = TRUE;
8696   }
8697
8698   if (game.magic_wall_active)
8699   {
8700     if (!(game.magic_wall_time_left % 4))
8701     {
8702       int element = Feld[magic_wall_x][magic_wall_y];
8703
8704       if (element == EL_BD_MAGIC_WALL_FULL ||
8705           element == EL_BD_MAGIC_WALL_ACTIVE ||
8706           element == EL_BD_MAGIC_WALL_EMPTYING)
8707         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8708       else
8709         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8710     }
8711
8712     if (game.magic_wall_time_left > 0)
8713     {
8714       game.magic_wall_time_left--;
8715       if (!game.magic_wall_time_left)
8716       {
8717         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8718         {
8719           element = Feld[x][y];
8720
8721           if (element == EL_MAGIC_WALL_ACTIVE ||
8722               element == EL_MAGIC_WALL_FULL)
8723           {
8724             Feld[x][y] = EL_MAGIC_WALL_DEAD;
8725             DrawLevelField(x, y);
8726           }
8727           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8728                    element == EL_BD_MAGIC_WALL_FULL)
8729           {
8730             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8731             DrawLevelField(x, y);
8732           }
8733         }
8734
8735         game.magic_wall_active = FALSE;
8736       }
8737     }
8738   }
8739
8740   if (game.light_time_left > 0)
8741   {
8742     game.light_time_left--;
8743
8744     if (game.light_time_left == 0)
8745       RedrawAllLightSwitchesAndInvisibleElements();
8746   }
8747
8748   if (game.timegate_time_left > 0)
8749   {
8750     game.timegate_time_left--;
8751
8752     if (game.timegate_time_left == 0)
8753       CloseAllOpenTimegates();
8754   }
8755
8756   for (i = 0; i < MAX_PLAYERS; i++)
8757   {
8758     struct PlayerInfo *player = &stored_player[i];
8759
8760     if (SHIELD_ON(player))
8761     {
8762       if (player->shield_deadly_time_left)
8763         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8764       else if (player->shield_normal_time_left)
8765         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8766     }
8767   }
8768
8769   if (TimeFrames >= FRAMES_PER_SECOND)
8770   {
8771     TimeFrames = 0;
8772     TapeTime++;
8773
8774     if (!level.use_step_counter)
8775     {
8776       TimePlayed++;
8777
8778       for (i = 0; i < MAX_PLAYERS; i++)
8779       {
8780         struct PlayerInfo *player = &stored_player[i];
8781
8782         if (SHIELD_ON(player))
8783         {
8784           player->shield_normal_time_left--;
8785
8786           if (player->shield_deadly_time_left > 0)
8787             player->shield_deadly_time_left--;
8788         }
8789       }
8790
8791       if (TimeLeft > 0)
8792       {
8793         TimeLeft--;
8794
8795         if (TimeLeft <= 10 && setup.time_limit)
8796           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8797
8798         DrawGameValue_Time(TimeLeft);
8799
8800         if (!TimeLeft && setup.time_limit)
8801           for (i = 0; i < MAX_PLAYERS; i++)
8802             KillHero(&stored_player[i]);
8803       }
8804       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8805         DrawGameValue_Time(TimePlayed);
8806     }
8807
8808     if (tape.recording || tape.playing)
8809       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8810   }
8811
8812   DrawAllPlayers();
8813   PlayAllPlayersSound();
8814
8815   if (options.debug)                    /* calculate frames per second */
8816   {
8817     static unsigned long fps_counter = 0;
8818     static int fps_frames = 0;
8819     unsigned long fps_delay_ms = Counter() - fps_counter;
8820
8821     fps_frames++;
8822
8823     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
8824     {
8825       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8826
8827       fps_frames = 0;
8828       fps_counter = Counter();
8829     }
8830
8831     redraw_mask |= REDRAW_FPS;
8832   }
8833
8834 #if 0
8835   if (stored_player[0].jx != stored_player[0].last_jx ||
8836       stored_player[0].jy != stored_player[0].last_jy)
8837     printf("::: %d, %d, %d, %d, %d\n",
8838            stored_player[0].MovDir,
8839            stored_player[0].MovPos,
8840            stored_player[0].GfxPos,
8841            stored_player[0].Frame,
8842            stored_player[0].StepFrame);
8843 #endif
8844
8845 #if 1
8846   FrameCounter++;
8847   TimeFrames++;
8848
8849   for (i = 0; i < MAX_PLAYERS; i++)
8850   {
8851     int move_frames =
8852       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
8853
8854     stored_player[i].Frame += move_frames;
8855
8856     if (stored_player[i].MovPos != 0)
8857       stored_player[i].StepFrame += move_frames;
8858
8859     if (stored_player[i].drop_delay > 0)
8860       stored_player[i].drop_delay--;
8861   }
8862 #endif
8863
8864 #if 1
8865   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8866   {
8867     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8868
8869     local_player->show_envelope = 0;
8870   }
8871 #endif
8872 }
8873
8874 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8875 {
8876   int min_x = x, min_y = y, max_x = x, max_y = y;
8877   int i;
8878
8879   for (i = 0; i < MAX_PLAYERS; i++)
8880   {
8881     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8882
8883     if (!stored_player[i].active || &stored_player[i] == player)
8884       continue;
8885
8886     min_x = MIN(min_x, jx);
8887     min_y = MIN(min_y, jy);
8888     max_x = MAX(max_x, jx);
8889     max_y = MAX(max_y, jy);
8890   }
8891
8892   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8893 }
8894
8895 static boolean AllPlayersInVisibleScreen()
8896 {
8897   int i;
8898
8899   for (i = 0; i < MAX_PLAYERS; i++)
8900   {
8901     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8902
8903     if (!stored_player[i].active)
8904       continue;
8905
8906     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8907       return FALSE;
8908   }
8909
8910   return TRUE;
8911 }
8912
8913 void ScrollLevel(int dx, int dy)
8914 {
8915   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8916   int x, y;
8917
8918   BlitBitmap(drawto_field, drawto_field,
8919              FX + TILEX * (dx == -1) - softscroll_offset,
8920              FY + TILEY * (dy == -1) - softscroll_offset,
8921              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8922              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8923              FX + TILEX * (dx == 1) - softscroll_offset,
8924              FY + TILEY * (dy == 1) - softscroll_offset);
8925
8926   if (dx)
8927   {
8928     x = (dx == 1 ? BX1 : BX2);
8929     for (y = BY1; y <= BY2; y++)
8930       DrawScreenField(x, y);
8931   }
8932
8933   if (dy)
8934   {
8935     y = (dy == 1 ? BY1 : BY2);
8936     for (x = BX1; x <= BX2; x++)
8937       DrawScreenField(x, y);
8938   }
8939
8940   redraw_mask |= REDRAW_FIELD;
8941 }
8942
8943 #if 0
8944 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8945 {
8946   int nextx = x + dx, nexty = y + dy;
8947   int element = Feld[x][y];
8948
8949   if ((dx == -1 &&
8950        element != EL_SP_PORT_LEFT &&
8951        element != EL_SP_GRAVITY_PORT_LEFT &&
8952        element != EL_SP_PORT_HORIZONTAL &&
8953        element != EL_SP_PORT_ANY) ||
8954       (dx == +1 &&
8955        element != EL_SP_PORT_RIGHT &&
8956        element != EL_SP_GRAVITY_PORT_RIGHT &&
8957        element != EL_SP_PORT_HORIZONTAL &&
8958        element != EL_SP_PORT_ANY) ||
8959       (dy == -1 &&
8960        element != EL_SP_PORT_UP &&
8961        element != EL_SP_GRAVITY_PORT_UP &&
8962        element != EL_SP_PORT_VERTICAL &&
8963        element != EL_SP_PORT_ANY) ||
8964       (dy == +1 &&
8965        element != EL_SP_PORT_DOWN &&
8966        element != EL_SP_GRAVITY_PORT_DOWN &&
8967        element != EL_SP_PORT_VERTICAL &&
8968        element != EL_SP_PORT_ANY) ||
8969       !IN_LEV_FIELD(nextx, nexty) ||
8970       !IS_FREE(nextx, nexty))
8971     return FALSE;
8972
8973   return TRUE;
8974 }
8975 #endif
8976
8977 static boolean canFallDown(struct PlayerInfo *player)
8978 {
8979   int jx = player->jx, jy = player->jy;
8980
8981   return (IN_LEV_FIELD(jx, jy + 1) &&
8982           (IS_FREE(jx, jy + 1) ||
8983            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
8984           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
8985           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
8986 }
8987
8988 static boolean canPassField(int x, int y, int move_dir)
8989 {
8990   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
8991   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
8992   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
8993   int nextx = x + dx;
8994   int nexty = y + dy;
8995   int element = Feld[x][y];
8996
8997   return (IS_PASSABLE_FROM(element, opposite_dir) &&
8998           !CAN_MOVE(element) &&
8999           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9000           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9001           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9002 }
9003
9004 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9005 {
9006   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9007   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9008   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9009   int newx = x + dx;
9010   int newy = y + dy;
9011 #if 0
9012   int nextx = newx + dx;
9013   int nexty = newy + dy;
9014 #endif
9015
9016 #if 1
9017   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9018           (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9019            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9020            canPassField(newx, newy, move_dir)));
9021 #else
9022   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9023           (IS_DIGGABLE(Feld[newx][newy]) ||
9024            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9025            (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9026             !CAN_MOVE(Feld[newx][newy]) &&
9027             IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9028             IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9029             (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9030 #endif
9031 }
9032
9033 static void CheckGravityMovement(struct PlayerInfo *player)
9034 {
9035   if (game.gravity && !player->programmed_action)
9036   {
9037 #if 1
9038     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9039     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
9040 #else
9041     int move_dir_horizontal = player->action & MV_HORIZONTAL;
9042     int move_dir_vertical   = player->action & MV_VERTICAL;
9043 #endif
9044
9045 #if 1
9046     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9047 #else
9048     boolean player_is_snapping = player->action & JOY_BUTTON_1;
9049 #endif
9050
9051     int jx = player->jx, jy = player->jy;
9052
9053     boolean player_is_moving_to_valid_field =
9054       (!player_is_snapping &&
9055        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9056         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9057
9058 #if 0
9059     int move_dir =
9060       (player->last_move_dir & MV_HORIZONTAL ?
9061        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9062        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9063 #endif
9064
9065 #if 0
9066     int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9067     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9068     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9069     int new_jx = jx + dx, new_jy = jy + dy;
9070     int nextx = new_jx + dx, nexty = new_jy + dy;
9071 #endif
9072
9073 #if 1
9074
9075 #if 1
9076     boolean player_can_fall_down = canFallDown(player);
9077 #else
9078     boolean player_can_fall_down =
9079       (IN_LEV_FIELD(jx, jy + 1) &&
9080        (IS_FREE(jx, jy + 1) ||
9081         (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9082 #endif
9083
9084 #else
9085     boolean player_can_fall_down =
9086       (IN_LEV_FIELD(jx, jy + 1) &&
9087        (IS_FREE(jx, jy + 1)));
9088 #endif
9089
9090 #if 0
9091     boolean player_is_moving_to_valid_field =
9092       (
9093 #if 1
9094        !player_is_snapping &&
9095 #endif
9096
9097 #if 1
9098        IN_LEV_FIELD(new_jx, new_jy) &&
9099        (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9100         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9101          element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9102          IN_LEV_FIELD(nextx, nexty) &&
9103          element_info[Feld[nextx][nexty]].access_direction & move_dir))
9104 #else
9105        IN_LEV_FIELD(new_jx, new_jy) &&
9106        (Feld[new_jx][new_jy] == EL_SP_BASE ||
9107         Feld[new_jx][new_jy] == EL_SAND ||
9108         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9109          canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9110     /* !!! extend EL_SAND to anything diggable !!! */
9111 #endif
9112        );
9113 #endif
9114
9115 #if 0
9116     boolean player_is_standing_on_valid_field =
9117       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9118        (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9119 #endif
9120
9121 #if 0
9122     printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9123            player_can_fall_down,
9124            player_is_standing_on_valid_field,
9125            player_is_moving_to_valid_field,
9126            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9127            player->effective_action,
9128            player->can_fall_into_acid);
9129 #endif
9130
9131     if (player_can_fall_down &&
9132 #if 0
9133         !player_is_standing_on_valid_field &&
9134 #endif
9135         !player_is_moving_to_valid_field)
9136     {
9137 #if 0
9138       printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9139              jx, jy, FrameCounter);
9140 #endif
9141
9142       player->programmed_action = MV_DOWN;
9143     }
9144   }
9145 }
9146
9147 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9148 {
9149 #if 1
9150   return CheckGravityMovement(player);
9151 #endif
9152
9153   if (game.gravity && !player->programmed_action)
9154   {
9155     int jx = player->jx, jy = player->jy;
9156     boolean field_under_player_is_free =
9157       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9158     boolean player_is_standing_on_valid_field =
9159       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9160        (IS_WALKABLE(Feld[jx][jy]) &&
9161         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9162
9163     if (field_under_player_is_free && !player_is_standing_on_valid_field)
9164       player->programmed_action = MV_DOWN;
9165   }
9166 }
9167
9168 /*
9169   MovePlayerOneStep()
9170   -----------------------------------------------------------------------------
9171   dx, dy:               direction (non-diagonal) to try to move the player to
9172   real_dx, real_dy:     direction as read from input device (can be diagonal)
9173 */
9174
9175 boolean MovePlayerOneStep(struct PlayerInfo *player,
9176                           int dx, int dy, int real_dx, int real_dy)
9177 {
9178 #if 0
9179   static int trigger_sides[4][2] =
9180   {
9181     /* enter side        leave side */
9182     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
9183     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
9184     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
9185     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
9186   };
9187   int move_direction = (dx == -1 ? MV_LEFT :
9188                         dx == +1 ? MV_RIGHT :
9189                         dy == -1 ? MV_UP :
9190                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9191   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9192   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9193 #endif
9194   int jx = player->jx, jy = player->jy;
9195   int new_jx = jx + dx, new_jy = jy + dy;
9196   int element;
9197   int can_move;
9198
9199   if (!player->active || (!dx && !dy))
9200     return MF_NO_ACTION;
9201
9202   player->MovDir = (dx < 0 ? MV_LEFT :
9203                     dx > 0 ? MV_RIGHT :
9204                     dy < 0 ? MV_UP :
9205                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
9206
9207   if (!IN_LEV_FIELD(new_jx, new_jy))
9208     return MF_NO_ACTION;
9209
9210   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9211     return MF_NO_ACTION;
9212
9213 #if 0
9214   element = MovingOrBlocked2Element(new_jx, new_jy);
9215 #else
9216   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9217 #endif
9218
9219   if (DONT_RUN_INTO(element))
9220   {
9221     if (element == EL_ACID && dx == 0 && dy == 1)
9222     {
9223       SplashAcid(new_jx, new_jy);
9224       Feld[jx][jy] = EL_PLAYER_1;
9225       InitMovingField(jx, jy, MV_DOWN);
9226       Store[jx][jy] = EL_ACID;
9227       ContinueMoving(jx, jy);
9228       BuryHero(player);
9229     }
9230     else
9231       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9232
9233     return MF_MOVING;
9234   }
9235
9236   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9237   if (can_move != MF_MOVING)
9238     return can_move;
9239
9240   /* check if DigField() has caused relocation of the player */
9241   if (player->jx != jx || player->jy != jy)
9242     return MF_NO_ACTION;
9243
9244   StorePlayer[jx][jy] = 0;
9245   player->last_jx = jx;
9246   player->last_jy = jy;
9247   player->jx = new_jx;
9248   player->jy = new_jy;
9249   StorePlayer[new_jx][new_jy] = player->element_nr;
9250
9251   player->MovPos =
9252     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9253
9254   player->step_counter++;
9255
9256 #if 0
9257   player->drop_delay = 0;
9258 #endif
9259
9260   PlayerVisit[jx][jy] = FrameCounter;
9261
9262   ScrollPlayer(player, SCROLL_INIT);
9263
9264 #if 0
9265   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9266   {
9267     CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9268                                       leave_side);
9269     CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9270   }
9271
9272   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9273   {
9274     CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9275                                       CE_OTHER_GETS_ENTERED, enter_side);
9276     CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9277                              CE_ENTERED_BY_PLAYER, enter_side);
9278   }
9279 #endif
9280
9281   return MF_MOVING;
9282 }
9283
9284 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9285 {
9286   int jx = player->jx, jy = player->jy;
9287   int old_jx = jx, old_jy = jy;
9288   int moved = MF_NO_ACTION;
9289
9290 #if 1
9291   if (!player->active)
9292     return FALSE;
9293
9294   if (!dx && !dy)
9295   {
9296     if (player->MovPos == 0)
9297     {
9298       player->is_moving = FALSE;
9299       player->is_digging = FALSE;
9300       player->is_collecting = FALSE;
9301       player->is_snapping = FALSE;
9302       player->is_pushing = FALSE;
9303     }
9304
9305     return FALSE;
9306   }
9307 #else
9308   if (!player->active || (!dx && !dy))
9309     return FALSE;
9310 #endif
9311
9312 #if 0
9313   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9314       !tape.playing)
9315     return FALSE;
9316 #else
9317
9318 #if 1
9319   if (!FrameReached(&player->move_delay, player->move_delay_value))
9320     return FALSE;
9321 #else
9322   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9323       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9324     return FALSE;
9325 #endif
9326
9327 #endif
9328
9329   /* store if player is automatically moved to next field */
9330   player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9331
9332   /* remove the last programmed player action */
9333   player->programmed_action = 0;
9334
9335   if (player->MovPos)
9336   {
9337     /* should only happen if pre-1.2 tape recordings are played */
9338     /* this is only for backward compatibility */
9339
9340     int original_move_delay_value = player->move_delay_value;
9341
9342 #if DEBUG
9343     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9344            tape.counter);
9345 #endif
9346
9347     /* scroll remaining steps with finest movement resolution */
9348     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9349
9350     while (player->MovPos)
9351     {
9352       ScrollPlayer(player, SCROLL_GO_ON);
9353       ScrollScreen(NULL, SCROLL_GO_ON);
9354       FrameCounter++;
9355       DrawAllPlayers();
9356       BackToFront();
9357     }
9358
9359     player->move_delay_value = original_move_delay_value;
9360   }
9361
9362   if (player->last_move_dir & MV_HORIZONTAL)
9363   {
9364     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9365       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9366   }
9367   else
9368   {
9369     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9370       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9371   }
9372
9373   jx = player->jx;
9374   jy = player->jy;
9375
9376   if (moved & MF_MOVING && !ScreenMovPos &&
9377       (player == local_player || !options.network))
9378   {
9379     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9380     int offset = (setup.scroll_delay ? 3 : 0);
9381
9382     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9383     {
9384       /* actual player has left the screen -- scroll in that direction */
9385       if (jx != old_jx)         /* player has moved horizontally */
9386         scroll_x += (jx - old_jx);
9387       else                      /* player has moved vertically */
9388         scroll_y += (jy - old_jy);
9389     }
9390     else
9391     {
9392       if (jx != old_jx)         /* player has moved horizontally */
9393       {
9394         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
9395             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9396           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9397
9398         /* don't scroll over playfield boundaries */
9399         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9400           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9401
9402         /* don't scroll more than one field at a time */
9403         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9404
9405         /* don't scroll against the player's moving direction */
9406         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
9407             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9408           scroll_x = old_scroll_x;
9409       }
9410       else                      /* player has moved vertically */
9411       {
9412         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
9413             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9414           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9415
9416         /* don't scroll over playfield boundaries */
9417         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9418           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9419
9420         /* don't scroll more than one field at a time */
9421         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9422
9423         /* don't scroll against the player's moving direction */
9424         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
9425             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9426           scroll_y = old_scroll_y;
9427       }
9428     }
9429
9430     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9431     {
9432       if (!options.network && !AllPlayersInVisibleScreen())
9433       {
9434         scroll_x = old_scroll_x;
9435         scroll_y = old_scroll_y;
9436       }
9437       else
9438       {
9439         ScrollScreen(player, SCROLL_INIT);
9440         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9441       }
9442     }
9443   }
9444
9445 #if 0
9446 #if 1
9447   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9448 #else
9449   if (!(moved & MF_MOVING) && !player->is_pushing)
9450     player->Frame = 0;
9451 #endif
9452 #endif
9453
9454   player->StepFrame = 0;
9455
9456   if (moved & MF_MOVING)
9457   {
9458     if (old_jx != jx && old_jy == jy)
9459       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9460     else if (old_jx == jx && old_jy != jy)
9461       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9462
9463     DrawLevelField(jx, jy);     /* for "crumbled sand" */
9464
9465     player->last_move_dir = player->MovDir;
9466     player->is_moving = TRUE;
9467 #if 1
9468     player->is_snapping = FALSE;
9469 #endif
9470
9471 #if 1
9472     player->is_switching = FALSE;
9473 #endif
9474
9475     player->is_dropping = FALSE;
9476
9477
9478 #if 0
9479     /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9480
9481 #if 1
9482     if (game.engine_version < VERSION_IDENT(3,1,0,0))
9483 #endif
9484     {
9485       static int trigger_sides[4][2] =
9486       {
9487         /* enter side           leave side */
9488         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
9489         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
9490         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
9491         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
9492       };
9493       int move_direction = player->MovDir;
9494       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9495       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9496       int old_element = Feld[old_jx][old_jy];
9497       int new_element = Feld[jx][jy];
9498
9499 #if 1
9500       /* !!! TEST ONLY !!! */
9501       if (IS_CUSTOM_ELEMENT(old_element))
9502         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9503                                    CE_LEFT_BY_PLAYER,
9504                                    player->index_bit, leave_side);
9505
9506       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9507                                           CE_OTHER_GETS_LEFT,
9508                                           player->index_bit, leave_side);
9509
9510       if (IS_CUSTOM_ELEMENT(new_element))
9511         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9512                                    player->index_bit, enter_side);
9513
9514       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9515                                           CE_OTHER_GETS_ENTERED,
9516                                           player->index_bit, enter_side);
9517 #endif
9518
9519     }
9520 #endif
9521
9522
9523   }
9524   else
9525   {
9526     CheckGravityMovementWhenNotMoving(player);
9527
9528     /*
9529     player->last_move_dir = MV_NO_MOVING;
9530     */
9531     player->is_moving = FALSE;
9532   }
9533
9534   if (game.engine_version < VERSION_IDENT(3,0,7,0))
9535   {
9536     TestIfHeroTouchesBadThing(jx, jy);
9537     TestIfPlayerTouchesCustomElement(jx, jy);
9538   }
9539
9540   if (!player->active)
9541     RemoveHero(player);
9542
9543   return moved;
9544 }
9545
9546 void ScrollPlayer(struct PlayerInfo *player, int mode)
9547 {
9548   int jx = player->jx, jy = player->jy;
9549   int last_jx = player->last_jx, last_jy = player->last_jy;
9550   int move_stepsize = TILEX / player->move_delay_value;
9551
9552   if (!player->active || !player->MovPos)
9553     return;
9554
9555   if (mode == SCROLL_INIT)
9556   {
9557     player->actual_frame_counter = FrameCounter;
9558     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9559
9560     if (Feld[last_jx][last_jy] == EL_EMPTY)
9561       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9562
9563 #if 0
9564     DrawPlayer(player);
9565 #endif
9566
9567     return;
9568   }
9569   else if (!FrameReached(&player->actual_frame_counter, 1))
9570     return;
9571
9572   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9573   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9574
9575   if (!player->block_last_field &&
9576       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9577     Feld[last_jx][last_jy] = EL_EMPTY;
9578
9579   /* before DrawPlayer() to draw correct player graphic for this case */
9580   if (player->MovPos == 0)
9581     CheckGravityMovement(player);
9582
9583 #if 0
9584   DrawPlayer(player);   /* needed here only to cleanup last field */
9585 #endif
9586
9587   if (player->MovPos == 0)      /* player reached destination field */
9588   {
9589 #if 1
9590     if (player->move_delay_reset_counter > 0)
9591     {
9592       player->move_delay_reset_counter--;
9593
9594       if (player->move_delay_reset_counter == 0)
9595       {
9596         /* continue with normal speed after quickly moving through gate */
9597         HALVE_PLAYER_SPEED(player);
9598
9599         /* be able to make the next move without delay */
9600         player->move_delay = 0;
9601       }
9602     }
9603 #else
9604     if (IS_PASSABLE(Feld[last_jx][last_jy]))
9605     {
9606       /* continue with normal speed after quickly moving through gate */
9607       HALVE_PLAYER_SPEED(player);
9608
9609       /* be able to make the next move without delay */
9610       player->move_delay = 0;
9611     }
9612 #endif
9613
9614     if (player->block_last_field &&
9615         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9616       Feld[last_jx][last_jy] = EL_EMPTY;
9617
9618     player->last_jx = jx;
9619     player->last_jy = jy;
9620
9621     if (Feld[jx][jy] == EL_EXIT_OPEN ||
9622         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9623         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
9624     {
9625       DrawPlayer(player);       /* needed here only to cleanup last field */
9626       RemoveHero(player);
9627
9628       if (local_player->friends_still_needed == 0 ||
9629           IS_SP_ELEMENT(Feld[jx][jy]))
9630         player->LevelSolved = player->GameOver = TRUE;
9631     }
9632
9633 #if 1
9634     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9635     /* this breaks one level: "machine", level 000 */
9636 #if 0
9637     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9638 #endif
9639     {
9640       static int trigger_sides[4][2] =
9641       {
9642         /* enter side           leave side */
9643         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
9644         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
9645         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
9646         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
9647       };
9648       int move_direction = player->MovDir;
9649       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9650       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9651       int old_jx = last_jx;
9652       int old_jy = last_jy;
9653       int old_element = Feld[old_jx][old_jy];
9654       int new_element = Feld[jx][jy];
9655
9656 #if 1
9657       /* !!! TEST ONLY !!! */
9658       if (IS_CUSTOM_ELEMENT(old_element))
9659         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9660                                    CE_LEFT_BY_PLAYER,
9661                                    player->index_bit, leave_side);
9662
9663       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9664                                           CE_OTHER_GETS_LEFT,
9665                                           player->index_bit, leave_side);
9666
9667       if (IS_CUSTOM_ELEMENT(new_element))
9668         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9669                                    player->index_bit, enter_side);
9670
9671       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9672                                           CE_OTHER_GETS_ENTERED,
9673                                           player->index_bit, enter_side);
9674 #endif
9675
9676     }
9677 #endif
9678
9679     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9680     {
9681       TestIfHeroTouchesBadThing(jx, jy);
9682       TestIfPlayerTouchesCustomElement(jx, jy);
9683 #if 1
9684       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
9685 #endif
9686
9687       if (!player->active)
9688         RemoveHero(player);
9689     }
9690
9691     if (level.use_step_counter)
9692     {
9693       int i;
9694
9695       TimePlayed++;
9696
9697       for (i = 0; i < MAX_PLAYERS; i++)
9698       {
9699         struct PlayerInfo *player = &stored_player[i];
9700
9701         if (SHIELD_ON(player))
9702         {
9703           player->shield_normal_time_left--;
9704
9705           if (player->shield_deadly_time_left > 0)
9706             player->shield_deadly_time_left--;
9707         }
9708       }
9709
9710       if (TimeLeft > 0)
9711       {
9712         TimeLeft--;
9713
9714         if (TimeLeft <= 10 && setup.time_limit)
9715           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9716
9717         DrawGameValue_Time(TimeLeft);
9718
9719         if (!TimeLeft && setup.time_limit)
9720           for (i = 0; i < MAX_PLAYERS; i++)
9721             KillHero(&stored_player[i]);
9722       }
9723       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9724         DrawGameValue_Time(TimePlayed);
9725     }
9726
9727     if (tape.single_step && tape.recording && !tape.pausing &&
9728         !player->programmed_action)
9729       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9730   }
9731 }
9732
9733 void ScrollScreen(struct PlayerInfo *player, int mode)
9734 {
9735   static unsigned long screen_frame_counter = 0;
9736
9737   if (mode == SCROLL_INIT)
9738   {
9739     /* set scrolling step size according to actual player's moving speed */
9740     ScrollStepSize = TILEX / player->move_delay_value;
9741
9742     screen_frame_counter = FrameCounter;
9743     ScreenMovDir = player->MovDir;
9744     ScreenMovPos = player->MovPos;
9745     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9746     return;
9747   }
9748   else if (!FrameReached(&screen_frame_counter, 1))
9749     return;
9750
9751   if (ScreenMovPos)
9752   {
9753     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9754     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9755     redraw_mask |= REDRAW_FIELD;
9756   }
9757   else
9758     ScreenMovDir = MV_NO_MOVING;
9759 }
9760
9761 void TestIfPlayerTouchesCustomElement(int x, int y)
9762 {
9763   static int xy[4][2] =
9764   {
9765     { 0, -1 },
9766     { -1, 0 },
9767     { +1, 0 },
9768     { 0, +1 }
9769   };
9770   static int trigger_sides[4][2] =
9771   {
9772     /* center side       border side */
9773     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9774     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9775     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9776     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9777   };
9778   static int touch_dir[4] =
9779   {
9780     MV_LEFT | MV_RIGHT,
9781     MV_UP   | MV_DOWN,
9782     MV_UP   | MV_DOWN,
9783     MV_LEFT | MV_RIGHT
9784   };
9785   int center_element = Feld[x][y];      /* should always be non-moving! */
9786   int i;
9787
9788   for (i = 0; i < NUM_DIRECTIONS; i++)
9789   {
9790     int xx = x + xy[i][0];
9791     int yy = y + xy[i][1];
9792     int center_side = trigger_sides[i][0];
9793     int border_side = trigger_sides[i][1];
9794     int border_element;
9795
9796     if (!IN_LEV_FIELD(xx, yy))
9797       continue;
9798
9799     if (IS_PLAYER(x, y))
9800     {
9801       struct PlayerInfo *player = PLAYERINFO(x, y);
9802
9803       if (game.engine_version < VERSION_IDENT(3,0,7,0))
9804         border_element = Feld[xx][yy];          /* may be moving! */
9805       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9806         border_element = Feld[xx][yy];
9807       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
9808         border_element = MovingOrBlocked2Element(xx, yy);
9809       else
9810         continue;               /* center and border element do not touch */
9811
9812 #if 1
9813       /* !!! TEST ONLY !!! */
9814       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9815                                  player->index_bit, border_side);
9816       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9817                                           CE_OTHER_GETS_TOUCHED,
9818                                           player->index_bit, border_side);
9819 #else
9820       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9821                                           CE_OTHER_GETS_TOUCHED,
9822                                           player->index_bit, border_side);
9823       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9824                                  player->index_bit, border_side);
9825 #endif
9826     }
9827     else if (IS_PLAYER(xx, yy))
9828     {
9829       struct PlayerInfo *player = PLAYERINFO(xx, yy);
9830
9831       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9832       {
9833         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9834           continue;             /* center and border element do not touch */
9835       }
9836
9837 #if 1
9838       /* !!! TEST ONLY !!! */
9839       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9840                                  player->index_bit, center_side);
9841       CheckTriggeredElementChangeByPlayer(x, y, center_element,
9842                                           CE_OTHER_GETS_TOUCHED,
9843                                           player->index_bit, center_side);
9844 #else
9845       CheckTriggeredElementChangeByPlayer(x, y, center_element,
9846                                           CE_OTHER_GETS_TOUCHED,
9847                                           player->index_bit, center_side);
9848       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9849                                  player->index_bit, center_side);
9850 #endif
9851
9852       break;
9853     }
9854   }
9855 }
9856
9857 void TestIfElementTouchesCustomElement(int x, int y)
9858 {
9859   static int xy[4][2] =
9860   {
9861     { 0, -1 },
9862     { -1, 0 },
9863     { +1, 0 },
9864     { 0, +1 }
9865   };
9866   static int trigger_sides[4][2] =
9867   {
9868     /* center side      border side */
9869     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9870     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9871     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9872     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9873   };
9874   static int touch_dir[4] =
9875   {
9876     MV_LEFT | MV_RIGHT,
9877     MV_UP   | MV_DOWN,
9878     MV_UP   | MV_DOWN,
9879     MV_LEFT | MV_RIGHT
9880   };
9881   boolean change_center_element = FALSE;
9882   int center_element_change_page = 0;
9883   int center_element = Feld[x][y];      /* should always be non-moving! */
9884   int border_trigger_element;
9885   int i, j;
9886
9887   for (i = 0; i < NUM_DIRECTIONS; i++)
9888   {
9889     int xx = x + xy[i][0];
9890     int yy = y + xy[i][1];
9891     int center_side = trigger_sides[i][0];
9892     int border_side = trigger_sides[i][1];
9893     int border_element;
9894
9895     if (!IN_LEV_FIELD(xx, yy))
9896       continue;
9897
9898     if (game.engine_version < VERSION_IDENT(3,0,7,0))
9899       border_element = Feld[xx][yy];    /* may be moving! */
9900     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9901       border_element = Feld[xx][yy];
9902     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
9903       border_element = MovingOrBlocked2Element(xx, yy);
9904     else
9905       continue;                 /* center and border element do not touch */
9906
9907     /* check for change of center element (but change it only once) */
9908     if (IS_CUSTOM_ELEMENT(center_element) &&
9909         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9910         !change_center_element)
9911     {
9912       for (j = 0; j < element_info[center_element].num_change_pages; j++)
9913       {
9914         struct ElementChangeInfo *change =
9915           &element_info[center_element].change_page[j];
9916
9917         if (change->can_change &&
9918             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9919             change->trigger_side & border_side &&
9920 #if 1
9921             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9922 #else
9923             change->trigger_element == border_element
9924 #endif
9925             )
9926         {
9927           change_center_element = TRUE;
9928           center_element_change_page = j;
9929           border_trigger_element = border_element;
9930
9931           break;
9932         }
9933       }
9934     }
9935
9936     /* check for change of border element */
9937     if (IS_CUSTOM_ELEMENT(border_element) &&
9938         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9939     {
9940       for (j = 0; j < element_info[border_element].num_change_pages; j++)
9941       {
9942         struct ElementChangeInfo *change =
9943           &element_info[border_element].change_page[j];
9944
9945         if (change->can_change &&
9946             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9947             change->trigger_side & center_side &&
9948 #if 1
9949             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9950 #else
9951             change->trigger_element == center_element
9952 #endif
9953             )
9954         {
9955 #if 0
9956           printf("::: border_element %d, %d\n", x, y);
9957 #endif
9958
9959           CheckElementChangeByPage(xx, yy, border_element, center_element,
9960                                    CE_OTHER_IS_TOUCHING, j);
9961           break;
9962         }
9963       }
9964     }
9965   }
9966
9967   if (change_center_element)
9968   {
9969 #if 0
9970     printf("::: center_element %d, %d\n", x, y);
9971 #endif
9972
9973     CheckElementChangeByPage(x, y, center_element, border_trigger_element,
9974                              CE_OTHER_IS_TOUCHING, center_element_change_page);
9975   }
9976 }
9977
9978 void TestIfElementHitsCustomElement(int x, int y, int direction)
9979 {
9980   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9981   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9982   int hitx = x + dx, hity = y + dy;
9983   int hitting_element = Feld[x][y];
9984   int touched_element;
9985 #if 0
9986   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
9987                         !IS_FREE(hitx, hity) &&
9988                         (!IS_MOVING(hitx, hity) ||
9989                          MovDir[hitx][hity] != direction ||
9990                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
9991 #endif
9992
9993   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
9994     return;
9995
9996 #if 0
9997   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
9998     return;
9999 #endif
10000
10001   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10002                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10003
10004   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10005                            CE_HITTING_SOMETHING, direction);
10006
10007   if (IN_LEV_FIELD(hitx, hity))
10008   {
10009     int opposite_direction = MV_DIR_OPPOSITE(direction);
10010     int hitting_side = direction;
10011     int touched_side = opposite_direction;
10012 #if 0
10013     int touched_element = MovingOrBlocked2Element(hitx, hity);
10014 #endif
10015 #if 1
10016     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10017                           MovDir[hitx][hity] != direction ||
10018                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10019
10020     object_hit = TRUE;
10021 #endif
10022
10023     if (object_hit)
10024     {
10025       int i;
10026
10027       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10028                                CE_HIT_BY_SOMETHING, opposite_direction);
10029
10030       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10031           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10032       {
10033         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10034         {
10035           struct ElementChangeInfo *change =
10036             &element_info[hitting_element].change_page[i];
10037
10038           if (change->can_change &&
10039               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10040               change->trigger_side & touched_side &&
10041           
10042 #if 1
10043               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10044 #else
10045               change->trigger_element == touched_element
10046 #endif
10047               )
10048           {
10049             CheckElementChangeByPage(x, y, hitting_element, touched_element,
10050                                      CE_OTHER_IS_HITTING, i);
10051             break;
10052           }
10053         }
10054       }
10055
10056       if (IS_CUSTOM_ELEMENT(touched_element) &&
10057           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10058       {
10059         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10060         {
10061           struct ElementChangeInfo *change =
10062             &element_info[touched_element].change_page[i];
10063
10064           if (change->can_change &&
10065               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10066               change->trigger_side & hitting_side &&
10067 #if 1
10068               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10069 #else
10070               change->trigger_element == hitting_element
10071 #endif
10072               )
10073           {
10074             CheckElementChangeByPage(hitx, hity, touched_element,
10075                                      hitting_element, CE_OTHER_GETS_HIT, i);
10076             break;
10077           }
10078         }
10079       }
10080     }
10081   }
10082 }
10083
10084 #if 0
10085 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10086 {
10087   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10088   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10089   int hitx = x + dx, hity = y + dy;
10090   int hitting_element = Feld[x][y];
10091   int touched_element;
10092 #if 0
10093   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10094                         !IS_FREE(hitx, hity) &&
10095                         (!IS_MOVING(hitx, hity) ||
10096                          MovDir[hitx][hity] != direction ||
10097                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10098 #endif
10099
10100   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10101     return;
10102
10103 #if 0
10104   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10105     return;
10106 #endif
10107
10108   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10109                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10110
10111   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10112                            EP_CAN_SMASH_EVERYTHING, direction);
10113
10114   if (IN_LEV_FIELD(hitx, hity))
10115   {
10116     int opposite_direction = MV_DIR_OPPOSITE(direction);
10117     int hitting_side = direction;
10118     int touched_side = opposite_direction;
10119 #if 0
10120     int touched_element = MovingOrBlocked2Element(hitx, hity);
10121 #endif
10122 #if 1
10123     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10124                           MovDir[hitx][hity] != direction ||
10125                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10126
10127     object_hit = TRUE;
10128 #endif
10129
10130     if (object_hit)
10131     {
10132       int i;
10133
10134       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10135                                CE_SMASHED_BY_SOMETHING, opposite_direction);
10136
10137       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10138           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10139       {
10140         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10141         {
10142           struct ElementChangeInfo *change =
10143             &element_info[hitting_element].change_page[i];
10144
10145           if (change->can_change &&
10146               change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10147               change->trigger_side & touched_side &&
10148           
10149 #if 1
10150               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10151 #else
10152               change->trigger_element == touched_element
10153 #endif
10154               )
10155           {
10156             CheckElementChangeByPage(x, y, hitting_element, touched_element,
10157                                      CE_OTHER_IS_SMASHING, i);
10158             break;
10159           }
10160         }
10161       }
10162
10163       if (IS_CUSTOM_ELEMENT(touched_element) &&
10164           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10165       {
10166         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10167         {
10168           struct ElementChangeInfo *change =
10169             &element_info[touched_element].change_page[i];
10170
10171           if (change->can_change &&
10172               change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10173               change->trigger_side & hitting_side &&
10174 #if 1
10175               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10176 #else
10177               change->trigger_element == hitting_element
10178 #endif
10179               )
10180           {
10181             CheckElementChangeByPage(hitx, hity, touched_element,
10182                                      hitting_element, CE_OTHER_GETS_SMASHED,i);
10183             break;
10184           }
10185         }
10186       }
10187     }
10188   }
10189 }
10190 #endif
10191
10192 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10193 {
10194   int i, kill_x = -1, kill_y = -1;
10195   int bad_element = -1;
10196   static int test_xy[4][2] =
10197   {
10198     { 0, -1 },
10199     { -1, 0 },
10200     { +1, 0 },
10201     { 0, +1 }
10202   };
10203   static int test_dir[4] =
10204   {
10205     MV_UP,
10206     MV_LEFT,
10207     MV_RIGHT,
10208     MV_DOWN
10209   };
10210
10211   for (i = 0; i < NUM_DIRECTIONS; i++)
10212   {
10213     int test_x, test_y, test_move_dir, test_element;
10214
10215     test_x = good_x + test_xy[i][0];
10216     test_y = good_y + test_xy[i][1];
10217
10218     if (!IN_LEV_FIELD(test_x, test_y))
10219       continue;
10220
10221     test_move_dir =
10222       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10223
10224 #if 0
10225     test_element = Feld[test_x][test_y];
10226 #else
10227     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10228 #endif
10229
10230     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10231        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10232     */
10233     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10234         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
10235     {
10236       kill_x = test_x;
10237       kill_y = test_y;
10238       bad_element = test_element;
10239
10240       break;
10241     }
10242   }
10243
10244   if (kill_x != -1 || kill_y != -1)
10245   {
10246     if (IS_PLAYER(good_x, good_y))
10247     {
10248       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10249
10250 #if 1
10251       if (player->shield_deadly_time_left > 0 &&
10252           !IS_INDESTRUCTIBLE(bad_element))
10253         Bang(kill_x, kill_y);
10254       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10255         KillHero(player);
10256 #else
10257       if (player->shield_deadly_time_left > 0)
10258         Bang(kill_x, kill_y);
10259       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10260         KillHero(player);
10261 #endif
10262     }
10263     else
10264       Bang(good_x, good_y);
10265   }
10266 }
10267
10268 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10269 {
10270   int i, kill_x = -1, kill_y = -1;
10271   int bad_element = Feld[bad_x][bad_y];
10272   static int test_xy[4][2] =
10273   {
10274     { 0, -1 },
10275     { -1, 0 },
10276     { +1, 0 },
10277     { 0, +1 }
10278   };
10279   static int touch_dir[4] =
10280   {
10281     MV_LEFT | MV_RIGHT,
10282     MV_UP   | MV_DOWN,
10283     MV_UP   | MV_DOWN,
10284     MV_LEFT | MV_RIGHT
10285   };
10286   static int test_dir[4] =
10287   {
10288     MV_UP,
10289     MV_LEFT,
10290     MV_RIGHT,
10291     MV_DOWN
10292   };
10293
10294   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
10295     return;
10296
10297   for (i = 0; i < NUM_DIRECTIONS; i++)
10298   {
10299     int test_x, test_y, test_move_dir, test_element;
10300
10301     test_x = bad_x + test_xy[i][0];
10302     test_y = bad_y + test_xy[i][1];
10303     if (!IN_LEV_FIELD(test_x, test_y))
10304       continue;
10305
10306     test_move_dir =
10307       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10308
10309     test_element = Feld[test_x][test_y];
10310
10311     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10312        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10313     */
10314     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
10315         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
10316     {
10317       /* good thing is player or penguin that does not move away */
10318       if (IS_PLAYER(test_x, test_y))
10319       {
10320         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10321
10322         if (bad_element == EL_ROBOT && player->is_moving)
10323           continue;     /* robot does not kill player if he is moving */
10324
10325         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10326         {
10327           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10328             continue;           /* center and border element do not touch */
10329         }
10330
10331         kill_x = test_x;
10332         kill_y = test_y;
10333         break;
10334       }
10335       else if (test_element == EL_PENGUIN)
10336       {
10337         kill_x = test_x;
10338         kill_y = test_y;
10339         break;
10340       }
10341     }
10342   }
10343
10344   if (kill_x != -1 || kill_y != -1)
10345   {
10346     if (IS_PLAYER(kill_x, kill_y))
10347     {
10348       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10349
10350 #if 1
10351       if (player->shield_deadly_time_left > 0 &&
10352           !IS_INDESTRUCTIBLE(bad_element))
10353         Bang(bad_x, bad_y);
10354       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10355         KillHero(player);
10356 #else
10357       if (player->shield_deadly_time_left > 0)
10358         Bang(bad_x, bad_y);
10359       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10360         KillHero(player);
10361 #endif
10362     }
10363     else
10364       Bang(kill_x, kill_y);
10365   }
10366 }
10367
10368 void TestIfHeroTouchesBadThing(int x, int y)
10369 {
10370   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10371 }
10372
10373 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10374 {
10375   TestIfGoodThingHitsBadThing(x, y, move_dir);
10376 }
10377
10378 void TestIfBadThingTouchesHero(int x, int y)
10379 {
10380   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10381 }
10382
10383 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10384 {
10385   TestIfBadThingHitsGoodThing(x, y, move_dir);
10386 }
10387
10388 void TestIfFriendTouchesBadThing(int x, int y)
10389 {
10390   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10391 }
10392
10393 void TestIfBadThingTouchesFriend(int x, int y)
10394 {
10395   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10396 }
10397
10398 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10399 {
10400   int i, kill_x = bad_x, kill_y = bad_y;
10401   static int xy[4][2] =
10402   {
10403     { 0, -1 },
10404     { -1, 0 },
10405     { +1, 0 },
10406     { 0, +1 }
10407   };
10408
10409   for (i = 0; i < NUM_DIRECTIONS; i++)
10410   {
10411     int x, y, element;
10412
10413     x = bad_x + xy[i][0];
10414     y = bad_y + xy[i][1];
10415     if (!IN_LEV_FIELD(x, y))
10416       continue;
10417
10418     element = Feld[x][y];
10419     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10420         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10421     {
10422       kill_x = x;
10423       kill_y = y;
10424       break;
10425     }
10426   }
10427
10428   if (kill_x != bad_x || kill_y != bad_y)
10429     Bang(bad_x, bad_y);
10430 }
10431
10432 void KillHero(struct PlayerInfo *player)
10433 {
10434   int jx = player->jx, jy = player->jy;
10435
10436   if (!player->active)
10437     return;
10438
10439   /* remove accessible field at the player's position */
10440   Feld[jx][jy] = EL_EMPTY;
10441
10442   /* deactivate shield (else Bang()/Explode() would not work right) */
10443   player->shield_normal_time_left = 0;
10444   player->shield_deadly_time_left = 0;
10445
10446   Bang(jx, jy);
10447   BuryHero(player);
10448 }
10449
10450 static void KillHeroUnlessEnemyProtected(int x, int y)
10451 {
10452   if (!PLAYER_ENEMY_PROTECTED(x, y))
10453     KillHero(PLAYERINFO(x, y));
10454 }
10455
10456 static void KillHeroUnlessExplosionProtected(int x, int y)
10457 {
10458   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10459     KillHero(PLAYERINFO(x, y));
10460 }
10461
10462 void BuryHero(struct PlayerInfo *player)
10463 {
10464   int jx = player->jx, jy = player->jy;
10465
10466   if (!player->active)
10467     return;
10468
10469 #if 1
10470   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10471 #else
10472   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10473 #endif
10474   PlayLevelSound(jx, jy, SND_GAME_LOSING);
10475
10476   player->GameOver = TRUE;
10477   RemoveHero(player);
10478 }
10479
10480 void RemoveHero(struct PlayerInfo *player)
10481 {
10482   int jx = player->jx, jy = player->jy;
10483   int i, found = FALSE;
10484
10485   player->present = FALSE;
10486   player->active = FALSE;
10487
10488   if (!ExplodeField[jx][jy])
10489     StorePlayer[jx][jy] = 0;
10490
10491   for (i = 0; i < MAX_PLAYERS; i++)
10492     if (stored_player[i].active)
10493       found = TRUE;
10494
10495   if (!found)
10496     AllPlayersGone = TRUE;
10497
10498   ExitX = ZX = jx;
10499   ExitY = ZY = jy;
10500 }
10501
10502 /*
10503   =============================================================================
10504   checkDiagonalPushing()
10505   -----------------------------------------------------------------------------
10506   check if diagonal input device direction results in pushing of object
10507   (by checking if the alternative direction is walkable, diggable, ...)
10508   =============================================================================
10509 */
10510
10511 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10512                                     int x, int y, int real_dx, int real_dy)
10513 {
10514   int jx, jy, dx, dy, xx, yy;
10515
10516   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
10517     return TRUE;
10518
10519   /* diagonal direction: check alternative direction */
10520   jx = player->jx;
10521   jy = player->jy;
10522   dx = x - jx;
10523   dy = y - jy;
10524   xx = jx + (dx == 0 ? real_dx : 0);
10525   yy = jy + (dy == 0 ? real_dy : 0);
10526
10527   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10528 }
10529
10530 /*
10531   =============================================================================
10532   DigField()
10533   -----------------------------------------------------------------------------
10534   x, y:                 field next to player (non-diagonal) to try to dig to
10535   real_dx, real_dy:     direction as read from input device (can be diagonal)
10536   =============================================================================
10537 */
10538
10539 int DigField(struct PlayerInfo *player,
10540              int oldx, int oldy, int x, int y,
10541              int real_dx, int real_dy, int mode)
10542 {
10543   static int trigger_sides[4] =
10544   {
10545     CH_SIDE_RIGHT,      /* moving left  */
10546     CH_SIDE_LEFT,       /* moving right */
10547     CH_SIDE_BOTTOM,     /* moving up    */
10548     CH_SIDE_TOP,        /* moving down  */
10549   };
10550 #if 0
10551   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10552 #endif
10553   int jx = oldx, jy = oldy;
10554   int dx = x - jx, dy = y - jy;
10555   int nextx = x + dx, nexty = y + dy;
10556   int move_direction = (dx == -1 ? MV_LEFT :
10557                         dx == +1 ? MV_RIGHT :
10558                         dy == -1 ? MV_UP :
10559                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
10560   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10561   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10562   int old_element = Feld[jx][jy];
10563   int element;
10564
10565   if (player->MovPos == 0)
10566   {
10567     player->is_digging = FALSE;
10568     player->is_collecting = FALSE;
10569   }
10570
10571   if (player->MovPos == 0)      /* last pushing move finished */
10572     player->is_pushing = FALSE;
10573
10574   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
10575   {
10576     player->is_switching = FALSE;
10577     player->push_delay = 0;
10578
10579     return MF_NO_ACTION;
10580   }
10581
10582   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10583     return MF_NO_ACTION;
10584
10585 #if 0
10586
10587 #if 0
10588   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10589 #else
10590   if (IS_TUBE(Feld[jx][jy]) ||
10591       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10592 #endif
10593   {
10594     int i = 0;
10595     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10596     int tube_leave_directions[][2] =
10597     {
10598       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10599       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
10600       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
10601       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
10602       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
10603       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
10604       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
10605       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
10606       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
10607       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
10608       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
10609       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10610     };
10611
10612     while (tube_leave_directions[i][0] != tube_element)
10613     {
10614       i++;
10615       if (tube_leave_directions[i][0] == -1)    /* should not happen */
10616         break;
10617     }
10618
10619     if (!(tube_leave_directions[i][1] & move_direction))
10620       return MF_NO_ACTION;      /* tube has no opening in this direction */
10621   }
10622
10623 #else
10624
10625   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10626     old_element = Back[jx][jy];
10627
10628 #endif
10629
10630   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10631     return MF_NO_ACTION;        /* field has no opening in this direction */
10632
10633   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10634     return MF_NO_ACTION;        /* field has no opening in this direction */
10635
10636   element = Feld[x][y];
10637
10638   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10639       game.engine_version >= VERSION_IDENT(2,2,0,0))
10640     return MF_NO_ACTION;
10641
10642 #if 1
10643   if (game.gravity && !player->is_auto_moving &&
10644       canFallDown(player) && move_direction != MV_DOWN &&
10645       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10646     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
10647 #endif
10648
10649 #if 0
10650   if (element == EL_EMPTY_SPACE &&
10651       game.gravity && !player->is_auto_moving &&
10652       canFallDown(player) && move_direction != MV_DOWN)
10653     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
10654 #endif
10655
10656   switch (element)
10657   {
10658 #if 0
10659     case EL_SP_PORT_LEFT:
10660     case EL_SP_PORT_RIGHT:
10661     case EL_SP_PORT_UP:
10662     case EL_SP_PORT_DOWN:
10663     case EL_SP_PORT_HORIZONTAL:
10664     case EL_SP_PORT_VERTICAL:
10665     case EL_SP_PORT_ANY:
10666     case EL_SP_GRAVITY_PORT_LEFT:
10667     case EL_SP_GRAVITY_PORT_RIGHT:
10668     case EL_SP_GRAVITY_PORT_UP:
10669     case EL_SP_GRAVITY_PORT_DOWN:
10670 #if 1
10671       if (!canEnterSupaplexPort(x, y, dx, dy))
10672         return MF_NO_ACTION;
10673 #else
10674       if ((dx == -1 &&
10675            element != EL_SP_PORT_LEFT &&
10676            element != EL_SP_GRAVITY_PORT_LEFT &&
10677            element != EL_SP_PORT_HORIZONTAL &&
10678            element != EL_SP_PORT_ANY) ||
10679           (dx == +1 &&
10680            element != EL_SP_PORT_RIGHT &&
10681            element != EL_SP_GRAVITY_PORT_RIGHT &&
10682            element != EL_SP_PORT_HORIZONTAL &&
10683            element != EL_SP_PORT_ANY) ||
10684           (dy == -1 &&
10685            element != EL_SP_PORT_UP &&
10686            element != EL_SP_GRAVITY_PORT_UP &&
10687            element != EL_SP_PORT_VERTICAL &&
10688            element != EL_SP_PORT_ANY) ||
10689           (dy == +1 &&
10690            element != EL_SP_PORT_DOWN &&
10691            element != EL_SP_GRAVITY_PORT_DOWN &&
10692            element != EL_SP_PORT_VERTICAL &&
10693            element != EL_SP_PORT_ANY) ||
10694           !IN_LEV_FIELD(nextx, nexty) ||
10695           !IS_FREE(nextx, nexty))
10696         return MF_NO_ACTION;
10697 #endif
10698
10699       if (element == EL_SP_GRAVITY_PORT_LEFT ||
10700           element == EL_SP_GRAVITY_PORT_RIGHT ||
10701           element == EL_SP_GRAVITY_PORT_UP ||
10702           element == EL_SP_GRAVITY_PORT_DOWN)
10703         game.gravity = !game.gravity;
10704
10705       /* automatically move to the next field with double speed */
10706       player->programmed_action = move_direction;
10707 #if 1
10708       if (player->move_delay_reset_counter == 0)
10709       {
10710         player->move_delay_reset_counter = 2;   /* two double speed steps */
10711
10712         DOUBLE_PLAYER_SPEED(player);
10713       }
10714 #else
10715       player->move_delay_reset_counter = 2;
10716
10717       DOUBLE_PLAYER_SPEED(player);
10718 #endif
10719
10720 #if 0
10721       printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10722 #endif
10723
10724       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10725       break;
10726 #endif
10727
10728 #if 0
10729     case EL_TUBE_ANY:
10730     case EL_TUBE_VERTICAL:
10731     case EL_TUBE_HORIZONTAL:
10732     case EL_TUBE_VERTICAL_LEFT:
10733     case EL_TUBE_VERTICAL_RIGHT:
10734     case EL_TUBE_HORIZONTAL_UP:
10735     case EL_TUBE_HORIZONTAL_DOWN:
10736     case EL_TUBE_LEFT_UP:
10737     case EL_TUBE_LEFT_DOWN:
10738     case EL_TUBE_RIGHT_UP:
10739     case EL_TUBE_RIGHT_DOWN:
10740       {
10741         int i = 0;
10742         int tube_enter_directions[][2] =
10743         {
10744           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10745           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
10746           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
10747           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
10748           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
10749           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
10750           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
10751           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
10752           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
10753           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
10754           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
10755           { -1,                         MV_NO_MOVING                         }
10756         };
10757
10758         while (tube_enter_directions[i][0] != element)
10759         {
10760           i++;
10761           if (tube_enter_directions[i][0] == -1)        /* should not happen */
10762             break;
10763         }
10764
10765         if (!(tube_enter_directions[i][1] & move_direction))
10766           return MF_NO_ACTION;  /* tube has no opening in this direction */
10767
10768         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10769       }
10770       break;
10771 #endif
10772
10773     default:
10774
10775 #if 1
10776       if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10777 #else
10778       if (IS_WALKABLE(element))
10779 #endif
10780       {
10781         int sound_action = ACTION_WALKING;
10782
10783 #if 0
10784         if (!ACCESS_FROM(element, opposite_direction))
10785           return MF_NO_ACTION;  /* field not accessible from this direction */
10786 #endif
10787
10788 #if 0
10789         if (element == EL_EMPTY_SPACE &&
10790             game.gravity && !player->is_auto_moving &&
10791             canFallDown(player) && move_direction != MV_DOWN)
10792           return MF_NO_ACTION;  /* player cannot walk here due to gravity */
10793 #endif
10794
10795         if (IS_GATE(element))
10796         {
10797           if (!player->key[element - EL_GATE_1])
10798             return MF_NO_ACTION;
10799         }
10800         else if (IS_GATE_GRAY(element))
10801         {
10802           if (!player->key[element - EL_GATE_1_GRAY])
10803             return MF_NO_ACTION;
10804         }
10805         else if (element == EL_EXIT_OPEN ||
10806                  element == EL_SP_EXIT_OPEN ||
10807                  element == EL_SP_EXIT_OPENING)
10808         {
10809           sound_action = ACTION_PASSING;        /* player is passing exit */
10810         }
10811         else if (element == EL_EMPTY)
10812         {
10813           sound_action = ACTION_MOVING;         /* nothing to walk on */
10814         }
10815
10816         /* play sound from background or player, whatever is available */
10817         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
10818           PlayLevelSoundElementAction(x, y, element, sound_action);
10819         else
10820           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10821
10822         break;
10823       }
10824 #if 1
10825       else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10826 #else
10827       else if (IS_PASSABLE(element))
10828 #endif
10829       {
10830 #if 0
10831         if (!canPassField(x, y, move_direction))
10832           return MF_NO_ACTION;
10833 #else
10834
10835 #if 0
10836 #if 1
10837         if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10838             !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10839             (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10840           return MF_NO_ACTION;
10841 #else
10842         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10843           return MF_NO_ACTION;
10844 #endif
10845 #endif
10846
10847 #if 1
10848         if (!ACCESS_FROM(element, opposite_direction))
10849           return MF_NO_ACTION;  /* field not accessible from this direction */
10850 #else
10851         if (IS_CUSTOM_ELEMENT(element) &&
10852             !ACCESS_FROM(element, opposite_direction))
10853           return MF_NO_ACTION;  /* field not accessible from this direction */
10854 #endif
10855
10856 #if 1
10857         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
10858           return MF_NO_ACTION;
10859 #endif
10860
10861 #endif
10862
10863         if (IS_EM_GATE(element))
10864         {
10865           if (!player->key[element - EL_EM_GATE_1])
10866             return MF_NO_ACTION;
10867         }
10868         else if (IS_EM_GATE_GRAY(element))
10869         {
10870           if (!player->key[element - EL_EM_GATE_1_GRAY])
10871             return MF_NO_ACTION;
10872         }
10873         else if (IS_SP_PORT(element))
10874         {
10875           if (element == EL_SP_GRAVITY_PORT_LEFT ||
10876               element == EL_SP_GRAVITY_PORT_RIGHT ||
10877               element == EL_SP_GRAVITY_PORT_UP ||
10878               element == EL_SP_GRAVITY_PORT_DOWN)
10879             game.gravity = !game.gravity;
10880         }
10881
10882         /* automatically move to the next field with double speed */
10883         player->programmed_action = move_direction;
10884 #if 1
10885         if (player->move_delay_reset_counter == 0)
10886         {
10887           player->move_delay_reset_counter = 2; /* two double speed steps */
10888
10889           DOUBLE_PLAYER_SPEED(player);
10890         }
10891 #else
10892         player->move_delay_reset_counter = 2;
10893
10894         DOUBLE_PLAYER_SPEED(player);
10895 #endif
10896
10897         PlayLevelSoundAction(x, y, ACTION_PASSING);
10898
10899         break;
10900       }
10901       else if (IS_DIGGABLE(element))
10902       {
10903         RemoveField(x, y);
10904
10905         if (mode != DF_SNAP)
10906         {
10907 #if 1
10908           GfxElement[x][y] = GFX_ELEMENT(element);
10909 #else
10910           GfxElement[x][y] =
10911             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10912 #endif
10913           player->is_digging = TRUE;
10914         }
10915
10916         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10917
10918         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10919                                             player->index_bit, dig_side);
10920
10921 #if 1
10922         if (mode == DF_SNAP)
10923           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
10924 #endif
10925
10926         break;
10927       }
10928       else if (IS_COLLECTIBLE(element))
10929       {
10930         RemoveField(x, y);
10931
10932         if (mode != DF_SNAP)
10933         {
10934           GfxElement[x][y] = element;
10935           player->is_collecting = TRUE;
10936         }
10937
10938         if (element == EL_SPEED_PILL)
10939           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10940         else if (element == EL_EXTRA_TIME && level.time > 0)
10941         {
10942           TimeLeft += 10;
10943           DrawGameValue_Time(TimeLeft);
10944         }
10945         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10946         {
10947           player->shield_normal_time_left += 10;
10948           if (element == EL_SHIELD_DEADLY)
10949             player->shield_deadly_time_left += 10;
10950         }
10951         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10952         {
10953           if (player->inventory_size < MAX_INVENTORY_SIZE)
10954             player->inventory_element[player->inventory_size++] = element;
10955
10956           DrawGameValue_Dynamite(local_player->inventory_size);
10957         }
10958         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10959         {
10960           player->dynabomb_count++;
10961           player->dynabombs_left++;
10962         }
10963         else if (element == EL_DYNABOMB_INCREASE_SIZE)
10964         {
10965           player->dynabomb_size++;
10966         }
10967         else if (element == EL_DYNABOMB_INCREASE_POWER)
10968         {
10969           player->dynabomb_xl = TRUE;
10970         }
10971         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
10972                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
10973         {
10974           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
10975                         element - EL_KEY_1 : element - EL_EM_KEY_1);
10976
10977           player->key[key_nr] = TRUE;
10978
10979           DrawGameValue_Keys(player);
10980
10981           redraw_mask |= REDRAW_DOOR_1;
10982         }
10983         else if (IS_ENVELOPE(element))
10984         {
10985 #if 1
10986           player->show_envelope = element;
10987 #else
10988           ShowEnvelope(element - EL_ENVELOPE_1);
10989 #endif
10990         }
10991         else if (IS_DROPPABLE(element) ||
10992                  IS_THROWABLE(element)) /* can be collected and dropped */
10993         {
10994           int i;
10995
10996           if (element_info[element].collect_count == 0)
10997             player->inventory_infinite_element = element;
10998           else
10999             for (i = 0; i < element_info[element].collect_count; i++)
11000               if (player->inventory_size < MAX_INVENTORY_SIZE)
11001                 player->inventory_element[player->inventory_size++] = element;
11002
11003           DrawGameValue_Dynamite(local_player->inventory_size);
11004         }
11005         else if (element_info[element].collect_count > 0)
11006         {
11007           local_player->gems_still_needed -=
11008             element_info[element].collect_count;
11009           if (local_player->gems_still_needed < 0)
11010             local_player->gems_still_needed = 0;
11011
11012           DrawGameValue_Emeralds(local_player->gems_still_needed);
11013         }
11014
11015         RaiseScoreElement(element);
11016         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11017
11018         CheckTriggeredElementChangeByPlayer(x, y, element,
11019                                             CE_OTHER_GETS_COLLECTED,
11020                                             player->index_bit, dig_side);
11021
11022 #if 1
11023         if (mode == DF_SNAP)
11024           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
11025 #endif
11026
11027         break;
11028       }
11029       else if (IS_PUSHABLE(element))
11030       {
11031         if (mode == DF_SNAP && element != EL_BD_ROCK)
11032           return MF_NO_ACTION;
11033
11034         if (CAN_FALL(element) && dy)
11035           return MF_NO_ACTION;
11036
11037         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11038             !(element == EL_SPRING && level.use_spring_bug))
11039           return MF_NO_ACTION;
11040
11041 #if 1
11042         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11043             ((move_direction & MV_VERTICAL &&
11044               ((element_info[element].move_pattern & MV_LEFT &&
11045                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11046                (element_info[element].move_pattern & MV_RIGHT &&
11047                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11048              (move_direction & MV_HORIZONTAL &&
11049               ((element_info[element].move_pattern & MV_UP &&
11050                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11051                (element_info[element].move_pattern & MV_DOWN &&
11052                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11053           return MF_NO_ACTION;
11054 #endif
11055
11056 #if 1
11057         /* do not push elements already moving away faster than player */
11058         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11059             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11060           return MF_NO_ACTION;
11061 #else
11062         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11063           return MF_NO_ACTION;
11064 #endif
11065
11066 #if 1
11067         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11068         {
11069           if (player->push_delay_value == -1)
11070             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11071         }
11072         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11073         {
11074           if (!player->is_pushing)
11075             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11076         }
11077
11078         /*
11079         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11080             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11081              !player_is_pushing))
11082           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11083         */
11084 #else
11085         if (!player->is_pushing &&
11086             game.engine_version >= VERSION_IDENT(2,2,0,7))
11087           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11088 #endif
11089
11090 #if 0
11091         printf("::: push delay: %ld [%d, %d] [%d]\n",
11092                player->push_delay_value, FrameCounter, game.engine_version,
11093                player->is_pushing);
11094 #endif
11095
11096         player->is_pushing = TRUE;
11097
11098         if (!(IN_LEV_FIELD(nextx, nexty) &&
11099               (IS_FREE(nextx, nexty) ||
11100                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11101                 IS_SB_ELEMENT(element)))))
11102           return MF_NO_ACTION;
11103
11104         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11105           return MF_NO_ACTION;
11106
11107         if (player->push_delay == 0)    /* new pushing; restart delay */
11108           player->push_delay = FrameCounter;
11109
11110         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11111             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11112             element != EL_SPRING && element != EL_BALLOON)
11113         {
11114           /* make sure that there is no move delay before next try to push */
11115           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11116             player->move_delay = INITIAL_MOVE_DELAY_OFF;
11117
11118           return MF_NO_ACTION;
11119         }
11120
11121 #if 0
11122         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11123 #endif
11124
11125         if (IS_SB_ELEMENT(element))
11126         {
11127           if (element == EL_SOKOBAN_FIELD_FULL)
11128           {
11129             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11130             local_player->sokobanfields_still_needed++;
11131           }
11132
11133           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11134           {
11135             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11136             local_player->sokobanfields_still_needed--;
11137           }
11138
11139           Feld[x][y] = EL_SOKOBAN_OBJECT;
11140
11141           if (Back[x][y] == Back[nextx][nexty])
11142             PlayLevelSoundAction(x, y, ACTION_PUSHING);
11143           else if (Back[x][y] != 0)
11144             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11145                                         ACTION_EMPTYING);
11146           else
11147             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11148                                         ACTION_FILLING);
11149
11150           if (local_player->sokobanfields_still_needed == 0 &&
11151               game.emulation == EMU_SOKOBAN)
11152           {
11153             player->LevelSolved = player->GameOver = TRUE;
11154             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11155           }
11156         }
11157         else
11158           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11159
11160         InitMovingField(x, y, move_direction);
11161         GfxAction[x][y] = ACTION_PUSHING;
11162
11163         if (mode == DF_SNAP)
11164           ContinueMoving(x, y);
11165         else
11166           MovPos[x][y] = (dx != 0 ? dx : dy);
11167
11168         Pushed[x][y] = TRUE;
11169         Pushed[nextx][nexty] = TRUE;
11170
11171         if (game.engine_version < VERSION_IDENT(2,2,0,7))
11172           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11173         else
11174           player->push_delay_value = -1;        /* get new value later */
11175
11176 #if 1
11177         /* check for element change _after_ element has been pushed! */
11178 #else
11179
11180 #if 1
11181       /* !!! TEST ONLY !!! */
11182         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11183                                    player->index_bit, dig_side);
11184         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11185                                             player->index_bit, dig_side);
11186 #else
11187         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11188                                             player->index_bit, dig_side);
11189         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11190                                    player->index_bit, dig_side);
11191 #endif
11192 #endif
11193
11194         break;
11195       }
11196       else if (IS_SWITCHABLE(element))
11197       {
11198         if (PLAYER_SWITCHING(player, x, y))
11199           return MF_ACTION;
11200
11201         player->is_switching = TRUE;
11202         player->switch_x = x;
11203         player->switch_y = y;
11204
11205         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11206
11207         if (element == EL_ROBOT_WHEEL)
11208         {
11209           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11210           ZX = x;
11211           ZY = y;
11212
11213           DrawLevelField(x, y);
11214         }
11215         else if (element == EL_SP_TERMINAL)
11216         {
11217           int xx, yy;
11218
11219           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11220           {
11221             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11222               Bang(xx, yy);
11223             else if (Feld[xx][yy] == EL_SP_TERMINAL)
11224               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11225           }
11226         }
11227         else if (IS_BELT_SWITCH(element))
11228         {
11229           ToggleBeltSwitch(x, y);
11230         }
11231         else if (element == EL_SWITCHGATE_SWITCH_UP ||
11232                  element == EL_SWITCHGATE_SWITCH_DOWN)
11233         {
11234           ToggleSwitchgateSwitch(x, y);
11235         }
11236         else if (element == EL_LIGHT_SWITCH ||
11237                  element == EL_LIGHT_SWITCH_ACTIVE)
11238         {
11239           ToggleLightSwitch(x, y);
11240
11241 #if 0
11242           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11243                          SND_LIGHT_SWITCH_ACTIVATING :
11244                          SND_LIGHT_SWITCH_DEACTIVATING);
11245 #endif
11246         }
11247         else if (element == EL_TIMEGATE_SWITCH)
11248         {
11249           ActivateTimegateSwitch(x, y);
11250         }
11251         else if (element == EL_BALLOON_SWITCH_LEFT ||
11252                  element == EL_BALLOON_SWITCH_RIGHT ||
11253                  element == EL_BALLOON_SWITCH_UP ||
11254                  element == EL_BALLOON_SWITCH_DOWN ||
11255                  element == EL_BALLOON_SWITCH_ANY)
11256         {
11257           if (element == EL_BALLOON_SWITCH_ANY)
11258             game.balloon_dir = move_direction;
11259           else
11260             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
11261                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11262                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
11263                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
11264                                 MV_NO_MOVING);
11265         }
11266         else if (element == EL_LAMP)
11267         {
11268           Feld[x][y] = EL_LAMP_ACTIVE;
11269           local_player->lights_still_needed--;
11270
11271           DrawLevelField(x, y);
11272         }
11273         else if (element == EL_TIME_ORB_FULL)
11274         {
11275           Feld[x][y] = EL_TIME_ORB_EMPTY;
11276           TimeLeft += 10;
11277           DrawGameValue_Time(TimeLeft);
11278
11279           DrawLevelField(x, y);
11280
11281 #if 0
11282           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11283 #endif
11284         }
11285
11286         return MF_ACTION;
11287       }
11288       else
11289       {
11290         if (!PLAYER_SWITCHING(player, x, y))
11291         {
11292           player->is_switching = TRUE;
11293           player->switch_x = x;
11294           player->switch_y = y;
11295
11296 #if 1
11297           /* !!! TEST ONLY !!! */
11298           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11299                                      player->index_bit, dig_side);
11300           CheckTriggeredElementChangeByPlayer(x, y, element,
11301                                               CE_OTHER_IS_SWITCHING,
11302                                               player->index_bit, dig_side);
11303 #else
11304           CheckTriggeredElementChangeByPlayer(x, y, element,
11305                                               CE_OTHER_IS_SWITCHING,
11306                                               player->index_bit, dig_side);
11307           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11308                                      player->index_bit, dig_side);
11309 #endif
11310         }
11311
11312 #if 1
11313         /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11314         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11315                                    player->index_bit, dig_side);
11316         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11317                                             player->index_bit, dig_side);
11318 #else
11319         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11320                                             player->index_bit, dig_side);
11321         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11322                                    player->index_bit, dig_side);
11323 #endif
11324       }
11325
11326       return MF_NO_ACTION;
11327   }
11328
11329   player->push_delay = 0;
11330
11331   if (Feld[x][y] != element)            /* really digged/collected something */
11332     player->is_collecting = !player->is_digging;
11333
11334   return MF_MOVING;
11335 }
11336
11337 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11338 {
11339   int jx = player->jx, jy = player->jy;
11340   int x = jx + dx, y = jy + dy;
11341   int snap_direction = (dx == -1 ? MV_LEFT :
11342                         dx == +1 ? MV_RIGHT :
11343                         dy == -1 ? MV_UP :
11344                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
11345
11346 #if 0
11347   if (player->MovPos != 0)
11348     return FALSE;
11349 #else
11350   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11351     return FALSE;
11352 #endif
11353
11354   if (!player->active || !IN_LEV_FIELD(x, y))
11355     return FALSE;
11356
11357   if (dx && dy)
11358     return FALSE;
11359
11360   if (!dx && !dy)
11361   {
11362     if (player->MovPos == 0)
11363       player->is_pushing = FALSE;
11364
11365     player->is_snapping = FALSE;
11366
11367     if (player->MovPos == 0)
11368     {
11369       player->is_moving = FALSE;
11370       player->is_digging = FALSE;
11371       player->is_collecting = FALSE;
11372     }
11373
11374     return FALSE;
11375   }
11376
11377   if (player->is_snapping)
11378     return FALSE;
11379
11380   player->MovDir = snap_direction;
11381
11382 #if 1
11383   if (player->MovPos == 0)
11384 #endif
11385   {
11386     player->is_moving = FALSE;
11387     player->is_digging = FALSE;
11388     player->is_collecting = FALSE;
11389   }
11390
11391   player->is_dropping = FALSE;
11392
11393   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11394     return FALSE;
11395
11396   player->is_snapping = TRUE;
11397
11398 #if 1
11399   if (player->MovPos == 0)
11400 #endif
11401   {
11402     player->is_moving = FALSE;
11403     player->is_digging = FALSE;
11404     player->is_collecting = FALSE;
11405   }
11406
11407 #if 1
11408   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
11409     DrawLevelField(player->last_jx, player->last_jy);
11410 #endif
11411
11412   DrawLevelField(x, y);
11413
11414 #if 0
11415   BackToFront();
11416 #endif
11417
11418   return TRUE;
11419 }
11420
11421 boolean DropElement(struct PlayerInfo *player)
11422 {
11423   static int trigger_sides[4] =
11424   {
11425     CH_SIDE_LEFT,       /* dropping left  */
11426     CH_SIDE_RIGHT,      /* dropping right */
11427     CH_SIDE_TOP,        /* dropping up    */
11428     CH_SIDE_BOTTOM,     /* dropping down  */
11429   };
11430   int old_element, new_element;
11431   int dropx = player->jx, dropy = player->jy;
11432   int drop_direction = player->MovDir;
11433   int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11434   int drop_element = (player->inventory_size > 0 ?
11435                       player->inventory_element[player->inventory_size - 1] :
11436                       player->inventory_infinite_element != EL_UNDEFINED ?
11437                       player->inventory_infinite_element :
11438                       player->dynabombs_left > 0 ?
11439                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11440                       EL_UNDEFINED);
11441
11442   if (IS_THROWABLE(drop_element))
11443   {
11444     dropx += GET_DX_FROM_DIR(drop_direction);
11445     dropy += GET_DY_FROM_DIR(drop_direction);
11446
11447     if (!IN_LEV_FIELD(dropx, dropy))
11448       return FALSE;
11449   }
11450
11451   old_element = Feld[dropx][dropy];     /* old element at dropping position */
11452   new_element = drop_element;           /* default: no change when dropping */
11453
11454   /* check if player is active, not moving and ready to drop */
11455   if (!player->active || player->MovPos || player->drop_delay > 0)
11456     return FALSE;
11457
11458   /* check if player has anything that can be dropped */
11459 #if 1
11460   if (new_element == EL_UNDEFINED)
11461     return FALSE;
11462 #else
11463   if (player->inventory_size == 0 &&
11464       player->inventory_infinite_element == EL_UNDEFINED &&
11465       player->dynabombs_left == 0)
11466     return FALSE;
11467 #endif
11468
11469   /* check if anything can be dropped at the current position */
11470   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11471     return FALSE;
11472
11473   /* collected custom elements can only be dropped on empty fields */
11474 #if 1
11475   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11476     return FALSE;
11477 #else
11478   if (player->inventory_size > 0 &&
11479       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11480       && old_element != EL_EMPTY)
11481     return FALSE;
11482 #endif
11483
11484   if (old_element != EL_EMPTY)
11485     Back[dropx][dropy] = old_element;   /* store old element on this field */
11486
11487   ResetGfxAnimation(dropx, dropy);
11488   ResetRandomAnimationValue(dropx, dropy);
11489
11490   if (player->inventory_size > 0 ||
11491       player->inventory_infinite_element != EL_UNDEFINED)
11492   {
11493     if (player->inventory_size > 0)
11494     {
11495       player->inventory_size--;
11496
11497 #if 0
11498       new_element = player->inventory_element[player->inventory_size];
11499 #endif
11500
11501       DrawGameValue_Dynamite(local_player->inventory_size);
11502
11503       if (new_element == EL_DYNAMITE)
11504         new_element = EL_DYNAMITE_ACTIVE;
11505       else if (new_element == EL_SP_DISK_RED)
11506         new_element = EL_SP_DISK_RED_ACTIVE;
11507     }
11508
11509     Feld[dropx][dropy] = new_element;
11510
11511     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11512       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11513                           el2img(Feld[dropx][dropy]), 0);
11514
11515     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11516
11517 #if 1
11518     /* needed if previous element just changed to "empty" in the last frame */
11519     Changed[dropx][dropy] = 0;          /* allow another change */
11520 #endif
11521
11522 #if 1
11523     /* !!! TEST ONLY !!! */
11524     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11525                                player->index_bit, drop_side);
11526     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11527                                         CE_OTHER_GETS_DROPPED,
11528                                         player->index_bit, drop_side);
11529 #else
11530     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11531                                         CE_OTHER_GETS_DROPPED,
11532                                         player->index_bit, drop_side);
11533     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11534                                player->index_bit, drop_side);
11535 #endif
11536
11537     TestIfElementTouchesCustomElement(dropx, dropy);
11538   }
11539   else          /* player is dropping a dyna bomb */
11540   {
11541     player->dynabombs_left--;
11542
11543 #if 0
11544     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11545 #endif
11546
11547     Feld[dropx][dropy] = new_element;
11548
11549     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11550       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11551                           el2img(Feld[dropx][dropy]), 0);
11552
11553     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11554   }
11555
11556
11557
11558 #if 1
11559
11560   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11561   {
11562 #if 1
11563     InitField_WithBug1(dropx, dropy, FALSE);
11564 #else
11565     InitField(dropx, dropy, FALSE);
11566     if (CAN_MOVE(Feld[dropx][dropy]))
11567       InitMovDir(dropx, dropy);
11568 #endif
11569   }
11570
11571   new_element = Feld[dropx][dropy];     /* element might have changed */
11572
11573   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11574       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11575   {
11576 #if 0
11577     int move_stepsize = element_info[new_element].move_stepsize;
11578 #endif
11579     int move_direction, nextx, nexty;
11580
11581     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11582       MovDir[dropx][dropy] = drop_direction;
11583
11584     move_direction = MovDir[dropx][dropy];
11585     nextx = dropx + GET_DX_FROM_DIR(move_direction);
11586     nexty = dropy + GET_DY_FROM_DIR(move_direction);
11587
11588 #if 1
11589       Changed[dropx][dropy] = 0;        /* allow another change */
11590       CheckCollision[dropx][dropy] = 2;
11591 #else
11592
11593     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11594     {
11595 #if 0
11596       WasJustMoving[dropx][dropy] = 3;
11597 #else
11598 #if 1
11599       InitMovingField(dropx, dropy, move_direction);
11600       ContinueMoving(dropx, dropy);
11601 #endif
11602 #endif
11603     }
11604 #if 1
11605     else
11606     {
11607       Changed[dropx][dropy] = 0;        /* allow another change */
11608
11609 #if 1
11610       TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11611 #else
11612       CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11613                                CE_HITTING_SOMETHING, move_direction);
11614 #endif
11615     }
11616 #endif
11617
11618 #endif
11619
11620 #if 0
11621     player->drop_delay = 2 * TILEX / move_stepsize + 1;
11622 #endif
11623   }
11624
11625 #if 0
11626   player->drop_delay = 8 + 8 + 8;
11627 #endif
11628
11629 #if 1
11630   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11631 #endif
11632
11633 #endif
11634
11635   player->is_dropping = TRUE;
11636
11637
11638   return TRUE;
11639 }
11640
11641 /* ------------------------------------------------------------------------- */
11642 /* game sound playing functions                                              */
11643 /* ------------------------------------------------------------------------- */
11644
11645 static int *loop_sound_frame = NULL;
11646 static int *loop_sound_volume = NULL;
11647
11648 void InitPlayLevelSound()
11649 {
11650   int num_sounds = getSoundListSize();
11651
11652   checked_free(loop_sound_frame);
11653   checked_free(loop_sound_volume);
11654
11655   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
11656   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11657 }
11658
11659 static void PlayLevelSound(int x, int y, int nr)
11660 {
11661   int sx = SCREENX(x), sy = SCREENY(y);
11662   int volume, stereo_position;
11663   int max_distance = 8;
11664   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11665
11666   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11667       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11668     return;
11669
11670   if (!IN_LEV_FIELD(x, y) ||
11671       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11672       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11673     return;
11674
11675   volume = SOUND_MAX_VOLUME;
11676
11677   if (!IN_SCR_FIELD(sx, sy))
11678   {
11679     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11680     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11681
11682     volume -= volume * (dx > dy ? dx : dy) / max_distance;
11683   }
11684
11685   stereo_position = (SOUND_MAX_LEFT +
11686                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11687                      (SCR_FIELDX + 2 * max_distance));
11688
11689   if (IS_LOOP_SOUND(nr))
11690   {
11691     /* This assures that quieter loop sounds do not overwrite louder ones,
11692        while restarting sound volume comparison with each new game frame. */
11693
11694     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11695       return;
11696
11697     loop_sound_volume[nr] = volume;
11698     loop_sound_frame[nr] = FrameCounter;
11699   }
11700
11701   PlaySoundExt(nr, volume, stereo_position, type);
11702 }
11703
11704 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11705 {
11706   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11707                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
11708                  y < LEVELY(BY1) ? LEVELY(BY1) :
11709                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
11710                  sound_action);
11711 }
11712
11713 static void PlayLevelSoundAction(int x, int y, int action)
11714 {
11715   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11716 }
11717
11718 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11719 {
11720   int sound_effect = element_info[element].sound[action];
11721
11722   if (sound_effect != SND_UNDEFINED)
11723     PlayLevelSound(x, y, sound_effect);
11724 }
11725
11726 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11727                                               int action)
11728 {
11729   int sound_effect = element_info[element].sound[action];
11730
11731   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11732     PlayLevelSound(x, y, sound_effect);
11733 }
11734
11735 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11736 {
11737   int sound_effect = element_info[Feld[x][y]].sound[action];
11738
11739   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11740     PlayLevelSound(x, y, sound_effect);
11741 }
11742
11743 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11744 {
11745   int sound_effect = element_info[Feld[x][y]].sound[action];
11746
11747   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11748     StopSound(sound_effect);
11749 }
11750
11751 static void PlayLevelMusic()
11752 {
11753   if (levelset.music[level_nr] != MUS_UNDEFINED)
11754     PlayMusic(levelset.music[level_nr]);        /* from config file */
11755   else
11756     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
11757 }
11758
11759 void RaiseScore(int value)
11760 {
11761   local_player->score += value;
11762
11763   DrawGameValue_Score(local_player->score);
11764 }
11765
11766 void RaiseScoreElement(int element)
11767 {
11768   switch(element)
11769   {
11770     case EL_EMERALD:
11771     case EL_BD_DIAMOND:
11772     case EL_EMERALD_YELLOW:
11773     case EL_EMERALD_RED:
11774     case EL_EMERALD_PURPLE:
11775     case EL_SP_INFOTRON:
11776       RaiseScore(level.score[SC_EMERALD]);
11777       break;
11778     case EL_DIAMOND:
11779       RaiseScore(level.score[SC_DIAMOND]);
11780       break;
11781     case EL_CRYSTAL:
11782       RaiseScore(level.score[SC_CRYSTAL]);
11783       break;
11784     case EL_PEARL:
11785       RaiseScore(level.score[SC_PEARL]);
11786       break;
11787     case EL_BUG:
11788     case EL_BD_BUTTERFLY:
11789     case EL_SP_ELECTRON:
11790       RaiseScore(level.score[SC_BUG]);
11791       break;
11792     case EL_SPACESHIP:
11793     case EL_BD_FIREFLY:
11794     case EL_SP_SNIKSNAK:
11795       RaiseScore(level.score[SC_SPACESHIP]);
11796       break;
11797     case EL_YAMYAM:
11798     case EL_DARK_YAMYAM:
11799       RaiseScore(level.score[SC_YAMYAM]);
11800       break;
11801     case EL_ROBOT:
11802       RaiseScore(level.score[SC_ROBOT]);
11803       break;
11804     case EL_PACMAN:
11805       RaiseScore(level.score[SC_PACMAN]);
11806       break;
11807     case EL_NUT:
11808       RaiseScore(level.score[SC_NUT]);
11809       break;
11810     case EL_DYNAMITE:
11811     case EL_SP_DISK_RED:
11812     case EL_DYNABOMB_INCREASE_NUMBER:
11813     case EL_DYNABOMB_INCREASE_SIZE:
11814     case EL_DYNABOMB_INCREASE_POWER:
11815       RaiseScore(level.score[SC_DYNAMITE]);
11816       break;
11817     case EL_SHIELD_NORMAL:
11818     case EL_SHIELD_DEADLY:
11819       RaiseScore(level.score[SC_SHIELD]);
11820       break;
11821     case EL_EXTRA_TIME:
11822       RaiseScore(level.score[SC_TIME_BONUS]);
11823       break;
11824     case EL_KEY_1:
11825     case EL_KEY_2:
11826     case EL_KEY_3:
11827     case EL_KEY_4:
11828       RaiseScore(level.score[SC_KEY]);
11829       break;
11830     default:
11831       RaiseScore(element_info[element].collect_score);
11832       break;
11833   }
11834 }
11835
11836 void RequestQuitGame(boolean ask_if_really_quit)
11837 {
11838   if (AllPlayersGone ||
11839       !ask_if_really_quit ||
11840       level_editor_test_game ||
11841       Request("Do you really want to quit the game ?",
11842               REQ_ASK | REQ_STAY_CLOSED))
11843   {
11844 #if defined(PLATFORM_UNIX)
11845     if (options.network)
11846       SendToServer_StopPlaying();
11847     else
11848 #endif
11849     {
11850       game_status = GAME_MODE_MAIN;
11851       DrawMainMenu();
11852     }
11853   }
11854   else
11855   {
11856
11857 #if 1
11858     if (tape.playing && tape.deactivate_display)
11859       TapeDeactivateDisplayOff(TRUE);
11860 #endif
11861
11862     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11863
11864 #if 1
11865     if (tape.playing && tape.deactivate_display)
11866       TapeDeactivateDisplayOn();
11867 #endif
11868
11869   }
11870 }
11871
11872
11873 /* ---------- new game button stuff ---------------------------------------- */
11874
11875 /* graphic position values for game buttons */
11876 #define GAME_BUTTON_XSIZE       30
11877 #define GAME_BUTTON_YSIZE       30
11878 #define GAME_BUTTON_XPOS        5
11879 #define GAME_BUTTON_YPOS        215
11880 #define SOUND_BUTTON_XPOS       5
11881 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11882
11883 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11884 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11885 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11886 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11887 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11888 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11889
11890 static struct
11891 {
11892   int x, y;
11893   int gadget_id;
11894   char *infotext;
11895 } gamebutton_info[NUM_GAME_BUTTONS] =
11896 {
11897   {
11898     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
11899     GAME_CTRL_ID_STOP,
11900     "stop game"
11901   },
11902   {
11903     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
11904     GAME_CTRL_ID_PAUSE,
11905     "pause game"
11906   },
11907   {
11908     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
11909     GAME_CTRL_ID_PLAY,
11910     "play game"
11911   },
11912   {
11913     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
11914     SOUND_CTRL_ID_MUSIC,
11915     "background music on/off"
11916   },
11917   {
11918     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
11919     SOUND_CTRL_ID_LOOPS,
11920     "sound loops on/off"
11921   },
11922   {
11923     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
11924     SOUND_CTRL_ID_SIMPLE,
11925     "normal sounds on/off"
11926   }
11927 };
11928
11929 void CreateGameButtons()
11930 {
11931   int i;
11932
11933   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11934   {
11935     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11936     struct GadgetInfo *gi;
11937     int button_type;
11938     boolean checked;
11939     unsigned long event_mask;
11940     int gd_xoffset, gd_yoffset;
11941     int gd_x1, gd_x2, gd_y1, gd_y2;
11942     int id = i;
11943
11944     gd_xoffset = gamebutton_info[i].x;
11945     gd_yoffset = gamebutton_info[i].y;
11946     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
11947     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
11948
11949     if (id == GAME_CTRL_ID_STOP ||
11950         id == GAME_CTRL_ID_PAUSE ||
11951         id == GAME_CTRL_ID_PLAY)
11952     {
11953       button_type = GD_TYPE_NORMAL_BUTTON;
11954       checked = FALSE;
11955       event_mask = GD_EVENT_RELEASED;
11956       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11957       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11958     }
11959     else
11960     {
11961       button_type = GD_TYPE_CHECK_BUTTON;
11962       checked =
11963         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
11964          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
11965          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
11966       event_mask = GD_EVENT_PRESSED;
11967       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
11968       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
11969     }
11970
11971     gi = CreateGadget(GDI_CUSTOM_ID, id,
11972                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
11973                       GDI_X, DX + gd_xoffset,
11974                       GDI_Y, DY + gd_yoffset,
11975                       GDI_WIDTH, GAME_BUTTON_XSIZE,
11976                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
11977                       GDI_TYPE, button_type,
11978                       GDI_STATE, GD_BUTTON_UNPRESSED,
11979                       GDI_CHECKED, checked,
11980                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
11981                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
11982                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
11983                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
11984                       GDI_EVENT_MASK, event_mask,
11985                       GDI_CALLBACK_ACTION, HandleGameButtons,
11986                       GDI_END);
11987
11988     if (gi == NULL)
11989       Error(ERR_EXIT, "cannot create gadget");
11990
11991     game_gadget[id] = gi;
11992   }
11993 }
11994
11995 void FreeGameButtons()
11996 {
11997   int i;
11998
11999   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12000     FreeGadget(game_gadget[i]);
12001 }
12002
12003 static void MapGameButtons()
12004 {
12005   int i;
12006
12007   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12008     MapGadget(game_gadget[i]);
12009 }
12010
12011 void UnmapGameButtons()
12012 {
12013   int i;
12014
12015   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12016     UnmapGadget(game_gadget[i]);
12017 }
12018
12019 static void HandleGameButtons(struct GadgetInfo *gi)
12020 {
12021   int id = gi->custom_id;
12022
12023   if (game_status != GAME_MODE_PLAYING)
12024     return;
12025
12026   switch (id)
12027   {
12028     case GAME_CTRL_ID_STOP:
12029       RequestQuitGame(TRUE);
12030       break;
12031
12032     case GAME_CTRL_ID_PAUSE:
12033       if (options.network)
12034       {
12035 #if defined(PLATFORM_UNIX)
12036         if (tape.pausing)
12037           SendToServer_ContinuePlaying();
12038         else
12039           SendToServer_PausePlaying();
12040 #endif
12041       }
12042       else
12043         TapeTogglePause(TAPE_TOGGLE_MANUAL);
12044       break;
12045
12046     case GAME_CTRL_ID_PLAY:
12047       if (tape.pausing)
12048       {
12049 #if defined(PLATFORM_UNIX)
12050         if (options.network)
12051           SendToServer_ContinuePlaying();
12052         else
12053 #endif
12054         {
12055           tape.pausing = FALSE;
12056           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12057         }
12058       }
12059       break;
12060
12061     case SOUND_CTRL_ID_MUSIC:
12062       if (setup.sound_music)
12063       { 
12064         setup.sound_music = FALSE;
12065         FadeMusic();
12066       }
12067       else if (audio.music_available)
12068       { 
12069         setup.sound = setup.sound_music = TRUE;
12070
12071         SetAudioMode(setup.sound);
12072
12073         PlayLevelMusic();
12074       }
12075       break;
12076
12077     case SOUND_CTRL_ID_LOOPS:
12078       if (setup.sound_loops)
12079         setup.sound_loops = FALSE;
12080       else if (audio.loops_available)
12081       {
12082         setup.sound = setup.sound_loops = TRUE;
12083         SetAudioMode(setup.sound);
12084       }
12085       break;
12086
12087     case SOUND_CTRL_ID_SIMPLE:
12088       if (setup.sound_simple)
12089         setup.sound_simple = FALSE;
12090       else if (audio.sound_available)
12091       {
12092         setup.sound = setup.sound_simple = TRUE;
12093         SetAudioMode(setup.sound);
12094       }
12095       break;
12096
12097     default:
12098       break;
12099   }
12100 }