eaf9ee12e8bb92dae1e76654a6dbbc120132c40b
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_STUFF           TRUE                            * 1
32
33 #define USE_NEW_MOVE_STYLE      TRUE    * USE_NEW_STUFF         * 1
34 #define USE_NEW_MOVE_DELAY      TRUE    * USE_NEW_STUFF         * 1
35 #define USE_NEW_PUSH_DELAY      TRUE    * USE_NEW_STUFF         * 1
36 #define USE_NEW_BLOCK_STYLE     TRUE    * USE_NEW_STUFF         * 1 * 1
37 #define USE_NEW_SP_SLIPPERY     TRUE    * USE_NEW_STUFF         * 1
38 #define USE_NEW_RANDOMIZE       TRUE    * USE_NEW_STUFF         * 1
39
40 #define USE_PUSH_BUGFIX         TRUE    * USE_NEW_STUFF         * 1
41
42 /* for DigField() */
43 #define DF_NO_PUSH              0
44 #define DF_DIG                  1
45 #define DF_SNAP                 2
46
47 /* for MovePlayer() */
48 #define MF_NO_ACTION            0
49 #define MF_MOVING               1
50 #define MF_ACTION               2
51
52 /* for ScrollPlayer() */
53 #define SCROLL_INIT             0
54 #define SCROLL_GO_ON            1
55
56 /* for Explode() */
57 #define EX_PHASE_START          0
58 #define EX_TYPE_NONE            0
59 #define EX_TYPE_NORMAL          (1 << 0)
60 #define EX_TYPE_CENTER          (1 << 1)
61 #define EX_TYPE_BORDER          (1 << 2)
62 #define EX_TYPE_CROSS           (1 << 3)
63 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
64
65 /* special positions in the game control window (relative to control window) */
66 #define XX_LEVEL                37
67 #define YY_LEVEL                20
68 #define XX_EMERALDS             29
69 #define YY_EMERALDS             54
70 #define XX_DYNAMITE             29
71 #define YY_DYNAMITE             89
72 #define XX_KEYS                 18
73 #define YY_KEYS                 123
74 #define XX_SCORE                15
75 #define YY_SCORE                159
76 #define XX_TIME1                29
77 #define XX_TIME2                30
78 #define YY_TIME                 194
79
80 /* special positions in the game control window (relative to main window) */
81 #define DX_LEVEL                (DX + XX_LEVEL)
82 #define DY_LEVEL                (DY + YY_LEVEL)
83 #define DX_EMERALDS             (DX + XX_EMERALDS)
84 #define DY_EMERALDS             (DY + YY_EMERALDS)
85 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
86 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
87 #define DX_KEYS                 (DX + XX_KEYS)
88 #define DY_KEYS                 (DY + YY_KEYS)
89 #define DX_SCORE                (DX + XX_SCORE)
90 #define DY_SCORE                (DY + YY_SCORE)
91 #define DX_TIME1                (DX + XX_TIME1)
92 #define DX_TIME2                (DX + XX_TIME2)
93 #define DY_TIME                 (DY + YY_TIME)
94
95 /* values for initial player move delay (initial delay counter value) */
96 #define INITIAL_MOVE_DELAY_OFF  -1
97 #define INITIAL_MOVE_DELAY_ON   0
98
99 /* values for player movement speed (which is in fact a delay value) */
100 #define MOVE_DELAY_NORMAL_SPEED 8
101 #define MOVE_DELAY_HIGH_SPEED   4
102
103 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
104 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
105 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
106 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
107
108 /* values for other actions */
109 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
110
111 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
112 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
113
114 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
115
116 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
117                                  RND(element_info[e].push_delay_random))
118 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
119                                  RND(element_info[e].drop_delay_random))
120 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
121                                  RND(element_info[e].move_delay_random))
122 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
123                                     (element_info[e].move_delay_random))
124
125 #define GET_TARGET_ELEMENT(e, ch)                                       \
126         ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element :     \
127          (e) == EL_TRIGGER_PLAYER  ? (ch)->actual_trigger_player : (e))
128
129 #define GET_VALID_PLAYER_ELEMENT(e)                                     \
130         ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
131
132 #define CAN_GROW_INTO(e)                                                \
133         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
134
135 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
136                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
137                                         (condition)))
138
139 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
140                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
141                                         (CAN_MOVE_INTO_ACID(e) &&       \
142                                          Feld[x][y] == EL_ACID) ||      \
143                                         (condition)))
144
145 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
146                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
147                                         (CAN_MOVE_INTO_ACID(e) &&       \
148                                          Feld[x][y] == EL_ACID) ||      \
149                                         (condition)))
150
151 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
152                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
153                                         (condition) ||                  \
154                                         (CAN_MOVE_INTO_ACID(e) &&       \
155                                          Feld[x][y] == EL_ACID) ||      \
156                                         (DONT_COLLIDE_WITH(e) &&        \
157                                          IS_PLAYER(x, y) &&             \
158                                          !PLAYER_ENEMY_PROTECTED(x, y))))
159
160 #if 0
161 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
162                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
163                                         (condition) ||                  \
164                                         (DONT_COLLIDE_WITH(e) &&        \
165                                          IS_PLAYER(x, y) &&             \
166                                          !PLAYER_ENEMY_PROTECTED(x, y))))
167 #endif
168
169 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
170         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
171
172 #if 1
173 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
174         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
175 #else
176 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
177         ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
178 #endif
179
180 #if 0
181 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
182 #endif
183
184 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
185         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
186
187 #if 1
188
189 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
190         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
191
192 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
193         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
194
195 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
196         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
197
198 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
199         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
200
201 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
202         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
203                                                  IS_FOOD_PENGUIN(Feld[x][y])))
204 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
205         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
206
207 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
208         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
209
210 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
211         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
212
213 #else
214
215 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
216                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
217                                         (CAN_MOVE_INTO_ACID(e) &&       \
218                                          Feld[x][y] == EL_ACID) ||      \
219                                         Feld[x][y] == EL_DIAMOND))
220
221 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
222                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
223                                         (CAN_MOVE_INTO_ACID(e) &&       \
224                                          Feld[x][y] == EL_ACID) ||      \
225                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
226
227 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
228                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
229                                         (CAN_MOVE_INTO_ACID(e) &&       \
230                                          Feld[x][y] == EL_ACID) ||      \
231                                         IS_AMOEBOID(Feld[x][y])))
232
233 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
234                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
235                                         (CAN_MOVE_INTO_ACID(e) &&       \
236                                          Feld[x][y] == EL_ACID) ||      \
237                                         IS_FOOD_PIG(Feld[x][y])))
238
239 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
240                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
241                                         (CAN_MOVE_INTO_ACID(e) &&       \
242                                          Feld[x][y] == EL_ACID) ||      \
243                                         IS_FOOD_PENGUIN(Feld[x][y]) ||  \
244                                         Feld[x][y] == EL_EXIT_OPEN))
245
246 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
247                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
248                                         (CAN_MOVE_INTO_ACID(e) &&       \
249                                          Feld[x][y] == EL_ACID)))
250
251 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
252                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
253                                         (CAN_MOVE_INTO_ACID(e) &&       \
254                                          Feld[x][y] == EL_ACID) ||      \
255                                         (condition)))
256
257 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
258                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
259                                         (CAN_MOVE_INTO_ACID(e) &&       \
260                                          Feld[x][y] == EL_ACID)))
261
262 #endif
263
264 #define GROUP_NR(e)             ((e) - EL_GROUP_START)
265 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
266 #define IS_IN_GROUP(e, nr)      (element_info[e].in_group[nr] == TRUE)
267 #define IS_IN_GROUP_EL(e, ge)   (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
268
269 #define IS_EQUAL_OR_IN_GROUP(e, ge)                                     \
270         (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
271
272 #if 0
273 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
274                 (!IS_PLAYER(x, y) &&                                    \
275                  (Feld[x][y] == EL_ACID ||                              \
276                   IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
277 #else
278 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
279                 (!IS_PLAYER(x, y) &&                                    \
280                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
281 #endif
282
283 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
284         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
285
286 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
287 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
288
289 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
290 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
291 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
292 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
293
294 /* game button identifiers */
295 #define GAME_CTRL_ID_STOP               0
296 #define GAME_CTRL_ID_PAUSE              1
297 #define GAME_CTRL_ID_PLAY               2
298 #define SOUND_CTRL_ID_MUSIC             3
299 #define SOUND_CTRL_ID_LOOPS             4
300 #define SOUND_CTRL_ID_SIMPLE            5
301
302 #define NUM_GAME_BUTTONS                6
303
304
305 /* forward declaration for internal use */
306
307 static void AdvanceFrameAndPlayerCounters(int);
308
309 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
310 static boolean MovePlayer(struct PlayerInfo *, int, int);
311 static void ScrollPlayer(struct PlayerInfo *, int);
312 static void ScrollScreen(struct PlayerInfo *, int);
313
314 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
315
316 static void InitBeltMovement(void);
317 static void CloseAllOpenTimegates(void);
318 static void CheckGravityMovement(struct PlayerInfo *);
319 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
320 static void KillHeroUnlessEnemyProtected(int, int);
321 static void KillHeroUnlessExplosionProtected(int, int);
322
323 static void TestIfPlayerTouchesCustomElement(int, int);
324 static void TestIfElementTouchesCustomElement(int, int);
325 static void TestIfElementHitsCustomElement(int, int, int);
326 #if 0
327 static void TestIfElementSmashesCustomElement(int, int, int);
328 #endif
329
330 static void ChangeElement(int, int, int);
331
332 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
333 #define CheckTriggeredElementChange(x, y, e, ev)                        \
334         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY,      \
335                                        CH_SIDE_ANY, -1)
336 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
337         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
338 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
339         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
340 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
341         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY,      \
342                                        CH_SIDE_ANY, p)
343
344 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
345 #define CheckElementChange(x, y, e, te, ev)                             \
346         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
347 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
348         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
349 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
350         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
351 #define CheckElementChangeByPage(x, y, e, te, ev, p)                    \
352         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
353
354 static void PlayLevelSound(int, int, int);
355 static void PlayLevelSoundNearest(int, int, int);
356 static void PlayLevelSoundAction(int, int, int);
357 static void PlayLevelSoundElementAction(int, int, int, int);
358 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
359 static void PlayLevelSoundActionIfLoop(int, int, int);
360 static void StopLevelSoundActionIfLoop(int, int, int);
361 static void PlayLevelMusic();
362
363 static void MapGameButtons();
364 static void HandleGameButtons(struct GadgetInfo *);
365
366 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
367
368
369 /* ------------------------------------------------------------------------- */
370 /* definition of elements that automatically change to other elements after  */
371 /* a specified time, eventually calling a function when changing             */
372 /* ------------------------------------------------------------------------- */
373
374 /* forward declaration for changer functions */
375 static void InitBuggyBase(int x, int y);
376 static void WarnBuggyBase(int x, int y);
377
378 static void InitTrap(int x, int y);
379 static void ActivateTrap(int x, int y);
380 static void ChangeActiveTrap(int x, int y);
381
382 static void InitRobotWheel(int x, int y);
383 static void RunRobotWheel(int x, int y);
384 static void StopRobotWheel(int x, int y);
385
386 static void InitTimegateWheel(int x, int y);
387 static void RunTimegateWheel(int x, int y);
388
389 struct ChangingElementInfo
390 {
391   int element;
392   int target_element;
393   int change_delay;
394   void (*pre_change_function)(int x, int y);
395   void (*change_function)(int x, int y);
396   void (*post_change_function)(int x, int y);
397 };
398
399 static struct ChangingElementInfo change_delay_list[] =
400 {
401   {
402     EL_NUT_BREAKING,
403     EL_EMERALD,
404     6,
405     NULL,
406     NULL,
407     NULL
408   },
409   {
410     EL_PEARL_BREAKING,
411     EL_EMPTY,
412     8,
413     NULL,
414     NULL,
415     NULL
416   },
417   {
418     EL_EXIT_OPENING,
419     EL_EXIT_OPEN,
420     29,
421     NULL,
422     NULL,
423     NULL
424   },
425   {
426     EL_EXIT_CLOSING,
427     EL_EXIT_CLOSED,
428     29,
429     NULL,
430     NULL,
431     NULL
432   },
433   {
434     EL_SP_EXIT_OPENING,
435     EL_SP_EXIT_OPEN,
436     29,
437     NULL,
438     NULL,
439     NULL
440   },
441   {
442     EL_SP_EXIT_CLOSING,
443     EL_SP_EXIT_CLOSED,
444     29,
445     NULL,
446     NULL,
447     NULL
448   },
449   {
450     EL_SWITCHGATE_OPENING,
451     EL_SWITCHGATE_OPEN,
452     29,
453     NULL,
454     NULL,
455     NULL
456   },
457   {
458     EL_SWITCHGATE_CLOSING,
459     EL_SWITCHGATE_CLOSED,
460     29,
461     NULL,
462     NULL,
463     NULL
464   },
465   {
466     EL_TIMEGATE_OPENING,
467     EL_TIMEGATE_OPEN,
468     29,
469     NULL,
470     NULL,
471     NULL
472   },
473   {
474     EL_TIMEGATE_CLOSING,
475     EL_TIMEGATE_CLOSED,
476     29,
477     NULL,
478     NULL,
479     NULL
480   },
481
482   {
483     EL_ACID_SPLASH_LEFT,
484     EL_EMPTY,
485     8,
486     NULL,
487     NULL,
488     NULL
489   },
490   {
491     EL_ACID_SPLASH_RIGHT,
492     EL_EMPTY,
493     8,
494     NULL,
495     NULL,
496     NULL
497   },
498   {
499     EL_SP_BUGGY_BASE,
500     EL_SP_BUGGY_BASE_ACTIVATING,
501     0,
502     InitBuggyBase,
503     NULL,
504     NULL
505   },
506   {
507     EL_SP_BUGGY_BASE_ACTIVATING,
508     EL_SP_BUGGY_BASE_ACTIVE,
509     0,
510     InitBuggyBase,
511     NULL,
512     NULL
513   },
514   {
515     EL_SP_BUGGY_BASE_ACTIVE,
516     EL_SP_BUGGY_BASE,
517     0,
518     InitBuggyBase,
519     WarnBuggyBase,
520     NULL
521   },
522   {
523     EL_TRAP,
524     EL_TRAP_ACTIVE,
525     0,
526     InitTrap,
527     NULL,
528     ActivateTrap
529   },
530   {
531     EL_TRAP_ACTIVE,
532     EL_TRAP,
533     31,
534     NULL,
535     ChangeActiveTrap,
536     NULL
537   },
538   {
539     EL_ROBOT_WHEEL_ACTIVE,
540     EL_ROBOT_WHEEL,
541     0,
542     InitRobotWheel,
543     RunRobotWheel,
544     StopRobotWheel
545   },
546   {
547     EL_TIMEGATE_SWITCH_ACTIVE,
548     EL_TIMEGATE_SWITCH,
549     0,
550     InitTimegateWheel,
551     RunTimegateWheel,
552     NULL
553   },
554
555   {
556     EL_UNDEFINED,
557     EL_UNDEFINED,
558     -1,
559     NULL,
560     NULL,
561     NULL
562   }
563 };
564
565 struct
566 {
567   int element;
568   int push_delay_fixed, push_delay_random;
569 }
570 push_delay_list[] =
571 {
572   { EL_SPRING,                  0, 0 },
573   { EL_BALLOON,                 0, 0 },
574
575   { EL_SOKOBAN_OBJECT,          2, 0 },
576   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
577   { EL_SATELLITE,               2, 0 },
578   { EL_SP_DISK_YELLOW,          2, 0 },
579
580   { EL_UNDEFINED,               0, 0 },
581 };
582
583 struct
584 {
585   int element;
586   int move_stepsize;
587 }
588 move_stepsize_list[] =
589 {
590   { EL_AMOEBA_DROP,             2 },
591   { EL_AMOEBA_DROPPING,         2 },
592   { EL_QUICKSAND_FILLING,       1 },
593   { EL_QUICKSAND_EMPTYING,      1 },
594   { EL_MAGIC_WALL_FILLING,      2 },
595   { EL_BD_MAGIC_WALL_FILLING,   2 },
596   { EL_MAGIC_WALL_EMPTYING,     2 },
597   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
598
599   { EL_UNDEFINED,               0 },
600 };
601
602 struct
603 {
604   int element;
605   int count;
606 }
607 collect_count_list[] =
608 {
609   { EL_EMERALD,                 1 },
610   { EL_BD_DIAMOND,              1 },
611   { EL_EMERALD_YELLOW,          1 },
612   { EL_EMERALD_RED,             1 },
613   { EL_EMERALD_PURPLE,          1 },
614   { EL_DIAMOND,                 3 },
615   { EL_SP_INFOTRON,             1 },
616   { EL_PEARL,                   5 },
617   { EL_CRYSTAL,                 8 },
618
619   { EL_UNDEFINED,               0 },
620 };
621
622 struct
623 {
624   int element;
625   int direction;
626 }
627 access_direction_list[] =
628 {
629   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
630   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
631   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
632   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
633   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
634   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
635   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
636   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
637   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
638   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
639   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
640
641   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
642   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
643   { EL_SP_PORT_UP,                                                   MV_DOWN },
644   { EL_SP_PORT_DOWN,                                         MV_UP           },
645   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
646   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
647   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
648   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
649   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
650   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
651   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
652   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
653   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
654   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
655   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
656   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
657   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
658   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
659   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
660
661   { EL_UNDEFINED,                       MV_NO_MOVING                         }
662 };
663
664 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
665
666 #define IS_AUTO_CHANGING(e)     (element_info[e].change_events & \
667                                  CH_EVENT_BIT(CE_DELAY))
668 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
669 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
670                                  IS_JUST_CHANGING(x, y))
671
672 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
673
674
675 void GetPlayerConfig()
676 {
677   if (!audio.sound_available)
678     setup.sound_simple = FALSE;
679
680   if (!audio.loops_available)
681     setup.sound_loops = FALSE;
682
683   if (!audio.music_available)
684     setup.sound_music = FALSE;
685
686   if (!video.fullscreen_available)
687     setup.fullscreen = FALSE;
688
689   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
690
691   SetAudioMode(setup.sound);
692   InitJoysticks();
693 }
694
695 static int getBeltNrFromBeltElement(int element)
696 {
697   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
698           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
699           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
700 }
701
702 static int getBeltNrFromBeltActiveElement(int element)
703 {
704   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
705           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
706           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
707 }
708
709 static int getBeltNrFromBeltSwitchElement(int element)
710 {
711   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
712           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
713           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
714 }
715
716 static int getBeltDirNrFromBeltSwitchElement(int element)
717 {
718   static int belt_base_element[4] =
719   {
720     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
721     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
722     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
723     EL_CONVEYOR_BELT_4_SWITCH_LEFT
724   };
725
726   int belt_nr = getBeltNrFromBeltSwitchElement(element);
727   int belt_dir_nr = element - belt_base_element[belt_nr];
728
729   return (belt_dir_nr % 3);
730 }
731
732 static int getBeltDirFromBeltSwitchElement(int element)
733 {
734   static int belt_move_dir[3] =
735   {
736     MV_LEFT,
737     MV_NO_MOVING,
738     MV_RIGHT
739   };
740
741   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
742
743   return belt_move_dir[belt_dir_nr];
744 }
745
746 static void InitPlayerField(int x, int y, int element, boolean init_game)
747 {
748   if (element == EL_SP_MURPHY)
749   {
750     if (init_game)
751     {
752       if (stored_player[0].present)
753       {
754         Feld[x][y] = EL_SP_MURPHY_CLONE;
755
756         return;
757       }
758       else
759       {
760         stored_player[0].use_murphy_graphic = TRUE;
761       }
762
763       Feld[x][y] = EL_PLAYER_1;
764     }
765   }
766
767   if (init_game)
768   {
769     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
770     int jx = player->jx, jy = player->jy;
771
772     player->present = TRUE;
773
774     player->block_last_field = (element == EL_SP_MURPHY ?
775                                 level.sp_block_last_field :
776                                 level.block_last_field);
777
778 #if USE_NEW_BLOCK_STYLE
779 #if 1
780     player->block_delay = (player->block_last_field ?
781                            (element == EL_SP_MURPHY ?
782                             level.sp_block_delay :
783                             level.block_delay) : 0);
784 #else
785     player->block_delay = (element == EL_SP_MURPHY ?
786                            (player->block_last_field ? 7 : 1) :
787                            (player->block_last_field ? 7 : 1));
788 #endif
789
790 #if 0
791     printf("::: block_last_field == %d, block_delay = %d\n",
792            player->block_last_field, player->block_delay);
793 #endif
794 #endif
795
796     if (!options.network || player->connected)
797     {
798       player->active = TRUE;
799
800       /* remove potentially duplicate players */
801       if (StorePlayer[jx][jy] == Feld[x][y])
802         StorePlayer[jx][jy] = 0;
803
804       StorePlayer[x][y] = Feld[x][y];
805
806       if (options.debug)
807       {
808         printf("Player %d activated.\n", player->element_nr);
809         printf("[Local player is %d and currently %s.]\n",
810                local_player->element_nr,
811                local_player->active ? "active" : "not active");
812       }
813     }
814
815     Feld[x][y] = EL_EMPTY;
816
817     player->jx = player->last_jx = x;
818     player->jy = player->last_jy = y;
819   }
820 }
821
822 static void InitField(int x, int y, boolean init_game)
823 {
824   int element = Feld[x][y];
825
826   switch (element)
827   {
828     case EL_SP_MURPHY:
829     case EL_PLAYER_1:
830     case EL_PLAYER_2:
831     case EL_PLAYER_3:
832     case EL_PLAYER_4:
833       InitPlayerField(x, y, element, init_game);
834       break;
835
836     case EL_SOKOBAN_FIELD_PLAYER:
837       element = Feld[x][y] = EL_PLAYER_1;
838       InitField(x, y, init_game);
839
840       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
841       InitField(x, y, init_game);
842       break;
843
844     case EL_SOKOBAN_FIELD_EMPTY:
845       local_player->sokobanfields_still_needed++;
846       break;
847
848     case EL_STONEBLOCK:
849       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
850         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
851       else if (x > 0 && Feld[x-1][y] == EL_ACID)
852         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
853       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
854         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
855       else if (y > 0 && Feld[x][y-1] == EL_ACID)
856         Feld[x][y] = EL_ACID_POOL_BOTTOM;
857       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
858         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
859       break;
860
861     case EL_BUG_RIGHT:
862     case EL_BUG_UP:
863     case EL_BUG_LEFT:
864     case EL_BUG_DOWN:
865     case EL_BUG:
866     case EL_SPACESHIP_RIGHT:
867     case EL_SPACESHIP_UP:
868     case EL_SPACESHIP_LEFT:
869     case EL_SPACESHIP_DOWN:
870     case EL_SPACESHIP:
871     case EL_BD_BUTTERFLY_RIGHT:
872     case EL_BD_BUTTERFLY_UP:
873     case EL_BD_BUTTERFLY_LEFT:
874     case EL_BD_BUTTERFLY_DOWN:
875     case EL_BD_BUTTERFLY:
876     case EL_BD_FIREFLY_RIGHT:
877     case EL_BD_FIREFLY_UP:
878     case EL_BD_FIREFLY_LEFT:
879     case EL_BD_FIREFLY_DOWN:
880     case EL_BD_FIREFLY:
881     case EL_PACMAN_RIGHT:
882     case EL_PACMAN_UP:
883     case EL_PACMAN_LEFT:
884     case EL_PACMAN_DOWN:
885     case EL_YAMYAM:
886     case EL_DARK_YAMYAM:
887     case EL_ROBOT:
888     case EL_PACMAN:
889     case EL_SP_SNIKSNAK:
890     case EL_SP_ELECTRON:
891     case EL_MOLE_LEFT:
892     case EL_MOLE_RIGHT:
893     case EL_MOLE_UP:
894     case EL_MOLE_DOWN:
895     case EL_MOLE:
896       InitMovDir(x, y);
897       break;
898
899     case EL_AMOEBA_FULL:
900     case EL_BD_AMOEBA:
901       InitAmoebaNr(x, y);
902       break;
903
904     case EL_AMOEBA_DROP:
905       if (y == lev_fieldy - 1)
906       {
907         Feld[x][y] = EL_AMOEBA_GROWING;
908         Store[x][y] = EL_AMOEBA_WET;
909       }
910       break;
911
912     case EL_DYNAMITE_ACTIVE:
913     case EL_SP_DISK_RED_ACTIVE:
914     case EL_DYNABOMB_PLAYER_1_ACTIVE:
915     case EL_DYNABOMB_PLAYER_2_ACTIVE:
916     case EL_DYNABOMB_PLAYER_3_ACTIVE:
917     case EL_DYNABOMB_PLAYER_4_ACTIVE:
918       MovDelay[x][y] = 96;
919       break;
920
921     case EL_LAMP:
922       local_player->lights_still_needed++;
923       break;
924
925     case EL_PENGUIN:
926       local_player->friends_still_needed++;
927       break;
928
929     case EL_PIG:
930     case EL_DRAGON:
931       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
932       break;
933
934 #if 0
935     case EL_SP_EMPTY:
936       Feld[x][y] = EL_EMPTY;
937       break;
938 #endif
939
940 #if 0
941     case EL_EM_KEY_1_FILE:
942       Feld[x][y] = EL_EM_KEY_1;
943       break;
944     case EL_EM_KEY_2_FILE:
945       Feld[x][y] = EL_EM_KEY_2;
946       break;
947     case EL_EM_KEY_3_FILE:
948       Feld[x][y] = EL_EM_KEY_3;
949       break;
950     case EL_EM_KEY_4_FILE:
951       Feld[x][y] = EL_EM_KEY_4;
952       break;
953 #endif
954
955     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
956     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
957     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
958     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
959     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
960     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
961     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
962     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
963     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
964     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
965     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
966     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
967       if (init_game)
968       {
969         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
970         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
971         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
972
973         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
974         {
975           game.belt_dir[belt_nr] = belt_dir;
976           game.belt_dir_nr[belt_nr] = belt_dir_nr;
977         }
978         else    /* more than one switch -- set it like the first switch */
979         {
980           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
981         }
982       }
983       break;
984
985     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
986       if (init_game)
987         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
988       break;
989
990     case EL_LIGHT_SWITCH_ACTIVE:
991       if (init_game)
992         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
993       break;
994
995     default:
996       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
997         InitMovDir(x, y);
998       else if (IS_GROUP_ELEMENT(element))
999       {
1000         struct ElementGroupInfo *group = element_info[element].group;
1001         int last_anim_random_frame = gfx.anim_random_frame;
1002         int element_pos;
1003
1004         if (group->choice_mode == ANIM_RANDOM)
1005           gfx.anim_random_frame = RND(group->num_elements_resolved);
1006
1007         element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1008                                         group->choice_mode, 0,
1009                                         group->choice_pos);
1010
1011         if (group->choice_mode == ANIM_RANDOM)
1012           gfx.anim_random_frame = last_anim_random_frame;
1013
1014         group->choice_pos++;
1015
1016         Feld[x][y] = group->element_resolved[element_pos];
1017
1018         InitField(x, y, init_game);
1019       }
1020       break;
1021   }
1022 }
1023
1024 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1025 {
1026   InitField(x, y, init_game);
1027
1028   /* not needed to call InitMovDir() -- already done by InitField()! */
1029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1030       CAN_MOVE(Feld[x][y]))
1031     InitMovDir(x, y);
1032 }
1033
1034 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1035 {
1036   int old_element = Feld[x][y];
1037
1038   InitField(x, y, init_game);
1039
1040   /* not needed to call InitMovDir() -- already done by InitField()! */
1041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1042       CAN_MOVE(old_element) &&
1043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1044     InitMovDir(x, y);
1045
1046   /* this case is in fact a combination of not less than three bugs:
1047      first, it calls InitMovDir() for elements that can move, although this is
1048      already done by InitField(); then, it checks the element that was at this
1049      field _before_ the call to InitField() (which can change it); lastly, it
1050      was not called for "mole with direction" elements, which were treated as
1051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1052   */
1053 }
1054
1055 inline void DrawGameValue_Emeralds(int value)
1056 {
1057   DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1058 }
1059
1060 inline void DrawGameValue_Dynamite(int value)
1061 {
1062   DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1063 }
1064
1065 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1066 {
1067   int i;
1068
1069   for (i = 0; i < MAX_KEYS; i++)
1070     if (player->key[i])
1071       DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1072                          el2edimg(EL_KEY_1 + i));
1073 }
1074
1075 inline void DrawGameValue_Score(int value)
1076 {
1077   DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1078 }
1079
1080 inline void DrawGameValue_Time(int value)
1081 {
1082   if (value < 1000)
1083     DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1084   else
1085     DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1086 }
1087
1088 inline void DrawGameValue_Level(int value)
1089 {
1090   if (level_nr < 100)
1091     DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1092   else
1093   {
1094     /* misuse area for displaying emeralds to draw bigger level number */
1095     DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1096                 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1097
1098     /* now copy it to the area for displaying level number */
1099     BlitBitmap(drawto, drawto,
1100                DX_EMERALDS, DY_EMERALDS + 1,
1101                getFontWidth(FONT_LEVEL_NUMBER) * 3,
1102                getFontHeight(FONT_LEVEL_NUMBER) - 1,
1103                DX_LEVEL - 1, DY_LEVEL + 1);
1104
1105     /* restore the area for displaying emeralds */
1106     DrawGameValue_Emeralds(local_player->gems_still_needed);
1107
1108     /* yes, this is all really ugly :-) */
1109   }
1110 }
1111
1112 void DrawGameDoorValues()
1113 {
1114   int i;
1115
1116   DrawGameValue_Level(level_nr);
1117
1118   for (i = 0; i < MAX_PLAYERS; i++)
1119     DrawGameValue_Keys(&stored_player[i]);
1120
1121   DrawGameValue_Emeralds(local_player->gems_still_needed);
1122   DrawGameValue_Dynamite(local_player->inventory_size);
1123   DrawGameValue_Score(local_player->score);
1124   DrawGameValue_Time(TimeLeft);
1125 }
1126
1127 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     {
5149 #if 1
5150       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5151 #else
5152       MovDelay[x][y] = 9;
5153 #endif
5154     }
5155   }
5156 }
5157
5158 static void TurnRound(int x, int y)
5159 {
5160   int direction = MovDir[x][y];
5161
5162 #if 0
5163   GfxDir[x][y] = MovDir[x][y];
5164 #endif
5165
5166   TurnRoundExt(x, y);
5167
5168 #if 1
5169   GfxDir[x][y] = MovDir[x][y];
5170 #endif
5171
5172   if (direction != MovDir[x][y])
5173     GfxFrame[x][y] = 0;
5174
5175 #if 1
5176   if (MovDelay[x][y])
5177     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5178 #else
5179   if (MovDelay[x][y])
5180     GfxAction[x][y] = ACTION_WAITING;
5181 #endif
5182 }
5183
5184 static boolean JustBeingPushed(int x, int y)
5185 {
5186   int i;
5187
5188   for (i = 0; i < MAX_PLAYERS; i++)
5189   {
5190     struct PlayerInfo *player = &stored_player[i];
5191
5192     if (player->active && player->is_pushing && player->MovPos)
5193     {
5194       int next_jx = player->jx + (player->jx - player->last_jx);
5195       int next_jy = player->jy + (player->jy - player->last_jy);
5196
5197       if (x == next_jx && y == next_jy)
5198         return TRUE;
5199     }
5200   }
5201
5202   return FALSE;
5203 }
5204
5205 void StartMoving(int x, int y)
5206 {
5207 #if 0
5208   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5209 #endif
5210   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
5211   int element = Feld[x][y];
5212
5213   if (Stop[x][y])
5214     return;
5215
5216 #if 1
5217   if (MovDelay[x][y] == 0)
5218     GfxAction[x][y] = ACTION_DEFAULT;
5219 #else
5220   /* !!! this should be handled more generic (not only for mole) !!! */
5221   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5222     GfxAction[x][y] = ACTION_DEFAULT;
5223 #endif
5224
5225   if (CAN_FALL(element) && y < lev_fieldy - 1)
5226   {
5227     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5228         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5229       if (JustBeingPushed(x, y))
5230         return;
5231
5232     if (element == EL_QUICKSAND_FULL)
5233     {
5234       if (IS_FREE(x, y + 1))
5235       {
5236         InitMovingField(x, y, MV_DOWN);
5237         started_moving = TRUE;
5238
5239         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5240         Store[x][y] = EL_ROCK;
5241 #if 1
5242         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5243 #else
5244         PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5245 #endif
5246       }
5247       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5248       {
5249         if (!MovDelay[x][y])
5250           MovDelay[x][y] = TILEY + 1;
5251
5252         if (MovDelay[x][y])
5253         {
5254           MovDelay[x][y]--;
5255           if (MovDelay[x][y])
5256             return;
5257         }
5258
5259         Feld[x][y] = EL_QUICKSAND_EMPTY;
5260         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5261         Store[x][y + 1] = Store[x][y];
5262         Store[x][y] = 0;
5263 #if 1
5264         PlayLevelSoundAction(x, y, ACTION_FILLING);
5265 #else
5266         PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5267 #endif
5268       }
5269     }
5270     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5271              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5272     {
5273       InitMovingField(x, y, MV_DOWN);
5274       started_moving = TRUE;
5275
5276       Feld[x][y] = EL_QUICKSAND_FILLING;
5277       Store[x][y] = element;
5278 #if 1
5279       PlayLevelSoundAction(x, y, ACTION_FILLING);
5280 #else
5281       PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5282 #endif
5283     }
5284     else if (element == EL_MAGIC_WALL_FULL)
5285     {
5286       if (IS_FREE(x, y + 1))
5287       {
5288         InitMovingField(x, y, MV_DOWN);
5289         started_moving = TRUE;
5290
5291         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5292         Store[x][y] = EL_CHANGED(Store[x][y]);
5293       }
5294       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5295       {
5296         if (!MovDelay[x][y])
5297           MovDelay[x][y] = TILEY/4 + 1;
5298
5299         if (MovDelay[x][y])
5300         {
5301           MovDelay[x][y]--;
5302           if (MovDelay[x][y])
5303             return;
5304         }
5305
5306         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5307         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5308         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5309         Store[x][y] = 0;
5310       }
5311     }
5312     else if (element == EL_BD_MAGIC_WALL_FULL)
5313     {
5314       if (IS_FREE(x, y + 1))
5315       {
5316         InitMovingField(x, y, MV_DOWN);
5317         started_moving = TRUE;
5318
5319         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5320         Store[x][y] = EL_CHANGED2(Store[x][y]);
5321       }
5322       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5323       {
5324         if (!MovDelay[x][y])
5325           MovDelay[x][y] = TILEY/4 + 1;
5326
5327         if (MovDelay[x][y])
5328         {
5329           MovDelay[x][y]--;
5330           if (MovDelay[x][y])
5331             return;
5332         }
5333
5334         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5335         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5336         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5337         Store[x][y] = 0;
5338       }
5339     }
5340     else if (CAN_PASS_MAGIC_WALL(element) &&
5341              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5342               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5343     {
5344       InitMovingField(x, y, MV_DOWN);
5345       started_moving = TRUE;
5346
5347       Feld[x][y] =
5348         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5349          EL_BD_MAGIC_WALL_FILLING);
5350       Store[x][y] = element;
5351     }
5352 #if 0
5353     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5354 #else
5355     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5356 #endif
5357     {
5358       SplashAcid(x, y + 1);
5359
5360       InitMovingField(x, y, MV_DOWN);
5361       started_moving = TRUE;
5362
5363       Store[x][y] = EL_ACID;
5364 #if 0
5365       /* !!! TEST !!! better use "_FALLING" etc. !!! */
5366       GfxAction[x][y + 1] = ACTION_ACTIVE;
5367 #endif
5368     }
5369 #if 1
5370     else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5371               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5372
5373              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5374               CAN_SMASH(element) && WasJustFalling[x][y] &&
5375               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5376
5377              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5378               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5379               (Feld[x][y + 1] == EL_BLOCKED)))
5380
5381 #else
5382 #if 1
5383     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5384              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5385              WasJustMoving[x][y] && !Pushed[x][y + 1])
5386 #else
5387     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5388              WasJustMoving[x][y])
5389 #endif
5390 #endif
5391
5392     {
5393       /* this is needed for a special case not covered by calling "Impact()"
5394          from "ContinueMoving()": if an element moves to a tile directly below
5395          another element which was just falling on that tile (which was empty
5396          in the previous frame), the falling element above would just stop
5397          instead of smashing the element below (in previous version, the above
5398          element was just checked for "moving" instead of "falling", resulting
5399          in incorrect smashes caused by horizontal movement of the above
5400          element; also, the case of the player being the element to smash was
5401          simply not covered here... :-/ ) */
5402
5403 #if 0
5404       WasJustMoving[x][y] = 0;
5405       WasJustFalling[x][y] = 0;
5406 #endif
5407
5408       CheckCollision[x][y] = 0;
5409
5410 #if 0
5411       if (IS_PLAYER(x, y + 1))
5412         printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5413 #endif
5414
5415       Impact(x, y);
5416     }
5417     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5418     {
5419       if (MovDir[x][y] == MV_NO_MOVING)
5420       {
5421         InitMovingField(x, y, MV_DOWN);
5422         started_moving = TRUE;
5423       }
5424     }
5425     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5426     {
5427       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5428         MovDir[x][y] = MV_DOWN;
5429
5430       InitMovingField(x, y, MV_DOWN);
5431       started_moving = TRUE;
5432     }
5433     else if (element == EL_AMOEBA_DROP)
5434     {
5435       Feld[x][y] = EL_AMOEBA_GROWING;
5436       Store[x][y] = EL_AMOEBA_WET;
5437     }
5438     /* Store[x][y + 1] must be zero, because:
5439        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5440     */
5441 #if 0
5442 #if OLD_GAME_BEHAVIOUR
5443     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5444 #else
5445     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5446              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5447              element != EL_DX_SUPABOMB)
5448 #endif
5449 #else
5450     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5451               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5452              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5453              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5454 #endif
5455     {
5456       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
5457                                 (IS_FREE(x - 1, y + 1) ||
5458                                  Feld[x - 1][y + 1] == EL_ACID));
5459       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5460                                 (IS_FREE(x + 1, y + 1) ||
5461                                  Feld[x + 1][y + 1] == EL_ACID));
5462       boolean can_fall_any  = (can_fall_left || can_fall_right);
5463       boolean can_fall_both = (can_fall_left && can_fall_right);
5464
5465       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5466       {
5467         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5468
5469         if (slippery_type == SLIPPERY_ONLY_LEFT)
5470           can_fall_right = FALSE;
5471         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5472           can_fall_left = FALSE;
5473         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5474           can_fall_right = FALSE;
5475         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5476           can_fall_left = FALSE;
5477
5478         can_fall_any  = (can_fall_left || can_fall_right);
5479         can_fall_both = (can_fall_left && can_fall_right);
5480       }
5481
5482 #if USE_NEW_SP_SLIPPERY
5483       /* !!! better use the same properties as for custom elements here !!! */
5484       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5485                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5486       {
5487         can_fall_right = FALSE;         /* slip down on left side */
5488         can_fall_both = FALSE;
5489       }
5490 #endif
5491
5492 #if 1
5493       if (can_fall_both)
5494       {
5495         if (game.emulation == EMU_BOULDERDASH ||
5496             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5497           can_fall_right = FALSE;       /* slip down on left side */
5498         else
5499           can_fall_left = !(can_fall_right = RND(2));
5500
5501         can_fall_both = FALSE;
5502       }
5503 #endif
5504
5505       if (can_fall_any)
5506       {
5507 #if 0
5508         if (can_fall_both &&
5509             (game.emulation != EMU_BOULDERDASH &&
5510              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5511           can_fall_left = !(can_fall_right = RND(2));
5512 #endif
5513
5514         /* if not determined otherwise, prefer left side for slipping down */
5515         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5516         started_moving = TRUE;
5517       }
5518     }
5519 #if 0
5520     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5521 #else
5522     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5523 #endif
5524     {
5525       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
5526       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5527       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5528       int belt_dir = game.belt_dir[belt_nr];
5529
5530       if ((belt_dir == MV_LEFT  && left_is_free) ||
5531           (belt_dir == MV_RIGHT && right_is_free))
5532       {
5533 #if 1
5534         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5535 #endif
5536
5537         InitMovingField(x, y, belt_dir);
5538         started_moving = TRUE;
5539
5540 #if 1
5541         Pushed[x][y] = TRUE;
5542         Pushed[nextx][y] = TRUE;
5543 #endif
5544
5545         GfxAction[x][y] = ACTION_DEFAULT;
5546       }
5547       else
5548       {
5549         MovDir[x][y] = 0;       /* if element was moving, stop it */
5550       }
5551     }
5552   }
5553
5554   /* not "else if" because of elements that can fall and move (EL_SPRING) */
5555 #if 0
5556   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5557 #else
5558   if (CAN_MOVE(element) && !started_moving)
5559 #endif
5560   {
5561     int move_pattern = element_info[element].move_pattern;
5562     int newx, newy;
5563
5564 #if 0
5565 #if DEBUG
5566     if (MovDir[x][y] == MV_NO_MOVING)
5567     {
5568       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5569              x, y, element, element_info[element].token_name);
5570       printf("StartMoving(): This should never happen!\n");
5571     }
5572 #endif
5573 #endif
5574
5575     Moving2Blocked(x, y, &newx, &newy);
5576
5577 #if 1
5578     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5579       return;
5580 #else
5581     if ((element == EL_SATELLITE ||
5582          element == EL_BALLOON ||
5583          element == EL_SPRING)
5584         && JustBeingPushed(x, y))
5585       return;
5586 #endif
5587
5588 #if 1
5589
5590 #if 1
5591     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5592         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5593 #else
5594     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5595         WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5596         (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5597 #endif
5598     {
5599 #if 0
5600       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5601              element, element_info[element].token_name,
5602              WasJustMoving[x][y],
5603              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5604              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5605              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5606              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5607 #endif
5608
5609 #if 1
5610       WasJustMoving[x][y] = 0;
5611 #endif
5612
5613       CheckCollision[x][y] = 0;
5614
5615       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5616
5617 #if 0
5618       if (Feld[x][y] != element)        /* element has changed */
5619       {
5620         element = Feld[x][y];
5621         move_pattern = element_info[element].move_pattern;
5622
5623         if (!CAN_MOVE(element))
5624           return;
5625       }
5626 #else
5627       if (Feld[x][y] != element)        /* element has changed */
5628         return;
5629 #endif
5630     }
5631 #endif
5632
5633 #if 0
5634 #if 0
5635     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5636       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
5637 #else
5638     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5639     {
5640       Moving2Blocked(x, y, &newx, &newy);
5641       if (Feld[newx][newy] == EL_BLOCKED)
5642         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
5643     }
5644 #endif
5645 #endif
5646
5647 #if 0
5648     if (FrameCounter < 1 && x == 0 && y == 29)
5649       printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5650 #endif
5651
5652     if (!MovDelay[x][y])        /* start new movement phase */
5653     {
5654       /* all objects that can change their move direction after each step
5655          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5656
5657       if (element != EL_YAMYAM &&
5658           element != EL_DARK_YAMYAM &&
5659           element != EL_PACMAN &&
5660           !(move_pattern & MV_ANY_DIRECTION) &&
5661           move_pattern != MV_TURNING_LEFT &&
5662           move_pattern != MV_TURNING_RIGHT &&
5663           move_pattern != MV_TURNING_LEFT_RIGHT &&
5664           move_pattern != MV_TURNING_RIGHT_LEFT &&
5665           move_pattern != MV_TURNING_RANDOM)
5666       {
5667         TurnRound(x, y);
5668
5669 #if 0
5670         if (FrameCounter < 1 && x == 0 && y == 29)
5671           printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5672 #endif
5673
5674         if (MovDelay[x][y] && (element == EL_BUG ||
5675                                element == EL_SPACESHIP ||
5676                                element == EL_SP_SNIKSNAK ||
5677                                element == EL_SP_ELECTRON ||
5678                                element == EL_MOLE))
5679           DrawLevelField(x, y);
5680       }
5681     }
5682
5683     if (MovDelay[x][y])         /* wait some time before next movement */
5684     {
5685       MovDelay[x][y]--;
5686
5687 #if 0
5688       if (element == EL_YAMYAM)
5689       {
5690         printf("::: %d\n",
5691                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5692         DrawLevelElementAnimation(x, y, element);
5693       }
5694 #endif
5695
5696       if (MovDelay[x][y])       /* element still has to wait some time */
5697       {
5698 #if 0
5699         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5700         ResetGfxAnimation(x, y);
5701 #endif
5702
5703 #if 0
5704         if (GfxAction[x][y] != ACTION_WAITING)
5705           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5706
5707         GfxAction[x][y] = ACTION_WAITING;
5708 #endif
5709       }
5710
5711       if (element == EL_ROBOT ||
5712 #if 0
5713           element == EL_PACMAN ||
5714 #endif
5715           element == EL_YAMYAM ||
5716           element == EL_DARK_YAMYAM)
5717       {
5718 #if 0
5719         DrawLevelElementAnimation(x, y, element);
5720 #else
5721         DrawLevelElementAnimationIfNeeded(x, y, element);
5722 #endif
5723         PlayLevelSoundAction(x, y, ACTION_WAITING);
5724       }
5725       else if (element == EL_SP_ELECTRON)
5726         DrawLevelElementAnimationIfNeeded(x, y, element);
5727       else if (element == EL_DRAGON)
5728       {
5729         int i;
5730         int dir = MovDir[x][y];
5731         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5732         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
5733         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
5734                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
5735                        dir == MV_UP     ? IMG_FLAMES_1_UP :
5736                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5737         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5738
5739 #if 0
5740         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5741 #endif
5742
5743         GfxAction[x][y] = ACTION_ATTACKING;
5744
5745         if (IS_PLAYER(x, y))
5746           DrawPlayerField(x, y);
5747         else
5748           DrawLevelField(x, y);
5749
5750         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5751
5752         for (i = 1; i <= 3; i++)
5753         {
5754           int xx = x + i * dx;
5755           int yy = y + i * dy;
5756           int sx = SCREENX(xx);
5757           int sy = SCREENY(yy);
5758           int flame_graphic = graphic + (i - 1);
5759
5760           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5761             break;
5762
5763           if (MovDelay[x][y])
5764           {
5765             int flamed = MovingOrBlocked2Element(xx, yy);
5766
5767             /* !!! */
5768 #if 0
5769             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5770               Bang(xx, yy);
5771             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5772               RemoveMovingField(xx, yy);
5773             else
5774               RemoveField(xx, yy);
5775 #else
5776             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5777               Bang(xx, yy);
5778             else
5779               RemoveMovingField(xx, yy);
5780 #endif
5781
5782 #if 0
5783             if (ChangeDelay[xx][yy])
5784               printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5785                                         Feld[xx][yy] == EL_BLOCKED));
5786 #endif
5787
5788 #if 1
5789             ChangeDelay[xx][yy] = 0;
5790 #endif
5791             Feld[xx][yy] = EL_FLAMES;
5792             if (IN_SCR_FIELD(sx, sy))
5793             {
5794               DrawLevelFieldCrumbledSand(xx, yy);
5795               DrawGraphic(sx, sy, flame_graphic, frame);
5796             }
5797           }
5798           else
5799           {
5800             if (Feld[xx][yy] == EL_FLAMES)
5801               Feld[xx][yy] = EL_EMPTY;
5802             DrawLevelField(xx, yy);
5803           }
5804         }
5805       }
5806
5807       if (MovDelay[x][y])       /* element still has to wait some time */
5808       {
5809         PlayLevelSoundAction(x, y, ACTION_WAITING);
5810
5811         return;
5812       }
5813
5814 #if 0
5815       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5816          for all other elements GfxAction will be set by InitMovingField() */
5817       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5818         GfxAction[x][y] = ACTION_MOVING;
5819 #endif
5820     }
5821
5822     /* now make next step */
5823
5824     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5825
5826     if (DONT_COLLIDE_WITH(element) &&
5827         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5828         !PLAYER_ENEMY_PROTECTED(newx, newy))
5829     {
5830 #if 1
5831       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5832
5833       return;
5834 #else
5835       /* player killed by element which is deadly when colliding with */
5836       MovDir[x][y] = 0;
5837       KillHero(PLAYERINFO(newx, newy));
5838       return;
5839 #endif
5840
5841     }
5842 #if 1
5843 #if 1
5844     else if (CAN_MOVE_INTO_ACID(element) &&
5845              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5846              (MovDir[x][y] == MV_DOWN ||
5847               game.engine_version >= VERSION_IDENT(3,1,0,0)))
5848 #else
5849     else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5850              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5851 #endif
5852 #else
5853
5854     else if ((element == EL_PENGUIN ||
5855               element == EL_ROBOT ||
5856               element == EL_SATELLITE ||
5857               element == EL_BALLOON ||
5858               IS_CUSTOM_ELEMENT(element)) &&
5859              IN_LEV_FIELD(newx, newy) &&
5860              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5861 #endif
5862     {
5863       SplashAcid(newx, newy);
5864       Store[x][y] = EL_ACID;
5865     }
5866     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5867     {
5868       if (Feld[newx][newy] == EL_EXIT_OPEN)
5869       {
5870 #if 1
5871         RemoveField(x, y);
5872         DrawLevelField(x, y);
5873 #else
5874         Feld[x][y] = EL_EMPTY;
5875         DrawLevelField(x, y);
5876 #endif
5877
5878         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5879         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5880           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5881
5882         local_player->friends_still_needed--;
5883         if (!local_player->friends_still_needed &&
5884             !local_player->GameOver && AllPlayersGone)
5885           local_player->LevelSolved = local_player->GameOver = TRUE;
5886
5887         return;
5888       }
5889       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5890       {
5891         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5892           DrawLevelField(newx, newy);
5893         else
5894           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5895       }
5896       else if (!IS_FREE(newx, newy))
5897       {
5898         GfxAction[x][y] = ACTION_WAITING;
5899
5900         if (IS_PLAYER(x, y))
5901           DrawPlayerField(x, y);
5902         else
5903           DrawLevelField(x, y);
5904
5905         return;
5906       }
5907     }
5908     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5909     {
5910       if (IS_FOOD_PIG(Feld[newx][newy]))
5911       {
5912         if (IS_MOVING(newx, newy))
5913           RemoveMovingField(newx, newy);
5914         else
5915         {
5916           Feld[newx][newy] = EL_EMPTY;
5917           DrawLevelField(newx, newy);
5918         }
5919
5920         PlayLevelSound(x, y, SND_PIG_DIGGING);
5921       }
5922       else if (!IS_FREE(newx, newy))
5923       {
5924         if (IS_PLAYER(x, y))
5925           DrawPlayerField(x, y);
5926         else
5927           DrawLevelField(x, y);
5928
5929         return;
5930       }
5931     }
5932
5933 #if 1
5934
5935     /*
5936     else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5937     */
5938
5939     else if (IS_CUSTOM_ELEMENT(element) &&
5940              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5941
5942 #if 0
5943  &&
5944              !IS_FREE(newx, newy)
5945 #endif
5946
5947 )
5948     {
5949       int new_element = Feld[newx][newy];
5950
5951 #if 0
5952       printf("::: '%s' digs '%s' [%d]\n",
5953              element_info[element].token_name,
5954              element_info[Feld[newx][newy]].token_name,
5955              StorePlayer[newx][newy]);
5956 #endif
5957
5958       if (!IS_FREE(newx, newy))
5959       {
5960         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5961                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5962                       ACTION_BREAKING);
5963
5964         /* no element can dig solid indestructible elements */
5965         if (IS_INDESTRUCTIBLE(new_element) &&
5966             !IS_DIGGABLE(new_element) &&
5967             !IS_COLLECTIBLE(new_element))
5968           return;
5969
5970         if (AmoebaNr[newx][newy] &&
5971             (new_element == EL_AMOEBA_FULL ||
5972              new_element == EL_BD_AMOEBA ||
5973              new_element == EL_AMOEBA_GROWING))
5974         {
5975           AmoebaCnt[AmoebaNr[newx][newy]]--;
5976           AmoebaCnt2[AmoebaNr[newx][newy]]--;
5977         }
5978
5979         if (IS_MOVING(newx, newy))
5980           RemoveMovingField(newx, newy);
5981         else
5982         {
5983           RemoveField(newx, newy);
5984           DrawLevelField(newx, newy);
5985         }
5986
5987         /* if digged element was about to explode, prevent the explosion */
5988         ExplodeField[newx][newy] = EX_TYPE_NONE;
5989
5990         PlayLevelSoundAction(x, y, action);
5991       }
5992
5993 #if 1
5994 #if 1
5995       Store[newx][newy] = EL_EMPTY;
5996       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5997         Store[newx][newy] = element_info[element].move_leave_element;
5998 #else
5999       Store[newx][newy] = EL_EMPTY;
6000       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6001           element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6002         Store[newx][newy] = element_info[element].move_leave_element;
6003 #endif
6004 #else
6005       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6006         element_info[element].can_leave_element = TRUE;
6007 #endif
6008
6009       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6010       {
6011         RunnerVisit[x][y] = FrameCounter;
6012         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6013       }
6014     }
6015
6016 #endif
6017
6018     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6019     {
6020       if (!IS_FREE(newx, newy))
6021       {
6022         if (IS_PLAYER(x, y))
6023           DrawPlayerField(x, y);
6024         else
6025           DrawLevelField(x, y);
6026
6027         return;
6028       }
6029       else
6030       {
6031         boolean wanna_flame = !RND(10);
6032         int dx = newx - x, dy = newy - y;
6033         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6034         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6035         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6036                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6037         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6038                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6039
6040         if ((wanna_flame ||
6041              IS_CLASSIC_ENEMY(element1) ||
6042              IS_CLASSIC_ENEMY(element2)) &&
6043             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6044             element1 != EL_FLAMES && element2 != EL_FLAMES)
6045         {
6046 #if 1
6047           ResetGfxAnimation(x, y);
6048           GfxAction[x][y] = ACTION_ATTACKING;
6049 #endif
6050
6051           if (IS_PLAYER(x, y))
6052             DrawPlayerField(x, y);
6053           else
6054             DrawLevelField(x, y);
6055
6056           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6057
6058           MovDelay[x][y] = 50;
6059
6060           /* !!! */
6061 #if 0
6062           RemoveField(newx, newy);
6063 #endif
6064           Feld[newx][newy] = EL_FLAMES;
6065           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6066           {
6067 #if 0
6068             RemoveField(newx1, newy1);
6069 #endif
6070             Feld[newx1][newy1] = EL_FLAMES;
6071           }
6072           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6073           {
6074 #if 0
6075             RemoveField(newx2, newy2);
6076 #endif
6077             Feld[newx2][newy2] = EL_FLAMES;
6078           }
6079
6080           return;
6081         }
6082       }
6083     }
6084     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6085              Feld[newx][newy] == EL_DIAMOND)
6086     {
6087       if (IS_MOVING(newx, newy))
6088         RemoveMovingField(newx, newy);
6089       else
6090       {
6091         Feld[newx][newy] = EL_EMPTY;
6092         DrawLevelField(newx, newy);
6093       }
6094
6095       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6096     }
6097     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6098              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6099     {
6100       if (AmoebaNr[newx][newy])
6101       {
6102         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6103         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6104             Feld[newx][newy] == EL_BD_AMOEBA)
6105           AmoebaCnt[AmoebaNr[newx][newy]]--;
6106       }
6107
6108 #if 0
6109       /* !!! test !!! */
6110       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6111 #else
6112       if (IS_MOVING(newx, newy))
6113 #endif
6114       {
6115         RemoveMovingField(newx, newy);
6116       }
6117       else
6118       {
6119         Feld[newx][newy] = EL_EMPTY;
6120         DrawLevelField(newx, newy);
6121       }
6122
6123       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6124     }
6125     else if ((element == EL_PACMAN || element == EL_MOLE)
6126              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6127     {
6128       if (AmoebaNr[newx][newy])
6129       {
6130         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6131         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6132             Feld[newx][newy] == EL_BD_AMOEBA)
6133           AmoebaCnt[AmoebaNr[newx][newy]]--;
6134       }
6135
6136       if (element == EL_MOLE)
6137       {
6138         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6139         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6140
6141         ResetGfxAnimation(x, y);
6142         GfxAction[x][y] = ACTION_DIGGING;
6143         DrawLevelField(x, y);
6144
6145         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6146
6147         return;                         /* wait for shrinking amoeba */
6148       }
6149       else      /* element == EL_PACMAN */
6150       {
6151         Feld[newx][newy] = EL_EMPTY;
6152         DrawLevelField(newx, newy);
6153         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6154       }
6155     }
6156     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6157              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6158               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6159     {
6160       /* wait for shrinking amoeba to completely disappear */
6161       return;
6162     }
6163     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6164     {
6165       /* object was running against a wall */
6166
6167       TurnRound(x, y);
6168
6169 #if 0
6170       if (move_pattern & MV_ANY_DIRECTION &&
6171           move_pattern == MovDir[x][y])
6172       {
6173         int blocking_element =
6174           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6175
6176 #if 0
6177         printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6178                element_info[element].token_name,
6179                element_info[blocking_element].token_name,
6180                x, y, newx, newy);
6181 #endif
6182
6183         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6184                                  MovDir[x][y]);
6185
6186         element = Feld[x][y];   /* element might have changed */
6187       }
6188 #endif
6189
6190 #if 1
6191       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
6192         DrawLevelElementAnimation(x, y, element);
6193 #else
6194       if (element == EL_BUG ||
6195           element == EL_SPACESHIP ||
6196           element == EL_SP_SNIKSNAK)
6197         DrawLevelField(x, y);
6198       else if (element == EL_MOLE)
6199         DrawLevelField(x, y);
6200       else if (element == EL_BD_BUTTERFLY ||
6201                element == EL_BD_FIREFLY)
6202         DrawLevelElementAnimationIfNeeded(x, y, element);
6203       else if (element == EL_SATELLITE)
6204         DrawLevelElementAnimationIfNeeded(x, y, element);
6205       else if (element == EL_SP_ELECTRON)
6206         DrawLevelElementAnimationIfNeeded(x, y, element);
6207 #endif
6208
6209       if (DONT_TOUCH(element))
6210         TestIfBadThingTouchesHero(x, y);
6211
6212 #if 0
6213       PlayLevelSoundAction(x, y, ACTION_WAITING);
6214 #endif
6215
6216       return;
6217     }
6218
6219     InitMovingField(x, y, MovDir[x][y]);
6220
6221     PlayLevelSoundAction(x, y, ACTION_MOVING);
6222   }
6223
6224   if (MovDir[x][y])
6225     ContinueMoving(x, y);
6226 }
6227
6228 void ContinueMoving(int x, int y)
6229 {
6230   int element = Feld[x][y];
6231   int stored = Store[x][y];
6232   struct ElementInfo *ei = &element_info[element];
6233   int direction = MovDir[x][y];
6234   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6235   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
6236   int newx = x + dx, newy = y + dy;
6237 #if 0
6238   int nextx = newx + dx, nexty = newy + dy;
6239 #endif
6240 #if 1
6241   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
6242   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6243 #else
6244   boolean pushed_by_player = Pushed[x][y];
6245 #endif
6246
6247   MovPos[x][y] += getElementMoveStepsize(x, y);
6248
6249 #if 0
6250   if (pushed_by_player && IS_PLAYER(x, y))
6251   {
6252     /* special case: moving object pushed by player */
6253     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6254   }
6255 #else
6256   if (pushed_by_player) /* special case: moving object pushed by player */
6257     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6258 #endif
6259
6260   if (ABS(MovPos[x][y]) < TILEX)
6261   {
6262     DrawLevelField(x, y);
6263
6264     return;     /* element is still moving */
6265   }
6266
6267   /* element reached destination field */
6268
6269   Feld[x][y] = EL_EMPTY;
6270   Feld[newx][newy] = element;
6271   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6272
6273 #if 1
6274   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6275   {
6276     element = Feld[newx][newy] = EL_ACID;
6277   }
6278 #endif
6279   else if (element == EL_MOLE)
6280   {
6281     Feld[x][y] = EL_SAND;
6282
6283     DrawLevelFieldCrumbledSandNeighbours(x, y);
6284   }
6285   else if (element == EL_QUICKSAND_FILLING)
6286   {
6287     element = Feld[newx][newy] = get_next_element(element);
6288     Store[newx][newy] = Store[x][y];
6289   }
6290   else if (element == EL_QUICKSAND_EMPTYING)
6291   {
6292     Feld[x][y] = get_next_element(element);
6293     element = Feld[newx][newy] = Store[x][y];
6294   }
6295   else if (element == EL_MAGIC_WALL_FILLING)
6296   {
6297     element = Feld[newx][newy] = get_next_element(element);
6298     if (!game.magic_wall_active)
6299       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6300     Store[newx][newy] = Store[x][y];
6301   }
6302   else if (element == EL_MAGIC_WALL_EMPTYING)
6303   {
6304     Feld[x][y] = get_next_element(element);
6305     if (!game.magic_wall_active)
6306       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6307     element = Feld[newx][newy] = Store[x][y];
6308   }
6309   else if (element == EL_BD_MAGIC_WALL_FILLING)
6310   {
6311     element = Feld[newx][newy] = get_next_element(element);
6312     if (!game.magic_wall_active)
6313       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6314     Store[newx][newy] = Store[x][y];
6315   }
6316   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6317   {
6318     Feld[x][y] = get_next_element(element);
6319     if (!game.magic_wall_active)
6320       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6321     element = Feld[newx][newy] = Store[x][y];
6322   }
6323   else if (element == EL_AMOEBA_DROPPING)
6324   {
6325     Feld[x][y] = get_next_element(element);
6326     element = Feld[newx][newy] = Store[x][y];
6327   }
6328   else if (element == EL_SOKOBAN_OBJECT)
6329   {
6330     if (Back[x][y])
6331       Feld[x][y] = Back[x][y];
6332
6333     if (Back[newx][newy])
6334       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6335
6336     Back[x][y] = Back[newx][newy] = 0;
6337   }
6338 #if 0
6339   else if (Store[x][y] == EL_ACID)
6340   {
6341     element = Feld[newx][newy] = EL_ACID;
6342   }
6343 #endif
6344 #if 0
6345   else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6346            ei->move_leave_element != EL_EMPTY &&
6347            (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6348             Store[x][y] != EL_EMPTY))
6349   {
6350     /* some elements can leave other elements behind after moving */
6351
6352     Feld[x][y] = ei->move_leave_element;
6353     InitField(x, y, FALSE);
6354
6355     if (GFX_CRUMBLED(Feld[x][y]))
6356       DrawLevelFieldCrumbledSandNeighbours(x, y);
6357   }
6358 #endif
6359
6360   Store[x][y] = EL_EMPTY;
6361   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6362   MovDelay[newx][newy] = 0;
6363
6364   if (CAN_CHANGE(element))
6365   {
6366     /* copy element change control values to new field */
6367     ChangeDelay[newx][newy] = ChangeDelay[x][y];
6368     ChangePage[newx][newy]  = ChangePage[x][y];
6369     Changed[newx][newy]     = Changed[x][y];
6370     ChangeEvent[newx][newy] = ChangeEvent[x][y];
6371   }
6372
6373   ChangeDelay[x][y] = 0;
6374   ChangePage[x][y] = -1;
6375   Changed[x][y] = CE_BITMASK_DEFAULT;
6376   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6377
6378   /* copy animation control values to new field */
6379   GfxFrame[newx][newy]  = GfxFrame[x][y];
6380   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
6381   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
6382   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
6383
6384   Pushed[x][y] = Pushed[newx][newy] = FALSE;
6385
6386   ResetGfxAnimation(x, y);      /* reset animation values for old field */
6387
6388 #if 1
6389   /* some elements can leave other elements behind after moving */
6390 #if 1
6391   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6392       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6393       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6394 #else
6395   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6396       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6397       !IS_PLAYER(x, y))
6398 #endif
6399   {
6400     int move_leave_element = ei->move_leave_element;
6401
6402     Feld[x][y] = move_leave_element;
6403     InitField(x, y, FALSE);
6404
6405     if (GFX_CRUMBLED(Feld[x][y]))
6406       DrawLevelFieldCrumbledSandNeighbours(x, y);
6407
6408     if (ELEM_IS_PLAYER(move_leave_element))
6409       RelocatePlayer(x, y, move_leave_element);
6410   }
6411 #endif
6412
6413 #if 0
6414   /* some elements can leave other elements behind after moving */
6415   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6416       ei->move_leave_element != EL_EMPTY &&
6417       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6418        ei->can_leave_element_last))
6419   {
6420     Feld[x][y] = ei->move_leave_element;
6421     InitField(x, y, FALSE);
6422
6423     if (GFX_CRUMBLED(Feld[x][y]))
6424       DrawLevelFieldCrumbledSandNeighbours(x, y);
6425   }
6426
6427   ei->can_leave_element_last = ei->can_leave_element;
6428   ei->can_leave_element = FALSE;
6429 #endif
6430
6431 #if 0
6432   /* 2.1.1 (does not work correctly for spring) */
6433   if (!CAN_MOVE(element))
6434     MovDir[newx][newy] = 0;
6435 #else
6436
6437 #if 0
6438   /* (does not work for falling objects that slide horizontally) */
6439   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6440     MovDir[newx][newy] = 0;
6441 #else
6442   /*
6443   if (!CAN_MOVE(element) ||
6444       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6445     MovDir[newx][newy] = 0;
6446   */
6447
6448 #if 0
6449   if (!CAN_MOVE(element) ||
6450       (CAN_FALL(element) && direction == MV_DOWN))
6451     GfxDir[x][y] = MovDir[newx][newy] = 0;
6452 #else
6453   if (!CAN_MOVE(element) ||
6454       (CAN_FALL(element) && direction == MV_DOWN &&
6455        (element == EL_SPRING ||
6456         element_info[element].move_pattern == MV_WHEN_PUSHED ||
6457         element_info[element].move_pattern == MV_WHEN_DROPPED)))
6458     GfxDir[x][y] = MovDir[newx][newy] = 0;
6459 #endif
6460
6461 #endif
6462 #endif
6463
6464   DrawLevelField(x, y);
6465   DrawLevelField(newx, newy);
6466
6467   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
6468
6469   /* prevent pushed element from moving on in pushed direction */
6470   if (pushed_by_player && CAN_MOVE(element) &&
6471       element_info[element].move_pattern & MV_ANY_DIRECTION &&
6472       !(element_info[element].move_pattern & direction))
6473     TurnRound(newx, newy);
6474
6475 #if 1
6476   /* prevent elements on conveyor belt from moving on in last direction */
6477   if (pushed_by_conveyor && CAN_FALL(element) &&
6478       direction & MV_HORIZONTAL)
6479   {
6480 #if 0
6481     if (CAN_MOVE(element))
6482       InitMovDir(newx, newy);
6483     else
6484       MovDir[newx][newy] = 0;
6485 #else
6486     MovDir[newx][newy] = 0;
6487 #endif
6488   }
6489 #endif
6490
6491   if (!pushed_by_player)
6492   {
6493     int nextx = newx + dx, nexty = newy + dy;
6494     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6495
6496     WasJustMoving[newx][newy] = 3;
6497
6498     if (CAN_FALL(element) && direction == MV_DOWN)
6499       WasJustFalling[newx][newy] = 3;
6500
6501     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6502       CheckCollision[newx][newy] = 2;
6503   }
6504
6505   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
6506   {
6507     TestIfBadThingTouchesHero(newx, newy);
6508     TestIfBadThingTouchesFriend(newx, newy);
6509
6510     if (!IS_CUSTOM_ELEMENT(element))
6511       TestIfBadThingTouchesOtherBadThing(newx, newy);
6512   }
6513   else if (element == EL_PENGUIN)
6514     TestIfFriendTouchesBadThing(newx, newy);
6515
6516 #if USE_NEW_MOVE_STYLE
6517 #if 0
6518   if (CAN_FALL(element) && direction == MV_DOWN &&
6519       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6520       IS_PLAYER(x, newy + 1))
6521     printf("::: we would now kill the player [%d]\n", FrameCounter);
6522 #endif
6523
6524   /* give the player one last chance (one more frame) to move away */
6525   if (CAN_FALL(element) && direction == MV_DOWN &&
6526       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)) &&
6527       (!IS_PLAYER(x, newy + 1) ||
6528        game.engine_version < VERSION_IDENT(3,1,1,0)))
6529     Impact(x, newy);
6530 #else
6531   if (CAN_FALL(element) && direction == MV_DOWN &&
6532       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6533     Impact(x, newy);
6534 #endif
6535
6536 #if 1
6537
6538 #if USE_PUSH_BUGFIX
6539 #if 1
6540   if (pushed_by_player && !game.use_bug_change_when_pushing)
6541 #else
6542   if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6543 #endif
6544 #else
6545   if (pushed_by_player)
6546 #endif
6547
6548   {
6549 #if 1
6550     int dig_side = MV_DIR_OPPOSITE(direction);
6551 #else
6552     static int trigger_sides[4] =
6553     {
6554       CH_SIDE_RIGHT,    /* moving left  */
6555       CH_SIDE_LEFT,     /* moving right */
6556       CH_SIDE_BOTTOM,   /* moving up    */
6557       CH_SIDE_TOP,      /* moving down  */
6558     };
6559     int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6560 #endif
6561     struct PlayerInfo *player = PLAYERINFO(x, y);
6562
6563     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6564                                player->index_bit, dig_side);
6565     CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6566                                         player->index_bit, dig_side);
6567   }
6568 #endif
6569
6570 #if 1
6571   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
6572 #endif
6573
6574 #if 0
6575   if (ChangePage[newx][newy] != -1)                     /* delayed change */
6576     ChangeElement(newx, newy, ChangePage[newx][newy]);
6577 #endif
6578
6579 #if 1
6580
6581   TestIfElementHitsCustomElement(newx, newy, direction);
6582
6583 #else
6584
6585   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6586   {
6587     int hitting_element = Feld[newx][newy];
6588
6589     /* !!! fix side (direction) orientation here and elsewhere !!! */
6590     CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6591                              direction);
6592
6593 #if 0
6594     if (IN_LEV_FIELD(nextx, nexty))
6595     {
6596       int opposite_direction = MV_DIR_OPPOSITE(direction);
6597       int hitting_side = direction;
6598       int touched_side = opposite_direction;
6599       int touched_element = MovingOrBlocked2Element(nextx, nexty);
6600       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6601                             MovDir[nextx][nexty] != direction ||
6602                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6603
6604       if (object_hit)
6605       {
6606         int i;
6607
6608         CheckElementChangeBySide(nextx, nexty, touched_element,
6609                                  CE_HIT_BY_SOMETHING, opposite_direction);
6610
6611         if (IS_CUSTOM_ELEMENT(hitting_element) &&
6612             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6613         {
6614           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6615           {
6616             struct ElementChangeInfo *change =
6617               &element_info[hitting_element].change_page[i];
6618
6619             if (change->can_change &&
6620                 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6621                 change->trigger_side & touched_side &&
6622                 change->trigger_element == touched_element)
6623             {
6624               CheckElementChangeByPage(newx, newy, hitting_element,
6625                                        touched_element, CE_OTHER_IS_HITTING,i);
6626               break;
6627             }
6628           }
6629         }
6630
6631         if (IS_CUSTOM_ELEMENT(touched_element) &&
6632             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6633         {
6634           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6635           {
6636             struct ElementChangeInfo *change =
6637               &element_info[touched_element].change_page[i];
6638
6639             if (change->can_change &&
6640                 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6641                 change->trigger_side & hitting_side &&
6642                 change->trigger_element == hitting_element)
6643             {
6644               CheckElementChangeByPage(nextx, nexty, touched_element,
6645                                        hitting_element, CE_OTHER_GETS_HIT, i);
6646               break;
6647             }
6648           }
6649         }
6650       }
6651     }
6652 #endif
6653   }
6654 #endif
6655
6656   TestIfPlayerTouchesCustomElement(newx, newy);
6657   TestIfElementTouchesCustomElement(newx, newy);
6658 }
6659
6660 int AmoebeNachbarNr(int ax, int ay)
6661 {
6662   int i;
6663   int element = Feld[ax][ay];
6664   int group_nr = 0;
6665   static int xy[4][2] =
6666   {
6667     { 0, -1 },
6668     { -1, 0 },
6669     { +1, 0 },
6670     { 0, +1 }
6671   };
6672
6673   for (i = 0; i < NUM_DIRECTIONS; i++)
6674   {
6675     int x = ax + xy[i][0];
6676     int y = ay + xy[i][1];
6677
6678     if (!IN_LEV_FIELD(x, y))
6679       continue;
6680
6681     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6682       group_nr = AmoebaNr[x][y];
6683   }
6684
6685   return group_nr;
6686 }
6687
6688 void AmoebenVereinigen(int ax, int ay)
6689 {
6690   int i, x, y, xx, yy;
6691   int new_group_nr = AmoebaNr[ax][ay];
6692   static int xy[4][2] =
6693   {
6694     { 0, -1 },
6695     { -1, 0 },
6696     { +1, 0 },
6697     { 0, +1 }
6698   };
6699
6700   if (new_group_nr == 0)
6701     return;
6702
6703   for (i = 0; i < NUM_DIRECTIONS; i++)
6704   {
6705     x = ax + xy[i][0];
6706     y = ay + xy[i][1];
6707
6708     if (!IN_LEV_FIELD(x, y))
6709       continue;
6710
6711     if ((Feld[x][y] == EL_AMOEBA_FULL ||
6712          Feld[x][y] == EL_BD_AMOEBA ||
6713          Feld[x][y] == EL_AMOEBA_DEAD) &&
6714         AmoebaNr[x][y] != new_group_nr)
6715     {
6716       int old_group_nr = AmoebaNr[x][y];
6717
6718       if (old_group_nr == 0)
6719         return;
6720
6721       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6722       AmoebaCnt[old_group_nr] = 0;
6723       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6724       AmoebaCnt2[old_group_nr] = 0;
6725
6726       for (yy = 0; yy < lev_fieldy; yy++)
6727       {
6728         for (xx = 0; xx < lev_fieldx; xx++)
6729         {
6730           if (AmoebaNr[xx][yy] == old_group_nr)
6731             AmoebaNr[xx][yy] = new_group_nr;
6732         }
6733       }
6734     }
6735   }
6736 }
6737
6738 void AmoebeUmwandeln(int ax, int ay)
6739 {
6740   int i, x, y;
6741
6742   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6743   {
6744     int group_nr = AmoebaNr[ax][ay];
6745
6746 #ifdef DEBUG
6747     if (group_nr == 0)
6748     {
6749       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6750       printf("AmoebeUmwandeln(): This should never happen!\n");
6751       return;
6752     }
6753 #endif
6754
6755     for (y = 0; y < lev_fieldy; y++)
6756     {
6757       for (x = 0; x < lev_fieldx; x++)
6758       {
6759         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6760         {
6761           AmoebaNr[x][y] = 0;
6762           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6763         }
6764       }
6765     }
6766     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6767                             SND_AMOEBA_TURNING_TO_GEM :
6768                             SND_AMOEBA_TURNING_TO_ROCK));
6769     Bang(ax, ay);
6770   }
6771   else
6772   {
6773     static int xy[4][2] =
6774     {
6775       { 0, -1 },
6776       { -1, 0 },
6777       { +1, 0 },
6778       { 0, +1 }
6779     };
6780
6781     for (i = 0; i < NUM_DIRECTIONS; i++)
6782     {
6783       x = ax + xy[i][0];
6784       y = ay + xy[i][1];
6785
6786       if (!IN_LEV_FIELD(x, y))
6787         continue;
6788
6789       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6790       {
6791         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6792                               SND_AMOEBA_TURNING_TO_GEM :
6793                               SND_AMOEBA_TURNING_TO_ROCK));
6794         Bang(x, y);
6795       }
6796     }
6797   }
6798 }
6799
6800 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6801 {
6802   int x, y;
6803   int group_nr = AmoebaNr[ax][ay];
6804   boolean done = FALSE;
6805
6806 #ifdef DEBUG
6807   if (group_nr == 0)
6808   {
6809     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6810     printf("AmoebeUmwandelnBD(): This should never happen!\n");
6811     return;
6812   }
6813 #endif
6814
6815   for (y = 0; y < lev_fieldy; y++)
6816   {
6817     for (x = 0; x < lev_fieldx; x++)
6818     {
6819       if (AmoebaNr[x][y] == group_nr &&
6820           (Feld[x][y] == EL_AMOEBA_DEAD ||
6821            Feld[x][y] == EL_BD_AMOEBA ||
6822            Feld[x][y] == EL_AMOEBA_GROWING))
6823       {
6824         AmoebaNr[x][y] = 0;
6825         Feld[x][y] = new_element;
6826         InitField(x, y, FALSE);
6827         DrawLevelField(x, y);
6828         done = TRUE;
6829       }
6830     }
6831   }
6832
6833   if (done)
6834     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6835                             SND_BD_AMOEBA_TURNING_TO_ROCK :
6836                             SND_BD_AMOEBA_TURNING_TO_GEM));
6837 }
6838
6839 void AmoebeWaechst(int x, int y)
6840 {
6841   static unsigned long sound_delay = 0;
6842   static unsigned long sound_delay_value = 0;
6843
6844   if (!MovDelay[x][y])          /* start new growing cycle */
6845   {
6846     MovDelay[x][y] = 7;
6847
6848     if (DelayReached(&sound_delay, sound_delay_value))
6849     {
6850 #if 1
6851       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6852 #else
6853       if (Store[x][y] == EL_BD_AMOEBA)
6854         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6855       else
6856         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6857 #endif
6858       sound_delay_value = 30;
6859     }
6860   }
6861
6862   if (MovDelay[x][y])           /* wait some time before growing bigger */
6863   {
6864     MovDelay[x][y]--;
6865     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6866     {
6867       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6868                                            6 - MovDelay[x][y]);
6869
6870       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6871     }
6872
6873     if (!MovDelay[x][y])
6874     {
6875       Feld[x][y] = Store[x][y];
6876       Store[x][y] = 0;
6877       DrawLevelField(x, y);
6878     }
6879   }
6880 }
6881
6882 void AmoebaDisappearing(int x, int y)
6883 {
6884   static unsigned long sound_delay = 0;
6885   static unsigned long sound_delay_value = 0;
6886
6887   if (!MovDelay[x][y])          /* start new shrinking cycle */
6888   {
6889     MovDelay[x][y] = 7;
6890
6891     if (DelayReached(&sound_delay, sound_delay_value))
6892       sound_delay_value = 30;
6893   }
6894
6895   if (MovDelay[x][y])           /* wait some time before shrinking */
6896   {
6897     MovDelay[x][y]--;
6898     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6899     {
6900       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6901                                            6 - MovDelay[x][y]);
6902
6903       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6904     }
6905
6906     if (!MovDelay[x][y])
6907     {
6908       Feld[x][y] = EL_EMPTY;
6909       DrawLevelField(x, y);
6910
6911       /* don't let mole enter this field in this cycle;
6912          (give priority to objects falling to this field from above) */
6913       Stop[x][y] = TRUE;
6914     }
6915   }
6916 }
6917
6918 void AmoebeAbleger(int ax, int ay)
6919 {
6920   int i;
6921   int element = Feld[ax][ay];
6922   int graphic = el2img(element);
6923   int newax = ax, neway = ay;
6924   static int xy[4][2] =
6925   {
6926     { 0, -1 },
6927     { -1, 0 },
6928     { +1, 0 },
6929     { 0, +1 }
6930   };
6931
6932   if (!level.amoeba_speed)
6933   {
6934     Feld[ax][ay] = EL_AMOEBA_DEAD;
6935     DrawLevelField(ax, ay);
6936     return;
6937   }
6938
6939   if (IS_ANIMATED(graphic))
6940     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6941
6942   if (!MovDelay[ax][ay])        /* start making new amoeba field */
6943     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6944
6945   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
6946   {
6947     MovDelay[ax][ay]--;
6948     if (MovDelay[ax][ay])
6949       return;
6950   }
6951
6952   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6953   {
6954     int start = RND(4);
6955     int x = ax + xy[start][0];
6956     int y = ay + xy[start][1];
6957
6958     if (!IN_LEV_FIELD(x, y))
6959       return;
6960
6961 #if 1
6962     if (IS_FREE(x, y) ||
6963         CAN_GROW_INTO(Feld[x][y]) ||
6964         Feld[x][y] == EL_QUICKSAND_EMPTY)
6965     {
6966       newax = x;
6967       neway = y;
6968     }
6969 #else
6970     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6971     if (IS_FREE(x, y) ||
6972         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6973     {
6974       newax = x;
6975       neway = y;
6976     }
6977 #endif
6978
6979     if (newax == ax && neway == ay)
6980       return;
6981   }
6982   else                          /* normal or "filled" (BD style) amoeba */
6983   {
6984     int start = RND(4);
6985     boolean waiting_for_player = FALSE;
6986
6987     for (i = 0; i < NUM_DIRECTIONS; i++)
6988     {
6989       int j = (start + i) % 4;
6990       int x = ax + xy[j][0];
6991       int y = ay + xy[j][1];
6992
6993       if (!IN_LEV_FIELD(x, y))
6994         continue;
6995
6996 #if 1
6997       if (IS_FREE(x, y) ||
6998           CAN_GROW_INTO(Feld[x][y]) ||
6999           Feld[x][y] == EL_QUICKSAND_EMPTY)
7000       {
7001         newax = x;
7002         neway = y;
7003         break;
7004       }
7005 #else
7006       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7007       if (IS_FREE(x, y) ||
7008           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7009       {
7010         newax = x;
7011         neway = y;
7012         break;
7013       }
7014 #endif
7015       else if (IS_PLAYER(x, y))
7016         waiting_for_player = TRUE;
7017     }
7018
7019     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7020     {
7021 #if 1
7022       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7023 #else
7024       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7025 #endif
7026       {
7027         Feld[ax][ay] = EL_AMOEBA_DEAD;
7028         DrawLevelField(ax, ay);
7029         AmoebaCnt[AmoebaNr[ax][ay]]--;
7030
7031         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7032         {
7033           if (element == EL_AMOEBA_FULL)
7034             AmoebeUmwandeln(ax, ay);
7035           else if (element == EL_BD_AMOEBA)
7036             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7037         }
7038       }
7039       return;
7040     }
7041     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7042     {
7043       /* amoeba gets larger by growing in some direction */
7044
7045       int new_group_nr = AmoebaNr[ax][ay];
7046
7047 #ifdef DEBUG
7048   if (new_group_nr == 0)
7049   {
7050     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7051     printf("AmoebeAbleger(): This should never happen!\n");
7052     return;
7053   }
7054 #endif
7055
7056       AmoebaNr[newax][neway] = new_group_nr;
7057       AmoebaCnt[new_group_nr]++;
7058       AmoebaCnt2[new_group_nr]++;
7059
7060       /* if amoeba touches other amoeba(s) after growing, unify them */
7061       AmoebenVereinigen(newax, neway);
7062
7063       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7064       {
7065         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7066         return;
7067       }
7068     }
7069   }
7070
7071   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7072       (neway == lev_fieldy - 1 && newax != ax))
7073   {
7074     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7075     Store[newax][neway] = element;
7076   }
7077   else if (neway == ay)
7078   {
7079     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7080 #if 1
7081     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7082 #else
7083     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7084 #endif
7085   }
7086   else
7087   {
7088     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7089     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7090     Store[ax][ay] = EL_AMOEBA_DROP;
7091     ContinueMoving(ax, ay);
7092     return;
7093   }
7094
7095   DrawLevelField(newax, neway);
7096 }
7097
7098 void Life(int ax, int ay)
7099 {
7100   int x1, y1, x2, y2;
7101   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
7102   int life_time = 40;
7103   int element = Feld[ax][ay];
7104   int graphic = el2img(element);
7105   boolean changed = FALSE;
7106
7107   if (IS_ANIMATED(graphic))
7108     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7109
7110   if (Stop[ax][ay])
7111     return;
7112
7113   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7114     MovDelay[ax][ay] = life_time;
7115
7116   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7117   {
7118     MovDelay[ax][ay]--;
7119     if (MovDelay[ax][ay])
7120       return;
7121   }
7122
7123   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7124   {
7125     int xx = ax+x1, yy = ay+y1;
7126     int nachbarn = 0;
7127
7128     if (!IN_LEV_FIELD(xx, yy))
7129       continue;
7130
7131     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7132     {
7133       int x = xx+x2, y = yy+y2;
7134
7135       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7136         continue;
7137
7138       if (((Feld[x][y] == element ||
7139             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7140            !Stop[x][y]) ||
7141           (IS_FREE(x, y) && Stop[x][y]))
7142         nachbarn++;
7143     }
7144
7145     if (xx == ax && yy == ay)           /* field in the middle */
7146     {
7147       if (nachbarn < life[0] || nachbarn > life[1])
7148       {
7149         Feld[xx][yy] = EL_EMPTY;
7150         if (!Stop[xx][yy])
7151           DrawLevelField(xx, yy);
7152         Stop[xx][yy] = TRUE;
7153         changed = TRUE;
7154       }
7155     }
7156 #if 1
7157     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7158     {                                   /* free border field */
7159       if (nachbarn >= life[2] && nachbarn <= life[3])
7160       {
7161         Feld[xx][yy] = element;
7162         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7163         if (!Stop[xx][yy])
7164           DrawLevelField(xx, yy);
7165         Stop[xx][yy] = TRUE;
7166         changed = TRUE;
7167       }
7168     }
7169 #else
7170     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7171     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7172     {                                   /* free border field */
7173       if (nachbarn >= life[2] && nachbarn <= life[3])
7174       {
7175         Feld[xx][yy] = element;
7176         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7177         if (!Stop[xx][yy])
7178           DrawLevelField(xx, yy);
7179         Stop[xx][yy] = TRUE;
7180         changed = TRUE;
7181       }
7182     }
7183 #endif
7184   }
7185
7186   if (changed)
7187     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7188                    SND_GAME_OF_LIFE_GROWING);
7189 }
7190
7191 static void InitRobotWheel(int x, int y)
7192 {
7193   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7194 }
7195
7196 static void RunRobotWheel(int x, int y)
7197 {
7198   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7199 }
7200
7201 static void StopRobotWheel(int x, int y)
7202 {
7203   if (ZX == x && ZY == y)
7204     ZX = ZY = -1;
7205 }
7206
7207 static void InitTimegateWheel(int x, int y)
7208 {
7209 #if 1
7210   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7211 #else
7212   /* another brainless, "type style" bug ... :-( */
7213   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7214 #endif
7215 }
7216
7217 static void RunTimegateWheel(int x, int y)
7218 {
7219   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7220 }
7221
7222 void CheckExit(int x, int y)
7223 {
7224   if (local_player->gems_still_needed > 0 ||
7225       local_player->sokobanfields_still_needed > 0 ||
7226       local_player->lights_still_needed > 0)
7227   {
7228     int element = Feld[x][y];
7229     int graphic = el2img(element);
7230
7231     if (IS_ANIMATED(graphic))
7232       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7233
7234     return;
7235   }
7236
7237   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7238     return;
7239
7240   Feld[x][y] = EL_EXIT_OPENING;
7241
7242   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7243 }
7244
7245 void CheckExitSP(int x, int y)
7246 {
7247   if (local_player->gems_still_needed > 0)
7248   {
7249     int element = Feld[x][y];
7250     int graphic = el2img(element);
7251
7252     if (IS_ANIMATED(graphic))
7253       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7254
7255     return;
7256   }
7257
7258   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7259     return;
7260
7261   Feld[x][y] = EL_SP_EXIT_OPENING;
7262
7263   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7264 }
7265
7266 static void CloseAllOpenTimegates()
7267 {
7268   int x, y;
7269
7270   for (y = 0; y < lev_fieldy; y++)
7271   {
7272     for (x = 0; x < lev_fieldx; x++)
7273     {
7274       int element = Feld[x][y];
7275
7276       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7277       {
7278         Feld[x][y] = EL_TIMEGATE_CLOSING;
7279 #if 1
7280         PlayLevelSoundAction(x, y, ACTION_CLOSING);
7281 #else
7282         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7283 #endif
7284       }
7285     }
7286   }
7287 }
7288
7289 void EdelsteinFunkeln(int x, int y)
7290 {
7291   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7292     return;
7293
7294   if (Feld[x][y] == EL_BD_DIAMOND)
7295     return;
7296
7297   if (MovDelay[x][y] == 0)      /* next animation frame */
7298     MovDelay[x][y] = 11 * !SimpleRND(500);
7299
7300   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7301   {
7302     MovDelay[x][y]--;
7303
7304     if (setup.direct_draw && MovDelay[x][y])
7305       SetDrawtoField(DRAW_BUFFERED);
7306
7307     DrawLevelElementAnimation(x, y, Feld[x][y]);
7308
7309     if (MovDelay[x][y] != 0)
7310     {
7311       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7312                                            10 - MovDelay[x][y]);
7313
7314       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7315
7316       if (setup.direct_draw)
7317       {
7318         int dest_x, dest_y;
7319
7320         dest_x = FX + SCREENX(x) * TILEX;
7321         dest_y = FY + SCREENY(y) * TILEY;
7322
7323         BlitBitmap(drawto_field, window,
7324                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7325         SetDrawtoField(DRAW_DIRECT);
7326       }
7327     }
7328   }
7329 }
7330
7331 void MauerWaechst(int x, int y)
7332 {
7333   int delay = 6;
7334
7335   if (!MovDelay[x][y])          /* next animation frame */
7336     MovDelay[x][y] = 3 * delay;
7337
7338   if (MovDelay[x][y])           /* wait some time before next frame */
7339   {
7340     MovDelay[x][y]--;
7341
7342     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7343     {
7344       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7345       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7346
7347       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7348     }
7349
7350     if (!MovDelay[x][y])
7351     {
7352       if (MovDir[x][y] == MV_LEFT)
7353       {
7354         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7355           DrawLevelField(x - 1, y);
7356       }
7357       else if (MovDir[x][y] == MV_RIGHT)
7358       {
7359         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7360           DrawLevelField(x + 1, y);
7361       }
7362       else if (MovDir[x][y] == MV_UP)
7363       {
7364         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7365           DrawLevelField(x, y - 1);
7366       }
7367       else
7368       {
7369         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7370           DrawLevelField(x, y + 1);
7371       }
7372
7373       Feld[x][y] = Store[x][y];
7374       Store[x][y] = 0;
7375       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7376       DrawLevelField(x, y);
7377     }
7378   }
7379 }
7380
7381 void MauerAbleger(int ax, int ay)
7382 {
7383   int element = Feld[ax][ay];
7384   int graphic = el2img(element);
7385   boolean oben_frei = FALSE, unten_frei = FALSE;
7386   boolean links_frei = FALSE, rechts_frei = FALSE;
7387   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7388   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7389   boolean new_wall = FALSE;
7390
7391   if (IS_ANIMATED(graphic))
7392     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7393
7394   if (!MovDelay[ax][ay])        /* start building new wall */
7395     MovDelay[ax][ay] = 6;
7396
7397   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7398   {
7399     MovDelay[ax][ay]--;
7400     if (MovDelay[ax][ay])
7401       return;
7402   }
7403
7404   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7405     oben_frei = TRUE;
7406   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7407     unten_frei = TRUE;
7408   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7409     links_frei = TRUE;
7410   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7411     rechts_frei = TRUE;
7412
7413   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7414       element == EL_EXPANDABLE_WALL_ANY)
7415   {
7416     if (oben_frei)
7417     {
7418       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7419       Store[ax][ay-1] = element;
7420       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7421       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7422         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7423                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7424       new_wall = TRUE;
7425     }
7426     if (unten_frei)
7427     {
7428       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7429       Store[ax][ay+1] = element;
7430       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7431       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7432         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7433                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7434       new_wall = TRUE;
7435     }
7436   }
7437
7438   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7439       element == EL_EXPANDABLE_WALL_ANY ||
7440       element == EL_EXPANDABLE_WALL)
7441   {
7442     if (links_frei)
7443     {
7444       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7445       Store[ax-1][ay] = element;
7446       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7447       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7448         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7449                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7450       new_wall = TRUE;
7451     }
7452
7453     if (rechts_frei)
7454     {
7455       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7456       Store[ax+1][ay] = element;
7457       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7458       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7459         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7460                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7461       new_wall = TRUE;
7462     }
7463   }
7464
7465   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7466     DrawLevelField(ax, ay);
7467
7468   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7469     oben_massiv = TRUE;
7470   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7471     unten_massiv = TRUE;
7472   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7473     links_massiv = TRUE;
7474   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7475     rechts_massiv = TRUE;
7476
7477   if (((oben_massiv && unten_massiv) ||
7478        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7479        element == EL_EXPANDABLE_WALL) &&
7480       ((links_massiv && rechts_massiv) ||
7481        element == EL_EXPANDABLE_WALL_VERTICAL))
7482     Feld[ax][ay] = EL_WALL;
7483
7484   if (new_wall)
7485 #if 1
7486     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7487 #else
7488     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7489 #endif
7490 }
7491
7492 void CheckForDragon(int x, int y)
7493 {
7494   int i, j;
7495   boolean dragon_found = FALSE;
7496   static int xy[4][2] =
7497   {
7498     { 0, -1 },
7499     { -1, 0 },
7500     { +1, 0 },
7501     { 0, +1 }
7502   };
7503
7504   for (i = 0; i < NUM_DIRECTIONS; i++)
7505   {
7506     for (j = 0; j < 4; j++)
7507     {
7508       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7509
7510       if (IN_LEV_FIELD(xx, yy) &&
7511           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7512       {
7513         if (Feld[xx][yy] == EL_DRAGON)
7514           dragon_found = TRUE;
7515       }
7516       else
7517         break;
7518     }
7519   }
7520
7521   if (!dragon_found)
7522   {
7523     for (i = 0; i < NUM_DIRECTIONS; i++)
7524     {
7525       for (j = 0; j < 3; j++)
7526       {
7527         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7528   
7529         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7530         {
7531           Feld[xx][yy] = EL_EMPTY;
7532           DrawLevelField(xx, yy);
7533         }
7534         else
7535           break;
7536       }
7537     }
7538   }
7539 }
7540
7541 static void InitBuggyBase(int x, int y)
7542 {
7543   int element = Feld[x][y];
7544   int activating_delay = FRAMES_PER_SECOND / 4;
7545
7546   ChangeDelay[x][y] =
7547     (element == EL_SP_BUGGY_BASE ?
7548      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7549      element == EL_SP_BUGGY_BASE_ACTIVATING ?
7550      activating_delay :
7551      element == EL_SP_BUGGY_BASE_ACTIVE ?
7552      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7553 }
7554
7555 static void WarnBuggyBase(int x, int y)
7556 {
7557   int i;
7558   static int xy[4][2] =
7559   {
7560     { 0, -1 },
7561     { -1, 0 },
7562     { +1, 0 },
7563     { 0, +1 }
7564   };
7565
7566   for (i = 0; i < NUM_DIRECTIONS; i++)
7567   {
7568     int xx = x + xy[i][0], yy = y + xy[i][1];
7569
7570     if (IS_PLAYER(xx, yy))
7571     {
7572       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7573
7574       break;
7575     }
7576   }
7577 }
7578
7579 static void InitTrap(int x, int y)
7580 {
7581   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7582 }
7583
7584 static void ActivateTrap(int x, int y)
7585 {
7586   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7587 }
7588
7589 static void ChangeActiveTrap(int x, int y)
7590 {
7591   int graphic = IMG_TRAP_ACTIVE;
7592
7593   /* if new animation frame was drawn, correct crumbled sand border */
7594   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7595     DrawLevelFieldCrumbledSand(x, y);
7596 }
7597
7598 static void ChangeElementNowExt(int x, int y, int target_element)
7599 {
7600   int previous_move_direction = MovDir[x][y];
7601 #if 1
7602   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7603                         IS_WALKABLE(Feld[x][y]));
7604 #else
7605   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7606                         IS_WALKABLE(Feld[x][y]) &&
7607                         !IS_MOVING(x, y));
7608 #endif
7609
7610   /* check if element under player changes from accessible to unaccessible
7611      (needed for special case of dropping element which then changes) */
7612   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7613       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7614   {
7615 #if 0
7616     printf("::: BOOOM! [%d, '%s']\n", target_element,
7617            element_info[target_element].token_name);
7618 #endif
7619
7620     Bang(x, y);
7621     return;
7622   }
7623
7624 #if 1
7625   if (!add_player)
7626 #endif
7627   {
7628 #if 1
7629     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7630       RemoveMovingField(x, y);
7631     else
7632       RemoveField(x, y);
7633
7634     Feld[x][y] = target_element;
7635 #else
7636     RemoveField(x, y);
7637     Feld[x][y] = target_element;
7638 #endif
7639
7640     ResetGfxAnimation(x, y);
7641     ResetRandomAnimationValue(x, y);
7642
7643     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7644       MovDir[x][y] = previous_move_direction;
7645
7646 #if 1
7647     InitField_WithBug1(x, y, FALSE);
7648 #else
7649     InitField(x, y, FALSE);
7650     if (CAN_MOVE(Feld[x][y]))
7651       InitMovDir(x, y);
7652 #endif
7653
7654     DrawLevelField(x, y);
7655
7656     if (GFX_CRUMBLED(Feld[x][y]))
7657       DrawLevelFieldCrumbledSandNeighbours(x, y);
7658   }
7659
7660 #if 0
7661   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7662 #endif
7663
7664 #if 0
7665   TestIfBadThingTouchesHero(x, y);
7666   TestIfPlayerTouchesCustomElement(x, y);
7667   TestIfElementTouchesCustomElement(x, y);
7668 #endif
7669
7670   /* "Changed[][]" not set yet to allow "entered by player" change one time */
7671   if (ELEM_IS_PLAYER(target_element))
7672     RelocatePlayer(x, y, target_element);
7673
7674 #if 1
7675   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7676 #endif
7677
7678 #if 1
7679   TestIfBadThingTouchesHero(x, y);
7680   TestIfPlayerTouchesCustomElement(x, y);
7681   TestIfElementTouchesCustomElement(x, y);
7682 #endif
7683 }
7684
7685 static boolean ChangeElementNow(int x, int y, int element, int page)
7686 {
7687   struct ElementChangeInfo *change = &element_info[element].change_page[page];
7688   int target_element;
7689   int old_element = Feld[x][y];
7690
7691   /* always use default change event to prevent running into a loop */
7692   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7693     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7694
7695   if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7696   {
7697     /* reset actual trigger element and player */
7698     change->actual_trigger_element = EL_EMPTY;
7699     change->actual_trigger_player = EL_PLAYER_1;
7700   }
7701
7702   /* do not change already changed elements with same change event */
7703 #if 0
7704   if (Changed[x][y] & ChangeEvent[x][y])
7705     return FALSE;
7706 #else
7707   if (Changed[x][y])
7708     return FALSE;
7709 #endif
7710
7711   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7712
7713 #if 0
7714   /* !!! indirect change before direct change !!! */
7715   CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7716 #endif
7717
7718   if (change->explode)
7719   {
7720     Bang(x, y);
7721
7722     return TRUE;
7723   }
7724
7725   if (change->use_target_content)
7726   {
7727     boolean complete_replace = TRUE;
7728     boolean can_replace[3][3];
7729     int xx, yy;
7730
7731     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7732     {
7733       boolean is_empty;
7734       boolean is_walkable;
7735       boolean is_diggable;
7736       boolean is_collectible;
7737       boolean is_removable;
7738       boolean is_destructible;
7739       int ex = x + xx - 1;
7740       int ey = y + yy - 1;
7741       int content_element = change->target_content[xx][yy];
7742       int e;
7743
7744       can_replace[xx][yy] = TRUE;
7745
7746       if (ex == x && ey == y)   /* do not check changing element itself */
7747         continue;
7748
7749       if (content_element == EL_EMPTY_SPACE)
7750       {
7751         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
7752
7753         continue;
7754       }
7755
7756       if (!IN_LEV_FIELD(ex, ey))
7757       {
7758         can_replace[xx][yy] = FALSE;
7759         complete_replace = FALSE;
7760
7761         continue;
7762       }
7763
7764 #if 0
7765       if (Changed[ex][ey])      /* do not change already changed elements */
7766       {
7767         can_replace[xx][yy] = FALSE;
7768         complete_replace = FALSE;
7769
7770         continue;
7771       }
7772 #endif
7773
7774       e = Feld[ex][ey];
7775
7776       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7777         e = MovingOrBlocked2Element(ex, ey);
7778
7779 #if 1
7780
7781 #if 0
7782       is_empty = (IS_FREE(ex, ey) ||
7783                   (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7784                   (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7785                    !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7786 #else
7787
7788 #if 0
7789       is_empty = (IS_FREE(ex, ey) ||
7790                   (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7791 #else
7792       is_empty = (IS_FREE(ex, ey) ||
7793                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7794 #endif
7795
7796 #endif
7797
7798       is_walkable     = (is_empty || IS_WALKABLE(e));
7799       is_diggable     = (is_empty || IS_DIGGABLE(e));
7800       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
7801       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7802       is_removable    = (is_diggable || is_collectible);
7803
7804       can_replace[xx][yy] =
7805         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
7806           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
7807           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
7808           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
7809           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
7810           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
7811          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
7812
7813       if (!can_replace[xx][yy])
7814         complete_replace = FALSE;
7815 #else
7816       empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7817                                                IS_WALKABLE(content_element)));
7818 #if 1
7819       half_destructible = (empty_for_element || IS_DIGGABLE(e));
7820 #else
7821       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7822 #endif
7823
7824       if ((change->replace_when <= CP_WHEN_EMPTY  && !empty_for_element) ||
7825           (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7826           (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7827       {
7828         can_replace[xx][yy] = FALSE;
7829         complete_replace = FALSE;
7830       }
7831 #endif
7832     }
7833
7834     if (!change->only_if_complete || complete_replace)
7835     {
7836       boolean something_has_changed = FALSE;
7837
7838       if (change->only_if_complete && change->use_random_replace &&
7839           RND(100) < change->random_percentage)
7840         return FALSE;
7841
7842       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7843       {
7844         int ex = x + xx - 1;
7845         int ey = y + yy - 1;
7846         int content_element;
7847
7848         if (can_replace[xx][yy] && (!change->use_random_replace ||
7849                                     RND(100) < change->random_percentage))
7850         {
7851           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7852             RemoveMovingField(ex, ey);
7853
7854           ChangeEvent[ex][ey] = ChangeEvent[x][y];
7855
7856           content_element = change->target_content[xx][yy];
7857           target_element = GET_TARGET_ELEMENT(content_element, change);
7858
7859           ChangeElementNowExt(ex, ey, target_element);
7860
7861           something_has_changed = TRUE;
7862
7863           /* for symmetry reasons, freeze newly created border elements */
7864           if (ex != x || ey != y)
7865             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
7866         }
7867       }
7868
7869       if (something_has_changed)
7870         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7871     }
7872   }
7873   else
7874   {
7875     target_element = GET_TARGET_ELEMENT(change->target_element, change);
7876
7877     ChangeElementNowExt(x, y, target_element);
7878
7879     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7880   }
7881
7882 #if 1
7883   /* this uses direct change before indirect change */
7884   CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7885 #endif
7886
7887   return TRUE;
7888 }
7889
7890 static void ChangeElement(int x, int y, int page)
7891 {
7892   int element = MovingOrBlocked2Element(x, y);
7893   struct ElementInfo *ei = &element_info[element];
7894   struct ElementChangeInfo *change = &ei->change_page[page];
7895
7896 #ifdef DEBUG
7897   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7898   {
7899     printf("\n\n");
7900     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7901            x, y, element, element_info[element].token_name);
7902     printf("ChangeElement(): This should never happen!\n");
7903     printf("\n\n");
7904   }
7905 #endif
7906
7907   /* this can happen with classic bombs on walkable, changing elements */
7908   if (!CAN_CHANGE(element))
7909   {
7910 #if 0
7911     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
7912       ChangeDelay[x][y] = 0;
7913 #endif
7914
7915     return;
7916   }
7917
7918   if (ChangeDelay[x][y] == 0)           /* initialize element change */
7919   {
7920     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
7921                          RND(change->delay_random * change->delay_frames)) + 1;
7922
7923     ResetGfxAnimation(x, y);
7924     ResetRandomAnimationValue(x, y);
7925
7926     if (change->pre_change_function)
7927       change->pre_change_function(x, y);
7928   }
7929
7930   ChangeDelay[x][y]--;
7931
7932   if (ChangeDelay[x][y] != 0)           /* continue element change */
7933   {
7934     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7935
7936     if (IS_ANIMATED(graphic))
7937       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7938
7939     if (change->change_function)
7940       change->change_function(x, y);
7941   }
7942   else                                  /* finish element change */
7943   {
7944     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
7945     {
7946       page = ChangePage[x][y];
7947       ChangePage[x][y] = -1;
7948
7949       change = &ei->change_page[page];
7950     }
7951
7952 #if 0
7953     if (IS_MOVING(x, y) && !change->explode)
7954 #else
7955     if (IS_MOVING(x, y))                /* never change a running system ;-) */
7956 #endif
7957     {
7958       ChangeDelay[x][y] = 1;            /* try change after next move step */
7959       ChangePage[x][y] = page;          /* remember page to use for change */
7960
7961       return;
7962     }
7963
7964     if (ChangeElementNow(x, y, element, page))
7965     {
7966       if (change->post_change_function)
7967         change->post_change_function(x, y);
7968     }
7969   }
7970 }
7971
7972 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7973                                               int trigger_element,
7974                                               int trigger_event,
7975                                               int trigger_player,
7976                                               int trigger_side,
7977                                               int trigger_page)
7978 {
7979   int i, j, x, y;
7980   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7981
7982   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7983     return FALSE;
7984
7985   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7986   {
7987     int element = EL_CUSTOM_START + i;
7988
7989     boolean change_element = FALSE;
7990     int page = 0;
7991
7992     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7993       continue;
7994
7995     for (j = 0; j < element_info[element].num_change_pages; j++)
7996     {
7997       struct ElementChangeInfo *change = &element_info[element].change_page[j];
7998
7999       if (change->can_change &&
8000           change->events & CH_EVENT_BIT(trigger_event) &&
8001           change->trigger_side & trigger_side &&
8002           change->trigger_player & trigger_player &&
8003           change->trigger_page & trigger_page_bits &&
8004           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8005       {
8006 #if 0
8007         if (!(change->events & CH_EVENT_BIT(trigger_event)))
8008           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8009                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8010 #endif
8011
8012         change_element = TRUE;
8013         page = j;
8014
8015         change->actual_trigger_element = trigger_element;
8016         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8017
8018         break;
8019       }
8020     }
8021
8022     if (!change_element)
8023       continue;
8024
8025     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8026     {
8027 #if 0
8028       if (x == lx && y == ly)   /* do not change trigger element itself */
8029         continue;
8030 #endif
8031
8032       if (Feld[x][y] == element)
8033       {
8034         ChangeDelay[x][y] = 1;
8035         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8036         ChangeElement(x, y, page);
8037       }
8038     }
8039   }
8040
8041   return TRUE;
8042 }
8043
8044 static boolean CheckElementChangeExt(int x, int y,
8045                                      int element,
8046                                      int trigger_element,
8047                                      int trigger_event,
8048                                      int trigger_player,
8049                                      int trigger_side,
8050                                      int trigger_page)
8051 {
8052   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8053     return FALSE;
8054
8055   if (Feld[x][y] == EL_BLOCKED)
8056   {
8057     Blocked2Moving(x, y, &x, &y);
8058     element = Feld[x][y];
8059   }
8060
8061 #if 1
8062   if (Feld[x][y] != element)    /* check if element has already changed */
8063   {
8064 #if 0
8065     printf("::: %d ('%s') != %d ('%s') [%d]\n",
8066            Feld[x][y], element_info[Feld[x][y]].token_name,
8067            element, element_info[element].token_name,
8068            trigger_event);
8069 #endif
8070
8071     return FALSE;
8072   }
8073 #endif
8074
8075 #if 1
8076   if (trigger_page < 0)
8077   {
8078     boolean change_element = FALSE;
8079     int i;
8080
8081     for (i = 0; i < element_info[element].num_change_pages; i++)
8082     {
8083       struct ElementChangeInfo *change = &element_info[element].change_page[i];
8084
8085       if (change->can_change &&
8086           change->events & CH_EVENT_BIT(trigger_event) &&
8087           change->trigger_side & trigger_side &&
8088           change->trigger_player & trigger_player)
8089       {
8090         change_element = TRUE;
8091         trigger_page = i;
8092
8093         change->actual_trigger_element = trigger_element;
8094         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8095
8096         break;
8097       }
8098     }
8099
8100     if (!change_element)
8101       return FALSE;
8102   }
8103   else
8104   {
8105     struct ElementInfo *ei = &element_info[element];
8106     struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8107
8108     change->actual_trigger_element = trigger_element;
8109     change->actual_trigger_player = EL_PLAYER_1;        /* unused */
8110   }
8111
8112 #else
8113
8114   /* !!! this check misses pages with same event, but different side !!! */
8115
8116   if (trigger_page < 0)
8117     trigger_page = element_info[element].event_page_nr[trigger_event];
8118
8119   if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8120     return FALSE;
8121 #endif
8122
8123   ChangeDelay[x][y] = 1;
8124   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
8125   ChangeElement(x, y, trigger_page);
8126
8127   return TRUE;
8128 }
8129
8130 static void PlayPlayerSound(struct PlayerInfo *player)
8131 {
8132   int jx = player->jx, jy = player->jy;
8133   int element = player->element_nr;
8134   int last_action = player->last_action_waiting;
8135   int action = player->action_waiting;
8136
8137   if (player->is_waiting)
8138   {
8139     if (action != last_action)
8140       PlayLevelSoundElementAction(jx, jy, element, action);
8141     else
8142       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8143   }
8144   else
8145   {
8146     if (action != last_action)
8147       StopSound(element_info[element].sound[last_action]);
8148
8149     if (last_action == ACTION_SLEEPING)
8150       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8151   }
8152 }
8153
8154 static void PlayAllPlayersSound()
8155 {
8156   int i;
8157
8158   for (i = 0; i < MAX_PLAYERS; i++)
8159     if (stored_player[i].active)
8160       PlayPlayerSound(&stored_player[i]);
8161 }
8162
8163 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8164 {
8165   boolean last_waiting = player->is_waiting;
8166   int move_dir = player->MovDir;
8167
8168   player->last_action_waiting = player->action_waiting;
8169
8170   if (is_waiting)
8171   {
8172     if (!last_waiting)          /* not waiting -> waiting */
8173     {
8174       player->is_waiting = TRUE;
8175
8176       player->frame_counter_bored =
8177         FrameCounter +
8178         game.player_boring_delay_fixed +
8179         SimpleRND(game.player_boring_delay_random);
8180       player->frame_counter_sleeping =
8181         FrameCounter +
8182         game.player_sleeping_delay_fixed +
8183         SimpleRND(game.player_sleeping_delay_random);
8184
8185       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8186     }
8187
8188     if (game.player_sleeping_delay_fixed +
8189         game.player_sleeping_delay_random > 0 &&
8190         player->anim_delay_counter == 0 &&
8191         player->post_delay_counter == 0 &&
8192         FrameCounter >= player->frame_counter_sleeping)
8193       player->is_sleeping = TRUE;
8194     else if (game.player_boring_delay_fixed +
8195              game.player_boring_delay_random > 0 &&
8196              FrameCounter >= player->frame_counter_bored)
8197       player->is_bored = TRUE;
8198
8199     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8200                               player->is_bored ? ACTION_BORING :
8201                               ACTION_WAITING);
8202
8203     if (player->is_sleeping)
8204     {
8205       if (player->num_special_action_sleeping > 0)
8206       {
8207         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8208         {
8209           int last_special_action = player->special_action_sleeping;
8210           int num_special_action = player->num_special_action_sleeping;
8211           int special_action =
8212             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8213              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8214              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8215              last_special_action + 1 : ACTION_SLEEPING);
8216           int special_graphic =
8217             el_act_dir2img(player->element_nr, special_action, move_dir);
8218
8219           player->anim_delay_counter =
8220             graphic_info[special_graphic].anim_delay_fixed +
8221             SimpleRND(graphic_info[special_graphic].anim_delay_random);
8222           player->post_delay_counter =
8223             graphic_info[special_graphic].post_delay_fixed +
8224             SimpleRND(graphic_info[special_graphic].post_delay_random);
8225
8226           player->special_action_sleeping = special_action;
8227         }
8228
8229         if (player->anim_delay_counter > 0)
8230         {
8231           player->action_waiting = player->special_action_sleeping;
8232           player->anim_delay_counter--;
8233         }
8234         else if (player->post_delay_counter > 0)
8235         {
8236           player->post_delay_counter--;
8237         }
8238       }
8239     }
8240     else if (player->is_bored)
8241     {
8242       if (player->num_special_action_bored > 0)
8243       {
8244         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8245         {
8246           int special_action =
8247             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8248           int special_graphic =
8249             el_act_dir2img(player->element_nr, special_action, move_dir);
8250
8251           player->anim_delay_counter =
8252             graphic_info[special_graphic].anim_delay_fixed +
8253             SimpleRND(graphic_info[special_graphic].anim_delay_random);
8254           player->post_delay_counter =
8255             graphic_info[special_graphic].post_delay_fixed +
8256             SimpleRND(graphic_info[special_graphic].post_delay_random);
8257
8258           player->special_action_bored = special_action;
8259         }
8260
8261         if (player->anim_delay_counter > 0)
8262         {
8263           player->action_waiting = player->special_action_bored;
8264           player->anim_delay_counter--;
8265         }
8266         else if (player->post_delay_counter > 0)
8267         {
8268           player->post_delay_counter--;
8269         }
8270       }
8271     }
8272   }
8273   else if (last_waiting)        /* waiting -> not waiting */
8274   {
8275     player->is_waiting = FALSE;
8276     player->is_bored = FALSE;
8277     player->is_sleeping = FALSE;
8278
8279     player->frame_counter_bored = -1;
8280     player->frame_counter_sleeping = -1;
8281
8282     player->anim_delay_counter = 0;
8283     player->post_delay_counter = 0;
8284
8285     player->action_waiting = ACTION_DEFAULT;
8286
8287     player->special_action_bored = ACTION_DEFAULT;
8288     player->special_action_sleeping = ACTION_DEFAULT;
8289   }
8290 }
8291
8292 #if 1
8293 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8294 {
8295 #if 0
8296   static byte stored_player_action[MAX_PLAYERS];
8297   static int num_stored_actions = 0;
8298 #endif
8299   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8300   int left      = player_action & JOY_LEFT;
8301   int right     = player_action & JOY_RIGHT;
8302   int up        = player_action & JOY_UP;
8303   int down      = player_action & JOY_DOWN;
8304   int button1   = player_action & JOY_BUTTON_1;
8305   int button2   = player_action & JOY_BUTTON_2;
8306   int dx        = (left ? -1    : right ? 1     : 0);
8307   int dy        = (up   ? -1    : down  ? 1     : 0);
8308
8309 #if 0
8310   stored_player_action[player->index_nr] = 0;
8311   num_stored_actions++;
8312 #endif
8313
8314 #if 0
8315   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8316 #endif
8317
8318   if (!player->active || tape.pausing)
8319     return 0;
8320
8321 #if 0
8322   printf("::: [%d %d %d %d] [%d %d]\n",
8323          left, right, up, down, button1, button2);
8324 #endif
8325
8326   if (player_action)
8327   {
8328 #if 0
8329     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8330 #endif
8331
8332 #if 0
8333     /* !!! TEST !!! */
8334     if (player->MovPos == 0)
8335       CheckGravityMovement(player);
8336 #endif
8337     if (button1)
8338       snapped = SnapField(player, dx, dy);
8339     else
8340     {
8341       if (button2)
8342         dropped = DropElement(player);
8343
8344       moved = MovePlayer(player, dx, dy);
8345     }
8346
8347     if (tape.single_step && tape.recording && !tape.pausing)
8348     {
8349       if (button1 || (dropped && !moved))
8350       {
8351         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8352         SnapField(player, 0, 0);                /* stop snapping */
8353       }
8354     }
8355
8356     SetPlayerWaiting(player, FALSE);
8357
8358 #if 1
8359     return player_action;
8360 #else
8361     stored_player_action[player->index_nr] = player_action;
8362 #endif
8363   }
8364   else
8365   {
8366 #if 0
8367     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8368 #endif
8369
8370     /* no actions for this player (no input at player's configured device) */
8371
8372     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8373     SnapField(player, 0, 0);
8374     CheckGravityMovementWhenNotMoving(player);
8375
8376     if (player->MovPos == 0)
8377       SetPlayerWaiting(player, TRUE);
8378
8379     if (player->MovPos == 0)    /* needed for tape.playing */
8380       player->is_moving = FALSE;
8381
8382     player->is_dropping = FALSE;
8383
8384     return 0;
8385   }
8386
8387 #if 0
8388   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8389   {
8390     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8391
8392     TapeRecordAction(stored_player_action);
8393     num_stored_actions = 0;
8394   }
8395 #endif
8396 }
8397
8398 #else
8399
8400 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8401 {
8402   static byte stored_player_action[MAX_PLAYERS];
8403   static int num_stored_actions = 0;
8404   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8405   int left      = player_action & JOY_LEFT;
8406   int right     = player_action & JOY_RIGHT;
8407   int up        = player_action & JOY_UP;
8408   int down      = player_action & JOY_DOWN;
8409   int button1   = player_action & JOY_BUTTON_1;
8410   int button2   = player_action & JOY_BUTTON_2;
8411   int dx        = (left ? -1    : right ? 1     : 0);
8412   int dy        = (up   ? -1    : down  ? 1     : 0);
8413
8414   stored_player_action[player->index_nr] = 0;
8415   num_stored_actions++;
8416
8417   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8418
8419   if (!player->active || tape.pausing)
8420     return;
8421
8422   if (player_action)
8423   {
8424     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8425
8426     if (button1)
8427       snapped = SnapField(player, dx, dy);
8428     else
8429     {
8430       if (button2)
8431         dropped = DropElement(player);
8432
8433       moved = MovePlayer(player, dx, dy);
8434     }
8435
8436     if (tape.single_step && tape.recording && !tape.pausing)
8437     {
8438       if (button1 || (dropped && !moved))
8439       {
8440         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8441         SnapField(player, 0, 0);                /* stop snapping */
8442       }
8443     }
8444
8445     stored_player_action[player->index_nr] = player_action;
8446   }
8447   else
8448   {
8449     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8450
8451     /* no actions for this player (no input at player's configured device) */
8452
8453     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8454     SnapField(player, 0, 0);
8455     CheckGravityMovementWhenNotMoving(player);
8456
8457     if (player->MovPos == 0)
8458       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8459
8460     if (player->MovPos == 0)    /* needed for tape.playing */
8461       player->is_moving = FALSE;
8462   }
8463
8464   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8465   {
8466     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8467
8468     TapeRecordAction(stored_player_action);
8469     num_stored_actions = 0;
8470   }
8471 }
8472 #endif
8473
8474 void AdvanceFrameAndPlayerCounters(int player_nr)
8475 {
8476   int i;
8477
8478   /* advance frame counters (global frame counter and time frame counter) */
8479   FrameCounter++;
8480   TimeFrames++;
8481
8482   /* advance player counters (counters for move delay, move animation etc.) */
8483   for (i = 0; i < MAX_PLAYERS; i++)
8484   {
8485     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
8486     int move_frames =
8487       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
8488
8489     if (!advance_player_counters)       /* not all players may be affected */
8490       continue;
8491
8492     stored_player[i].Frame += move_frames;
8493
8494     if (stored_player[i].MovPos != 0)
8495       stored_player[i].StepFrame += move_frames;
8496
8497 #if USE_NEW_MOVE_DELAY
8498     if (stored_player[i].move_delay > 0)
8499       stored_player[i].move_delay--;
8500 #endif
8501
8502 #if USE_NEW_PUSH_DELAY
8503     /* due to bugs in previous versions, counter must count up, not down */
8504     if (stored_player[i].push_delay != -1)
8505       stored_player[i].push_delay++;
8506 #endif
8507
8508     if (stored_player[i].drop_delay > 0)
8509       stored_player[i].drop_delay--;
8510   }
8511 }
8512
8513 void GameActions()
8514 {
8515   static unsigned long action_delay = 0;
8516   unsigned long action_delay_value;
8517   int magic_wall_x = 0, magic_wall_y = 0;
8518   int i, x, y, element, graphic;
8519   byte *recorded_player_action;
8520   byte summarized_player_action = 0;
8521 #if 1
8522   byte tape_action[MAX_PLAYERS];
8523 #endif
8524
8525   if (game_status != GAME_MODE_PLAYING)
8526     return;
8527
8528   action_delay_value =
8529     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8530
8531   if (tape.playing && tape.warp_forward && !tape.pausing)
8532     action_delay_value = 0;
8533
8534   /* ---------- main game synchronization point ---------- */
8535
8536   WaitUntilDelayReached(&action_delay, action_delay_value);
8537
8538   if (network_playing && !network_player_action_received)
8539   {
8540     /*
8541 #ifdef DEBUG
8542     printf("DEBUG: try to get network player actions in time\n");
8543 #endif
8544     */
8545
8546 #if defined(NETWORK_AVALIABLE)
8547     /* last chance to get network player actions without main loop delay */
8548     HandleNetworking();
8549 #endif
8550
8551     if (game_status != GAME_MODE_PLAYING)
8552       return;
8553
8554     if (!network_player_action_received)
8555     {
8556       /*
8557 #ifdef DEBUG
8558       printf("DEBUG: failed to get network player actions in time\n");
8559 #endif
8560       */
8561       return;
8562     }
8563   }
8564
8565   if (tape.pausing)
8566     return;
8567
8568 #if 0
8569   printf("::: getting new tape action [%d]\n", FrameCounter);
8570 #endif
8571
8572   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8573
8574 #if 1
8575   if (recorded_player_action == NULL && tape.pausing)
8576     return;
8577 #endif
8578
8579 #if 0
8580   printf("::: %d\n", stored_player[0].action);
8581 #endif
8582
8583 #if 0
8584   if (recorded_player_action != NULL)
8585     for (i = 0; i < MAX_PLAYERS; i++)
8586       stored_player[i].action = recorded_player_action[i];
8587 #endif
8588
8589   for (i = 0; i < MAX_PLAYERS; i++)
8590   {
8591     summarized_player_action |= stored_player[i].action;
8592
8593     if (!network_playing)
8594       stored_player[i].effective_action = stored_player[i].action;
8595   }
8596
8597 #if defined(NETWORK_AVALIABLE)
8598   if (network_playing)
8599     SendToServer_MovePlayer(summarized_player_action);
8600 #endif
8601
8602   if (!options.network && !setup.team_mode)
8603     local_player->effective_action = summarized_player_action;
8604
8605 #if 1
8606   if (recorded_player_action != NULL)
8607     for (i = 0; i < MAX_PLAYERS; i++)
8608       stored_player[i].effective_action = recorded_player_action[i];
8609 #endif
8610
8611 #if 1
8612   for (i = 0; i < MAX_PLAYERS; i++)
8613   {
8614     tape_action[i] = stored_player[i].effective_action;
8615
8616     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8617       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8618   }
8619
8620   /* only save actions from input devices, but not programmed actions */
8621   if (tape.recording)
8622     TapeRecordAction(tape_action);
8623 #endif
8624
8625   for (i = 0; i < MAX_PLAYERS; i++)
8626   {
8627     int actual_player_action = stored_player[i].effective_action;
8628
8629 #if 1
8630     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8631        - rnd_equinox_tetrachloride 048
8632        - rnd_equinox_tetrachloride_ii 096
8633        - rnd_emanuel_schmieg 002
8634        - doctor_sloan_ww 001, 020
8635     */
8636     if (stored_player[i].MovPos == 0)
8637       CheckGravityMovement(&stored_player[i]);
8638 #endif
8639
8640 #if 1
8641     /* overwrite programmed action with tape action */
8642     if (stored_player[i].programmed_action)
8643       actual_player_action = stored_player[i].programmed_action;
8644 #endif
8645
8646 #if 0
8647     if (stored_player[i].programmed_action)
8648       printf("::: %d\n", stored_player[i].programmed_action);
8649 #endif
8650
8651     if (recorded_player_action)
8652     {
8653 #if 0
8654       if (stored_player[i].programmed_action &&
8655           stored_player[i].programmed_action != recorded_player_action[i])
8656         printf("::: %d: %d <-> %d\n", i,
8657                stored_player[i].programmed_action, recorded_player_action[i]);
8658 #endif
8659
8660 #if 0
8661       actual_player_action = recorded_player_action[i];
8662 #endif
8663     }
8664
8665 #if 0
8666     /* overwrite tape action with programmed action */
8667     if (stored_player[i].programmed_action)
8668       actual_player_action = stored_player[i].programmed_action;
8669 #endif
8670
8671 #if 0
8672     if (i == 0)
8673       printf("::: action: %d: %x [%d]\n",
8674              stored_player[i].MovPos, actual_player_action, FrameCounter);
8675 #endif
8676
8677 #if 1
8678     PlayerActions(&stored_player[i], actual_player_action);
8679 #else
8680     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8681
8682     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8683       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8684 #endif
8685
8686     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8687   }
8688
8689 #if 0
8690   if (tape.recording)
8691     TapeRecordAction(tape_action);
8692 #endif
8693
8694   network_player_action_received = FALSE;
8695
8696   ScrollScreen(NULL, SCROLL_GO_ON);
8697
8698 #if 0
8699   FrameCounter++;
8700   TimeFrames++;
8701
8702   for (i = 0; i < MAX_PLAYERS; i++)
8703     stored_player[i].Frame++;
8704 #endif
8705
8706 #if 1
8707   /* for backwards compatibility, the following code emulates a fixed bug that
8708      occured when pushing elements (causing elements that just made their last
8709      pushing step to already (if possible) make their first falling step in the
8710      same game frame, which is bad); this code is also needed to use the famous
8711      "spring push bug" which is used in older levels and might be wanted to be
8712      used also in newer levels, but in this case the buggy pushing code is only
8713      affecting the "spring" element and no other elements */
8714
8715 #if 1
8716   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8717 #else
8718   if (game.engine_version < VERSION_IDENT(2,2,0,7))
8719 #endif
8720   {
8721     for (i = 0; i < MAX_PLAYERS; i++)
8722     {
8723       struct PlayerInfo *player = &stored_player[i];
8724       int x = player->jx;
8725       int y = player->jy;
8726
8727 #if 1
8728       if (player->active && player->is_pushing && player->is_moving &&
8729           IS_MOVING(x, y) &&
8730           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8731            Feld[x][y] == EL_SPRING))
8732 #else
8733       if (player->active && player->is_pushing && player->is_moving &&
8734           IS_MOVING(x, y))
8735 #endif
8736       {
8737         ContinueMoving(x, y);
8738
8739         /* continue moving after pushing (this is actually a bug) */
8740         if (!IS_MOVING(x, y))
8741         {
8742           Stop[x][y] = FALSE;
8743         }
8744       }
8745     }
8746   }
8747 #endif
8748
8749   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8750   {
8751     Changed[x][y] = CE_BITMASK_DEFAULT;
8752     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8753
8754 #if USE_NEW_BLOCK_STYLE
8755     /* this must be handled before main playfield loop */
8756     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8757     {
8758       MovDelay[x][y]--;
8759       if (MovDelay[x][y] <= 0)
8760         RemoveField(x, y);
8761     }
8762 #endif
8763
8764 #if DEBUG
8765     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8766     {
8767       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8768       printf("GameActions(): This should never happen!\n");
8769
8770       ChangePage[x][y] = -1;
8771     }
8772 #endif
8773
8774     Stop[x][y] = FALSE;
8775     if (WasJustMoving[x][y] > 0)
8776       WasJustMoving[x][y]--;
8777     if (WasJustFalling[x][y] > 0)
8778       WasJustFalling[x][y]--;
8779     if (CheckCollision[x][y] > 0)
8780       CheckCollision[x][y]--;
8781
8782     GfxFrame[x][y]++;
8783
8784 #if 1
8785     /* reset finished pushing action (not done in ContinueMoving() to allow
8786        continous pushing animation for elements with zero push delay) */
8787     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8788     {
8789       ResetGfxAnimation(x, y);
8790       DrawLevelField(x, y);
8791     }
8792 #endif
8793
8794 #if DEBUG
8795     if (IS_BLOCKED(x, y))
8796     {
8797       int oldx, oldy;
8798
8799       Blocked2Moving(x, y, &oldx, &oldy);
8800       if (!IS_MOVING(oldx, oldy))
8801       {
8802         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8803         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8804         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8805         printf("GameActions(): This should never happen!\n");
8806       }
8807     }
8808 #endif
8809   }
8810
8811   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8812   {
8813     element = Feld[x][y];
8814 #if 1
8815     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8816 #else
8817     graphic = el2img(element);
8818 #endif
8819
8820 #if 0
8821     if (element == -1)
8822     {
8823       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8824
8825       element = graphic = 0;
8826     }
8827 #endif
8828
8829     if (graphic_info[graphic].anim_global_sync)
8830       GfxFrame[x][y] = FrameCounter;
8831
8832     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8833         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8834       ResetRandomAnimationValue(x, y);
8835
8836     SetRandomAnimationValue(x, y);
8837
8838 #if 1
8839     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8840 #endif
8841
8842     if (IS_INACTIVE(element))
8843     {
8844       if (IS_ANIMATED(graphic))
8845         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8846
8847       continue;
8848     }
8849
8850 #if 1
8851     /* this may take place after moving, so 'element' may have changed */
8852 #if 0
8853     if (IS_CHANGING(x, y))
8854 #else
8855     if (IS_CHANGING(x, y) &&
8856         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8857 #endif
8858     {
8859 #if 0
8860       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8861                     element_info[element].event_page_nr[CE_DELAY]);
8862 #else
8863       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8864 #endif
8865
8866       element = Feld[x][y];
8867       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8868     }
8869 #endif
8870
8871     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8872     {
8873       StartMoving(x, y);
8874
8875 #if 1
8876       element = Feld[x][y];
8877       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8878 #if 0
8879       if (element == EL_MOLE)
8880         printf("::: %d, %d, %d [%d]\n",
8881                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8882                GfxAction[x][y]);
8883 #endif
8884 #if 0
8885       if (element == EL_YAMYAM)
8886         printf("::: %d, %d, %d\n",
8887                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8888 #endif
8889 #endif
8890
8891       if (IS_ANIMATED(graphic) &&
8892           !IS_MOVING(x, y) &&
8893           !Stop[x][y])
8894       {
8895         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8896
8897 #if 0
8898         if (element == EL_BUG)
8899           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8900 #endif
8901
8902 #if 0
8903         if (element == EL_MOLE)
8904           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8905 #endif
8906       }
8907
8908       if (IS_GEM(element) || element == EL_SP_INFOTRON)
8909         EdelsteinFunkeln(x, y);
8910     }
8911     else if ((element == EL_ACID ||
8912               element == EL_EXIT_OPEN ||
8913               element == EL_SP_EXIT_OPEN ||
8914               element == EL_SP_TERMINAL ||
8915               element == EL_SP_TERMINAL_ACTIVE ||
8916               element == EL_EXTRA_TIME ||
8917               element == EL_SHIELD_NORMAL ||
8918               element == EL_SHIELD_DEADLY) &&
8919              IS_ANIMATED(graphic))
8920       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8921     else if (IS_MOVING(x, y))
8922       ContinueMoving(x, y);
8923     else if (IS_ACTIVE_BOMB(element))
8924       CheckDynamite(x, y);
8925 #if 0
8926     else if (element == EL_EXPLOSION && !game.explosions_delayed)
8927       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8928 #endif
8929     else if (element == EL_AMOEBA_GROWING)
8930       AmoebeWaechst(x, y);
8931     else if (element == EL_AMOEBA_SHRINKING)
8932       AmoebaDisappearing(x, y);
8933
8934 #if !USE_NEW_AMOEBA_CODE
8935     else if (IS_AMOEBALIVE(element))
8936       AmoebeAbleger(x, y);
8937 #endif
8938
8939     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8940       Life(x, y);
8941     else if (element == EL_EXIT_CLOSED)
8942       CheckExit(x, y);
8943     else if (element == EL_SP_EXIT_CLOSED)
8944       CheckExitSP(x, y);
8945     else if (element == EL_EXPANDABLE_WALL_GROWING)
8946       MauerWaechst(x, y);
8947     else if (element == EL_EXPANDABLE_WALL ||
8948              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8949              element == EL_EXPANDABLE_WALL_VERTICAL ||
8950              element == EL_EXPANDABLE_WALL_ANY)
8951       MauerAbleger(x, y);
8952     else if (element == EL_FLAMES)
8953       CheckForDragon(x, y);
8954 #if 0
8955     else if (IS_AUTO_CHANGING(element))
8956       ChangeElement(x, y);
8957 #endif
8958     else if (element == EL_EXPLOSION)
8959       ; /* drawing of correct explosion animation is handled separately */
8960     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8961       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8962
8963 #if 0
8964     /* this may take place after moving, so 'element' may have changed */
8965     if (IS_AUTO_CHANGING(Feld[x][y]))
8966       ChangeElement(x, y);
8967 #endif
8968
8969     if (IS_BELT_ACTIVE(element))
8970       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8971
8972     if (game.magic_wall_active)
8973     {
8974       int jx = local_player->jx, jy = local_player->jy;
8975
8976       /* play the element sound at the position nearest to the player */
8977       if ((element == EL_MAGIC_WALL_FULL ||
8978            element == EL_MAGIC_WALL_ACTIVE ||
8979            element == EL_MAGIC_WALL_EMPTYING ||
8980            element == EL_BD_MAGIC_WALL_FULL ||
8981            element == EL_BD_MAGIC_WALL_ACTIVE ||
8982            element == EL_BD_MAGIC_WALL_EMPTYING) &&
8983           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8984       {
8985         magic_wall_x = x;
8986         magic_wall_y = y;
8987       }
8988     }
8989   }
8990
8991 #if USE_NEW_AMOEBA_CODE
8992   /* new experimental amoeba growth stuff */
8993 #if 1
8994   if (!(FrameCounter % 8))
8995 #endif
8996   {
8997     static unsigned long random = 1684108901;
8998
8999     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9000     {
9001 #if 0
9002       x = (random >> 10) % lev_fieldx;
9003       y = (random >> 20) % lev_fieldy;
9004 #else
9005       x = RND(lev_fieldx);
9006       y = RND(lev_fieldy);
9007 #endif
9008       element = Feld[x][y];
9009
9010 #if 1
9011       if (!IS_PLAYER(x,y) &&
9012           (element == EL_EMPTY ||
9013            CAN_GROW_INTO(element) ||
9014            element == EL_QUICKSAND_EMPTY ||
9015            element == EL_ACID_SPLASH_LEFT ||
9016            element == EL_ACID_SPLASH_RIGHT))
9017       {
9018         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9019             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9020             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9021             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9022           Feld[x][y] = EL_AMOEBA_DROP;
9023       }
9024 #else
9025       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9026       if (!IS_PLAYER(x,y) &&
9027           (element == EL_EMPTY ||
9028            element == EL_SAND ||
9029            element == EL_QUICKSAND_EMPTY ||
9030            element == EL_ACID_SPLASH_LEFT ||
9031            element == EL_ACID_SPLASH_RIGHT))
9032       {
9033         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9034             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9035             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9036             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9037           Feld[x][y] = EL_AMOEBA_DROP;
9038       }
9039 #endif
9040
9041       random = random * 129 + 1;
9042     }
9043   }
9044 #endif
9045
9046 #if 0
9047   if (game.explosions_delayed)
9048 #endif
9049   {
9050     game.explosions_delayed = FALSE;
9051
9052     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9053     {
9054       element = Feld[x][y];
9055
9056       if (ExplodeField[x][y])
9057         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9058       else if (element == EL_EXPLOSION)
9059         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9060
9061       ExplodeField[x][y] = EX_TYPE_NONE;
9062     }
9063
9064     game.explosions_delayed = TRUE;
9065   }
9066
9067   if (game.magic_wall_active)
9068   {
9069     if (!(game.magic_wall_time_left % 4))
9070     {
9071       int element = Feld[magic_wall_x][magic_wall_y];
9072
9073       if (element == EL_BD_MAGIC_WALL_FULL ||
9074           element == EL_BD_MAGIC_WALL_ACTIVE ||
9075           element == EL_BD_MAGIC_WALL_EMPTYING)
9076         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9077       else
9078         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9079     }
9080
9081     if (game.magic_wall_time_left > 0)
9082     {
9083       game.magic_wall_time_left--;
9084       if (!game.magic_wall_time_left)
9085       {
9086         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9087         {
9088           element = Feld[x][y];
9089
9090           if (element == EL_MAGIC_WALL_ACTIVE ||
9091               element == EL_MAGIC_WALL_FULL)
9092           {
9093             Feld[x][y] = EL_MAGIC_WALL_DEAD;
9094             DrawLevelField(x, y);
9095           }
9096           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9097                    element == EL_BD_MAGIC_WALL_FULL)
9098           {
9099             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9100             DrawLevelField(x, y);
9101           }
9102         }
9103
9104         game.magic_wall_active = FALSE;
9105       }
9106     }
9107   }
9108
9109   if (game.light_time_left > 0)
9110   {
9111     game.light_time_left--;
9112
9113     if (game.light_time_left == 0)
9114       RedrawAllLightSwitchesAndInvisibleElements();
9115   }
9116
9117   if (game.timegate_time_left > 0)
9118   {
9119     game.timegate_time_left--;
9120
9121     if (game.timegate_time_left == 0)
9122       CloseAllOpenTimegates();
9123   }
9124
9125   for (i = 0; i < MAX_PLAYERS; i++)
9126   {
9127     struct PlayerInfo *player = &stored_player[i];
9128
9129     if (SHIELD_ON(player))
9130     {
9131       if (player->shield_deadly_time_left)
9132         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9133       else if (player->shield_normal_time_left)
9134         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9135     }
9136   }
9137
9138   if (TimeFrames >= FRAMES_PER_SECOND)
9139   {
9140     TimeFrames = 0;
9141     TapeTime++;
9142
9143     for (i = 0; i < MAX_PLAYERS; i++)
9144     {
9145       struct PlayerInfo *player = &stored_player[i];
9146
9147       if (SHIELD_ON(player))
9148       {
9149         player->shield_normal_time_left--;
9150
9151         if (player->shield_deadly_time_left > 0)
9152           player->shield_deadly_time_left--;
9153       }
9154     }
9155
9156     if (!level.use_step_counter)
9157     {
9158       TimePlayed++;
9159
9160       if (TimeLeft > 0)
9161       {
9162         TimeLeft--;
9163
9164         if (TimeLeft <= 10 && setup.time_limit)
9165           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9166
9167         DrawGameValue_Time(TimeLeft);
9168
9169         if (!TimeLeft && setup.time_limit)
9170           for (i = 0; i < MAX_PLAYERS; i++)
9171             KillHero(&stored_player[i]);
9172       }
9173       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9174         DrawGameValue_Time(TimePlayed);
9175     }
9176
9177     if (tape.recording || tape.playing)
9178       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9179   }
9180
9181   DrawAllPlayers();
9182   PlayAllPlayersSound();
9183
9184   if (options.debug)                    /* calculate frames per second */
9185   {
9186     static unsigned long fps_counter = 0;
9187     static int fps_frames = 0;
9188     unsigned long fps_delay_ms = Counter() - fps_counter;
9189
9190     fps_frames++;
9191
9192     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
9193     {
9194       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9195
9196       fps_frames = 0;
9197       fps_counter = Counter();
9198     }
9199
9200     redraw_mask |= REDRAW_FPS;
9201   }
9202
9203 #if 0
9204   if (stored_player[0].jx != stored_player[0].last_jx ||
9205       stored_player[0].jy != stored_player[0].last_jy)
9206     printf("::: %d, %d, %d, %d, %d\n",
9207            stored_player[0].MovDir,
9208            stored_player[0].MovPos,
9209            stored_player[0].GfxPos,
9210            stored_player[0].Frame,
9211            stored_player[0].StepFrame);
9212 #endif
9213
9214 #if USE_NEW_MOVE_DELAY
9215   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9216 #else
9217   FrameCounter++;
9218   TimeFrames++;
9219
9220   for (i = 0; i < MAX_PLAYERS; i++)
9221   {
9222     int move_frames =
9223       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
9224
9225     stored_player[i].Frame += move_frames;
9226
9227     if (stored_player[i].MovPos != 0)
9228       stored_player[i].StepFrame += move_frames;
9229
9230 #if USE_NEW_MOVE_DELAY
9231     if (stored_player[i].move_delay > 0)
9232       stored_player[i].move_delay--;
9233 #endif
9234
9235     if (stored_player[i].drop_delay > 0)
9236       stored_player[i].drop_delay--;
9237   }
9238 #endif
9239
9240 #if 1
9241   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9242   {
9243     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9244
9245     local_player->show_envelope = 0;
9246   }
9247 #endif
9248
9249 #if USE_NEW_RANDOMIZE
9250   /* use random number generator in every frame to make it less predictable */
9251   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9252     RND(1);
9253 #endif
9254 }
9255
9256 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9257 {
9258   int min_x = x, min_y = y, max_x = x, max_y = y;
9259   int i;
9260
9261   for (i = 0; i < MAX_PLAYERS; i++)
9262   {
9263     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9264
9265     if (!stored_player[i].active || &stored_player[i] == player)
9266       continue;
9267
9268     min_x = MIN(min_x, jx);
9269     min_y = MIN(min_y, jy);
9270     max_x = MAX(max_x, jx);
9271     max_y = MAX(max_y, jy);
9272   }
9273
9274   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9275 }
9276
9277 static boolean AllPlayersInVisibleScreen()
9278 {
9279   int i;
9280
9281   for (i = 0; i < MAX_PLAYERS; i++)
9282   {
9283     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9284
9285     if (!stored_player[i].active)
9286       continue;
9287
9288     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9289       return FALSE;
9290   }
9291
9292   return TRUE;
9293 }
9294
9295 void ScrollLevel(int dx, int dy)
9296 {
9297   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9298   int x, y;
9299
9300   BlitBitmap(drawto_field, drawto_field,
9301              FX + TILEX * (dx == -1) - softscroll_offset,
9302              FY + TILEY * (dy == -1) - softscroll_offset,
9303              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9304              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9305              FX + TILEX * (dx == 1) - softscroll_offset,
9306              FY + TILEY * (dy == 1) - softscroll_offset);
9307
9308   if (dx)
9309   {
9310     x = (dx == 1 ? BX1 : BX2);
9311     for (y = BY1; y <= BY2; y++)
9312       DrawScreenField(x, y);
9313   }
9314
9315   if (dy)
9316   {
9317     y = (dy == 1 ? BY1 : BY2);
9318     for (x = BX1; x <= BX2; x++)
9319       DrawScreenField(x, y);
9320   }
9321
9322   redraw_mask |= REDRAW_FIELD;
9323 }
9324
9325 #if 0
9326 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9327 {
9328   int nextx = x + dx, nexty = y + dy;
9329   int element = Feld[x][y];
9330
9331   if ((dx == -1 &&
9332        element != EL_SP_PORT_LEFT &&
9333        element != EL_SP_GRAVITY_PORT_LEFT &&
9334        element != EL_SP_PORT_HORIZONTAL &&
9335        element != EL_SP_PORT_ANY) ||
9336       (dx == +1 &&
9337        element != EL_SP_PORT_RIGHT &&
9338        element != EL_SP_GRAVITY_PORT_RIGHT &&
9339        element != EL_SP_PORT_HORIZONTAL &&
9340        element != EL_SP_PORT_ANY) ||
9341       (dy == -1 &&
9342        element != EL_SP_PORT_UP &&
9343        element != EL_SP_GRAVITY_PORT_UP &&
9344        element != EL_SP_PORT_VERTICAL &&
9345        element != EL_SP_PORT_ANY) ||
9346       (dy == +1 &&
9347        element != EL_SP_PORT_DOWN &&
9348        element != EL_SP_GRAVITY_PORT_DOWN &&
9349        element != EL_SP_PORT_VERTICAL &&
9350        element != EL_SP_PORT_ANY) ||
9351       !IN_LEV_FIELD(nextx, nexty) ||
9352       !IS_FREE(nextx, nexty))
9353     return FALSE;
9354
9355   return TRUE;
9356 }
9357 #endif
9358
9359 static boolean canFallDown(struct PlayerInfo *player)
9360 {
9361   int jx = player->jx, jy = player->jy;
9362
9363   return (IN_LEV_FIELD(jx, jy + 1) &&
9364           (IS_FREE(jx, jy + 1) ||
9365            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9366           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9367           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9368 }
9369
9370 static boolean canPassField(int x, int y, int move_dir)
9371 {
9372   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9373   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9374   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9375   int nextx = x + dx;
9376   int nexty = y + dy;
9377   int element = Feld[x][y];
9378
9379   return (IS_PASSABLE_FROM(element, opposite_dir) &&
9380           !CAN_MOVE(element) &&
9381           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9382           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9383           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9384 }
9385
9386 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9387 {
9388   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9389   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9390   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9391   int newx = x + dx;
9392   int newy = y + dy;
9393 #if 0
9394   int nextx = newx + dx;
9395   int nexty = newy + dy;
9396 #endif
9397
9398 #if 1
9399   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9400           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9401 #if 0
9402           (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9403 #endif
9404           (IS_DIGGABLE(Feld[newx][newy]) ||
9405            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9406            canPassField(newx, newy, move_dir)));
9407 #else
9408 #if 1
9409   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9410           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9411           (IS_DIGGABLE(Feld[newx][newy]) ||
9412            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9413            canPassField(newx, newy, move_dir)));
9414 #else
9415 #if 1
9416   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9417           (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9418            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9419            canPassField(newx, newy, move_dir)));
9420 #else
9421   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9422           (IS_DIGGABLE(Feld[newx][newy]) ||
9423            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9424            (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9425             !CAN_MOVE(Feld[newx][newy]) &&
9426             IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9427             IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9428             (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9429 #endif
9430 #endif
9431 #endif
9432 }
9433
9434 static void CheckGravityMovement(struct PlayerInfo *player)
9435 {
9436   if (game.gravity && !player->programmed_action)
9437   {
9438 #if 1
9439     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9440     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
9441 #else
9442     int move_dir_horizontal = player->action & MV_HORIZONTAL;
9443     int move_dir_vertical   = player->action & MV_VERTICAL;
9444 #endif
9445
9446 #if 1
9447     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9448 #else
9449     boolean player_is_snapping = player->action & JOY_BUTTON_1;
9450 #endif
9451
9452     int jx = player->jx, jy = player->jy;
9453
9454     boolean player_is_moving_to_valid_field =
9455       (!player_is_snapping &&
9456        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9457         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9458
9459 #if 0
9460     int move_dir =
9461       (player->last_move_dir & MV_HORIZONTAL ?
9462        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9463        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9464 #endif
9465
9466 #if 0
9467     int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9468     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9469     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9470     int new_jx = jx + dx, new_jy = jy + dy;
9471     int nextx = new_jx + dx, nexty = new_jy + dy;
9472 #endif
9473
9474 #if 1
9475
9476 #if 1
9477     boolean player_can_fall_down = canFallDown(player);
9478 #else
9479     boolean player_can_fall_down =
9480       (IN_LEV_FIELD(jx, jy + 1) &&
9481        (IS_FREE(jx, jy + 1) ||
9482         (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9483 #endif
9484
9485 #else
9486     boolean player_can_fall_down =
9487       (IN_LEV_FIELD(jx, jy + 1) &&
9488        (IS_FREE(jx, jy + 1)));
9489 #endif
9490
9491 #if 0
9492     boolean player_is_moving_to_valid_field =
9493       (
9494 #if 1
9495        !player_is_snapping &&
9496 #endif
9497
9498 #if 1
9499        IN_LEV_FIELD(new_jx, new_jy) &&
9500        (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9501         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9502          element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9503          IN_LEV_FIELD(nextx, nexty) &&
9504          element_info[Feld[nextx][nexty]].access_direction & move_dir))
9505 #else
9506        IN_LEV_FIELD(new_jx, new_jy) &&
9507        (Feld[new_jx][new_jy] == EL_SP_BASE ||
9508         Feld[new_jx][new_jy] == EL_SAND ||
9509         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9510          canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9511     /* !!! extend EL_SAND to anything diggable !!! */
9512 #endif
9513        );
9514 #endif
9515
9516 #if 0
9517     boolean player_is_standing_on_valid_field =
9518       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9519        (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9520 #endif
9521
9522 #if 0
9523     printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9524            player_can_fall_down,
9525            player_is_standing_on_valid_field,
9526            player_is_moving_to_valid_field,
9527            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9528            player->effective_action,
9529            player->can_fall_into_acid);
9530 #endif
9531
9532     if (player_can_fall_down &&
9533 #if 0
9534         !player_is_standing_on_valid_field &&
9535 #endif
9536         !player_is_moving_to_valid_field)
9537     {
9538 #if 0
9539       printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9540              jx, jy, FrameCounter);
9541 #endif
9542
9543       player->programmed_action = MV_DOWN;
9544     }
9545   }
9546 }
9547
9548 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9549 {
9550 #if 1
9551   return CheckGravityMovement(player);
9552 #endif
9553
9554   if (game.gravity && !player->programmed_action)
9555   {
9556     int jx = player->jx, jy = player->jy;
9557     boolean field_under_player_is_free =
9558       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9559     boolean player_is_standing_on_valid_field =
9560       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9561        (IS_WALKABLE(Feld[jx][jy]) &&
9562         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9563
9564     if (field_under_player_is_free && !player_is_standing_on_valid_field)
9565       player->programmed_action = MV_DOWN;
9566   }
9567 }
9568
9569 /*
9570   MovePlayerOneStep()
9571   -----------------------------------------------------------------------------
9572   dx, dy:               direction (non-diagonal) to try to move the player to
9573   real_dx, real_dy:     direction as read from input device (can be diagonal)
9574 */
9575
9576 boolean MovePlayerOneStep(struct PlayerInfo *player,
9577                           int dx, int dy, int real_dx, int real_dy)
9578 {
9579 #if 0
9580   static int trigger_sides[4][2] =
9581   {
9582     /* enter side        leave side */
9583     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
9584     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
9585     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
9586     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
9587   };
9588   int move_direction = (dx == -1 ? MV_LEFT :
9589                         dx == +1 ? MV_RIGHT :
9590                         dy == -1 ? MV_UP :
9591                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9592   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9593   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9594 #endif
9595   int jx = player->jx, jy = player->jy;
9596   int new_jx = jx + dx, new_jy = jy + dy;
9597   int element;
9598   int can_move;
9599
9600   if (!player->active || (!dx && !dy))
9601     return MF_NO_ACTION;
9602
9603   player->MovDir = (dx < 0 ? MV_LEFT :
9604                     dx > 0 ? MV_RIGHT :
9605                     dy < 0 ? MV_UP :
9606                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
9607
9608   if (!IN_LEV_FIELD(new_jx, new_jy))
9609     return MF_NO_ACTION;
9610
9611   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9612     return MF_NO_ACTION;
9613
9614 #if 0
9615   element = MovingOrBlocked2Element(new_jx, new_jy);
9616 #else
9617   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9618 #endif
9619
9620   if (DONT_RUN_INTO(element))
9621   {
9622     if (element == EL_ACID && dx == 0 && dy == 1)
9623     {
9624       SplashAcid(new_jx, new_jy);
9625       Feld[jx][jy] = EL_PLAYER_1;
9626       InitMovingField(jx, jy, MV_DOWN);
9627       Store[jx][jy] = EL_ACID;
9628       ContinueMoving(jx, jy);
9629       BuryHero(player);
9630     }
9631     else
9632       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9633
9634     return MF_MOVING;
9635   }
9636
9637   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9638   if (can_move != MF_MOVING)
9639     return can_move;
9640
9641   /* check if DigField() has caused relocation of the player */
9642   if (player->jx != jx || player->jy != jy)
9643     return MF_NO_ACTION;
9644
9645   StorePlayer[jx][jy] = 0;
9646   player->last_jx = jx;
9647   player->last_jy = jy;
9648   player->jx = new_jx;
9649   player->jy = new_jy;
9650   StorePlayer[new_jx][new_jy] = player->element_nr;
9651
9652   player->MovPos =
9653     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9654
9655   player->step_counter++;
9656
9657 #if 0
9658   player->drop_delay = 0;
9659 #endif
9660
9661   PlayerVisit[jx][jy] = FrameCounter;
9662
9663   ScrollPlayer(player, SCROLL_INIT);
9664
9665 #if 0
9666   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9667   {
9668     CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9669                                       leave_side);
9670     CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9671   }
9672
9673   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9674   {
9675     CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9676                                       CE_OTHER_GETS_ENTERED, enter_side);
9677     CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9678                              CE_ENTERED_BY_PLAYER, enter_side);
9679   }
9680 #endif
9681
9682   return MF_MOVING;
9683 }
9684
9685 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9686 {
9687   int jx = player->jx, jy = player->jy;
9688   int old_jx = jx, old_jy = jy;
9689   int moved = MF_NO_ACTION;
9690
9691 #if 1
9692   if (!player->active)
9693     return FALSE;
9694
9695   if (!dx && !dy)
9696   {
9697     if (player->MovPos == 0)
9698     {
9699       player->is_moving = FALSE;
9700       player->is_digging = FALSE;
9701       player->is_collecting = FALSE;
9702       player->is_snapping = FALSE;
9703       player->is_pushing = FALSE;
9704     }
9705
9706     return FALSE;
9707   }
9708 #else
9709   if (!player->active || (!dx && !dy))
9710     return FALSE;
9711 #endif
9712
9713 #if 0
9714   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9715       !tape.playing)
9716     return FALSE;
9717 #else
9718
9719 #if 1
9720
9721 #if 0
9722   printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9723          player->move_delay + player->move_delay_value);
9724 #endif
9725
9726 #if USE_NEW_MOVE_DELAY
9727   if (player->move_delay > 0)
9728 #else
9729   if (!FrameReached(&player->move_delay, player->move_delay_value))
9730 #endif
9731   {
9732 #if 0
9733     printf("::: can NOT move\n");
9734 #endif
9735
9736     return FALSE;
9737   }
9738 #else
9739   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9740       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9741     return FALSE;
9742 #endif
9743
9744 #endif
9745
9746 #if 0
9747   printf("::: COULD move now\n");
9748 #endif
9749
9750 #if USE_NEW_MOVE_DELAY
9751   player->move_delay = -1;              /* set to "uninitialized" value */
9752 #endif
9753
9754   /* store if player is automatically moved to next field */
9755   player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9756
9757   /* remove the last programmed player action */
9758   player->programmed_action = 0;
9759
9760   if (player->MovPos)
9761   {
9762     /* should only happen if pre-1.2 tape recordings are played */
9763     /* this is only for backward compatibility */
9764
9765     int original_move_delay_value = player->move_delay_value;
9766
9767 #if DEBUG
9768     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9769            tape.counter);
9770 #endif
9771
9772     /* scroll remaining steps with finest movement resolution */
9773     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9774
9775     while (player->MovPos)
9776     {
9777       ScrollPlayer(player, SCROLL_GO_ON);
9778       ScrollScreen(NULL, SCROLL_GO_ON);
9779
9780 #if USE_NEW_MOVE_DELAY
9781       AdvanceFrameAndPlayerCounters(player->index_nr);
9782 #else
9783       FrameCounter++;
9784 #endif
9785
9786       DrawAllPlayers();
9787       BackToFront();
9788     }
9789
9790     player->move_delay_value = original_move_delay_value;
9791   }
9792
9793   if (player->last_move_dir & MV_HORIZONTAL)
9794   {
9795     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9796       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9797   }
9798   else
9799   {
9800     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9801       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9802   }
9803
9804   jx = player->jx;
9805   jy = player->jy;
9806
9807   if (moved & MF_MOVING && !ScreenMovPos &&
9808       (player == local_player || !options.network))
9809   {
9810     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9811     int offset = (setup.scroll_delay ? 3 : 0);
9812
9813     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9814     {
9815       /* actual player has left the screen -- scroll in that direction */
9816       if (jx != old_jx)         /* player has moved horizontally */
9817         scroll_x += (jx - old_jx);
9818       else                      /* player has moved vertically */
9819         scroll_y += (jy - old_jy);
9820     }
9821     else
9822     {
9823       if (jx != old_jx)         /* player has moved horizontally */
9824       {
9825         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
9826             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9827           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9828
9829         /* don't scroll over playfield boundaries */
9830         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9831           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9832
9833         /* don't scroll more than one field at a time */
9834         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9835
9836         /* don't scroll against the player's moving direction */
9837         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
9838             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9839           scroll_x = old_scroll_x;
9840       }
9841       else                      /* player has moved vertically */
9842       {
9843         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
9844             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9845           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9846
9847         /* don't scroll over playfield boundaries */
9848         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9849           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9850
9851         /* don't scroll more than one field at a time */
9852         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9853
9854         /* don't scroll against the player's moving direction */
9855         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
9856             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9857           scroll_y = old_scroll_y;
9858       }
9859     }
9860
9861     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9862     {
9863       if (!options.network && !AllPlayersInVisibleScreen())
9864       {
9865         scroll_x = old_scroll_x;
9866         scroll_y = old_scroll_y;
9867       }
9868       else
9869       {
9870         ScrollScreen(player, SCROLL_INIT);
9871         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9872       }
9873     }
9874   }
9875
9876 #if 0
9877 #if 1
9878   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9879 #else
9880   if (!(moved & MF_MOVING) && !player->is_pushing)
9881     player->Frame = 0;
9882 #endif
9883 #endif
9884
9885   player->StepFrame = 0;
9886
9887   if (moved & MF_MOVING)
9888   {
9889 #if 0
9890     printf("::: REALLY moves now\n");
9891 #endif
9892
9893     if (old_jx != jx && old_jy == jy)
9894       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9895     else if (old_jx == jx && old_jy != jy)
9896       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9897
9898     DrawLevelField(jx, jy);     /* for "crumbled sand" */
9899
9900     player->last_move_dir = player->MovDir;
9901     player->is_moving = TRUE;
9902 #if 1
9903     player->is_snapping = FALSE;
9904 #endif
9905
9906 #if 1
9907     player->is_switching = FALSE;
9908 #endif
9909
9910     player->is_dropping = FALSE;
9911
9912
9913 #if 0
9914     /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9915
9916 #if 1
9917     if (game.engine_version < VERSION_IDENT(3,1,0,0))
9918 #endif
9919     {
9920       int move_direction = player->MovDir;
9921 #if 1
9922       int enter_side = MV_DIR_OPPOSITE(move_direction);
9923       int leave_side = move_direction;
9924 #else
9925       static int trigger_sides[4][2] =
9926       {
9927         /* enter side           leave side */
9928         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
9929         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
9930         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
9931         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
9932       };
9933       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9934       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9935 #endif
9936       int old_element = Feld[old_jx][old_jy];
9937       int new_element = Feld[jx][jy];
9938
9939 #if 1
9940       /* !!! TEST ONLY !!! */
9941       if (IS_CUSTOM_ELEMENT(old_element))
9942         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9943                                    CE_LEFT_BY_PLAYER,
9944                                    player->index_bit, leave_side);
9945
9946       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9947                                           CE_OTHER_GETS_LEFT,
9948                                           player->index_bit, leave_side);
9949
9950       if (IS_CUSTOM_ELEMENT(new_element))
9951         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9952                                    player->index_bit, enter_side);
9953
9954       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9955                                           CE_OTHER_GETS_ENTERED,
9956                                           player->index_bit, enter_side);
9957 #endif
9958
9959     }
9960 #endif
9961
9962
9963   }
9964   else
9965   {
9966     CheckGravityMovementWhenNotMoving(player);
9967
9968     /*
9969     player->last_move_dir = MV_NO_MOVING;
9970     */
9971     player->is_moving = FALSE;
9972
9973 #if USE_NEW_MOVE_STYLE
9974     /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
9975     /* ensure that the player is also allowed to move in the next frame */
9976     /* (currently, the player is forced to wait eight frames before he can try
9977        again!!!) */
9978
9979     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9980       player->move_delay = 0;   /* allow direct movement in the next frame */
9981 #endif
9982   }
9983
9984 #if USE_NEW_MOVE_DELAY
9985   if (player->move_delay == -1)         /* not yet initialized by DigField() */
9986     player->move_delay = player->move_delay_value;
9987 #endif
9988
9989   if (game.engine_version < VERSION_IDENT(3,0,7,0))
9990   {
9991     TestIfHeroTouchesBadThing(jx, jy);
9992     TestIfPlayerTouchesCustomElement(jx, jy);
9993   }
9994
9995   if (!player->active)
9996     RemoveHero(player);
9997
9998   return moved;
9999 }
10000
10001 void ScrollPlayer(struct PlayerInfo *player, int mode)
10002 {
10003   int jx = player->jx, jy = player->jy;
10004   int last_jx = player->last_jx, last_jy = player->last_jy;
10005   int move_stepsize = TILEX / player->move_delay_value;
10006
10007   if (!player->active || !player->MovPos)
10008     return;
10009
10010   if (mode == SCROLL_INIT)
10011   {
10012     player->actual_frame_counter = FrameCounter;
10013     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10014
10015 #if 0
10016     printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10017            FrameCounter,
10018            last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10019            player->block_delay);
10020 #endif
10021
10022 #if USE_NEW_BLOCK_STYLE
10023
10024 #if 0
10025     if (player->block_delay <= 0)
10026       printf("::: ALERT! block_delay == %d\n", player->block_delay);
10027 #endif
10028
10029     if (player->block_delay > 0 &&
10030         Feld[last_jx][last_jy] == EL_EMPTY)
10031     {
10032       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10033       MovDelay[last_jx][last_jy] = player->block_delay + 1;
10034     }
10035 #else
10036 #if USE_NEW_MOVE_STYLE
10037     if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10038          player->block_last_field) &&
10039         Feld[last_jx][last_jy] == EL_EMPTY)
10040       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10041 #else
10042     if (Feld[last_jx][last_jy] == EL_EMPTY)
10043       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10044 #endif
10045 #endif
10046
10047 #if 0
10048     DrawPlayer(player);
10049 #endif
10050
10051     return;
10052   }
10053   else if (!FrameReached(&player->actual_frame_counter, 1))
10054     return;
10055
10056   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10057   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10058
10059 #if USE_NEW_BLOCK_STYLE
10060 #else
10061   if (!player->block_last_field &&
10062       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10063 #if 1
10064     RemoveField(last_jx, last_jy);
10065 #else
10066     Feld[last_jx][last_jy] = EL_EMPTY;
10067 #endif
10068 #endif
10069
10070   /* before DrawPlayer() to draw correct player graphic for this case */
10071   if (player->MovPos == 0)
10072     CheckGravityMovement(player);
10073
10074 #if 0
10075   DrawPlayer(player);   /* needed here only to cleanup last field */
10076 #endif
10077
10078   if (player->MovPos == 0)      /* player reached destination field */
10079   {
10080 #if 1
10081     if (player->move_delay_reset_counter > 0)
10082     {
10083       player->move_delay_reset_counter--;
10084
10085       if (player->move_delay_reset_counter == 0)
10086       {
10087         /* continue with normal speed after quickly moving through gate */
10088         HALVE_PLAYER_SPEED(player);
10089
10090         /* be able to make the next move without delay */
10091         player->move_delay = 0;
10092       }
10093     }
10094 #else
10095     if (IS_PASSABLE(Feld[last_jx][last_jy]))
10096     {
10097       /* continue with normal speed after quickly moving through gate */
10098       HALVE_PLAYER_SPEED(player);
10099
10100       /* be able to make the next move without delay */
10101       player->move_delay = 0;
10102     }
10103 #endif
10104
10105 #if USE_NEW_BLOCK_STYLE
10106 #else
10107     if (player->block_last_field &&
10108         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10109 #if 1
10110       RemoveField(last_jx, last_jy);
10111 #else
10112       Feld[last_jx][last_jy] = EL_EMPTY;
10113 #endif
10114 #endif
10115
10116     player->last_jx = jx;
10117     player->last_jy = jy;
10118
10119     if (Feld[jx][jy] == EL_EXIT_OPEN ||
10120         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10121         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
10122     {
10123       DrawPlayer(player);       /* needed here only to cleanup last field */
10124       RemoveHero(player);
10125
10126       if (local_player->friends_still_needed == 0 ||
10127           IS_SP_ELEMENT(Feld[jx][jy]))
10128         player->LevelSolved = player->GameOver = TRUE;
10129     }
10130
10131 #if 1
10132     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10133     /* this breaks one level: "machine", level 000 */
10134 #if 0
10135     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10136 #endif
10137     {
10138       int move_direction = player->MovDir;
10139 #if 1
10140       int enter_side = MV_DIR_OPPOSITE(move_direction);
10141       int leave_side = move_direction;
10142 #else
10143       static int trigger_sides[4][2] =
10144       {
10145         /* enter side           leave side */
10146         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
10147         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
10148         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
10149         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
10150       };
10151       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10152       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10153 #endif
10154       int old_jx = last_jx;
10155       int old_jy = last_jy;
10156       int old_element = Feld[old_jx][old_jy];
10157       int new_element = Feld[jx][jy];
10158
10159 #if 1
10160       /* !!! TEST ONLY !!! */
10161       if (IS_CUSTOM_ELEMENT(old_element))
10162         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10163                                    CE_LEFT_BY_PLAYER,
10164                                    player->index_bit, leave_side);
10165
10166       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10167                                           CE_OTHER_GETS_LEFT,
10168                                           player->index_bit, leave_side);
10169
10170       if (IS_CUSTOM_ELEMENT(new_element))
10171         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10172                                    player->index_bit, enter_side);
10173
10174       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10175                                           CE_OTHER_GETS_ENTERED,
10176                                           player->index_bit, enter_side);
10177 #endif
10178
10179     }
10180 #endif
10181
10182     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10183     {
10184       TestIfHeroTouchesBadThing(jx, jy);
10185       TestIfPlayerTouchesCustomElement(jx, jy);
10186 #if 1
10187 #if 1
10188       /* needed because pushed element has not yet reached its destination,
10189          so it would trigger a change event at its previous field location */
10190       if (!player->is_pushing)
10191 #endif
10192         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
10193 #endif
10194
10195       if (!player->active)
10196         RemoveHero(player);
10197     }
10198
10199     if (level.use_step_counter)
10200     {
10201       int i;
10202
10203       TimePlayed++;
10204
10205       if (TimeLeft > 0)
10206       {
10207         TimeLeft--;
10208
10209         if (TimeLeft <= 10 && setup.time_limit)
10210           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10211
10212         DrawGameValue_Time(TimeLeft);
10213
10214         if (!TimeLeft && setup.time_limit)
10215           for (i = 0; i < MAX_PLAYERS; i++)
10216             KillHero(&stored_player[i]);
10217       }
10218       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10219         DrawGameValue_Time(TimePlayed);
10220     }
10221
10222     if (tape.single_step && tape.recording && !tape.pausing &&
10223         !player->programmed_action)
10224       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10225   }
10226 }
10227
10228 void ScrollScreen(struct PlayerInfo *player, int mode)
10229 {
10230   static unsigned long screen_frame_counter = 0;
10231
10232   if (mode == SCROLL_INIT)
10233   {
10234     /* set scrolling step size according to actual player's moving speed */
10235     ScrollStepSize = TILEX / player->move_delay_value;
10236
10237     screen_frame_counter = FrameCounter;
10238     ScreenMovDir = player->MovDir;
10239     ScreenMovPos = player->MovPos;
10240     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10241     return;
10242   }
10243   else if (!FrameReached(&screen_frame_counter, 1))
10244     return;
10245
10246   if (ScreenMovPos)
10247   {
10248     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10249     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10250     redraw_mask |= REDRAW_FIELD;
10251   }
10252   else
10253     ScreenMovDir = MV_NO_MOVING;
10254 }
10255
10256 void TestIfPlayerTouchesCustomElement(int x, int y)
10257 {
10258   static int xy[4][2] =
10259   {
10260     { 0, -1 },
10261     { -1, 0 },
10262     { +1, 0 },
10263     { 0, +1 }
10264   };
10265   static int trigger_sides[4][2] =
10266   {
10267     /* center side       border side */
10268     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10269     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10270     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10271     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10272   };
10273   static int touch_dir[4] =
10274   {
10275     MV_LEFT | MV_RIGHT,
10276     MV_UP   | MV_DOWN,
10277     MV_UP   | MV_DOWN,
10278     MV_LEFT | MV_RIGHT
10279   };
10280   int center_element = Feld[x][y];      /* should always be non-moving! */
10281   int i;
10282
10283   for (i = 0; i < NUM_DIRECTIONS; i++)
10284   {
10285     int xx = x + xy[i][0];
10286     int yy = y + xy[i][1];
10287     int center_side = trigger_sides[i][0];
10288     int border_side = trigger_sides[i][1];
10289     int border_element;
10290
10291     if (!IN_LEV_FIELD(xx, yy))
10292       continue;
10293
10294     if (IS_PLAYER(x, y))
10295     {
10296       struct PlayerInfo *player = PLAYERINFO(x, y);
10297
10298       if (game.engine_version < VERSION_IDENT(3,0,7,0))
10299         border_element = Feld[xx][yy];          /* may be moving! */
10300       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10301         border_element = Feld[xx][yy];
10302       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
10303         border_element = MovingOrBlocked2Element(xx, yy);
10304       else
10305         continue;               /* center and border element do not touch */
10306
10307 #if 1
10308       /* !!! TEST ONLY !!! */
10309       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10310                                  player->index_bit, border_side);
10311       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10312                                           CE_OTHER_GETS_TOUCHED,
10313                                           player->index_bit, border_side);
10314 #else
10315       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10316                                           CE_OTHER_GETS_TOUCHED,
10317                                           player->index_bit, border_side);
10318       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10319                                  player->index_bit, border_side);
10320 #endif
10321     }
10322     else if (IS_PLAYER(xx, yy))
10323     {
10324       struct PlayerInfo *player = PLAYERINFO(xx, yy);
10325
10326       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10327       {
10328         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10329           continue;             /* center and border element do not touch */
10330       }
10331
10332 #if 1
10333       /* !!! TEST ONLY !!! */
10334       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10335                                  player->index_bit, center_side);
10336       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10337                                           CE_OTHER_GETS_TOUCHED,
10338                                           player->index_bit, center_side);
10339 #else
10340       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10341                                           CE_OTHER_GETS_TOUCHED,
10342                                           player->index_bit, center_side);
10343       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10344                                  player->index_bit, center_side);
10345 #endif
10346
10347       break;
10348     }
10349   }
10350 }
10351
10352 void TestIfElementTouchesCustomElement(int x, int y)
10353 {
10354   static int xy[4][2] =
10355   {
10356     { 0, -1 },
10357     { -1, 0 },
10358     { +1, 0 },
10359     { 0, +1 }
10360   };
10361   static int trigger_sides[4][2] =
10362   {
10363     /* center side      border side */
10364     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10365     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10366     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10367     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10368   };
10369   static int touch_dir[4] =
10370   {
10371     MV_LEFT | MV_RIGHT,
10372     MV_UP   | MV_DOWN,
10373     MV_UP   | MV_DOWN,
10374     MV_LEFT | MV_RIGHT
10375   };
10376   boolean change_center_element = FALSE;
10377   int center_element_change_page = 0;
10378   int center_element = Feld[x][y];      /* should always be non-moving! */
10379   int border_trigger_element = EL_UNDEFINED;
10380   int i, j;
10381
10382   for (i = 0; i < NUM_DIRECTIONS; i++)
10383   {
10384     int xx = x + xy[i][0];
10385     int yy = y + xy[i][1];
10386     int center_side = trigger_sides[i][0];
10387     int border_side = trigger_sides[i][1];
10388     int border_element;
10389
10390     if (!IN_LEV_FIELD(xx, yy))
10391       continue;
10392
10393     if (game.engine_version < VERSION_IDENT(3,0,7,0))
10394       border_element = Feld[xx][yy];    /* may be moving! */
10395     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10396       border_element = Feld[xx][yy];
10397     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
10398       border_element = MovingOrBlocked2Element(xx, yy);
10399     else
10400       continue;                 /* center and border element do not touch */
10401
10402     /* check for change of center element (but change it only once) */
10403     if (IS_CUSTOM_ELEMENT(center_element) &&
10404         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10405         !change_center_element)
10406     {
10407       for (j = 0; j < element_info[center_element].num_change_pages; j++)
10408       {
10409         struct ElementChangeInfo *change =
10410           &element_info[center_element].change_page[j];
10411
10412         if (change->can_change &&
10413             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10414             change->trigger_side & border_side &&
10415 #if 1
10416             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10417 #else
10418             change->trigger_element == border_element
10419 #endif
10420             )
10421         {
10422           change_center_element = TRUE;
10423           center_element_change_page = j;
10424           border_trigger_element = border_element;
10425
10426           break;
10427         }
10428       }
10429     }
10430
10431     /* check for change of border element */
10432     if (IS_CUSTOM_ELEMENT(border_element) &&
10433         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10434     {
10435       for (j = 0; j < element_info[border_element].num_change_pages; j++)
10436       {
10437         struct ElementChangeInfo *change =
10438           &element_info[border_element].change_page[j];
10439
10440         if (change->can_change &&
10441             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10442             change->trigger_side & center_side &&
10443 #if 1
10444             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10445 #else
10446             change->trigger_element == center_element
10447 #endif
10448             )
10449         {
10450 #if 0
10451           printf("::: border_element %d, %d\n", x, y);
10452 #endif
10453
10454           CheckElementChangeByPage(xx, yy, border_element, center_element,
10455                                    CE_OTHER_IS_TOUCHING, j);
10456           break;
10457         }
10458       }
10459     }
10460   }
10461
10462   if (change_center_element)
10463   {
10464 #if 0
10465     printf("::: center_element %d, %d\n", x, y);
10466 #endif
10467
10468     CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10469                              CE_OTHER_IS_TOUCHING, center_element_change_page);
10470   }
10471 }
10472
10473 void TestIfElementHitsCustomElement(int x, int y, int direction)
10474 {
10475   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10476   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10477   int hitx = x + dx, hity = y + dy;
10478   int hitting_element = Feld[x][y];
10479   int touched_element;
10480 #if 0
10481   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10482                         !IS_FREE(hitx, hity) &&
10483                         (!IS_MOVING(hitx, hity) ||
10484                          MovDir[hitx][hity] != direction ||
10485                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10486 #endif
10487
10488   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10489     return;
10490
10491 #if 0
10492   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10493     return;
10494 #endif
10495
10496   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10497                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10498
10499   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10500                            CE_HITTING_SOMETHING, direction);
10501
10502   if (IN_LEV_FIELD(hitx, hity))
10503   {
10504     int opposite_direction = MV_DIR_OPPOSITE(direction);
10505     int hitting_side = direction;
10506     int touched_side = opposite_direction;
10507 #if 0
10508     int touched_element = MovingOrBlocked2Element(hitx, hity);
10509 #endif
10510 #if 1
10511     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10512                           MovDir[hitx][hity] != direction ||
10513                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10514
10515     object_hit = TRUE;
10516 #endif
10517
10518     if (object_hit)
10519     {
10520       int i;
10521
10522       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10523                                CE_HIT_BY_SOMETHING, opposite_direction);
10524
10525       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10526           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10527       {
10528         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10529         {
10530           struct ElementChangeInfo *change =
10531             &element_info[hitting_element].change_page[i];
10532
10533           if (change->can_change &&
10534               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10535               change->trigger_side & touched_side &&
10536           
10537 #if 1
10538               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10539 #else
10540               change->trigger_element == touched_element
10541 #endif
10542               )
10543           {
10544             CheckElementChangeByPage(x, y, hitting_element, touched_element,
10545                                      CE_OTHER_IS_HITTING, i);
10546             break;
10547           }
10548         }
10549       }
10550
10551       if (IS_CUSTOM_ELEMENT(touched_element) &&
10552           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10553       {
10554         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10555         {
10556           struct ElementChangeInfo *change =
10557             &element_info[touched_element].change_page[i];
10558
10559           if (change->can_change &&
10560               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10561               change->trigger_side & hitting_side &&
10562 #if 1
10563               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10564 #else
10565               change->trigger_element == hitting_element
10566 #endif
10567               )
10568           {
10569             CheckElementChangeByPage(hitx, hity, touched_element,
10570                                      hitting_element, CE_OTHER_GETS_HIT, i);
10571             break;
10572           }
10573         }
10574       }
10575     }
10576   }
10577 }
10578
10579 #if 0
10580 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10581 {
10582   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10583   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10584   int hitx = x + dx, hity = y + dy;
10585   int hitting_element = Feld[x][y];
10586   int touched_element;
10587 #if 0
10588   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10589                         !IS_FREE(hitx, hity) &&
10590                         (!IS_MOVING(hitx, hity) ||
10591                          MovDir[hitx][hity] != direction ||
10592                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10593 #endif
10594
10595   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10596     return;
10597
10598 #if 0
10599   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10600     return;
10601 #endif
10602
10603   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10604                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10605
10606   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10607                            EP_CAN_SMASH_EVERYTHING, direction);
10608
10609   if (IN_LEV_FIELD(hitx, hity))
10610   {
10611     int opposite_direction = MV_DIR_OPPOSITE(direction);
10612     int hitting_side = direction;
10613     int touched_side = opposite_direction;
10614 #if 0
10615     int touched_element = MovingOrBlocked2Element(hitx, hity);
10616 #endif
10617 #if 1
10618     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10619                           MovDir[hitx][hity] != direction ||
10620                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10621
10622     object_hit = TRUE;
10623 #endif
10624
10625     if (object_hit)
10626     {
10627       int i;
10628
10629       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10630                                CE_SMASHED_BY_SOMETHING, opposite_direction);
10631
10632       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10633           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10634       {
10635         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10636         {
10637           struct ElementChangeInfo *change =
10638             &element_info[hitting_element].change_page[i];
10639
10640           if (change->can_change &&
10641               change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10642               change->trigger_side & touched_side &&
10643           
10644 #if 1
10645               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10646 #else
10647               change->trigger_element == touched_element
10648 #endif
10649               )
10650           {
10651             CheckElementChangeByPage(x, y, hitting_element, touched_element,
10652                                      CE_OTHER_IS_SMASHING, i);
10653             break;
10654           }
10655         }
10656       }
10657
10658       if (IS_CUSTOM_ELEMENT(touched_element) &&
10659           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10660       {
10661         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10662         {
10663           struct ElementChangeInfo *change =
10664             &element_info[touched_element].change_page[i];
10665
10666           if (change->can_change &&
10667               change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10668               change->trigger_side & hitting_side &&
10669 #if 1
10670               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10671 #else
10672               change->trigger_element == hitting_element
10673 #endif
10674               )
10675           {
10676             CheckElementChangeByPage(hitx, hity, touched_element,
10677                                      hitting_element, CE_OTHER_GETS_SMASHED,i);
10678             break;
10679           }
10680         }
10681       }
10682     }
10683   }
10684 }
10685 #endif
10686
10687 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10688 {
10689   int i, kill_x = -1, kill_y = -1;
10690   int bad_element = -1;
10691   static int test_xy[4][2] =
10692   {
10693     { 0, -1 },
10694     { -1, 0 },
10695     { +1, 0 },
10696     { 0, +1 }
10697   };
10698   static int test_dir[4] =
10699   {
10700     MV_UP,
10701     MV_LEFT,
10702     MV_RIGHT,
10703     MV_DOWN
10704   };
10705
10706   for (i = 0; i < NUM_DIRECTIONS; i++)
10707   {
10708     int test_x, test_y, test_move_dir, test_element;
10709
10710     test_x = good_x + test_xy[i][0];
10711     test_y = good_y + test_xy[i][1];
10712
10713     if (!IN_LEV_FIELD(test_x, test_y))
10714       continue;
10715
10716     test_move_dir =
10717       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10718
10719 #if 0
10720     test_element = Feld[test_x][test_y];
10721 #else
10722     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10723 #endif
10724
10725     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10726        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10727     */
10728     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10729         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
10730     {
10731       kill_x = test_x;
10732       kill_y = test_y;
10733       bad_element = test_element;
10734
10735       break;
10736     }
10737   }
10738
10739   if (kill_x != -1 || kill_y != -1)
10740   {
10741     if (IS_PLAYER(good_x, good_y))
10742     {
10743       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10744
10745 #if 1
10746       if (player->shield_deadly_time_left > 0 &&
10747           !IS_INDESTRUCTIBLE(bad_element))
10748         Bang(kill_x, kill_y);
10749       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10750         KillHero(player);
10751 #else
10752       if (player->shield_deadly_time_left > 0)
10753         Bang(kill_x, kill_y);
10754       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10755         KillHero(player);
10756 #endif
10757     }
10758     else
10759       Bang(good_x, good_y);
10760   }
10761 }
10762
10763 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10764 {
10765   int i, kill_x = -1, kill_y = -1;
10766   int bad_element = Feld[bad_x][bad_y];
10767   static int test_xy[4][2] =
10768   {
10769     { 0, -1 },
10770     { -1, 0 },
10771     { +1, 0 },
10772     { 0, +1 }
10773   };
10774   static int touch_dir[4] =
10775   {
10776     MV_LEFT | MV_RIGHT,
10777     MV_UP   | MV_DOWN,
10778     MV_UP   | MV_DOWN,
10779     MV_LEFT | MV_RIGHT
10780   };
10781   static int test_dir[4] =
10782   {
10783     MV_UP,
10784     MV_LEFT,
10785     MV_RIGHT,
10786     MV_DOWN
10787   };
10788
10789   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
10790     return;
10791
10792   for (i = 0; i < NUM_DIRECTIONS; i++)
10793   {
10794     int test_x, test_y, test_move_dir, test_element;
10795
10796     test_x = bad_x + test_xy[i][0];
10797     test_y = bad_y + test_xy[i][1];
10798     if (!IN_LEV_FIELD(test_x, test_y))
10799       continue;
10800
10801     test_move_dir =
10802       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10803
10804     test_element = Feld[test_x][test_y];
10805
10806     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10807        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10808     */
10809     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
10810         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
10811     {
10812       /* good thing is player or penguin that does not move away */
10813       if (IS_PLAYER(test_x, test_y))
10814       {
10815         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10816
10817         if (bad_element == EL_ROBOT && player->is_moving)
10818           continue;     /* robot does not kill player if he is moving */
10819
10820         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10821         {
10822           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10823             continue;           /* center and border element do not touch */
10824         }
10825
10826         kill_x = test_x;
10827         kill_y = test_y;
10828         break;
10829       }
10830       else if (test_element == EL_PENGUIN)
10831       {
10832         kill_x = test_x;
10833         kill_y = test_y;
10834         break;
10835       }
10836     }
10837   }
10838
10839   if (kill_x != -1 || kill_y != -1)
10840   {
10841     if (IS_PLAYER(kill_x, kill_y))
10842     {
10843       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10844
10845 #if 1
10846       if (player->shield_deadly_time_left > 0 &&
10847           !IS_INDESTRUCTIBLE(bad_element))
10848         Bang(bad_x, bad_y);
10849       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10850         KillHero(player);
10851 #else
10852       if (player->shield_deadly_time_left > 0)
10853         Bang(bad_x, bad_y);
10854       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10855         KillHero(player);
10856 #endif
10857     }
10858     else
10859       Bang(kill_x, kill_y);
10860   }
10861 }
10862
10863 void TestIfHeroTouchesBadThing(int x, int y)
10864 {
10865   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10866 }
10867
10868 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10869 {
10870   TestIfGoodThingHitsBadThing(x, y, move_dir);
10871 }
10872
10873 void TestIfBadThingTouchesHero(int x, int y)
10874 {
10875   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10876 }
10877
10878 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10879 {
10880   TestIfBadThingHitsGoodThing(x, y, move_dir);
10881 }
10882
10883 void TestIfFriendTouchesBadThing(int x, int y)
10884 {
10885   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10886 }
10887
10888 void TestIfBadThingTouchesFriend(int x, int y)
10889 {
10890   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10891 }
10892
10893 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10894 {
10895   int i, kill_x = bad_x, kill_y = bad_y;
10896   static int xy[4][2] =
10897   {
10898     { 0, -1 },
10899     { -1, 0 },
10900     { +1, 0 },
10901     { 0, +1 }
10902   };
10903
10904   for (i = 0; i < NUM_DIRECTIONS; i++)
10905   {
10906     int x, y, element;
10907
10908     x = bad_x + xy[i][0];
10909     y = bad_y + xy[i][1];
10910     if (!IN_LEV_FIELD(x, y))
10911       continue;
10912
10913     element = Feld[x][y];
10914     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10915         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10916     {
10917       kill_x = x;
10918       kill_y = y;
10919       break;
10920     }
10921   }
10922
10923   if (kill_x != bad_x || kill_y != bad_y)
10924     Bang(bad_x, bad_y);
10925 }
10926
10927 void KillHero(struct PlayerInfo *player)
10928 {
10929   int jx = player->jx, jy = player->jy;
10930
10931   if (!player->active)
10932     return;
10933
10934   /* remove accessible field at the player's position */
10935   Feld[jx][jy] = EL_EMPTY;
10936
10937   /* deactivate shield (else Bang()/Explode() would not work right) */
10938   player->shield_normal_time_left = 0;
10939   player->shield_deadly_time_left = 0;
10940
10941   Bang(jx, jy);
10942   BuryHero(player);
10943 }
10944
10945 static void KillHeroUnlessEnemyProtected(int x, int y)
10946 {
10947   if (!PLAYER_ENEMY_PROTECTED(x, y))
10948     KillHero(PLAYERINFO(x, y));
10949 }
10950
10951 static void KillHeroUnlessExplosionProtected(int x, int y)
10952 {
10953   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10954     KillHero(PLAYERINFO(x, y));
10955 }
10956
10957 void BuryHero(struct PlayerInfo *player)
10958 {
10959   int jx = player->jx, jy = player->jy;
10960
10961   if (!player->active)
10962     return;
10963
10964 #if 1
10965   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10966 #else
10967   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10968 #endif
10969   PlayLevelSound(jx, jy, SND_GAME_LOSING);
10970
10971   player->GameOver = TRUE;
10972   RemoveHero(player);
10973 }
10974
10975 void RemoveHero(struct PlayerInfo *player)
10976 {
10977   int jx = player->jx, jy = player->jy;
10978   int i, found = FALSE;
10979
10980   player->present = FALSE;
10981   player->active = FALSE;
10982
10983   if (!ExplodeField[jx][jy])
10984     StorePlayer[jx][jy] = 0;
10985
10986   for (i = 0; i < MAX_PLAYERS; i++)
10987     if (stored_player[i].active)
10988       found = TRUE;
10989
10990   if (!found)
10991     AllPlayersGone = TRUE;
10992
10993   ExitX = ZX = jx;
10994   ExitY = ZY = jy;
10995 }
10996
10997 /*
10998   =============================================================================
10999   checkDiagonalPushing()
11000   -----------------------------------------------------------------------------
11001   check if diagonal input device direction results in pushing of object
11002   (by checking if the alternative direction is walkable, diggable, ...)
11003   =============================================================================
11004 */
11005
11006 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11007                                     int x, int y, int real_dx, int real_dy)
11008 {
11009   int jx, jy, dx, dy, xx, yy;
11010
11011   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11012     return TRUE;
11013
11014   /* diagonal direction: check alternative direction */
11015   jx = player->jx;
11016   jy = player->jy;
11017   dx = x - jx;
11018   dy = y - jy;
11019   xx = jx + (dx == 0 ? real_dx : 0);
11020   yy = jy + (dy == 0 ? real_dy : 0);
11021
11022   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11023 }
11024
11025 /*
11026   =============================================================================
11027   DigField()
11028   -----------------------------------------------------------------------------
11029   x, y:                 field next to player (non-diagonal) to try to dig to
11030   real_dx, real_dy:     direction as read from input device (can be diagonal)
11031   =============================================================================
11032 */
11033
11034 int DigField(struct PlayerInfo *player,
11035              int oldx, int oldy, int x, int y,
11036              int real_dx, int real_dy, int mode)
11037 {
11038 #if 0
11039   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11040 #endif
11041   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11042   boolean player_was_pushing = player->is_pushing;
11043   int jx = oldx, jy = oldy;
11044   int dx = x - jx, dy = y - jy;
11045   int nextx = x + dx, nexty = y + dy;
11046   int move_direction = (dx == -1 ? MV_LEFT :
11047                         dx == +1 ? MV_RIGHT :
11048                         dy == -1 ? MV_UP :
11049                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
11050   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11051 #if 1
11052   int dig_side = MV_DIR_OPPOSITE(move_direction);
11053 #else
11054   static int trigger_sides[4] =
11055   {
11056     CH_SIDE_RIGHT,      /* moving left  */
11057     CH_SIDE_LEFT,       /* moving right */
11058     CH_SIDE_BOTTOM,     /* moving up    */
11059     CH_SIDE_TOP,        /* moving down  */
11060   };
11061   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11062 #endif
11063   int old_element = Feld[jx][jy];
11064   int element;
11065
11066   if (is_player)                /* function can also be called by EL_PENGUIN */
11067   {
11068     if (player->MovPos == 0)
11069     {
11070       player->is_digging = FALSE;
11071       player->is_collecting = FALSE;
11072     }
11073
11074     if (player->MovPos == 0)    /* last pushing move finished */
11075       player->is_pushing = FALSE;
11076
11077     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11078     {
11079       player->is_switching = FALSE;
11080 #if USE_NEW_PUSH_DELAY
11081       player->push_delay = -1;
11082 #else
11083       player->push_delay = 0;
11084 #endif
11085
11086       return MF_NO_ACTION;
11087     }
11088   }
11089
11090   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11091     return MF_NO_ACTION;
11092
11093 #if 0
11094
11095 #if 0
11096   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11097 #else
11098   if (IS_TUBE(Feld[jx][jy]) ||
11099       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11100 #endif
11101   {
11102     int i = 0;
11103     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11104     int tube_leave_directions[][2] =
11105     {
11106       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11107       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
11108       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
11109       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
11110       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
11111       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
11112       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
11113       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
11114       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
11115       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
11116       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
11117       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11118     };
11119
11120     while (tube_leave_directions[i][0] != tube_element)
11121     {
11122       i++;
11123       if (tube_leave_directions[i][0] == -1)    /* should not happen */
11124         break;
11125     }
11126
11127     if (!(tube_leave_directions[i][1] & move_direction))
11128       return MF_NO_ACTION;      /* tube has no opening in this direction */
11129   }
11130
11131 #else
11132
11133   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11134     old_element = Back[jx][jy];
11135
11136 #endif
11137
11138   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11139     return MF_NO_ACTION;        /* field has no opening in this direction */
11140
11141   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11142     return MF_NO_ACTION;        /* field has no opening in this direction */
11143
11144   element = Feld[x][y];
11145
11146   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
11147     return MF_NO_ACTION;
11148
11149   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11150       game.engine_version >= VERSION_IDENT(2,2,0,0))
11151     return MF_NO_ACTION;
11152
11153 #if 1
11154   if (game.gravity && is_player && !player->is_auto_moving &&
11155       canFallDown(player) && move_direction != MV_DOWN &&
11156       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11157     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
11158 #endif
11159
11160 #if 0
11161   if (element == EL_EMPTY_SPACE &&
11162       game.gravity && !player->is_auto_moving &&
11163       canFallDown(player) && move_direction != MV_DOWN)
11164     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
11165 #endif
11166
11167   switch (element)
11168   {
11169 #if 0
11170     case EL_SP_PORT_LEFT:
11171     case EL_SP_PORT_RIGHT:
11172     case EL_SP_PORT_UP:
11173     case EL_SP_PORT_DOWN:
11174     case EL_SP_PORT_HORIZONTAL:
11175     case EL_SP_PORT_VERTICAL:
11176     case EL_SP_PORT_ANY:
11177     case EL_SP_GRAVITY_PORT_LEFT:
11178     case EL_SP_GRAVITY_PORT_RIGHT:
11179     case EL_SP_GRAVITY_PORT_UP:
11180     case EL_SP_GRAVITY_PORT_DOWN:
11181 #if 1
11182       if (!canEnterSupaplexPort(x, y, dx, dy))
11183         return MF_NO_ACTION;
11184 #else
11185       if ((dx == -1 &&
11186            element != EL_SP_PORT_LEFT &&
11187            element != EL_SP_GRAVITY_PORT_LEFT &&
11188            element != EL_SP_PORT_HORIZONTAL &&
11189            element != EL_SP_PORT_ANY) ||
11190           (dx == +1 &&
11191            element != EL_SP_PORT_RIGHT &&
11192            element != EL_SP_GRAVITY_PORT_RIGHT &&
11193            element != EL_SP_PORT_HORIZONTAL &&
11194            element != EL_SP_PORT_ANY) ||
11195           (dy == -1 &&
11196            element != EL_SP_PORT_UP &&
11197            element != EL_SP_GRAVITY_PORT_UP &&
11198            element != EL_SP_PORT_VERTICAL &&
11199            element != EL_SP_PORT_ANY) ||
11200           (dy == +1 &&
11201            element != EL_SP_PORT_DOWN &&
11202            element != EL_SP_GRAVITY_PORT_DOWN &&
11203            element != EL_SP_PORT_VERTICAL &&
11204            element != EL_SP_PORT_ANY) ||
11205           !IN_LEV_FIELD(nextx, nexty) ||
11206           !IS_FREE(nextx, nexty))
11207         return MF_NO_ACTION;
11208 #endif
11209
11210       if (element == EL_SP_GRAVITY_PORT_LEFT ||
11211           element == EL_SP_GRAVITY_PORT_RIGHT ||
11212           element == EL_SP_GRAVITY_PORT_UP ||
11213           element == EL_SP_GRAVITY_PORT_DOWN)
11214         game.gravity = !game.gravity;
11215
11216       /* automatically move to the next field with double speed */
11217       player->programmed_action = move_direction;
11218 #if 1
11219       if (player->move_delay_reset_counter == 0)
11220       {
11221         player->move_delay_reset_counter = 2;   /* two double speed steps */
11222
11223         DOUBLE_PLAYER_SPEED(player);
11224       }
11225 #else
11226       player->move_delay_reset_counter = 2;
11227
11228       DOUBLE_PLAYER_SPEED(player);
11229 #endif
11230
11231 #if 0
11232       printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11233 #endif
11234
11235       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11236       break;
11237 #endif
11238
11239 #if 0
11240     case EL_TUBE_ANY:
11241     case EL_TUBE_VERTICAL:
11242     case EL_TUBE_HORIZONTAL:
11243     case EL_TUBE_VERTICAL_LEFT:
11244     case EL_TUBE_VERTICAL_RIGHT:
11245     case EL_TUBE_HORIZONTAL_UP:
11246     case EL_TUBE_HORIZONTAL_DOWN:
11247     case EL_TUBE_LEFT_UP:
11248     case EL_TUBE_LEFT_DOWN:
11249     case EL_TUBE_RIGHT_UP:
11250     case EL_TUBE_RIGHT_DOWN:
11251       {
11252         int i = 0;
11253         int tube_enter_directions[][2] =
11254         {
11255           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11256           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
11257           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
11258           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
11259           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
11260           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
11261           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
11262           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
11263           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
11264           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
11265           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
11266           { -1,                         MV_NO_MOVING                         }
11267         };
11268
11269         while (tube_enter_directions[i][0] != element)
11270         {
11271           i++;
11272           if (tube_enter_directions[i][0] == -1)        /* should not happen */
11273             break;
11274         }
11275
11276         if (!(tube_enter_directions[i][1] & move_direction))
11277           return MF_NO_ACTION;  /* tube has no opening in this direction */
11278
11279         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11280       }
11281       break;
11282 #endif
11283
11284     default:
11285
11286 #if 1
11287       if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11288 #else
11289       if (IS_WALKABLE(element))
11290 #endif
11291       {
11292         int sound_element = SND_ELEMENT(element);
11293         int sound_action = ACTION_WALKING;
11294
11295 #if 0
11296         if (!ACCESS_FROM(element, opposite_direction))
11297           return MF_NO_ACTION;  /* field not accessible from this direction */
11298 #endif
11299
11300 #if 0
11301         if (element == EL_EMPTY_SPACE &&
11302             game.gravity && !player->is_auto_moving &&
11303             canFallDown(player) && move_direction != MV_DOWN)
11304           return MF_NO_ACTION;  /* player cannot walk here due to gravity */
11305 #endif
11306
11307         if (IS_GATE(element))
11308         {
11309           if (!player->key[element - EL_GATE_1])
11310             return MF_NO_ACTION;
11311         }
11312         else if (IS_GATE_GRAY(element))
11313         {
11314           if (!player->key[element - EL_GATE_1_GRAY])
11315             return MF_NO_ACTION;
11316         }
11317         else if (element == EL_EXIT_OPEN ||
11318                  element == EL_SP_EXIT_OPEN ||
11319                  element == EL_SP_EXIT_OPENING)
11320         {
11321           sound_action = ACTION_PASSING;        /* player is passing exit */
11322         }
11323         else if (element == EL_EMPTY)
11324         {
11325           sound_action = ACTION_MOVING;         /* nothing to walk on */
11326         }
11327
11328         /* play sound from background or player, whatever is available */
11329         if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11330           PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11331         else
11332           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11333
11334         break;
11335       }
11336 #if 1
11337       else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11338 #else
11339       else if (IS_PASSABLE(element))
11340 #endif
11341       {
11342 #if 0
11343         if (!canPassField(x, y, move_direction))
11344           return MF_NO_ACTION;
11345 #else
11346
11347 #if 0
11348 #if 1
11349         if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11350             !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11351             (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11352           return MF_NO_ACTION;
11353 #else
11354         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11355           return MF_NO_ACTION;
11356 #endif
11357 #endif
11358
11359 #if 1
11360         if (!ACCESS_FROM(element, opposite_direction))
11361           return MF_NO_ACTION;  /* field not accessible from this direction */
11362 #else
11363         if (IS_CUSTOM_ELEMENT(element) &&
11364             !ACCESS_FROM(element, opposite_direction))
11365           return MF_NO_ACTION;  /* field not accessible from this direction */
11366 #endif
11367
11368 #if 1
11369         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
11370           return MF_NO_ACTION;
11371 #endif
11372
11373 #endif
11374
11375         if (IS_EM_GATE(element))
11376         {
11377           if (!player->key[element - EL_EM_GATE_1])
11378             return MF_NO_ACTION;
11379         }
11380         else if (IS_EM_GATE_GRAY(element))
11381         {
11382           if (!player->key[element - EL_EM_GATE_1_GRAY])
11383             return MF_NO_ACTION;
11384         }
11385         else if (IS_SP_PORT(element))
11386         {
11387           if (element == EL_SP_GRAVITY_PORT_LEFT ||
11388               element == EL_SP_GRAVITY_PORT_RIGHT ||
11389               element == EL_SP_GRAVITY_PORT_UP ||
11390               element == EL_SP_GRAVITY_PORT_DOWN)
11391             game.gravity = !game.gravity;
11392           else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11393                    element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11394                    element == EL_SP_GRAVITY_ON_PORT_UP ||
11395                    element == EL_SP_GRAVITY_ON_PORT_DOWN)
11396             game.gravity = TRUE;
11397           else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11398                    element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11399                    element == EL_SP_GRAVITY_OFF_PORT_UP ||
11400                    element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11401             game.gravity = FALSE;
11402         }
11403
11404         /* automatically move to the next field with double speed */
11405         player->programmed_action = move_direction;
11406 #if 1
11407         if (player->move_delay_reset_counter == 0)
11408         {
11409           player->move_delay_reset_counter = 2; /* two double speed steps */
11410
11411           DOUBLE_PLAYER_SPEED(player);
11412         }
11413 #else
11414         player->move_delay_reset_counter = 2;
11415
11416         DOUBLE_PLAYER_SPEED(player);
11417 #endif
11418
11419         PlayLevelSoundAction(x, y, ACTION_PASSING);
11420
11421         break;
11422       }
11423       else if (IS_DIGGABLE(element))
11424       {
11425         RemoveField(x, y);
11426
11427         if (mode != DF_SNAP)
11428         {
11429 #if 1
11430           GfxElement[x][y] = GFX_ELEMENT(element);
11431 #else
11432           GfxElement[x][y] =
11433             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11434 #endif
11435           player->is_digging = TRUE;
11436         }
11437
11438         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11439
11440         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11441                                             player->index_bit, dig_side);
11442
11443 #if 1
11444         if (mode == DF_SNAP)
11445           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
11446 #endif
11447
11448         break;
11449       }
11450       else if (IS_COLLECTIBLE(element))
11451       {
11452         RemoveField(x, y);
11453
11454         if (is_player && mode != DF_SNAP)
11455         {
11456           GfxElement[x][y] = element;
11457           player->is_collecting = TRUE;
11458         }
11459
11460         if (element == EL_SPEED_PILL)
11461           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11462         else if (element == EL_EXTRA_TIME && level.time > 0)
11463         {
11464           TimeLeft += 10;
11465           DrawGameValue_Time(TimeLeft);
11466         }
11467         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11468         {
11469           player->shield_normal_time_left += 10;
11470           if (element == EL_SHIELD_DEADLY)
11471             player->shield_deadly_time_left += 10;
11472         }
11473         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11474         {
11475           if (player->inventory_size < MAX_INVENTORY_SIZE)
11476             player->inventory_element[player->inventory_size++] = element;
11477
11478           DrawGameValue_Dynamite(local_player->inventory_size);
11479         }
11480         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11481         {
11482           player->dynabomb_count++;
11483           player->dynabombs_left++;
11484         }
11485         else if (element == EL_DYNABOMB_INCREASE_SIZE)
11486         {
11487           player->dynabomb_size++;
11488         }
11489         else if (element == EL_DYNABOMB_INCREASE_POWER)
11490         {
11491           player->dynabomb_xl = TRUE;
11492         }
11493         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11494                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11495         {
11496           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11497                         element - EL_KEY_1 : element - EL_EM_KEY_1);
11498
11499           player->key[key_nr] = TRUE;
11500
11501           DrawGameValue_Keys(player);
11502
11503           redraw_mask |= REDRAW_DOOR_1;
11504         }
11505         else if (IS_ENVELOPE(element))
11506         {
11507 #if 1
11508           player->show_envelope = element;
11509 #else
11510           ShowEnvelope(element - EL_ENVELOPE_1);
11511 #endif
11512         }
11513         else if (IS_DROPPABLE(element) ||
11514                  IS_THROWABLE(element)) /* can be collected and dropped */
11515         {
11516           int i;
11517
11518           if (element_info[element].collect_count == 0)
11519             player->inventory_infinite_element = element;
11520           else
11521             for (i = 0; i < element_info[element].collect_count; i++)
11522               if (player->inventory_size < MAX_INVENTORY_SIZE)
11523                 player->inventory_element[player->inventory_size++] = element;
11524
11525           DrawGameValue_Dynamite(local_player->inventory_size);
11526         }
11527         else if (element_info[element].collect_count > 0)
11528         {
11529           local_player->gems_still_needed -=
11530             element_info[element].collect_count;
11531           if (local_player->gems_still_needed < 0)
11532             local_player->gems_still_needed = 0;
11533
11534           DrawGameValue_Emeralds(local_player->gems_still_needed);
11535         }
11536
11537         RaiseScoreElement(element);
11538         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11539
11540         if (is_player)
11541           CheckTriggeredElementChangeByPlayer(x, y, element,
11542                                               CE_OTHER_GETS_COLLECTED,
11543                                               player->index_bit, dig_side);
11544
11545 #if 1
11546         if (mode == DF_SNAP)
11547           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
11548 #endif
11549
11550         break;
11551       }
11552       else if (IS_PUSHABLE(element))
11553       {
11554         if (mode == DF_SNAP && element != EL_BD_ROCK)
11555           return MF_NO_ACTION;
11556
11557         if (CAN_FALL(element) && dy)
11558           return MF_NO_ACTION;
11559
11560         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11561             !(element == EL_SPRING && level.use_spring_bug))
11562           return MF_NO_ACTION;
11563
11564 #if 1
11565         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11566             ((move_direction & MV_VERTICAL &&
11567               ((element_info[element].move_pattern & MV_LEFT &&
11568                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11569                (element_info[element].move_pattern & MV_RIGHT &&
11570                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11571              (move_direction & MV_HORIZONTAL &&
11572               ((element_info[element].move_pattern & MV_UP &&
11573                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11574                (element_info[element].move_pattern & MV_DOWN &&
11575                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11576           return MF_NO_ACTION;
11577 #endif
11578
11579 #if 1
11580         /* do not push elements already moving away faster than player */
11581         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11582             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11583           return MF_NO_ACTION;
11584 #else
11585         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11586           return MF_NO_ACTION;
11587 #endif
11588
11589 #if 1
11590
11591 #if 1
11592         if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11593         {
11594           if (player->push_delay_value == -1 || !player_was_pushing)
11595             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11596         }
11597         else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11598         {
11599           if (player->push_delay_value == -1)
11600             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11601         }
11602 #else
11603         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11604         {
11605           if (player->push_delay_value == -1 || !player_was_pushing)
11606             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11607         }
11608 #endif
11609         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11610         {
11611           if (!player->is_pushing)
11612             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11613         }
11614
11615         /*
11616         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11617             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11618              !player_is_pushing))
11619           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11620         */
11621 #else
11622         if (!player->is_pushing &&
11623             game.engine_version >= VERSION_IDENT(2,2,0,7))
11624           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11625 #endif
11626
11627 #if 0
11628         printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11629                player->push_delay, player->push_delay_value,
11630                FrameCounter, game.engine_version,
11631                player_was_pushing, player->is_pushing,
11632                element, element_info[element].token_name,
11633                GET_NEW_PUSH_DELAY(element));
11634 #endif
11635
11636         player->is_pushing = TRUE;
11637
11638         if (!(IN_LEV_FIELD(nextx, nexty) &&
11639               (IS_FREE(nextx, nexty) ||
11640                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11641                 IS_SB_ELEMENT(element)))))
11642           return MF_NO_ACTION;
11643
11644         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11645           return MF_NO_ACTION;
11646
11647 #if USE_NEW_PUSH_DELAY
11648
11649 #if 0
11650         if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11651           printf("::: ALERT: %d, %d [%d / %d]\n",
11652                  player->push_delay, player->push_delay2,
11653                  FrameCounter, FrameCounter / 50);
11654 #endif
11655
11656         if (player->push_delay == -1)   /* new pushing; restart delay */
11657           player->push_delay = 0;
11658 #else
11659         if (player->push_delay == 0)    /* new pushing; restart delay */
11660           player->push_delay = FrameCounter;
11661 #endif
11662
11663 #if USE_NEW_PUSH_DELAY
11664 #if 0
11665         if ( (player->push_delay > 0) != (!xxx_fr) )
11666           printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11667                  player->push_delay,
11668                  xxx_pdv2, player->push_delay2, player->push_delay_value,
11669                  FrameCounter, FrameCounter / 50);
11670 #endif
11671
11672 #if 0
11673         if (player->push_delay > 0 &&
11674             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11675             element != EL_SPRING && element != EL_BALLOON)
11676 #else
11677         /* !!! */
11678         if (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 #else
11684         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11685             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11686             element != EL_SPRING && element != EL_BALLOON)
11687 #endif
11688         {
11689           /* make sure that there is no move delay before next try to push */
11690 #if USE_NEW_MOVE_DELAY
11691           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11692             player->move_delay = 0;
11693 #else
11694           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11695             player->move_delay = INITIAL_MOVE_DELAY_OFF;
11696 #endif
11697
11698           return MF_NO_ACTION;
11699         }
11700
11701 #if 0
11702         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11703 #endif
11704
11705         if (IS_SB_ELEMENT(element))
11706         {
11707           if (element == EL_SOKOBAN_FIELD_FULL)
11708           {
11709             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11710             local_player->sokobanfields_still_needed++;
11711           }
11712
11713           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11714           {
11715             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11716             local_player->sokobanfields_still_needed--;
11717           }
11718
11719           Feld[x][y] = EL_SOKOBAN_OBJECT;
11720
11721           if (Back[x][y] == Back[nextx][nexty])
11722             PlayLevelSoundAction(x, y, ACTION_PUSHING);
11723           else if (Back[x][y] != 0)
11724             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11725                                         ACTION_EMPTYING);
11726           else
11727             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11728                                         ACTION_FILLING);
11729
11730           if (local_player->sokobanfields_still_needed == 0 &&
11731               game.emulation == EMU_SOKOBAN)
11732           {
11733             player->LevelSolved = player->GameOver = TRUE;
11734             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11735           }
11736         }
11737         else
11738           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11739
11740         InitMovingField(x, y, move_direction);
11741         GfxAction[x][y] = ACTION_PUSHING;
11742
11743         if (mode == DF_SNAP)
11744           ContinueMoving(x, y);
11745         else
11746           MovPos[x][y] = (dx != 0 ? dx : dy);
11747
11748         Pushed[x][y] = TRUE;
11749         Pushed[nextx][nexty] = TRUE;
11750
11751         if (game.engine_version < VERSION_IDENT(2,2,0,7))
11752           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11753         else
11754           player->push_delay_value = -1;        /* get new value later */
11755
11756 #if USE_PUSH_BUGFIX
11757         /* now: check for element change _after_ element has been pushed! */
11758 #if 1
11759         if (game.use_bug_change_when_pushing)
11760 #else
11761         if (game.engine_version < VERSION_IDENT(3,1,0,0))
11762 #endif
11763         {
11764           CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11765                                      player->index_bit, dig_side);
11766           CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
11767                                               player->index_bit, dig_side);
11768         }
11769
11770 #else
11771
11772 #if 1
11773         /* check for element change _after_ element has been pushed! */
11774 #else
11775
11776 #if 1
11777         /* !!! TEST ONLY !!! */
11778         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11779                                    player->index_bit, dig_side);
11780         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11781                                             player->index_bit, dig_side);
11782 #else
11783         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11784                                             player->index_bit, dig_side);
11785         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11786                                    player->index_bit, dig_side);
11787 #endif
11788 #endif
11789
11790 #endif
11791
11792         break;
11793       }
11794       else if (IS_SWITCHABLE(element))
11795       {
11796         if (PLAYER_SWITCHING(player, x, y))
11797         {
11798           CheckTriggeredElementChangeByPlayer(x,y, element,
11799                                               CE_OTHER_GETS_PRESSED,
11800                                               player->index_bit, dig_side);
11801
11802           return MF_ACTION;
11803         }
11804
11805         player->is_switching = TRUE;
11806         player->switch_x = x;
11807         player->switch_y = y;
11808
11809         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11810
11811         if (element == EL_ROBOT_WHEEL)
11812         {
11813           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11814           ZX = x;
11815           ZY = y;
11816
11817           DrawLevelField(x, y);
11818         }
11819         else if (element == EL_SP_TERMINAL)
11820         {
11821           int xx, yy;
11822
11823           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11824           {
11825             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11826               Bang(xx, yy);
11827             else if (Feld[xx][yy] == EL_SP_TERMINAL)
11828               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11829           }
11830         }
11831         else if (IS_BELT_SWITCH(element))
11832         {
11833           ToggleBeltSwitch(x, y);
11834         }
11835         else if (element == EL_SWITCHGATE_SWITCH_UP ||
11836                  element == EL_SWITCHGATE_SWITCH_DOWN)
11837         {
11838           ToggleSwitchgateSwitch(x, y);
11839         }
11840         else if (element == EL_LIGHT_SWITCH ||
11841                  element == EL_LIGHT_SWITCH_ACTIVE)
11842         {
11843           ToggleLightSwitch(x, y);
11844
11845 #if 0
11846           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11847                          SND_LIGHT_SWITCH_ACTIVATING :
11848                          SND_LIGHT_SWITCH_DEACTIVATING);
11849 #endif
11850         }
11851         else if (element == EL_TIMEGATE_SWITCH)
11852         {
11853           ActivateTimegateSwitch(x, y);
11854         }
11855         else if (element == EL_BALLOON_SWITCH_LEFT ||
11856                  element == EL_BALLOON_SWITCH_RIGHT ||
11857                  element == EL_BALLOON_SWITCH_UP ||
11858                  element == EL_BALLOON_SWITCH_DOWN ||
11859                  element == EL_BALLOON_SWITCH_ANY)
11860         {
11861           if (element == EL_BALLOON_SWITCH_ANY)
11862             game.balloon_dir = move_direction;
11863           else
11864             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
11865                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11866                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
11867                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
11868                                 MV_NO_MOVING);
11869         }
11870         else if (element == EL_LAMP)
11871         {
11872           Feld[x][y] = EL_LAMP_ACTIVE;
11873           local_player->lights_still_needed--;
11874
11875           DrawLevelField(x, y);
11876         }
11877         else if (element == EL_TIME_ORB_FULL)
11878         {
11879           Feld[x][y] = EL_TIME_ORB_EMPTY;
11880           TimeLeft += 10;
11881           DrawGameValue_Time(TimeLeft);
11882
11883           DrawLevelField(x, y);
11884
11885 #if 0
11886           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11887 #endif
11888         }
11889
11890         CheckTriggeredElementChangeByPlayer(x, y, element,
11891                                             CE_OTHER_IS_SWITCHING,
11892                                             player->index_bit, dig_side);
11893
11894         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11895                                             player->index_bit, dig_side);
11896
11897         return MF_ACTION;
11898       }
11899       else
11900       {
11901         if (!PLAYER_SWITCHING(player, x, y))
11902         {
11903           player->is_switching = TRUE;
11904           player->switch_x = x;
11905           player->switch_y = y;
11906
11907 #if 1
11908           /* !!! TEST ONLY !!! */
11909           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11910                                      player->index_bit, dig_side);
11911           CheckTriggeredElementChangeByPlayer(x, y, element,
11912                                               CE_OTHER_IS_SWITCHING,
11913                                               player->index_bit, dig_side);
11914 #else
11915           CheckTriggeredElementChangeByPlayer(x, y, element,
11916                                               CE_OTHER_IS_SWITCHING,
11917                                               player->index_bit, dig_side);
11918           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11919                                      player->index_bit, dig_side);
11920 #endif
11921         }
11922
11923 #if 1
11924         /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11925         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11926                                    player->index_bit, dig_side);
11927         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11928                                             player->index_bit, dig_side);
11929 #else
11930         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11931                                             player->index_bit, dig_side);
11932         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11933                                    player->index_bit, dig_side);
11934 #endif
11935       }
11936
11937       return MF_NO_ACTION;
11938   }
11939
11940 #if USE_NEW_PUSH_DELAY
11941   player->push_delay = -1;
11942 #else
11943   player->push_delay = 0;
11944 #endif
11945
11946   if (Feld[x][y] != element)            /* really digged/collected something */
11947     player->is_collecting = !player->is_digging;
11948
11949   return MF_MOVING;
11950 }
11951
11952 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11953 {
11954   int jx = player->jx, jy = player->jy;
11955   int x = jx + dx, y = jy + dy;
11956   int snap_direction = (dx == -1 ? MV_LEFT :
11957                         dx == +1 ? MV_RIGHT :
11958                         dy == -1 ? MV_UP :
11959                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
11960
11961 #if 0
11962   if (player->MovPos != 0)
11963     return FALSE;
11964 #else
11965   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11966     return FALSE;
11967 #endif
11968
11969   if (!player->active || !IN_LEV_FIELD(x, y))
11970     return FALSE;
11971
11972   if (dx && dy)
11973     return FALSE;
11974
11975   if (!dx && !dy)
11976   {
11977     if (player->MovPos == 0)
11978       player->is_pushing = FALSE;
11979
11980     player->is_snapping = FALSE;
11981
11982     if (player->MovPos == 0)
11983     {
11984       player->is_moving = FALSE;
11985       player->is_digging = FALSE;
11986       player->is_collecting = FALSE;
11987     }
11988
11989     return FALSE;
11990   }
11991
11992   if (player->is_snapping)
11993     return FALSE;
11994
11995   player->MovDir = snap_direction;
11996
11997 #if 1
11998   if (player->MovPos == 0)
11999 #endif
12000   {
12001     player->is_moving = FALSE;
12002     player->is_digging = FALSE;
12003     player->is_collecting = FALSE;
12004   }
12005
12006   player->is_dropping = FALSE;
12007
12008   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12009     return FALSE;
12010
12011   player->is_snapping = TRUE;
12012
12013 #if 1
12014   if (player->MovPos == 0)
12015 #endif
12016   {
12017     player->is_moving = FALSE;
12018     player->is_digging = FALSE;
12019     player->is_collecting = FALSE;
12020   }
12021
12022 #if 1
12023   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12024     DrawLevelField(player->last_jx, player->last_jy);
12025 #endif
12026
12027   DrawLevelField(x, y);
12028
12029 #if 0
12030   BackToFront();
12031 #endif
12032
12033   return TRUE;
12034 }
12035
12036 boolean DropElement(struct PlayerInfo *player)
12037 {
12038   int old_element, new_element;
12039   int dropx = player->jx, dropy = player->jy;
12040   int drop_direction = player->MovDir;
12041 #if 1
12042   int drop_side = drop_direction;
12043 #else
12044   static int trigger_sides[4] =
12045   {
12046     CH_SIDE_LEFT,       /* dropping left  */
12047     CH_SIDE_RIGHT,      /* dropping right */
12048     CH_SIDE_TOP,        /* dropping up    */
12049     CH_SIDE_BOTTOM,     /* dropping down  */
12050   };
12051   int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12052 #endif
12053   int drop_element = (player->inventory_size > 0 ?
12054                       player->inventory_element[player->inventory_size - 1] :
12055                       player->inventory_infinite_element != EL_UNDEFINED ?
12056                       player->inventory_infinite_element :
12057                       player->dynabombs_left > 0 ?
12058                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12059                       EL_UNDEFINED);
12060
12061   if (IS_THROWABLE(drop_element))
12062   {
12063     dropx += GET_DX_FROM_DIR(drop_direction);
12064     dropy += GET_DY_FROM_DIR(drop_direction);
12065
12066     if (!IN_LEV_FIELD(dropx, dropy))
12067       return FALSE;
12068   }
12069
12070   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12071   new_element = drop_element;           /* default: no change when dropping */
12072
12073   /* check if player is active, not moving and ready to drop */
12074   if (!player->active || player->MovPos || player->drop_delay > 0)
12075     return FALSE;
12076
12077   /* check if player has anything that can be dropped */
12078 #if 1
12079   if (new_element == EL_UNDEFINED)
12080     return FALSE;
12081 #else
12082   if (player->inventory_size == 0 &&
12083       player->inventory_infinite_element == EL_UNDEFINED &&
12084       player->dynabombs_left == 0)
12085     return FALSE;
12086 #endif
12087
12088   /* check if anything can be dropped at the current position */
12089   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12090     return FALSE;
12091
12092   /* collected custom elements can only be dropped on empty fields */
12093 #if 1
12094   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12095     return FALSE;
12096 #else
12097   if (player->inventory_size > 0 &&
12098       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12099       && old_element != EL_EMPTY)
12100     return FALSE;
12101 #endif
12102
12103   if (old_element != EL_EMPTY)
12104     Back[dropx][dropy] = old_element;   /* store old element on this field */
12105
12106   ResetGfxAnimation(dropx, dropy);
12107   ResetRandomAnimationValue(dropx, dropy);
12108
12109   if (player->inventory_size > 0 ||
12110       player->inventory_infinite_element != EL_UNDEFINED)
12111   {
12112     if (player->inventory_size > 0)
12113     {
12114       player->inventory_size--;
12115
12116 #if 0
12117       new_element = player->inventory_element[player->inventory_size];
12118 #endif
12119
12120       DrawGameValue_Dynamite(local_player->inventory_size);
12121
12122       if (new_element == EL_DYNAMITE)
12123         new_element = EL_DYNAMITE_ACTIVE;
12124       else if (new_element == EL_SP_DISK_RED)
12125         new_element = EL_SP_DISK_RED_ACTIVE;
12126     }
12127
12128     Feld[dropx][dropy] = new_element;
12129
12130     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12131       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12132                           el2img(Feld[dropx][dropy]), 0);
12133
12134     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12135
12136 #if 1
12137     /* needed if previous element just changed to "empty" in the last frame */
12138     Changed[dropx][dropy] = 0;          /* allow another change */
12139 #endif
12140
12141 #if 1
12142     /* !!! TEST ONLY !!! */
12143     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12144                                player->index_bit, drop_side);
12145     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12146                                         CE_OTHER_GETS_DROPPED,
12147                                         player->index_bit, drop_side);
12148 #else
12149     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12150                                         CE_OTHER_GETS_DROPPED,
12151                                         player->index_bit, drop_side);
12152     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12153                                player->index_bit, drop_side);
12154 #endif
12155
12156     TestIfElementTouchesCustomElement(dropx, dropy);
12157   }
12158   else          /* player is dropping a dyna bomb */
12159   {
12160     player->dynabombs_left--;
12161
12162 #if 0
12163     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12164 #endif
12165
12166     Feld[dropx][dropy] = new_element;
12167
12168     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12169       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12170                           el2img(Feld[dropx][dropy]), 0);
12171
12172     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12173   }
12174
12175
12176
12177 #if 1
12178
12179   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12180   {
12181 #if 1
12182     InitField_WithBug1(dropx, dropy, FALSE);
12183 #else
12184     InitField(dropx, dropy, FALSE);
12185     if (CAN_MOVE(Feld[dropx][dropy]))
12186       InitMovDir(dropx, dropy);
12187 #endif
12188   }
12189
12190   new_element = Feld[dropx][dropy];     /* element might have changed */
12191
12192   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12193       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12194   {
12195 #if 0
12196     int move_stepsize = element_info[new_element].move_stepsize;
12197 #endif
12198     int move_direction, nextx, nexty;
12199
12200     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12201       MovDir[dropx][dropy] = drop_direction;
12202
12203     move_direction = MovDir[dropx][dropy];
12204     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12205     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12206
12207 #if 1
12208     Changed[dropx][dropy] = 0;          /* allow another change */
12209     CheckCollision[dropx][dropy] = 2;
12210 #else
12211
12212     if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12213     {
12214 #if 0
12215       WasJustMoving[dropx][dropy] = 3;
12216 #else
12217 #if 1
12218       InitMovingField(dropx, dropy, move_direction);
12219       ContinueMoving(dropx, dropy);
12220 #endif
12221 #endif
12222     }
12223 #if 0
12224     /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12225     else
12226     {
12227       Changed[dropx][dropy] = 0;        /* allow another change */
12228
12229 #if 1
12230       TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12231 #else
12232       CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12233                                CE_HITTING_SOMETHING, move_direction);
12234 #endif
12235     }
12236 #endif
12237
12238 #endif
12239
12240 #if 0
12241     player->drop_delay = 2 * TILEX / move_stepsize + 1;
12242 #endif
12243   }
12244
12245 #if 0
12246   player->drop_delay = 8 + 8 + 8;
12247 #endif
12248
12249 #if 1
12250   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12251 #endif
12252
12253 #endif
12254
12255   player->is_dropping = TRUE;
12256
12257
12258   return TRUE;
12259 }
12260
12261 /* ------------------------------------------------------------------------- */
12262 /* game sound playing functions                                              */
12263 /* ------------------------------------------------------------------------- */
12264
12265 static int *loop_sound_frame = NULL;
12266 static int *loop_sound_volume = NULL;
12267
12268 void InitPlayLevelSound()
12269 {
12270   int num_sounds = getSoundListSize();
12271
12272   checked_free(loop_sound_frame);
12273   checked_free(loop_sound_volume);
12274
12275   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12276   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12277 }
12278
12279 static void PlayLevelSound(int x, int y, int nr)
12280 {
12281   int sx = SCREENX(x), sy = SCREENY(y);
12282   int volume, stereo_position;
12283   int max_distance = 8;
12284   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12285
12286   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12287       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12288     return;
12289
12290   if (!IN_LEV_FIELD(x, y) ||
12291       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12292       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12293     return;
12294
12295   volume = SOUND_MAX_VOLUME;
12296
12297   if (!IN_SCR_FIELD(sx, sy))
12298   {
12299     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12300     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12301
12302     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12303   }
12304
12305   stereo_position = (SOUND_MAX_LEFT +
12306                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12307                      (SCR_FIELDX + 2 * max_distance));
12308
12309   if (IS_LOOP_SOUND(nr))
12310   {
12311     /* This assures that quieter loop sounds do not overwrite louder ones,
12312        while restarting sound volume comparison with each new game frame. */
12313
12314     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12315       return;
12316
12317     loop_sound_volume[nr] = volume;
12318     loop_sound_frame[nr] = FrameCounter;
12319   }
12320
12321   PlaySoundExt(nr, volume, stereo_position, type);
12322 }
12323
12324 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12325 {
12326   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12327                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12328                  y < LEVELY(BY1) ? LEVELY(BY1) :
12329                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12330                  sound_action);
12331 }
12332
12333 static void PlayLevelSoundAction(int x, int y, int action)
12334 {
12335   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12336 }
12337
12338 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12339 {
12340   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12341
12342   if (sound_effect != SND_UNDEFINED)
12343     PlayLevelSound(x, y, sound_effect);
12344 }
12345
12346 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12347                                               int action)
12348 {
12349   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12350
12351   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12352     PlayLevelSound(x, y, sound_effect);
12353 }
12354
12355 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12356 {
12357   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12358
12359   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12360     PlayLevelSound(x, y, sound_effect);
12361 }
12362
12363 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12364 {
12365   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12366
12367   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12368     StopSound(sound_effect);
12369 }
12370
12371 static void PlayLevelMusic()
12372 {
12373   if (levelset.music[level_nr] != MUS_UNDEFINED)
12374     PlayMusic(levelset.music[level_nr]);        /* from config file */
12375   else
12376     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
12377 }
12378
12379 void RaiseScore(int value)
12380 {
12381   local_player->score += value;
12382
12383   DrawGameValue_Score(local_player->score);
12384 }
12385
12386 void RaiseScoreElement(int element)
12387 {
12388   switch(element)
12389   {
12390     case EL_EMERALD:
12391     case EL_BD_DIAMOND:
12392     case EL_EMERALD_YELLOW:
12393     case EL_EMERALD_RED:
12394     case EL_EMERALD_PURPLE:
12395     case EL_SP_INFOTRON:
12396       RaiseScore(level.score[SC_EMERALD]);
12397       break;
12398     case EL_DIAMOND:
12399       RaiseScore(level.score[SC_DIAMOND]);
12400       break;
12401     case EL_CRYSTAL:
12402       RaiseScore(level.score[SC_CRYSTAL]);
12403       break;
12404     case EL_PEARL:
12405       RaiseScore(level.score[SC_PEARL]);
12406       break;
12407     case EL_BUG:
12408     case EL_BD_BUTTERFLY:
12409     case EL_SP_ELECTRON:
12410       RaiseScore(level.score[SC_BUG]);
12411       break;
12412     case EL_SPACESHIP:
12413     case EL_BD_FIREFLY:
12414     case EL_SP_SNIKSNAK:
12415       RaiseScore(level.score[SC_SPACESHIP]);
12416       break;
12417     case EL_YAMYAM:
12418     case EL_DARK_YAMYAM:
12419       RaiseScore(level.score[SC_YAMYAM]);
12420       break;
12421     case EL_ROBOT:
12422       RaiseScore(level.score[SC_ROBOT]);
12423       break;
12424     case EL_PACMAN:
12425       RaiseScore(level.score[SC_PACMAN]);
12426       break;
12427     case EL_NUT:
12428       RaiseScore(level.score[SC_NUT]);
12429       break;
12430     case EL_DYNAMITE:
12431     case EL_SP_DISK_RED:
12432     case EL_DYNABOMB_INCREASE_NUMBER:
12433     case EL_DYNABOMB_INCREASE_SIZE:
12434     case EL_DYNABOMB_INCREASE_POWER:
12435       RaiseScore(level.score[SC_DYNAMITE]);
12436       break;
12437     case EL_SHIELD_NORMAL:
12438     case EL_SHIELD_DEADLY:
12439       RaiseScore(level.score[SC_SHIELD]);
12440       break;
12441     case EL_EXTRA_TIME:
12442       RaiseScore(level.score[SC_TIME_BONUS]);
12443       break;
12444     case EL_KEY_1:
12445     case EL_KEY_2:
12446     case EL_KEY_3:
12447     case EL_KEY_4:
12448       RaiseScore(level.score[SC_KEY]);
12449       break;
12450     default:
12451       RaiseScore(element_info[element].collect_score);
12452       break;
12453   }
12454 }
12455
12456 void RequestQuitGame(boolean ask_if_really_quit)
12457 {
12458   if (AllPlayersGone ||
12459       !ask_if_really_quit ||
12460       level_editor_test_game ||
12461       Request("Do you really want to quit the game ?",
12462               REQ_ASK | REQ_STAY_CLOSED))
12463   {
12464 #if defined(NETWORK_AVALIABLE)
12465     if (options.network)
12466       SendToServer_StopPlaying();
12467     else
12468 #endif
12469     {
12470       game_status = GAME_MODE_MAIN;
12471       DrawMainMenu();
12472     }
12473   }
12474   else
12475   {
12476
12477 #if 1
12478     if (tape.playing && tape.deactivate_display)
12479       TapeDeactivateDisplayOff(TRUE);
12480 #endif
12481
12482     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12483
12484 #if 1
12485     if (tape.playing && tape.deactivate_display)
12486       TapeDeactivateDisplayOn();
12487 #endif
12488
12489   }
12490 }
12491
12492
12493 /* ---------- new game button stuff ---------------------------------------- */
12494
12495 /* graphic position values for game buttons */
12496 #define GAME_BUTTON_XSIZE       30
12497 #define GAME_BUTTON_YSIZE       30
12498 #define GAME_BUTTON_XPOS        5
12499 #define GAME_BUTTON_YPOS        215
12500 #define SOUND_BUTTON_XPOS       5
12501 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12502
12503 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12504 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12505 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12506 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12507 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12508 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12509
12510 static struct
12511 {
12512   int x, y;
12513   int gadget_id;
12514   char *infotext;
12515 } gamebutton_info[NUM_GAME_BUTTONS] =
12516 {
12517   {
12518     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
12519     GAME_CTRL_ID_STOP,
12520     "stop game"
12521   },
12522   {
12523     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
12524     GAME_CTRL_ID_PAUSE,
12525     "pause game"
12526   },
12527   {
12528     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
12529     GAME_CTRL_ID_PLAY,
12530     "play game"
12531   },
12532   {
12533     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
12534     SOUND_CTRL_ID_MUSIC,
12535     "background music on/off"
12536   },
12537   {
12538     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
12539     SOUND_CTRL_ID_LOOPS,
12540     "sound loops on/off"
12541   },
12542   {
12543     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
12544     SOUND_CTRL_ID_SIMPLE,
12545     "normal sounds on/off"
12546   }
12547 };
12548
12549 void CreateGameButtons()
12550 {
12551   int i;
12552
12553   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12554   {
12555     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12556     struct GadgetInfo *gi;
12557     int button_type;
12558     boolean checked;
12559     unsigned long event_mask;
12560     int gd_xoffset, gd_yoffset;
12561     int gd_x1, gd_x2, gd_y1, gd_y2;
12562     int id = i;
12563
12564     gd_xoffset = gamebutton_info[i].x;
12565     gd_yoffset = gamebutton_info[i].y;
12566     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12567     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12568
12569     if (id == GAME_CTRL_ID_STOP ||
12570         id == GAME_CTRL_ID_PAUSE ||
12571         id == GAME_CTRL_ID_PLAY)
12572     {
12573       button_type = GD_TYPE_NORMAL_BUTTON;
12574       checked = FALSE;
12575       event_mask = GD_EVENT_RELEASED;
12576       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12577       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12578     }
12579     else
12580     {
12581       button_type = GD_TYPE_CHECK_BUTTON;
12582       checked =
12583         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12584          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12585          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12586       event_mask = GD_EVENT_PRESSED;
12587       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
12588       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12589     }
12590
12591     gi = CreateGadget(GDI_CUSTOM_ID, id,
12592                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
12593                       GDI_X, DX + gd_xoffset,
12594                       GDI_Y, DY + gd_yoffset,
12595                       GDI_WIDTH, GAME_BUTTON_XSIZE,
12596                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
12597                       GDI_TYPE, button_type,
12598                       GDI_STATE, GD_BUTTON_UNPRESSED,
12599                       GDI_CHECKED, checked,
12600                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12601                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12602                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12603                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12604                       GDI_EVENT_MASK, event_mask,
12605                       GDI_CALLBACK_ACTION, HandleGameButtons,
12606                       GDI_END);
12607
12608     if (gi == NULL)
12609       Error(ERR_EXIT, "cannot create gadget");
12610
12611     game_gadget[id] = gi;
12612   }
12613 }
12614
12615 void FreeGameButtons()
12616 {
12617   int i;
12618
12619   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12620     FreeGadget(game_gadget[i]);
12621 }
12622
12623 static void MapGameButtons()
12624 {
12625   int i;
12626
12627   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12628     MapGadget(game_gadget[i]);
12629 }
12630
12631 void UnmapGameButtons()
12632 {
12633   int i;
12634
12635   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12636     UnmapGadget(game_gadget[i]);
12637 }
12638
12639 static void HandleGameButtons(struct GadgetInfo *gi)
12640 {
12641   int id = gi->custom_id;
12642
12643   if (game_status != GAME_MODE_PLAYING)
12644     return;
12645
12646   switch (id)
12647   {
12648     case GAME_CTRL_ID_STOP:
12649       RequestQuitGame(TRUE);
12650       break;
12651
12652     case GAME_CTRL_ID_PAUSE:
12653       if (options.network)
12654       {
12655 #if defined(NETWORK_AVALIABLE)
12656         if (tape.pausing)
12657           SendToServer_ContinuePlaying();
12658         else
12659           SendToServer_PausePlaying();
12660 #endif
12661       }
12662       else
12663         TapeTogglePause(TAPE_TOGGLE_MANUAL);
12664       break;
12665
12666     case GAME_CTRL_ID_PLAY:
12667       if (tape.pausing)
12668       {
12669 #if defined(NETWORK_AVALIABLE)
12670         if (options.network)
12671           SendToServer_ContinuePlaying();
12672         else
12673 #endif
12674         {
12675           tape.pausing = FALSE;
12676           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12677         }
12678       }
12679       break;
12680
12681     case SOUND_CTRL_ID_MUSIC:
12682       if (setup.sound_music)
12683       { 
12684         setup.sound_music = FALSE;
12685         FadeMusic();
12686       }
12687       else if (audio.music_available)
12688       { 
12689         setup.sound = setup.sound_music = TRUE;
12690
12691         SetAudioMode(setup.sound);
12692
12693         PlayLevelMusic();
12694       }
12695       break;
12696
12697     case SOUND_CTRL_ID_LOOPS:
12698       if (setup.sound_loops)
12699         setup.sound_loops = FALSE;
12700       else if (audio.loops_available)
12701       {
12702         setup.sound = setup.sound_loops = TRUE;
12703         SetAudioMode(setup.sound);
12704       }
12705       break;
12706
12707     case SOUND_CTRL_ID_SIMPLE:
12708       if (setup.sound_simple)
12709         setup.sound_simple = FALSE;
12710       else if (audio.sound_available)
12711       {
12712         setup.sound = setup.sound_simple = TRUE;
12713         SetAudioMode(setup.sound);
12714       }
12715       break;
12716
12717     default:
12718       break;
12719   }
12720 }