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