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