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