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