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