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