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