423f64d50ed563a88e76b486ccff40a1d130dd7c
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_STUFF           TRUE                            * 1
32
33 #define USE_NEW_MOVE_STYLE      TRUE    * USE_NEW_STUFF         * 1
34 #define USE_NEW_MOVE_DELAY      TRUE    * USE_NEW_STUFF         * 1
35 #define USE_NEW_PUSH_DELAY      TRUE    * USE_NEW_STUFF         * 1
36 #define USE_NEW_BLOCK_STYLE     TRUE    * USE_NEW_STUFF         * 1 * 1
37 #define USE_NEW_SP_SLIPPERY     TRUE    * USE_NEW_STUFF         * 1
38 #define USE_NEW_RANDOMIZE       TRUE    * USE_NEW_STUFF         * 1
39
40 #define USE_PUSH_BUGFIX         TRUE    * USE_NEW_STUFF         * 1
41
42 /* for DigField() */
43 #define DF_NO_PUSH              0
44 #define DF_DIG                  1
45 #define DF_SNAP                 2
46
47 /* for MovePlayer() */
48 #define MF_NO_ACTION            0
49 #define MF_MOVING               1
50 #define MF_ACTION               2
51
52 /* for ScrollPlayer() */
53 #define SCROLL_INIT             0
54 #define SCROLL_GO_ON            1
55
56 /* for Explode() */
57 #define EX_PHASE_START          0
58 #define EX_TYPE_NONE            0
59 #define EX_TYPE_NORMAL          (1 << 0)
60 #define EX_TYPE_CENTER          (1 << 1)
61 #define EX_TYPE_BORDER          (1 << 2)
62 #define EX_TYPE_CROSS           (1 << 3)
63 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
64
65 /* special positions in the game control window (relative to control window) */
66 #define XX_LEVEL                37
67 #define YY_LEVEL                20
68 #define XX_EMERALDS             29
69 #define YY_EMERALDS             54
70 #define XX_DYNAMITE             29
71 #define YY_DYNAMITE             89
72 #define XX_KEYS                 18
73 #define YY_KEYS                 123
74 #define XX_SCORE                15
75 #define YY_SCORE                159
76 #define XX_TIME1                29
77 #define XX_TIME2                30
78 #define YY_TIME                 194
79
80 /* special positions in the game control window (relative to main window) */
81 #define DX_LEVEL                (DX + XX_LEVEL)
82 #define DY_LEVEL                (DY + YY_LEVEL)
83 #define DX_EMERALDS             (DX + XX_EMERALDS)
84 #define DY_EMERALDS             (DY + YY_EMERALDS)
85 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
86 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
87 #define DX_KEYS                 (DX + XX_KEYS)
88 #define DY_KEYS                 (DY + YY_KEYS)
89 #define DX_SCORE                (DX + XX_SCORE)
90 #define DY_SCORE                (DY + YY_SCORE)
91 #define DX_TIME1                (DX + XX_TIME1)
92 #define DX_TIME2                (DX + XX_TIME2)
93 #define DY_TIME                 (DY + YY_TIME)
94
95 /* values for initial player move delay (initial delay counter value) */
96 #define INITIAL_MOVE_DELAY_OFF  -1
97 #define INITIAL_MOVE_DELAY_ON   0
98
99 /* values for player movement speed (which is in fact a delay value) */
100 #define MOVE_DELAY_NORMAL_SPEED 8
101 #define MOVE_DELAY_HIGH_SPEED   4
102
103 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
104 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
105 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
106 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
107
108 /* values for other actions */
109 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
110
111 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
112 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
113
114 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
115
116 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
117                                  RND(element_info[e].push_delay_random))
118 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
119                                  RND(element_info[e].drop_delay_random))
120 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
121                                  RND(element_info[e].move_delay_random))
122 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
123                                     (element_info[e].move_delay_random))
124
125 #define GET_TARGET_ELEMENT(e, ch)                                       \
126         ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element :     \
127          (e) == EL_TRIGGER_PLAYER  ? (ch)->actual_trigger_player : (e))
128
129 #define GET_VALID_PLAYER_ELEMENT(e)                                     \
130         ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
131
132 #define CAN_GROW_INTO(e)                                                \
133         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
134
135 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
136                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
137                                         (condition)))
138
139 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
140                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
141                                         (CAN_MOVE_INTO_ACID(e) &&       \
142                                          Feld[x][y] == EL_ACID) ||      \
143                                         (condition)))
144
145 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
146                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
147                                         (CAN_MOVE_INTO_ACID(e) &&       \
148                                          Feld[x][y] == EL_ACID) ||      \
149                                         (condition)))
150
151 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
152                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
153                                         (condition) ||                  \
154                                         (CAN_MOVE_INTO_ACID(e) &&       \
155                                          Feld[x][y] == EL_ACID) ||      \
156                                         (DONT_COLLIDE_WITH(e) &&        \
157                                          IS_PLAYER(x, y) &&             \
158                                          !PLAYER_ENEMY_PROTECTED(x, y))))
159
160 #if 0
161 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
162                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
163                                         (condition) ||                  \
164                                         (DONT_COLLIDE_WITH(e) &&        \
165                                          IS_PLAYER(x, y) &&             \
166                                          !PLAYER_ENEMY_PROTECTED(x, y))))
167 #endif
168
169 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
170         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
171
172 #if 1
173 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
174         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
175 #else
176 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
177         ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
178 #endif
179
180 #if 0
181 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
182 #endif
183
184 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
185         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
186
187 #if 1
188
189 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
190         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
191
192 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
193         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
194
195 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
196         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
197
198 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
199         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
200
201 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
202         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
203                                                  IS_FOOD_PENGUIN(Feld[x][y])))
204 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
205         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
206
207 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
208         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
209
210 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
211         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
212
213 #else
214
215 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
216                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
217                                         (CAN_MOVE_INTO_ACID(e) &&       \
218                                          Feld[x][y] == EL_ACID) ||      \
219                                         Feld[x][y] == EL_DIAMOND))
220
221 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
222                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
223                                         (CAN_MOVE_INTO_ACID(e) &&       \
224                                          Feld[x][y] == EL_ACID) ||      \
225                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
226
227 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
228                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
229                                         (CAN_MOVE_INTO_ACID(e) &&       \
230                                          Feld[x][y] == EL_ACID) ||      \
231                                         IS_AMOEBOID(Feld[x][y])))
232
233 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
234                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
235                                         (CAN_MOVE_INTO_ACID(e) &&       \
236                                          Feld[x][y] == EL_ACID) ||      \
237                                         IS_FOOD_PIG(Feld[x][y])))
238
239 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
240                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
241                                         (CAN_MOVE_INTO_ACID(e) &&       \
242                                          Feld[x][y] == EL_ACID) ||      \
243                                         IS_FOOD_PENGUIN(Feld[x][y]) ||  \
244                                         Feld[x][y] == EL_EXIT_OPEN))
245
246 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
247                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
248                                         (CAN_MOVE_INTO_ACID(e) &&       \
249                                          Feld[x][y] == EL_ACID)))
250
251 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
252                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
253                                         (CAN_MOVE_INTO_ACID(e) &&       \
254                                          Feld[x][y] == EL_ACID) ||      \
255                                         (condition)))
256
257 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
258                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
259                                         (CAN_MOVE_INTO_ACID(e) &&       \
260                                          Feld[x][y] == EL_ACID)))
261
262 #endif
263
264 #define GROUP_NR(e)             ((e) - EL_GROUP_START)
265 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
266 #define IS_IN_GROUP(e, nr)      (element_info[e].in_group[nr] == TRUE)
267 #define IS_IN_GROUP_EL(e, ge)   (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
268
269 #define IS_EQUAL_OR_IN_GROUP(e, ge)                                     \
270         (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
271
272 #if 0
273 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
274                 (!IS_PLAYER(x, y) &&                                    \
275                  (Feld[x][y] == EL_ACID ||                              \
276                   IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
277 #else
278 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
279                 (!IS_PLAYER(x, y) &&                                    \
280                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
281 #endif
282
283 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
284         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
285
286 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
287 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
288
289 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
290 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
291 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
292 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
293
294 /* game button identifiers */
295 #define GAME_CTRL_ID_STOP               0
296 #define GAME_CTRL_ID_PAUSE              1
297 #define GAME_CTRL_ID_PLAY               2
298 #define SOUND_CTRL_ID_MUSIC             3
299 #define SOUND_CTRL_ID_LOOPS             4
300 #define SOUND_CTRL_ID_SIMPLE            5
301
302 #define NUM_GAME_BUTTONS                6
303
304
305 /* forward declaration for internal use */
306
307 static void AdvanceFrameAndPlayerCounters(int);
308
309 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
310 static boolean MovePlayer(struct PlayerInfo *, int, int);
311 static void ScrollPlayer(struct PlayerInfo *, int);
312 static void ScrollScreen(struct PlayerInfo *, int);
313
314 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
315
316 static void InitBeltMovement(void);
317 static void CloseAllOpenTimegates(void);
318 static void CheckGravityMovement(struct PlayerInfo *);
319 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
320 static void KillHeroUnlessEnemyProtected(int, int);
321 static void KillHeroUnlessExplosionProtected(int, int);
322
323 static void TestIfPlayerTouchesCustomElement(int, int);
324 static void TestIfElementTouchesCustomElement(int, int);
325 static void TestIfElementHitsCustomElement(int, int, int);
326 #if 0
327 static void TestIfElementSmashesCustomElement(int, int, int);
328 #endif
329
330 static void ChangeElement(int, int, int);
331
332 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
333 #define CheckTriggeredElementChange(x, y, e, ev)                        \
334         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY,      \
335                                        CH_SIDE_ANY, -1)
336 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
337         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
338 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
339         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
340 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
341         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY,      \
342                                        CH_SIDE_ANY, p)
343
344 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
345 #define CheckElementChange(x, y, e, te, ev)                             \
346         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
347 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
348         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
349 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
350         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
351 #define CheckElementChangeByPage(x, y, e, te, ev, p)                    \
352         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
353
354 static void PlayLevelSound(int, int, int);
355 static void PlayLevelSoundNearest(int, int, int);
356 static void PlayLevelSoundAction(int, int, int);
357 static void PlayLevelSoundElementAction(int, int, int, int);
358 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
359 static void PlayLevelSoundActionIfLoop(int, int, int);
360 static void StopLevelSoundActionIfLoop(int, int, int);
361 static void PlayLevelMusic();
362
363 static void MapGameButtons();
364 static void HandleGameButtons(struct GadgetInfo *);
365
366 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
367
368
369 /* ------------------------------------------------------------------------- */
370 /* definition of elements that automatically change to other elements after  */
371 /* a specified time, eventually calling a function when changing             */
372 /* ------------------------------------------------------------------------- */
373
374 /* forward declaration for changer functions */
375 static void InitBuggyBase(int x, int y);
376 static void WarnBuggyBase(int x, int y);
377
378 static void InitTrap(int x, int y);
379 static void ActivateTrap(int x, int y);
380 static void ChangeActiveTrap(int x, int y);
381
382 static void InitRobotWheel(int x, int y);
383 static void RunRobotWheel(int x, int y);
384 static void StopRobotWheel(int x, int y);
385
386 static void InitTimegateWheel(int x, int y);
387 static void RunTimegateWheel(int x, int y);
388
389 struct ChangingElementInfo
390 {
391   int element;
392   int target_element;
393   int change_delay;
394   void (*pre_change_function)(int x, int y);
395   void (*change_function)(int x, int y);
396   void (*post_change_function)(int x, int y);
397 };
398
399 static struct ChangingElementInfo change_delay_list[] =
400 {
401   {
402     EL_NUT_BREAKING,
403     EL_EMERALD,
404     6,
405     NULL,
406     NULL,
407     NULL
408   },
409   {
410     EL_PEARL_BREAKING,
411     EL_EMPTY,
412     8,
413     NULL,
414     NULL,
415     NULL
416   },
417   {
418     EL_EXIT_OPENING,
419     EL_EXIT_OPEN,
420     29,
421     NULL,
422     NULL,
423     NULL
424   },
425   {
426     EL_EXIT_CLOSING,
427     EL_EXIT_CLOSED,
428     29,
429     NULL,
430     NULL,
431     NULL
432   },
433   {
434     EL_SP_EXIT_OPENING,
435     EL_SP_EXIT_OPEN,
436     29,
437     NULL,
438     NULL,
439     NULL
440   },
441   {
442     EL_SP_EXIT_CLOSING,
443     EL_SP_EXIT_CLOSED,
444     29,
445     NULL,
446     NULL,
447     NULL
448   },
449   {
450     EL_SWITCHGATE_OPENING,
451     EL_SWITCHGATE_OPEN,
452     29,
453     NULL,
454     NULL,
455     NULL
456   },
457   {
458     EL_SWITCHGATE_CLOSING,
459     EL_SWITCHGATE_CLOSED,
460     29,
461     NULL,
462     NULL,
463     NULL
464   },
465   {
466     EL_TIMEGATE_OPENING,
467     EL_TIMEGATE_OPEN,
468     29,
469     NULL,
470     NULL,
471     NULL
472   },
473   {
474     EL_TIMEGATE_CLOSING,
475     EL_TIMEGATE_CLOSED,
476     29,
477     NULL,
478     NULL,
479     NULL
480   },
481
482   {
483     EL_ACID_SPLASH_LEFT,
484     EL_EMPTY,
485     8,
486     NULL,
487     NULL,
488     NULL
489   },
490   {
491     EL_ACID_SPLASH_RIGHT,
492     EL_EMPTY,
493     8,
494     NULL,
495     NULL,
496     NULL
497   },
498   {
499     EL_SP_BUGGY_BASE,
500     EL_SP_BUGGY_BASE_ACTIVATING,
501     0,
502     InitBuggyBase,
503     NULL,
504     NULL
505   },
506   {
507     EL_SP_BUGGY_BASE_ACTIVATING,
508     EL_SP_BUGGY_BASE_ACTIVE,
509     0,
510     InitBuggyBase,
511     NULL,
512     NULL
513   },
514   {
515     EL_SP_BUGGY_BASE_ACTIVE,
516     EL_SP_BUGGY_BASE,
517     0,
518     InitBuggyBase,
519     WarnBuggyBase,
520     NULL
521   },
522   {
523     EL_TRAP,
524     EL_TRAP_ACTIVE,
525     0,
526     InitTrap,
527     NULL,
528     ActivateTrap
529   },
530   {
531     EL_TRAP_ACTIVE,
532     EL_TRAP,
533     31,
534     NULL,
535     ChangeActiveTrap,
536     NULL
537   },
538   {
539     EL_ROBOT_WHEEL_ACTIVE,
540     EL_ROBOT_WHEEL,
541     0,
542     InitRobotWheel,
543     RunRobotWheel,
544     StopRobotWheel
545   },
546   {
547     EL_TIMEGATE_SWITCH_ACTIVE,
548     EL_TIMEGATE_SWITCH,
549     0,
550     InitTimegateWheel,
551     RunTimegateWheel,
552     NULL
553   },
554
555   {
556     EL_UNDEFINED,
557     EL_UNDEFINED,
558     -1,
559     NULL,
560     NULL,
561     NULL
562   }
563 };
564
565 struct
566 {
567   int element;
568   int push_delay_fixed, push_delay_random;
569 }
570 push_delay_list[] =
571 {
572   { EL_SPRING,                  0, 0 },
573   { EL_BALLOON,                 0, 0 },
574
575   { EL_SOKOBAN_OBJECT,          2, 0 },
576   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
577   { EL_SATELLITE,               2, 0 },
578   { EL_SP_DISK_YELLOW,          2, 0 },
579
580   { EL_UNDEFINED,               0, 0 },
581 };
582
583 struct
584 {
585   int element;
586   int move_stepsize;
587 }
588 move_stepsize_list[] =
589 {
590   { EL_AMOEBA_DROP,             2 },
591   { EL_AMOEBA_DROPPING,         2 },
592   { EL_QUICKSAND_FILLING,       1 },
593   { EL_QUICKSAND_EMPTYING,      1 },
594   { EL_MAGIC_WALL_FILLING,      2 },
595   { EL_BD_MAGIC_WALL_FILLING,   2 },
596   { EL_MAGIC_WALL_EMPTYING,     2 },
597   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
598
599   { EL_UNDEFINED,               0 },
600 };
601
602 struct
603 {
604   int element;
605   int count;
606 }
607 collect_count_list[] =
608 {
609   { EL_EMERALD,                 1 },
610   { EL_BD_DIAMOND,              1 },
611   { EL_EMERALD_YELLOW,          1 },
612   { EL_EMERALD_RED,             1 },
613   { EL_EMERALD_PURPLE,          1 },
614   { EL_DIAMOND,                 3 },
615   { EL_SP_INFOTRON,             1 },
616   { EL_PEARL,                   5 },
617   { EL_CRYSTAL,                 8 },
618
619   { EL_UNDEFINED,               0 },
620 };
621
622 struct
623 {
624   int element;
625   int direction;
626 }
627 access_direction_list[] =
628 {
629   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
630   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
631   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
632   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
633   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
634   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
635   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
636   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
637   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
638   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
639   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
640
641   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
642   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
643   { EL_SP_PORT_UP,                                                   MV_DOWN },
644   { EL_SP_PORT_DOWN,                                         MV_UP           },
645   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
646   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
647   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
648   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
649   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
650   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
651   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
652   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
653   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
654   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
655   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
656   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
657   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
658   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
659   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
660
661   { EL_UNDEFINED,                       MV_NO_MOVING                         }
662 };
663
664 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
665
666 #define IS_AUTO_CHANGING(e)     (element_info[e].change_events & \
667                                  CH_EVENT_BIT(CE_DELAY))
668 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
669 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
670                                  IS_JUST_CHANGING(x, y))
671
672 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
673
674
675 void GetPlayerConfig()
676 {
677   if (!audio.sound_available)
678     setup.sound_simple = FALSE;
679
680   if (!audio.loops_available)
681     setup.sound_loops = FALSE;
682
683   if (!audio.music_available)
684     setup.sound_music = FALSE;
685
686   if (!video.fullscreen_available)
687     setup.fullscreen = FALSE;
688
689   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
690
691   SetAudioMode(setup.sound);
692   InitJoysticks();
693 }
694
695 static int getBeltNrFromBeltElement(int element)
696 {
697   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
698           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
699           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
700 }
701
702 static int getBeltNrFromBeltActiveElement(int element)
703 {
704   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
705           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
706           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
707 }
708
709 static int getBeltNrFromBeltSwitchElement(int element)
710 {
711   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
712           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
713           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
714 }
715
716 static int getBeltDirNrFromBeltSwitchElement(int element)
717 {
718   static int belt_base_element[4] =
719   {
720     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
721     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
722     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
723     EL_CONVEYOR_BELT_4_SWITCH_LEFT
724   };
725
726   int belt_nr = getBeltNrFromBeltSwitchElement(element);
727   int belt_dir_nr = element - belt_base_element[belt_nr];
728
729   return (belt_dir_nr % 3);
730 }
731
732 static int getBeltDirFromBeltSwitchElement(int element)
733 {
734   static int belt_move_dir[3] =
735   {
736     MV_LEFT,
737     MV_NO_MOVING,
738     MV_RIGHT
739   };
740
741   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
742
743   return belt_move_dir[belt_dir_nr];
744 }
745
746 static void InitPlayerField(int x, int y, int element, boolean init_game)
747 {
748   if (element == EL_SP_MURPHY)
749   {
750     if (init_game)
751     {
752       if (stored_player[0].present)
753       {
754         Feld[x][y] = EL_SP_MURPHY_CLONE;
755
756         return;
757       }
758       else
759       {
760         stored_player[0].use_murphy_graphic = TRUE;
761       }
762
763       Feld[x][y] = EL_PLAYER_1;
764     }
765   }
766
767   if (init_game)
768   {
769     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
770     int jx = player->jx, jy = player->jy;
771
772     player->present = TRUE;
773
774     player->block_last_field = (element == EL_SP_MURPHY ?
775                                 level.sp_block_last_field :
776                                 level.block_last_field);
777
778 #if USE_NEW_BLOCK_STYLE
779 #if 1
780     player->block_delay = (player->block_last_field ?
781                            (element == EL_SP_MURPHY ?
782                             level.sp_block_delay :
783                             level.block_delay) : 0);
784 #else
785     player->block_delay = (element == EL_SP_MURPHY ?
786                            (player->block_last_field ? 7 : 1) :
787                            (player->block_last_field ? 7 : 1));
788 #endif
789
790 #if 0
791     printf("::: block_last_field == %d, block_delay = %d\n",
792            player->block_last_field, player->block_delay);
793 #endif
794 #endif
795
796     if (!options.network || player->connected)
797     {
798       player->active = TRUE;
799
800       /* remove potentially duplicate players */
801       if (StorePlayer[jx][jy] == Feld[x][y])
802         StorePlayer[jx][jy] = 0;
803
804       StorePlayer[x][y] = Feld[x][y];
805
806       if (options.debug)
807       {
808         printf("Player %d activated.\n", player->element_nr);
809         printf("[Local player is %d and currently %s.]\n",
810                local_player->element_nr,
811                local_player->active ? "active" : "not active");
812       }
813     }
814
815     Feld[x][y] = EL_EMPTY;
816
817     player->jx = player->last_jx = x;
818     player->jy = player->last_jy = y;
819   }
820 }
821
822 static void InitField(int x, int y, boolean init_game)
823 {
824   int element = Feld[x][y];
825
826   switch (element)
827   {
828     case EL_SP_MURPHY:
829     case EL_PLAYER_1:
830     case EL_PLAYER_2:
831     case EL_PLAYER_3:
832     case EL_PLAYER_4:
833       InitPlayerField(x, y, element, init_game);
834       break;
835
836     case EL_SOKOBAN_FIELD_PLAYER:
837       element = Feld[x][y] = EL_PLAYER_1;
838       InitField(x, y, init_game);
839
840       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
841       InitField(x, y, init_game);
842       break;
843
844     case EL_SOKOBAN_FIELD_EMPTY:
845       local_player->sokobanfields_still_needed++;
846       break;
847
848     case EL_STONEBLOCK:
849       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
850         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
851       else if (x > 0 && Feld[x-1][y] == EL_ACID)
852         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
853       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
854         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
855       else if (y > 0 && Feld[x][y-1] == EL_ACID)
856         Feld[x][y] = EL_ACID_POOL_BOTTOM;
857       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
858         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
859       break;
860
861     case EL_BUG_RIGHT:
862     case EL_BUG_UP:
863     case EL_BUG_LEFT:
864     case EL_BUG_DOWN:
865     case EL_BUG:
866     case EL_SPACESHIP_RIGHT:
867     case EL_SPACESHIP_UP:
868     case EL_SPACESHIP_LEFT:
869     case EL_SPACESHIP_DOWN:
870     case EL_SPACESHIP:
871     case EL_BD_BUTTERFLY_RIGHT:
872     case EL_BD_BUTTERFLY_UP:
873     case EL_BD_BUTTERFLY_LEFT:
874     case EL_BD_BUTTERFLY_DOWN:
875     case EL_BD_BUTTERFLY:
876     case EL_BD_FIREFLY_RIGHT:
877     case EL_BD_FIREFLY_UP:
878     case EL_BD_FIREFLY_LEFT:
879     case EL_BD_FIREFLY_DOWN:
880     case EL_BD_FIREFLY:
881     case EL_PACMAN_RIGHT:
882     case EL_PACMAN_UP:
883     case EL_PACMAN_LEFT:
884     case EL_PACMAN_DOWN:
885     case EL_YAMYAM:
886     case EL_DARK_YAMYAM:
887     case EL_ROBOT:
888     case EL_PACMAN:
889     case EL_SP_SNIKSNAK:
890     case EL_SP_ELECTRON:
891     case EL_MOLE_LEFT:
892     case EL_MOLE_RIGHT:
893     case EL_MOLE_UP:
894     case EL_MOLE_DOWN:
895     case EL_MOLE:
896       InitMovDir(x, y);
897       break;
898
899     case EL_AMOEBA_FULL:
900     case EL_BD_AMOEBA:
901       InitAmoebaNr(x, y);
902       break;
903
904     case EL_AMOEBA_DROP:
905       if (y == lev_fieldy - 1)
906       {
907         Feld[x][y] = EL_AMOEBA_GROWING;
908         Store[x][y] = EL_AMOEBA_WET;
909       }
910       break;
911
912     case EL_DYNAMITE_ACTIVE:
913     case EL_SP_DISK_RED_ACTIVE:
914     case EL_DYNABOMB_PLAYER_1_ACTIVE:
915     case EL_DYNABOMB_PLAYER_2_ACTIVE:
916     case EL_DYNABOMB_PLAYER_3_ACTIVE:
917     case EL_DYNABOMB_PLAYER_4_ACTIVE:
918       MovDelay[x][y] = 96;
919       break;
920
921     case EL_LAMP:
922       local_player->lights_still_needed++;
923       break;
924
925     case EL_PENGUIN:
926       local_player->friends_still_needed++;
927       break;
928
929     case EL_PIG:
930     case EL_DRAGON:
931       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
932       break;
933
934 #if 0
935     case EL_SP_EMPTY:
936       Feld[x][y] = EL_EMPTY;
937       break;
938 #endif
939
940 #if 0
941     case EL_EM_KEY_1_FILE:
942       Feld[x][y] = EL_EM_KEY_1;
943       break;
944     case EL_EM_KEY_2_FILE:
945       Feld[x][y] = EL_EM_KEY_2;
946       break;
947     case EL_EM_KEY_3_FILE:
948       Feld[x][y] = EL_EM_KEY_3;
949       break;
950     case EL_EM_KEY_4_FILE:
951       Feld[x][y] = EL_EM_KEY_4;
952       break;
953 #endif
954
955     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
956     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
957     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
958     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
959     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
960     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
961     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
962     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
963     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
964     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
965     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
966     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
967       if (init_game)
968       {
969         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
970         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
971         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
972
973         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
974         {
975           game.belt_dir[belt_nr] = belt_dir;
976           game.belt_dir_nr[belt_nr] = belt_dir_nr;
977         }
978         else    /* more than one switch -- set it like the first switch */
979         {
980           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
981         }
982       }
983       break;
984
985     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
986       if (init_game)
987         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
988       break;
989
990     case EL_LIGHT_SWITCH_ACTIVE:
991       if (init_game)
992         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
993       break;
994
995     default:
996       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
997         InitMovDir(x, y);
998       else if (IS_GROUP_ELEMENT(element))
999       {
1000         struct ElementGroupInfo *group = element_info[element].group;
1001         int last_anim_random_frame = gfx.anim_random_frame;
1002         int element_pos;
1003
1004         if (group->choice_mode == ANIM_RANDOM)
1005           gfx.anim_random_frame = RND(group->num_elements_resolved);
1006
1007         element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1008                                         group->choice_mode, 0,
1009                                         group->choice_pos);
1010
1011         if (group->choice_mode == ANIM_RANDOM)
1012           gfx.anim_random_frame = last_anim_random_frame;
1013
1014         group->choice_pos++;
1015
1016         Feld[x][y] = group->element_resolved[element_pos];
1017
1018         InitField(x, y, init_game);
1019       }
1020       break;
1021   }
1022 }
1023
1024 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1025 {
1026   InitField(x, y, init_game);
1027
1028   /* not needed to call InitMovDir() -- already done by InitField()! */
1029   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1030       CAN_MOVE(Feld[x][y]))
1031     InitMovDir(x, y);
1032 }
1033
1034 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1035 {
1036   int old_element = Feld[x][y];
1037
1038   InitField(x, y, init_game);
1039
1040   /* not needed to call InitMovDir() -- already done by InitField()! */
1041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1042       CAN_MOVE(old_element) &&
1043       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1044     InitMovDir(x, y);
1045
1046   /* this case is in fact a combination of not less than three bugs:
1047      first, it calls InitMovDir() for elements that can move, although this is
1048      already done by InitField(); then, it checks the element that was at this
1049      field _before_ the call to InitField() (which can change it); lastly, it
1050      was not called for "mole with direction" elements, which were treated as
1051      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1052   */
1053 }
1054
1055 inline void DrawGameValue_Emeralds(int value)
1056 {
1057   DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1058 }
1059
1060 inline void DrawGameValue_Dynamite(int value)
1061 {
1062   DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1063 }
1064
1065 inline void DrawGameValue_Keys(struct PlayerInfo *player)
1066 {
1067   int i;
1068
1069   for (i = 0; i < MAX_KEYS; i++)
1070     if (player->key[i])
1071       DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1072                          el2edimg(EL_KEY_1 + i));
1073 }
1074
1075 inline void DrawGameValue_Score(int value)
1076 {
1077   DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1078 }
1079
1080 inline void DrawGameValue_Time(int value)
1081 {
1082   if (value < 1000)
1083     DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1084   else
1085     DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1086 }
1087
1088 inline void DrawGameValue_Level(int value)
1089 {
1090   if (level_nr < 100)
1091     DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1092   else
1093   {
1094     /* misuse area for displaying emeralds to draw bigger level number */
1095     DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1096                 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1097
1098     /* now copy it to the area for displaying level number */
1099     BlitBitmap(drawto, drawto,
1100                DX_EMERALDS, DY_EMERALDS + 1,
1101                getFontWidth(FONT_LEVEL_NUMBER) * 3,
1102                getFontHeight(FONT_LEVEL_NUMBER) - 1,
1103                DX_LEVEL - 1, DY_LEVEL + 1);
1104
1105     /* restore the area for displaying emeralds */
1106     DrawGameValue_Emeralds(local_player->gems_still_needed);
1107
1108     /* yes, this is all really ugly :-) */
1109   }
1110 }
1111
1112 void DrawGameDoorValues()
1113 {
1114   int i;
1115
1116   DrawGameValue_Level(level_nr);
1117
1118   for (i = 0; i < MAX_PLAYERS; i++)
1119     DrawGameValue_Keys(&stored_player[i]);
1120
1121   DrawGameValue_Emeralds(local_player->gems_still_needed);
1122   DrawGameValue_Dynamite(local_player->inventory_size);
1123   DrawGameValue_Score(local_player->score);
1124   DrawGameValue_Time(TimeLeft);
1125 }
1126
1127 void DrawGameDoorValues_EM(int emeralds, int dynamite, int score, int time)
1128 {
1129   DrawGameValue_Emeralds(emeralds);
1130   DrawGameValue_Dynamite(dynamite);
1131   DrawGameValue_Score(score);
1132   DrawGameValue_Time(time);
1133 }
1134
1135 static void resolve_group_element(int group_element, int recursion_depth)
1136 {
1137   static int group_nr;
1138   static struct ElementGroupInfo *group;
1139   struct ElementGroupInfo *actual_group = element_info[group_element].group;
1140   int i;
1141
1142   if (recursion_depth > NUM_GROUP_ELEMENTS)     /* recursion too deep */
1143   {
1144     Error(ERR_WARN, "recursion too deep when resolving group element %d",
1145           group_element - EL_GROUP_START + 1);
1146
1147     /* replace element which caused too deep recursion by question mark */
1148     group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1149
1150     return;
1151   }
1152
1153   if (recursion_depth == 0)                     /* initialization */
1154   {
1155     group = element_info[group_element].group;
1156     group_nr = group_element - EL_GROUP_START;
1157
1158     group->num_elements_resolved = 0;
1159     group->choice_pos = 0;
1160   }
1161
1162   for (i = 0; i < actual_group->num_elements; i++)
1163   {
1164     int element = actual_group->element[i];
1165
1166     if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1167       break;
1168
1169     if (IS_GROUP_ELEMENT(element))
1170       resolve_group_element(element, recursion_depth + 1);
1171     else
1172     {
1173       group->element_resolved[group->num_elements_resolved++] = element;
1174       element_info[element].in_group[group_nr] = TRUE;
1175     }
1176   }
1177
1178 #if 0
1179   if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1180   {
1181     printf("::: group %d: %d resolved elements\n",
1182            group_element - EL_GROUP_START, group->num_elements_resolved);
1183     for (i = 0; i < group->num_elements_resolved; i++)
1184       printf("::: - %d ['%s']\n", group->element_resolved[i],
1185              element_info[group->element_resolved[i]].token_name);
1186   }
1187 #endif
1188 }
1189
1190
1191 /*
1192   =============================================================================
1193   InitGameEngine()
1194   -----------------------------------------------------------------------------
1195   initialize game engine due to level / tape version number
1196   =============================================================================
1197 */
1198
1199 static void InitGameEngine()
1200 {
1201   int i, j, k;
1202
1203   /* set game engine from tape file when re-playing, else from level file */
1204   game.engine_version = (tape.playing ? tape.engine_version :
1205                          level.game_version);
1206
1207   /* ---------------------------------------------------------------------- */
1208   /* set flags for bugs and changes according to active game engine version */
1209   /* ---------------------------------------------------------------------- */
1210
1211   /*
1212     Type of bug/change:
1213     Before 3.1.0, custom elements that "change when pushing" changed directly
1214     after the player started pushing them (until then handled in "DigField()").
1215     Since 3.1.0, these custom elements are not changed until the "pushing"
1216     move of the element is finished (now handled in "ContinueMoving()").
1217
1218     Affected levels/tapes:
1219     The first condition is generally needed for all levels/tapes before version
1220     3.1.0, which might use the old behaviour before it was changed; known tapes
1221     that are affected are some tapes from the level set "Walpurgis Gardens" by
1222     Jamie Cullen.
1223     The second condition is an exception from the above case and is needed for
1224     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1225     above (including some development versions of 3.1.0), but before it was
1226     known that this change would break tapes like the above and was fixed in
1227     3.1.1, so that the changed behaviour was active although the engine version
1228     while recording maybe was before 3.1.0. There is at least one tape that is
1229     affected by this exception, which is the tape for the one-level set "Bug
1230     Machine" by Juergen Bonhagen.
1231   */
1232
1233   game.use_bug_change_when_pushing =
1234     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1235      !(tape.playing &&
1236        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1237        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1238
1239   /* ---------------------------------------------------------------------- */
1240
1241   /* dynamically adjust element properties according to game engine version */
1242   InitElementPropertiesEngine(game.engine_version);
1243
1244 #if 0
1245   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1246   printf("          tape version == %06d [%s] [file: %06d]\n",
1247          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1248          tape.file_version);
1249   printf("       => game.engine_version == %06d\n", game.engine_version);
1250 #endif
1251
1252   /* ---------- recursively resolve group elements ------------------------- */
1253
1254   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1255     for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1256       element_info[i].in_group[j] = FALSE;
1257
1258   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1259     resolve_group_element(EL_GROUP_START + i, 0);
1260
1261   /* ---------- initialize player's initial move delay --------------------- */
1262
1263 #if USE_NEW_MOVE_DELAY
1264   /* dynamically adjust player properties according to level information */
1265   game.initial_move_delay_value =
1266     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1267
1268   /* dynamically adjust player properties according to game engine version */
1269   game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1270                              game.initial_move_delay_value : 0);
1271 #else
1272   /* dynamically adjust player properties according to game engine version */
1273   game.initial_move_delay =
1274     (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1275      INITIAL_MOVE_DELAY_OFF);
1276
1277   /* dynamically adjust player properties according to level information */
1278   game.initial_move_delay_value =
1279     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1280 #endif
1281
1282   /* ---------- initialize player's initial push delay --------------------- */
1283
1284   /* dynamically adjust player properties according to game engine version */
1285   game.initial_push_delay_value =
1286     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1287
1288   /* ---------- initialize changing elements ------------------------------- */
1289
1290   /* initialize changing elements information */
1291   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1292   {
1293     struct ElementInfo *ei = &element_info[i];
1294
1295     /* this pointer might have been changed in the level editor */
1296     ei->change = &ei->change_page[0];
1297
1298     if (!IS_CUSTOM_ELEMENT(i))
1299     {
1300       ei->change->target_element = EL_EMPTY_SPACE;
1301       ei->change->delay_fixed = 0;
1302       ei->change->delay_random = 0;
1303       ei->change->delay_frames = 1;
1304     }
1305
1306     ei->change_events = CE_BITMASK_DEFAULT;
1307     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1308     {
1309       ei->event_page_nr[j] = 0;
1310       ei->event_page[j] = &ei->change_page[0];
1311     }
1312   }
1313
1314   /* add changing elements from pre-defined list */
1315   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1316   {
1317     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1318     struct ElementInfo *ei = &element_info[ch_delay->element];
1319
1320     ei->change->target_element       = ch_delay->target_element;
1321     ei->change->delay_fixed          = ch_delay->change_delay;
1322
1323     ei->change->pre_change_function  = ch_delay->pre_change_function;
1324     ei->change->change_function      = ch_delay->change_function;
1325     ei->change->post_change_function = ch_delay->post_change_function;
1326
1327     ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1328
1329 #if 1
1330     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1331 #endif
1332   }
1333
1334 #if 1
1335   /* add change events from custom element configuration */
1336   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1337   {
1338     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1339
1340     for (j = 0; j < ei->num_change_pages; j++)
1341     {
1342       if (!ei->change_page[j].can_change)
1343         continue;
1344
1345       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1346       {
1347         /* only add event page for the first page found with this event */
1348         if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1349             !(ei->change_events & CH_EVENT_BIT(k)))
1350         {
1351           ei->change_events |= CH_EVENT_BIT(k);
1352           ei->event_page_nr[k] = j;
1353           ei->event_page[k] = &ei->change_page[j];
1354         }
1355       }
1356     }
1357   }
1358
1359 #else
1360
1361   /* add change events from custom element configuration */
1362   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1363   {
1364     int element = EL_CUSTOM_START + i;
1365
1366     /* only add custom elements that change after fixed/random frame delay */
1367     if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1368       element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1369   }
1370 #endif
1371
1372   /* ---------- initialize run-time trigger player and element ------------- */
1373
1374   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1375   {
1376     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1377
1378     for (j = 0; j < ei->num_change_pages; j++)
1379     {
1380       ei->change_page[j].actual_trigger_element = EL_EMPTY;
1381       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1382     }
1383   }
1384
1385   /* ---------- initialize trigger events ---------------------------------- */
1386
1387   /* initialize trigger events information */
1388   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1389     trigger_events[i] = EP_BITMASK_DEFAULT;
1390
1391 #if 1
1392   /* add trigger events from element change event properties */
1393   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1394   {
1395     struct ElementInfo *ei = &element_info[i];
1396
1397     for (j = 0; j < ei->num_change_pages; j++)
1398     {
1399       if (!ei->change_page[j].can_change)
1400         continue;
1401
1402       if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1403       {
1404         int trigger_element = ei->change_page[j].trigger_element;
1405
1406         if (IS_GROUP_ELEMENT(trigger_element))
1407         {
1408           struct ElementGroupInfo *group = element_info[trigger_element].group;
1409
1410           for (k = 0; k < group->num_elements_resolved; k++)
1411             trigger_events[group->element_resolved[k]]
1412               |= ei->change_page[j].events;
1413         }
1414         else
1415           trigger_events[trigger_element] |= ei->change_page[j].events;
1416       }
1417     }
1418   }
1419 #else
1420   /* add trigger events from element change event properties */
1421   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1422     if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1423       trigger_events[element_info[i].change->trigger_element] |=
1424         element_info[i].change->events;
1425 #endif
1426
1427   /* ---------- initialize push delay -------------------------------------- */
1428
1429   /* initialize push delay values to default */
1430   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1431   {
1432     if (!IS_CUSTOM_ELEMENT(i))
1433     {
1434       element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
1435       element_info[i].push_delay_random = game.default_push_delay_random;
1436     }
1437   }
1438
1439   /* set push delay value for certain elements from pre-defined list */
1440   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1441   {
1442     int e = push_delay_list[i].element;
1443
1444     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1445     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1446   }
1447
1448   /* set push delay value for Supaplex elements for newer engine versions */
1449   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1450   {
1451     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1452     {
1453       if (IS_SP_ELEMENT(i))
1454       {
1455 #if USE_NEW_MOVE_STYLE
1456         /* set SP push delay to just enough to push under a falling zonk */
1457         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1458
1459         element_info[i].push_delay_fixed  = delay;
1460         element_info[i].push_delay_random = 0;
1461 #else
1462         element_info[i].push_delay_fixed  = 6;  /* just enough to escape ... */
1463         element_info[i].push_delay_random = 0;  /* ... from falling zonk     */
1464 #endif
1465       }
1466     }
1467   }
1468
1469   /* ---------- initialize move stepsize ----------------------------------- */
1470
1471   /* initialize move stepsize values to default */
1472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1473     if (!IS_CUSTOM_ELEMENT(i))
1474       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1475
1476   /* set move stepsize value for certain elements from pre-defined list */
1477   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1478   {
1479     int e = move_stepsize_list[i].element;
1480
1481     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1482   }
1483
1484 #if 0
1485   /* ---------- initialize move dig/leave ---------------------------------- */
1486
1487   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1488   {
1489     element_info[i].can_leave_element = FALSE;
1490     element_info[i].can_leave_element_last = FALSE;
1491   }
1492 #endif
1493
1494   /* ---------- initialize gem count --------------------------------------- */
1495
1496   /* initialize gem count values for each element */
1497   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1498     if (!IS_CUSTOM_ELEMENT(i))
1499       element_info[i].collect_count = 0;
1500
1501   /* add gem count values for all elements from pre-defined list */
1502   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1503     element_info[collect_count_list[i].element].collect_count =
1504       collect_count_list[i].count;
1505
1506   /* ---------- initialize access direction -------------------------------- */
1507
1508   /* initialize access direction values to default (access from every side) */
1509   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1510     if (!IS_CUSTOM_ELEMENT(i))
1511       element_info[i].access_direction = MV_ALL_DIRECTIONS;
1512
1513   /* set access direction value for certain elements from pre-defined list */
1514   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1515     element_info[access_direction_list[i].element].access_direction =
1516       access_direction_list[i].direction;
1517 }
1518
1519
1520 /*
1521   =============================================================================
1522   InitGame()
1523   -----------------------------------------------------------------------------
1524   initialize and start new game
1525   =============================================================================
1526 */
1527
1528 void InitGame()
1529 {
1530   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
1531   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
1532   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
1533   int i, j, k, x, y;
1534
1535   InitGameEngine();
1536
1537 #if 0
1538 #if DEBUG
1539 #if USE_NEW_AMOEBA_CODE
1540   printf("Using new amoeba code.\n");
1541 #else
1542   printf("Using old amoeba code.\n");
1543 #endif
1544 #endif
1545 #endif
1546
1547   /* don't play tapes over network */
1548   network_playing = (options.network && !tape.playing);
1549
1550   for (i = 0; i < MAX_PLAYERS; i++)
1551   {
1552     struct PlayerInfo *player = &stored_player[i];
1553
1554     player->index_nr = i;
1555     player->index_bit = (1 << i);
1556     player->element_nr = EL_PLAYER_1 + i;
1557
1558     player->present = FALSE;
1559     player->active = FALSE;
1560
1561     player->action = 0;
1562     player->effective_action = 0;
1563     player->programmed_action = 0;
1564
1565     player->score = 0;
1566     player->gems_still_needed = level.gems_needed;
1567     player->sokobanfields_still_needed = 0;
1568     player->lights_still_needed = 0;
1569     player->friends_still_needed = 0;
1570
1571     for (j = 0; j < MAX_KEYS; j++)
1572       player->key[j] = FALSE;
1573
1574     player->dynabomb_count = 0;
1575     player->dynabomb_size = 1;
1576     player->dynabombs_left = 0;
1577     player->dynabomb_xl = FALSE;
1578
1579     player->MovDir = MV_NO_MOVING;
1580     player->MovPos = 0;
1581     player->GfxPos = 0;
1582     player->GfxDir = MV_NO_MOVING;
1583     player->GfxAction = ACTION_DEFAULT;
1584     player->Frame = 0;
1585     player->StepFrame = 0;
1586
1587     player->use_murphy_graphic = FALSE;
1588
1589     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
1590     player->block_delay = -1;           /* initialized in InitPlayerField() */
1591
1592     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1593
1594     player->actual_frame_counter = 0;
1595
1596     player->step_counter = 0;
1597
1598     player->last_move_dir = MV_NO_MOVING;
1599
1600     player->is_waiting = FALSE;
1601     player->is_moving = FALSE;
1602     player->is_auto_moving = FALSE;
1603     player->is_digging = FALSE;
1604     player->is_snapping = FALSE;
1605     player->is_collecting = FALSE;
1606     player->is_pushing = FALSE;
1607     player->is_switching = FALSE;
1608     player->is_dropping = FALSE;
1609
1610     player->is_bored = FALSE;
1611     player->is_sleeping = FALSE;
1612
1613     player->frame_counter_bored = -1;
1614     player->frame_counter_sleeping = -1;
1615
1616     player->anim_delay_counter = 0;
1617     player->post_delay_counter = 0;
1618
1619     player->action_waiting = ACTION_DEFAULT;
1620     player->last_action_waiting = ACTION_DEFAULT;
1621     player->special_action_bored = ACTION_DEFAULT;
1622     player->special_action_sleeping = ACTION_DEFAULT;
1623
1624     player->num_special_action_bored = 0;
1625     player->num_special_action_sleeping = 0;
1626
1627     /* determine number of special actions for bored and sleeping animation */
1628     for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1629     {
1630       boolean found = FALSE;
1631
1632       for (k = 0; k < NUM_DIRECTIONS; k++)
1633         if (el_act_dir2img(player->element_nr, j, k) !=
1634             el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1635           found = TRUE;
1636
1637       if (found)
1638         player->num_special_action_bored++;
1639       else
1640         break;
1641     }
1642     for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1643     {
1644       boolean found = FALSE;
1645
1646       for (k = 0; k < NUM_DIRECTIONS; k++)
1647         if (el_act_dir2img(player->element_nr, j, k) !=
1648             el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1649           found = TRUE;
1650
1651       if (found)
1652         player->num_special_action_sleeping++;
1653       else
1654         break;
1655     }
1656
1657     player->switch_x = -1;
1658     player->switch_y = -1;
1659
1660     player->show_envelope = 0;
1661
1662     player->move_delay       = game.initial_move_delay;
1663     player->move_delay_value = game.initial_move_delay_value;
1664
1665     player->move_delay_reset_counter = 0;
1666
1667 #if USE_NEW_PUSH_DELAY
1668     player->push_delay       = -1;      /* initialized when pushing starts */
1669     player->push_delay_value = game.initial_push_delay_value;
1670 #else
1671     player->push_delay       = 0;
1672     player->push_delay_value = game.initial_push_delay_value;
1673 #endif
1674
1675     player->drop_delay = 0;
1676
1677     player->last_jx = player->last_jy = 0;
1678     player->jx = player->jy = 0;
1679
1680     player->shield_normal_time_left = 0;
1681     player->shield_deadly_time_left = 0;
1682
1683     player->inventory_infinite_element = EL_UNDEFINED;
1684     player->inventory_size = 0;
1685
1686     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1687     SnapField(player, 0, 0);
1688
1689     player->LevelSolved = FALSE;
1690     player->GameOver = FALSE;
1691   }
1692
1693   network_player_action_received = FALSE;
1694
1695 #if defined(NETWORK_AVALIABLE)
1696   /* initial null action */
1697   if (network_playing)
1698     SendToServer_MovePlayer(MV_NO_MOVING);
1699 #endif
1700
1701   ZX = ZY = -1;
1702
1703   FrameCounter = 0;
1704   TimeFrames = 0;
1705   TimePlayed = 0;
1706   TimeLeft = level.time;
1707   TapeTime = 0;
1708
1709   ScreenMovDir = MV_NO_MOVING;
1710   ScreenMovPos = 0;
1711   ScreenGfxPos = 0;
1712
1713   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
1714
1715   AllPlayersGone = FALSE;
1716
1717   game.yamyam_content_nr = 0;
1718   game.magic_wall_active = FALSE;
1719   game.magic_wall_time_left = 0;
1720   game.light_time_left = 0;
1721   game.timegate_time_left = 0;
1722   game.switchgate_pos = 0;
1723   game.balloon_dir = MV_NO_MOVING;
1724   game.gravity = level.initial_gravity;
1725   game.explosions_delayed = TRUE;
1726
1727   game.envelope_active = FALSE;
1728
1729   for (i = 0; i < NUM_BELTS; i++)
1730   {
1731     game.belt_dir[i] = MV_NO_MOVING;
1732     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
1733   }
1734
1735   for (i = 0; i < MAX_NUM_AMOEBA; i++)
1736     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1737
1738   for (x = 0; x < lev_fieldx; x++)
1739   {
1740     for (y = 0; y < lev_fieldy; y++)
1741     {
1742       Feld[x][y] = level.field[x][y];
1743       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1744       ChangeDelay[x][y] = 0;
1745       ChangePage[x][y] = -1;
1746       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1747       AmoebaNr[x][y] = 0;
1748       WasJustMoving[x][y] = 0;
1749       WasJustFalling[x][y] = 0;
1750       CheckCollision[x][y] = 0;
1751       Stop[x][y] = FALSE;
1752       Pushed[x][y] = FALSE;
1753
1754       Changed[x][y] = CE_BITMASK_DEFAULT;
1755       ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1756
1757       ExplodePhase[x][y] = 0;
1758       ExplodeDelay[x][y] = 0;
1759       ExplodeField[x][y] = EX_TYPE_NONE;
1760
1761       RunnerVisit[x][y] = 0;
1762       PlayerVisit[x][y] = 0;
1763
1764       GfxFrame[x][y] = 0;
1765       GfxRandom[x][y] = INIT_GFX_RANDOM();
1766       GfxElement[x][y] = EL_UNDEFINED;
1767       GfxAction[x][y] = ACTION_DEFAULT;
1768       GfxDir[x][y] = MV_NO_MOVING;
1769     }
1770   }
1771
1772   for (y = 0; y < lev_fieldy; y++)
1773   {
1774     for (x = 0; x < lev_fieldx; x++)
1775     {
1776       if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1777         emulate_bd = FALSE;
1778       if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1779         emulate_sb = FALSE;
1780       if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1781         emulate_sp = FALSE;
1782
1783       InitField(x, y, TRUE);
1784     }
1785   }
1786
1787   InitBeltMovement();
1788
1789   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1790                     emulate_sb ? EMU_SOKOBAN :
1791                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1792
1793   /* initialize explosion and ignition delay */
1794   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1795   {
1796     if (!IS_CUSTOM_ELEMENT(i))
1797     {
1798       int num_phase = 8;
1799       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1800                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1801                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
1802       int last_phase = (num_phase + 1) * delay;
1803       int half_phase = (num_phase / 2) * delay;
1804
1805       element_info[i].explosion_delay = last_phase - 1;
1806       element_info[i].ignition_delay = half_phase;
1807
1808 #if 0
1809       if (i == EL_BLACK_ORB)
1810         element_info[i].ignition_delay = 0;
1811 #else
1812       if (i == EL_BLACK_ORB)
1813         element_info[i].ignition_delay = 1;
1814 #endif
1815     }
1816
1817 #if 0
1818     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
1819       element_info[i].explosion_delay = 1;
1820
1821     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
1822       element_info[i].ignition_delay = 1;
1823 #endif
1824   }
1825
1826   /* correct non-moving belts to start moving left */
1827   for (i = 0; i < NUM_BELTS; i++)
1828     if (game.belt_dir[i] == MV_NO_MOVING)
1829       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
1830
1831   /* check if any connected player was not found in playfield */
1832   for (i = 0; i < MAX_PLAYERS; i++)
1833   {
1834     struct PlayerInfo *player = &stored_player[i];
1835
1836     if (player->connected && !player->present)
1837     {
1838       for (j = 0; j < MAX_PLAYERS; j++)
1839       {
1840         struct PlayerInfo *some_player = &stored_player[j];
1841         int jx = some_player->jx, jy = some_player->jy;
1842
1843         /* assign first free player found that is present in the playfield */
1844         if (some_player->present && !some_player->connected)
1845         {
1846           player->present = TRUE;
1847           player->active = TRUE;
1848
1849           some_player->present = FALSE;
1850           some_player->active = FALSE;
1851
1852 #if 0
1853           player->element_nr = some_player->element_nr;
1854 #endif
1855
1856 #if USE_NEW_BLOCK_STYLE
1857           player->block_last_field = some_player->block_last_field;
1858           player->block_delay = some_player->block_delay;
1859 #endif
1860
1861           StorePlayer[jx][jy] = player->element_nr;
1862           player->jx = player->last_jx = jx;
1863           player->jy = player->last_jy = jy;
1864
1865           break;
1866         }
1867       }
1868     }
1869   }
1870
1871   if (tape.playing)
1872   {
1873     /* when playing a tape, eliminate all players which do not participate */
1874
1875     for (i = 0; i < MAX_PLAYERS; i++)
1876     {
1877       if (stored_player[i].active && !tape.player_participates[i])
1878       {
1879         struct PlayerInfo *player = &stored_player[i];
1880         int jx = player->jx, jy = player->jy;
1881
1882         player->active = FALSE;
1883         StorePlayer[jx][jy] = 0;
1884         Feld[jx][jy] = EL_EMPTY;
1885       }
1886     }
1887   }
1888   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
1889   {
1890     /* when in single player mode, eliminate all but the first active player */
1891
1892     for (i = 0; i < MAX_PLAYERS; i++)
1893     {
1894       if (stored_player[i].active)
1895       {
1896         for (j = i + 1; j < MAX_PLAYERS; j++)
1897         {
1898           if (stored_player[j].active)
1899           {
1900             struct PlayerInfo *player = &stored_player[j];
1901             int jx = player->jx, jy = player->jy;
1902
1903             player->active = FALSE;
1904             player->present = FALSE;
1905
1906             StorePlayer[jx][jy] = 0;
1907             Feld[jx][jy] = EL_EMPTY;
1908           }
1909         }
1910       }
1911     }
1912   }
1913
1914   /* when recording the game, store which players take part in the game */
1915   if (tape.recording)
1916   {
1917     for (i = 0; i < MAX_PLAYERS; i++)
1918       if (stored_player[i].active)
1919         tape.player_participates[i] = TRUE;
1920   }
1921
1922   if (options.debug)
1923   {
1924     for (i = 0; i < MAX_PLAYERS; i++)
1925     {
1926       struct PlayerInfo *player = &stored_player[i];
1927
1928       printf("Player %d: present == %d, connected == %d, active == %d.\n",
1929              i+1,
1930              player->present,
1931              player->connected,
1932              player->active);
1933       if (local_player == player)
1934         printf("Player  %d is local player.\n", i+1);
1935     }
1936   }
1937
1938   if (BorderElement == EL_EMPTY)
1939   {
1940     SBX_Left = 0;
1941     SBX_Right = lev_fieldx - SCR_FIELDX;
1942     SBY_Upper = 0;
1943     SBY_Lower = lev_fieldy - SCR_FIELDY;
1944   }
1945   else
1946   {
1947     SBX_Left = -1;
1948     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1949     SBY_Upper = -1;
1950     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1951   }
1952
1953   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1954     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1955
1956   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1957     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1958
1959   /* if local player not found, look for custom element that might create
1960      the player (make some assumptions about the right custom element) */
1961   if (!local_player->present)
1962   {
1963     int start_x = 0, start_y = 0;
1964     int found_rating = 0;
1965     int found_element = EL_UNDEFINED;
1966
1967     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1968     {
1969       int element = Feld[x][y];
1970       int content;
1971       int xx, yy;
1972       boolean is_player;
1973
1974       if (!IS_CUSTOM_ELEMENT(element))
1975         continue;
1976
1977       if (CAN_CHANGE(element))
1978       {
1979         for (i = 0; i < element_info[element].num_change_pages; i++)
1980         {
1981           content = element_info[element].change_page[i].target_element;
1982           is_player = ELEM_IS_PLAYER(content);
1983
1984           if (is_player && (found_rating < 3 || element < found_element))
1985           {
1986             start_x = x;
1987             start_y = y;
1988
1989             found_rating = 3;
1990             found_element = element;
1991           }
1992         }
1993       }
1994
1995       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1996       {
1997         content = element_info[element].content[xx][yy];
1998         is_player = ELEM_IS_PLAYER(content);
1999
2000         if (is_player && (found_rating < 2 || element < found_element))
2001         {
2002           start_x = x + xx - 1;
2003           start_y = y + yy - 1;
2004
2005           found_rating = 2;
2006           found_element = element;
2007         }
2008
2009         if (!CAN_CHANGE(element))
2010           continue;
2011
2012         for (i = 0; i < element_info[element].num_change_pages; i++)
2013         {
2014           content= element_info[element].change_page[i].target_content[xx][yy];
2015           is_player = ELEM_IS_PLAYER(content);
2016
2017           if (is_player && (found_rating < 1 || element < found_element))
2018           {
2019             start_x = x + xx - 1;
2020             start_y = y + yy - 1;
2021
2022             found_rating = 1;
2023             found_element = element;
2024           }
2025         }
2026       }
2027     }
2028
2029     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
2030                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2031                 start_x - MIDPOSX);
2032
2033     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2034                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2035                 start_y - MIDPOSY);
2036   }
2037   else
2038   {
2039 #if 1
2040     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2041                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2042                 local_player->jx - MIDPOSX);
2043
2044     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2045                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2046                 local_player->jy - MIDPOSY);
2047 #else
2048     scroll_x = SBX_Left;
2049     scroll_y = SBY_Upper;
2050     if (local_player->jx >= SBX_Left + MIDPOSX)
2051       scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2052                   local_player->jx - MIDPOSX :
2053                   SBX_Right);
2054     if (local_player->jy >= SBY_Upper + MIDPOSY)
2055       scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2056                   local_player->jy - MIDPOSY :
2057                   SBY_Lower);
2058 #endif
2059   }
2060
2061   CloseDoor(DOOR_CLOSE_1);
2062
2063   /* !!! FIX THIS (START) !!! */
2064   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2065   {
2066     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   /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
8599   if (recorded_player_action == NULL && tape.pausing)
8600     return;
8601 #endif
8602
8603 #if 0
8604   printf("::: %d\n", stored_player[0].action);
8605 #endif
8606
8607 #if 0
8608   if (recorded_player_action != NULL)
8609     for (i = 0; i < MAX_PLAYERS; i++)
8610       stored_player[i].action = recorded_player_action[i];
8611 #endif
8612
8613   for (i = 0; i < MAX_PLAYERS; i++)
8614   {
8615     summarized_player_action |= stored_player[i].action;
8616
8617     if (!network_playing)
8618       stored_player[i].effective_action = stored_player[i].action;
8619   }
8620
8621 #if defined(NETWORK_AVALIABLE)
8622   if (network_playing)
8623     SendToServer_MovePlayer(summarized_player_action);
8624 #endif
8625
8626   if (!options.network && !setup.team_mode)
8627     local_player->effective_action = summarized_player_action;
8628
8629 #if 1
8630   if (recorded_player_action != NULL)
8631     for (i = 0; i < MAX_PLAYERS; i++)
8632       stored_player[i].effective_action = recorded_player_action[i];
8633 #endif
8634
8635 #if 1
8636   for (i = 0; i < MAX_PLAYERS; i++)
8637   {
8638     tape_action[i] = stored_player[i].effective_action;
8639
8640     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8641       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8642   }
8643
8644   /* only save actions from input devices, but not programmed actions */
8645   if (tape.recording)
8646     TapeRecordAction(tape_action);
8647 #endif
8648
8649   for (i = 0; i < MAX_PLAYERS; i++)
8650   {
8651     int actual_player_action = stored_player[i].effective_action;
8652
8653 #if 1
8654     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8655        - rnd_equinox_tetrachloride 048
8656        - rnd_equinox_tetrachloride_ii 096
8657        - rnd_emanuel_schmieg 002
8658        - doctor_sloan_ww 001, 020
8659     */
8660     if (stored_player[i].MovPos == 0)
8661       CheckGravityMovement(&stored_player[i]);
8662 #endif
8663
8664 #if 1
8665     /* overwrite programmed action with tape action */
8666     if (stored_player[i].programmed_action)
8667       actual_player_action = stored_player[i].programmed_action;
8668 #endif
8669
8670 #if 0
8671     if (stored_player[i].programmed_action)
8672       printf("::: %d\n", stored_player[i].programmed_action);
8673 #endif
8674
8675     if (recorded_player_action)
8676     {
8677 #if 0
8678       if (stored_player[i].programmed_action &&
8679           stored_player[i].programmed_action != recorded_player_action[i])
8680         printf("::: %d: %d <-> %d\n", i,
8681                stored_player[i].programmed_action, recorded_player_action[i]);
8682 #endif
8683
8684 #if 0
8685       actual_player_action = recorded_player_action[i];
8686 #endif
8687     }
8688
8689 #if 0
8690     /* overwrite tape action with programmed action */
8691     if (stored_player[i].programmed_action)
8692       actual_player_action = stored_player[i].programmed_action;
8693 #endif
8694
8695 #if 0
8696     if (i == 0)
8697       printf("::: action: %d: %x [%d]\n",
8698              stored_player[i].MovPos, actual_player_action, FrameCounter);
8699 #endif
8700
8701 #if 1
8702     PlayerActions(&stored_player[i], actual_player_action);
8703 #else
8704     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8705
8706     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8707       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8708 #endif
8709
8710     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8711   }
8712
8713 #if 0
8714   if (tape.recording)
8715     TapeRecordAction(tape_action);
8716 #endif
8717
8718   network_player_action_received = FALSE;
8719
8720   ScrollScreen(NULL, SCROLL_GO_ON);
8721
8722 #if 0
8723   FrameCounter++;
8724   TimeFrames++;
8725
8726   for (i = 0; i < MAX_PLAYERS; i++)
8727     stored_player[i].Frame++;
8728 #endif
8729
8730 #if 1
8731   /* for backwards compatibility, the following code emulates a fixed bug that
8732      occured when pushing elements (causing elements that just made their last
8733      pushing step to already (if possible) make their first falling step in the
8734      same game frame, which is bad); this code is also needed to use the famous
8735      "spring push bug" which is used in older levels and might be wanted to be
8736      used also in newer levels, but in this case the buggy pushing code is only
8737      affecting the "spring" element and no other elements */
8738
8739 #if 1
8740   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8741 #else
8742   if (game.engine_version < VERSION_IDENT(2,2,0,7))
8743 #endif
8744   {
8745     for (i = 0; i < MAX_PLAYERS; i++)
8746     {
8747       struct PlayerInfo *player = &stored_player[i];
8748       int x = player->jx;
8749       int y = player->jy;
8750
8751 #if 1
8752       if (player->active && player->is_pushing && player->is_moving &&
8753           IS_MOVING(x, y) &&
8754           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8755            Feld[x][y] == EL_SPRING))
8756 #else
8757       if (player->active && player->is_pushing && player->is_moving &&
8758           IS_MOVING(x, y))
8759 #endif
8760       {
8761         ContinueMoving(x, y);
8762
8763         /* continue moving after pushing (this is actually a bug) */
8764         if (!IS_MOVING(x, y))
8765         {
8766           Stop[x][y] = FALSE;
8767         }
8768       }
8769     }
8770   }
8771 #endif
8772
8773   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8774   {
8775     Changed[x][y] = CE_BITMASK_DEFAULT;
8776     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8777
8778 #if USE_NEW_BLOCK_STYLE
8779     /* this must be handled before main playfield loop */
8780     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
8781     {
8782       MovDelay[x][y]--;
8783       if (MovDelay[x][y] <= 0)
8784         RemoveField(x, y);
8785     }
8786 #endif
8787
8788 #if DEBUG
8789     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8790     {
8791       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8792       printf("GameActions(): This should never happen!\n");
8793
8794       ChangePage[x][y] = -1;
8795     }
8796 #endif
8797
8798     Stop[x][y] = FALSE;
8799     if (WasJustMoving[x][y] > 0)
8800       WasJustMoving[x][y]--;
8801     if (WasJustFalling[x][y] > 0)
8802       WasJustFalling[x][y]--;
8803     if (CheckCollision[x][y] > 0)
8804       CheckCollision[x][y]--;
8805
8806     GfxFrame[x][y]++;
8807
8808 #if 1
8809     /* reset finished pushing action (not done in ContinueMoving() to allow
8810        continous pushing animation for elements with zero push delay) */
8811     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8812     {
8813       ResetGfxAnimation(x, y);
8814       DrawLevelField(x, y);
8815     }
8816 #endif
8817
8818 #if DEBUG
8819     if (IS_BLOCKED(x, y))
8820     {
8821       int oldx, oldy;
8822
8823       Blocked2Moving(x, y, &oldx, &oldy);
8824       if (!IS_MOVING(oldx, oldy))
8825       {
8826         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8827         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8828         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8829         printf("GameActions(): This should never happen!\n");
8830       }
8831     }
8832 #endif
8833   }
8834
8835   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8836   {
8837     element = Feld[x][y];
8838 #if 1
8839     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8840 #else
8841     graphic = el2img(element);
8842 #endif
8843
8844 #if 0
8845     if (element == -1)
8846     {
8847       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8848
8849       element = graphic = 0;
8850     }
8851 #endif
8852
8853     if (graphic_info[graphic].anim_global_sync)
8854       GfxFrame[x][y] = FrameCounter;
8855
8856     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8857         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8858       ResetRandomAnimationValue(x, y);
8859
8860     SetRandomAnimationValue(x, y);
8861
8862 #if 1
8863     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8864 #endif
8865
8866     if (IS_INACTIVE(element))
8867     {
8868       if (IS_ANIMATED(graphic))
8869         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8870
8871       continue;
8872     }
8873
8874 #if 1
8875     /* this may take place after moving, so 'element' may have changed */
8876 #if 0
8877     if (IS_CHANGING(x, y))
8878 #else
8879     if (IS_CHANGING(x, y) &&
8880         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8881 #endif
8882     {
8883 #if 0
8884       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8885                     element_info[element].event_page_nr[CE_DELAY]);
8886 #else
8887       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8888 #endif
8889
8890       element = Feld[x][y];
8891       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8892     }
8893 #endif
8894
8895     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8896     {
8897       StartMoving(x, y);
8898
8899 #if 1
8900       element = Feld[x][y];
8901       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8902 #if 0
8903       if (element == EL_MOLE)
8904         printf("::: %d, %d, %d [%d]\n",
8905                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8906                GfxAction[x][y]);
8907 #endif
8908 #if 0
8909       if (element == EL_YAMYAM)
8910         printf("::: %d, %d, %d\n",
8911                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8912 #endif
8913 #endif
8914
8915       if (IS_ANIMATED(graphic) &&
8916           !IS_MOVING(x, y) &&
8917           !Stop[x][y])
8918       {
8919         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8920
8921 #if 0
8922         if (element == EL_BUG)
8923           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8924 #endif
8925
8926 #if 0
8927         if (element == EL_MOLE)
8928           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8929 #endif
8930       }
8931
8932       if (IS_GEM(element) || element == EL_SP_INFOTRON)
8933         EdelsteinFunkeln(x, y);
8934     }
8935     else if ((element == EL_ACID ||
8936               element == EL_EXIT_OPEN ||
8937               element == EL_SP_EXIT_OPEN ||
8938               element == EL_SP_TERMINAL ||
8939               element == EL_SP_TERMINAL_ACTIVE ||
8940               element == EL_EXTRA_TIME ||
8941               element == EL_SHIELD_NORMAL ||
8942               element == EL_SHIELD_DEADLY) &&
8943              IS_ANIMATED(graphic))
8944       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8945     else if (IS_MOVING(x, y))
8946       ContinueMoving(x, y);
8947     else if (IS_ACTIVE_BOMB(element))
8948       CheckDynamite(x, y);
8949 #if 0
8950     else if (element == EL_EXPLOSION && !game.explosions_delayed)
8951       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8952 #endif
8953     else if (element == EL_AMOEBA_GROWING)
8954       AmoebeWaechst(x, y);
8955     else if (element == EL_AMOEBA_SHRINKING)
8956       AmoebaDisappearing(x, y);
8957
8958 #if !USE_NEW_AMOEBA_CODE
8959     else if (IS_AMOEBALIVE(element))
8960       AmoebeAbleger(x, y);
8961 #endif
8962
8963     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8964       Life(x, y);
8965     else if (element == EL_EXIT_CLOSED)
8966       CheckExit(x, y);
8967     else if (element == EL_SP_EXIT_CLOSED)
8968       CheckExitSP(x, y);
8969     else if (element == EL_EXPANDABLE_WALL_GROWING)
8970       MauerWaechst(x, y);
8971     else if (element == EL_EXPANDABLE_WALL ||
8972              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8973              element == EL_EXPANDABLE_WALL_VERTICAL ||
8974              element == EL_EXPANDABLE_WALL_ANY)
8975       MauerAbleger(x, y);
8976     else if (element == EL_FLAMES)
8977       CheckForDragon(x, y);
8978 #if 0
8979     else if (IS_AUTO_CHANGING(element))
8980       ChangeElement(x, y);
8981 #endif
8982     else if (element == EL_EXPLOSION)
8983       ; /* drawing of correct explosion animation is handled separately */
8984     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8985       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8986
8987 #if 0
8988     /* this may take place after moving, so 'element' may have changed */
8989     if (IS_AUTO_CHANGING(Feld[x][y]))
8990       ChangeElement(x, y);
8991 #endif
8992
8993     if (IS_BELT_ACTIVE(element))
8994       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8995
8996     if (game.magic_wall_active)
8997     {
8998       int jx = local_player->jx, jy = local_player->jy;
8999
9000       /* play the element sound at the position nearest to the player */
9001       if ((element == EL_MAGIC_WALL_FULL ||
9002            element == EL_MAGIC_WALL_ACTIVE ||
9003            element == EL_MAGIC_WALL_EMPTYING ||
9004            element == EL_BD_MAGIC_WALL_FULL ||
9005            element == EL_BD_MAGIC_WALL_ACTIVE ||
9006            element == EL_BD_MAGIC_WALL_EMPTYING) &&
9007           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9008       {
9009         magic_wall_x = x;
9010         magic_wall_y = y;
9011       }
9012     }
9013   }
9014
9015 #if USE_NEW_AMOEBA_CODE
9016   /* new experimental amoeba growth stuff */
9017 #if 1
9018   if (!(FrameCounter % 8))
9019 #endif
9020   {
9021     static unsigned long random = 1684108901;
9022
9023     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9024     {
9025 #if 0
9026       x = (random >> 10) % lev_fieldx;
9027       y = (random >> 20) % lev_fieldy;
9028 #else
9029       x = RND(lev_fieldx);
9030       y = RND(lev_fieldy);
9031 #endif
9032       element = Feld[x][y];
9033
9034 #if 1
9035       if (!IS_PLAYER(x,y) &&
9036           (element == EL_EMPTY ||
9037            CAN_GROW_INTO(element) ||
9038            element == EL_QUICKSAND_EMPTY ||
9039            element == EL_ACID_SPLASH_LEFT ||
9040            element == EL_ACID_SPLASH_RIGHT))
9041       {
9042         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9043             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9044             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9045             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9046           Feld[x][y] = EL_AMOEBA_DROP;
9047       }
9048 #else
9049       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9050       if (!IS_PLAYER(x,y) &&
9051           (element == EL_EMPTY ||
9052            element == EL_SAND ||
9053            element == EL_QUICKSAND_EMPTY ||
9054            element == EL_ACID_SPLASH_LEFT ||
9055            element == EL_ACID_SPLASH_RIGHT))
9056       {
9057         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9058             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9059             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9060             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9061           Feld[x][y] = EL_AMOEBA_DROP;
9062       }
9063 #endif
9064
9065       random = random * 129 + 1;
9066     }
9067   }
9068 #endif
9069
9070 #if 0
9071   if (game.explosions_delayed)
9072 #endif
9073   {
9074     game.explosions_delayed = FALSE;
9075
9076     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9077     {
9078       element = Feld[x][y];
9079
9080       if (ExplodeField[x][y])
9081         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9082       else if (element == EL_EXPLOSION)
9083         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9084
9085       ExplodeField[x][y] = EX_TYPE_NONE;
9086     }
9087
9088     game.explosions_delayed = TRUE;
9089   }
9090
9091   if (game.magic_wall_active)
9092   {
9093     if (!(game.magic_wall_time_left % 4))
9094     {
9095       int element = Feld[magic_wall_x][magic_wall_y];
9096
9097       if (element == EL_BD_MAGIC_WALL_FULL ||
9098           element == EL_BD_MAGIC_WALL_ACTIVE ||
9099           element == EL_BD_MAGIC_WALL_EMPTYING)
9100         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9101       else
9102         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9103     }
9104
9105     if (game.magic_wall_time_left > 0)
9106     {
9107       game.magic_wall_time_left--;
9108       if (!game.magic_wall_time_left)
9109       {
9110         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9111         {
9112           element = Feld[x][y];
9113
9114           if (element == EL_MAGIC_WALL_ACTIVE ||
9115               element == EL_MAGIC_WALL_FULL)
9116           {
9117             Feld[x][y] = EL_MAGIC_WALL_DEAD;
9118             DrawLevelField(x, y);
9119           }
9120           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9121                    element == EL_BD_MAGIC_WALL_FULL)
9122           {
9123             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9124             DrawLevelField(x, y);
9125           }
9126         }
9127
9128         game.magic_wall_active = FALSE;
9129       }
9130     }
9131   }
9132
9133   if (game.light_time_left > 0)
9134   {
9135     game.light_time_left--;
9136
9137     if (game.light_time_left == 0)
9138       RedrawAllLightSwitchesAndInvisibleElements();
9139   }
9140
9141   if (game.timegate_time_left > 0)
9142   {
9143     game.timegate_time_left--;
9144
9145     if (game.timegate_time_left == 0)
9146       CloseAllOpenTimegates();
9147   }
9148
9149   for (i = 0; i < MAX_PLAYERS; i++)
9150   {
9151     struct PlayerInfo *player = &stored_player[i];
9152
9153     if (SHIELD_ON(player))
9154     {
9155       if (player->shield_deadly_time_left)
9156         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9157       else if (player->shield_normal_time_left)
9158         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9159     }
9160   }
9161
9162   if (TimeFrames >= FRAMES_PER_SECOND)
9163   {
9164     TimeFrames = 0;
9165     TapeTime++;
9166
9167     for (i = 0; i < MAX_PLAYERS; i++)
9168     {
9169       struct PlayerInfo *player = &stored_player[i];
9170
9171       if (SHIELD_ON(player))
9172       {
9173         player->shield_normal_time_left--;
9174
9175         if (player->shield_deadly_time_left > 0)
9176           player->shield_deadly_time_left--;
9177       }
9178     }
9179
9180     if (!level.use_step_counter)
9181     {
9182       TimePlayed++;
9183
9184       if (TimeLeft > 0)
9185       {
9186         TimeLeft--;
9187
9188         if (TimeLeft <= 10 && setup.time_limit)
9189           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9190
9191         DrawGameValue_Time(TimeLeft);
9192
9193         if (!TimeLeft && setup.time_limit)
9194           for (i = 0; i < MAX_PLAYERS; i++)
9195             KillHero(&stored_player[i]);
9196       }
9197       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9198         DrawGameValue_Time(TimePlayed);
9199     }
9200
9201     if (tape.recording || tape.playing)
9202       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9203   }
9204
9205   DrawAllPlayers();
9206   PlayAllPlayersSound();
9207
9208   if (options.debug)                    /* calculate frames per second */
9209   {
9210     static unsigned long fps_counter = 0;
9211     static int fps_frames = 0;
9212     unsigned long fps_delay_ms = Counter() - fps_counter;
9213
9214     fps_frames++;
9215
9216     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
9217     {
9218       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9219
9220       fps_frames = 0;
9221       fps_counter = Counter();
9222     }
9223
9224     redraw_mask |= REDRAW_FPS;
9225   }
9226
9227 #if 0
9228   if (stored_player[0].jx != stored_player[0].last_jx ||
9229       stored_player[0].jy != stored_player[0].last_jy)
9230     printf("::: %d, %d, %d, %d, %d\n",
9231            stored_player[0].MovDir,
9232            stored_player[0].MovPos,
9233            stored_player[0].GfxPos,
9234            stored_player[0].Frame,
9235            stored_player[0].StepFrame);
9236 #endif
9237
9238 #if USE_NEW_MOVE_DELAY
9239   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9240 #else
9241   FrameCounter++;
9242   TimeFrames++;
9243
9244   for (i = 0; i < MAX_PLAYERS; i++)
9245   {
9246     int move_frames =
9247       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
9248
9249     stored_player[i].Frame += move_frames;
9250
9251     if (stored_player[i].MovPos != 0)
9252       stored_player[i].StepFrame += move_frames;
9253
9254 #if USE_NEW_MOVE_DELAY
9255     if (stored_player[i].move_delay > 0)
9256       stored_player[i].move_delay--;
9257 #endif
9258
9259     if (stored_player[i].drop_delay > 0)
9260       stored_player[i].drop_delay--;
9261   }
9262 #endif
9263
9264 #if 1
9265   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9266   {
9267     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9268
9269     local_player->show_envelope = 0;
9270   }
9271 #endif
9272
9273 #if USE_NEW_RANDOMIZE
9274   /* use random number generator in every frame to make it less predictable */
9275   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9276     RND(1);
9277 #endif
9278 }
9279
9280 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9281 {
9282   int min_x = x, min_y = y, max_x = x, max_y = y;
9283   int i;
9284
9285   for (i = 0; i < MAX_PLAYERS; i++)
9286   {
9287     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9288
9289     if (!stored_player[i].active || &stored_player[i] == player)
9290       continue;
9291
9292     min_x = MIN(min_x, jx);
9293     min_y = MIN(min_y, jy);
9294     max_x = MAX(max_x, jx);
9295     max_y = MAX(max_y, jy);
9296   }
9297
9298   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9299 }
9300
9301 static boolean AllPlayersInVisibleScreen()
9302 {
9303   int i;
9304
9305   for (i = 0; i < MAX_PLAYERS; i++)
9306   {
9307     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9308
9309     if (!stored_player[i].active)
9310       continue;
9311
9312     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9313       return FALSE;
9314   }
9315
9316   return TRUE;
9317 }
9318
9319 void ScrollLevel(int dx, int dy)
9320 {
9321   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9322   int x, y;
9323
9324   BlitBitmap(drawto_field, drawto_field,
9325              FX + TILEX * (dx == -1) - softscroll_offset,
9326              FY + TILEY * (dy == -1) - softscroll_offset,
9327              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9328              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9329              FX + TILEX * (dx == 1) - softscroll_offset,
9330              FY + TILEY * (dy == 1) - softscroll_offset);
9331
9332   if (dx)
9333   {
9334     x = (dx == 1 ? BX1 : BX2);
9335     for (y = BY1; y <= BY2; y++)
9336       DrawScreenField(x, y);
9337   }
9338
9339   if (dy)
9340   {
9341     y = (dy == 1 ? BY1 : BY2);
9342     for (x = BX1; x <= BX2; x++)
9343       DrawScreenField(x, y);
9344   }
9345
9346   redraw_mask |= REDRAW_FIELD;
9347 }
9348
9349 #if 0
9350 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9351 {
9352   int nextx = x + dx, nexty = y + dy;
9353   int element = Feld[x][y];
9354
9355   if ((dx == -1 &&
9356        element != EL_SP_PORT_LEFT &&
9357        element != EL_SP_GRAVITY_PORT_LEFT &&
9358        element != EL_SP_PORT_HORIZONTAL &&
9359        element != EL_SP_PORT_ANY) ||
9360       (dx == +1 &&
9361        element != EL_SP_PORT_RIGHT &&
9362        element != EL_SP_GRAVITY_PORT_RIGHT &&
9363        element != EL_SP_PORT_HORIZONTAL &&
9364        element != EL_SP_PORT_ANY) ||
9365       (dy == -1 &&
9366        element != EL_SP_PORT_UP &&
9367        element != EL_SP_GRAVITY_PORT_UP &&
9368        element != EL_SP_PORT_VERTICAL &&
9369        element != EL_SP_PORT_ANY) ||
9370       (dy == +1 &&
9371        element != EL_SP_PORT_DOWN &&
9372        element != EL_SP_GRAVITY_PORT_DOWN &&
9373        element != EL_SP_PORT_VERTICAL &&
9374        element != EL_SP_PORT_ANY) ||
9375       !IN_LEV_FIELD(nextx, nexty) ||
9376       !IS_FREE(nextx, nexty))
9377     return FALSE;
9378
9379   return TRUE;
9380 }
9381 #endif
9382
9383 static boolean canFallDown(struct PlayerInfo *player)
9384 {
9385   int jx = player->jx, jy = player->jy;
9386
9387   return (IN_LEV_FIELD(jx, jy + 1) &&
9388           (IS_FREE(jx, jy + 1) ||
9389            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9390           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9391           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9392 }
9393
9394 static boolean canPassField(int x, int y, int move_dir)
9395 {
9396   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9397   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9398   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9399   int nextx = x + dx;
9400   int nexty = y + dy;
9401   int element = Feld[x][y];
9402
9403   return (IS_PASSABLE_FROM(element, opposite_dir) &&
9404           !CAN_MOVE(element) &&
9405           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9406           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9407           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9408 }
9409
9410 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9411 {
9412   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9413   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9414   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9415   int newx = x + dx;
9416   int newy = y + dy;
9417 #if 0
9418   int nextx = newx + dx;
9419   int nexty = newy + dy;
9420 #endif
9421
9422 #if 1
9423   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9424           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9425 #if 0
9426           (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9427 #endif
9428           (IS_DIGGABLE(Feld[newx][newy]) ||
9429            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9430            canPassField(newx, newy, move_dir)));
9431 #else
9432 #if 1
9433   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9434           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9435           (IS_DIGGABLE(Feld[newx][newy]) ||
9436            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9437            canPassField(newx, newy, move_dir)));
9438 #else
9439 #if 1
9440   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9441           (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9442            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9443            canPassField(newx, newy, move_dir)));
9444 #else
9445   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9446           (IS_DIGGABLE(Feld[newx][newy]) ||
9447            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9448            (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9449             !CAN_MOVE(Feld[newx][newy]) &&
9450             IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9451             IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9452             (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9453 #endif
9454 #endif
9455 #endif
9456 }
9457
9458 static void CheckGravityMovement(struct PlayerInfo *player)
9459 {
9460   if (game.gravity && !player->programmed_action)
9461   {
9462 #if 1
9463     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9464     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
9465 #else
9466     int move_dir_horizontal = player->action & MV_HORIZONTAL;
9467     int move_dir_vertical   = player->action & MV_VERTICAL;
9468 #endif
9469
9470 #if 1
9471     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9472 #else
9473     boolean player_is_snapping = player->action & JOY_BUTTON_1;
9474 #endif
9475
9476     int jx = player->jx, jy = player->jy;
9477
9478     boolean player_is_moving_to_valid_field =
9479       (!player_is_snapping &&
9480        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9481         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9482
9483 #if 0
9484     int move_dir =
9485       (player->last_move_dir & MV_HORIZONTAL ?
9486        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9487        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9488 #endif
9489
9490 #if 0
9491     int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9492     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9493     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9494     int new_jx = jx + dx, new_jy = jy + dy;
9495     int nextx = new_jx + dx, nexty = new_jy + dy;
9496 #endif
9497
9498 #if 1
9499
9500 #if 1
9501     boolean player_can_fall_down = canFallDown(player);
9502 #else
9503     boolean player_can_fall_down =
9504       (IN_LEV_FIELD(jx, jy + 1) &&
9505        (IS_FREE(jx, jy + 1) ||
9506         (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9507 #endif
9508
9509 #else
9510     boolean player_can_fall_down =
9511       (IN_LEV_FIELD(jx, jy + 1) &&
9512        (IS_FREE(jx, jy + 1)));
9513 #endif
9514
9515 #if 0
9516     boolean player_is_moving_to_valid_field =
9517       (
9518 #if 1
9519        !player_is_snapping &&
9520 #endif
9521
9522 #if 1
9523        IN_LEV_FIELD(new_jx, new_jy) &&
9524        (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9525         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9526          element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9527          IN_LEV_FIELD(nextx, nexty) &&
9528          element_info[Feld[nextx][nexty]].access_direction & move_dir))
9529 #else
9530        IN_LEV_FIELD(new_jx, new_jy) &&
9531        (Feld[new_jx][new_jy] == EL_SP_BASE ||
9532         Feld[new_jx][new_jy] == EL_SAND ||
9533         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9534          canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9535     /* !!! extend EL_SAND to anything diggable !!! */
9536 #endif
9537        );
9538 #endif
9539
9540 #if 0
9541     boolean player_is_standing_on_valid_field =
9542       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9543        (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9544 #endif
9545
9546 #if 0
9547     printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9548            player_can_fall_down,
9549            player_is_standing_on_valid_field,
9550            player_is_moving_to_valid_field,
9551            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9552            player->effective_action,
9553            player->can_fall_into_acid);
9554 #endif
9555
9556     if (player_can_fall_down &&
9557 #if 0
9558         !player_is_standing_on_valid_field &&
9559 #endif
9560         !player_is_moving_to_valid_field)
9561     {
9562 #if 0
9563       printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9564              jx, jy, FrameCounter);
9565 #endif
9566
9567       player->programmed_action = MV_DOWN;
9568     }
9569   }
9570 }
9571
9572 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9573 {
9574 #if 1
9575   return CheckGravityMovement(player);
9576 #endif
9577
9578   if (game.gravity && !player->programmed_action)
9579   {
9580     int jx = player->jx, jy = player->jy;
9581     boolean field_under_player_is_free =
9582       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9583     boolean player_is_standing_on_valid_field =
9584       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9585        (IS_WALKABLE(Feld[jx][jy]) &&
9586         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9587
9588     if (field_under_player_is_free && !player_is_standing_on_valid_field)
9589       player->programmed_action = MV_DOWN;
9590   }
9591 }
9592
9593 /*
9594   MovePlayerOneStep()
9595   -----------------------------------------------------------------------------
9596   dx, dy:               direction (non-diagonal) to try to move the player to
9597   real_dx, real_dy:     direction as read from input device (can be diagonal)
9598 */
9599
9600 boolean MovePlayerOneStep(struct PlayerInfo *player,
9601                           int dx, int dy, int real_dx, int real_dy)
9602 {
9603 #if 0
9604   static int trigger_sides[4][2] =
9605   {
9606     /* enter side        leave side */
9607     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
9608     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
9609     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
9610     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
9611   };
9612   int move_direction = (dx == -1 ? MV_LEFT :
9613                         dx == +1 ? MV_RIGHT :
9614                         dy == -1 ? MV_UP :
9615                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9616   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9617   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9618 #endif
9619   int jx = player->jx, jy = player->jy;
9620   int new_jx = jx + dx, new_jy = jy + dy;
9621   int element;
9622   int can_move;
9623
9624   if (!player->active || (!dx && !dy))
9625     return MF_NO_ACTION;
9626
9627   player->MovDir = (dx < 0 ? MV_LEFT :
9628                     dx > 0 ? MV_RIGHT :
9629                     dy < 0 ? MV_UP :
9630                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
9631
9632   if (!IN_LEV_FIELD(new_jx, new_jy))
9633     return MF_NO_ACTION;
9634
9635   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9636     return MF_NO_ACTION;
9637
9638 #if 0
9639   element = MovingOrBlocked2Element(new_jx, new_jy);
9640 #else
9641   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9642 #endif
9643
9644   if (DONT_RUN_INTO(element))
9645   {
9646     if (element == EL_ACID && dx == 0 && dy == 1)
9647     {
9648       SplashAcid(new_jx, new_jy);
9649       Feld[jx][jy] = EL_PLAYER_1;
9650       InitMovingField(jx, jy, MV_DOWN);
9651       Store[jx][jy] = EL_ACID;
9652       ContinueMoving(jx, jy);
9653       BuryHero(player);
9654     }
9655     else
9656       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9657
9658     return MF_MOVING;
9659   }
9660
9661   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9662   if (can_move != MF_MOVING)
9663     return can_move;
9664
9665   /* check if DigField() has caused relocation of the player */
9666   if (player->jx != jx || player->jy != jy)
9667     return MF_NO_ACTION;
9668
9669   StorePlayer[jx][jy] = 0;
9670   player->last_jx = jx;
9671   player->last_jy = jy;
9672   player->jx = new_jx;
9673   player->jy = new_jy;
9674   StorePlayer[new_jx][new_jy] = player->element_nr;
9675
9676   player->MovPos =
9677     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9678
9679   player->step_counter++;
9680
9681 #if 0
9682   player->drop_delay = 0;
9683 #endif
9684
9685   PlayerVisit[jx][jy] = FrameCounter;
9686
9687   ScrollPlayer(player, SCROLL_INIT);
9688
9689 #if 0
9690   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9691   {
9692     CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9693                                       leave_side);
9694     CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9695   }
9696
9697   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9698   {
9699     CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9700                                       CE_OTHER_GETS_ENTERED, enter_side);
9701     CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9702                              CE_ENTERED_BY_PLAYER, enter_side);
9703   }
9704 #endif
9705
9706   return MF_MOVING;
9707 }
9708
9709 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9710 {
9711   int jx = player->jx, jy = player->jy;
9712   int old_jx = jx, old_jy = jy;
9713   int moved = MF_NO_ACTION;
9714
9715 #if 1
9716   if (!player->active)
9717     return FALSE;
9718
9719   if (!dx && !dy)
9720   {
9721     if (player->MovPos == 0)
9722     {
9723       player->is_moving = FALSE;
9724       player->is_digging = FALSE;
9725       player->is_collecting = FALSE;
9726       player->is_snapping = FALSE;
9727       player->is_pushing = FALSE;
9728     }
9729
9730     return FALSE;
9731   }
9732 #else
9733   if (!player->active || (!dx && !dy))
9734     return FALSE;
9735 #endif
9736
9737 #if 0
9738   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9739       !tape.playing)
9740     return FALSE;
9741 #else
9742
9743 #if 1
9744
9745 #if 0
9746   printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9747          player->move_delay + player->move_delay_value);
9748 #endif
9749
9750 #if USE_NEW_MOVE_DELAY
9751   if (player->move_delay > 0)
9752 #else
9753   if (!FrameReached(&player->move_delay, player->move_delay_value))
9754 #endif
9755   {
9756 #if 0
9757     printf("::: can NOT move\n");
9758 #endif
9759
9760     return FALSE;
9761   }
9762 #else
9763   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9764       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9765     return FALSE;
9766 #endif
9767
9768 #endif
9769
9770 #if 0
9771   printf("::: COULD move now\n");
9772 #endif
9773
9774 #if USE_NEW_MOVE_DELAY
9775   player->move_delay = -1;              /* set to "uninitialized" value */
9776 #endif
9777
9778   /* store if player is automatically moved to next field */
9779   player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9780
9781   /* remove the last programmed player action */
9782   player->programmed_action = 0;
9783
9784   if (player->MovPos)
9785   {
9786     /* should only happen if pre-1.2 tape recordings are played */
9787     /* this is only for backward compatibility */
9788
9789     int original_move_delay_value = player->move_delay_value;
9790
9791 #if DEBUG
9792     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9793            tape.counter);
9794 #endif
9795
9796     /* scroll remaining steps with finest movement resolution */
9797     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9798
9799     while (player->MovPos)
9800     {
9801       ScrollPlayer(player, SCROLL_GO_ON);
9802       ScrollScreen(NULL, SCROLL_GO_ON);
9803
9804 #if USE_NEW_MOVE_DELAY
9805       AdvanceFrameAndPlayerCounters(player->index_nr);
9806 #else
9807       FrameCounter++;
9808 #endif
9809
9810       DrawAllPlayers();
9811       BackToFront();
9812     }
9813
9814     player->move_delay_value = original_move_delay_value;
9815   }
9816
9817   if (player->last_move_dir & MV_HORIZONTAL)
9818   {
9819     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9820       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9821   }
9822   else
9823   {
9824     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9825       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9826   }
9827
9828   jx = player->jx;
9829   jy = player->jy;
9830
9831   if (moved & MF_MOVING && !ScreenMovPos &&
9832       (player == local_player || !options.network))
9833   {
9834     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9835     int offset = (setup.scroll_delay ? 3 : 0);
9836
9837     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9838     {
9839       /* actual player has left the screen -- scroll in that direction */
9840       if (jx != old_jx)         /* player has moved horizontally */
9841         scroll_x += (jx - old_jx);
9842       else                      /* player has moved vertically */
9843         scroll_y += (jy - old_jy);
9844     }
9845     else
9846     {
9847       if (jx != old_jx)         /* player has moved horizontally */
9848       {
9849         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
9850             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9851           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9852
9853         /* don't scroll over playfield boundaries */
9854         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9855           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9856
9857         /* don't scroll more than one field at a time */
9858         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9859
9860         /* don't scroll against the player's moving direction */
9861         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
9862             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9863           scroll_x = old_scroll_x;
9864       }
9865       else                      /* player has moved vertically */
9866       {
9867         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
9868             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9869           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9870
9871         /* don't scroll over playfield boundaries */
9872         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9873           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9874
9875         /* don't scroll more than one field at a time */
9876         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9877
9878         /* don't scroll against the player's moving direction */
9879         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
9880             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9881           scroll_y = old_scroll_y;
9882       }
9883     }
9884
9885     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9886     {
9887       if (!options.network && !AllPlayersInVisibleScreen())
9888       {
9889         scroll_x = old_scroll_x;
9890         scroll_y = old_scroll_y;
9891       }
9892       else
9893       {
9894         ScrollScreen(player, SCROLL_INIT);
9895         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9896       }
9897     }
9898   }
9899
9900 #if 0
9901 #if 1
9902   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9903 #else
9904   if (!(moved & MF_MOVING) && !player->is_pushing)
9905     player->Frame = 0;
9906 #endif
9907 #endif
9908
9909   player->StepFrame = 0;
9910
9911   if (moved & MF_MOVING)
9912   {
9913 #if 0
9914     printf("::: REALLY moves now\n");
9915 #endif
9916
9917     if (old_jx != jx && old_jy == jy)
9918       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9919     else if (old_jx == jx && old_jy != jy)
9920       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9921
9922     DrawLevelField(jx, jy);     /* for "crumbled sand" */
9923
9924     player->last_move_dir = player->MovDir;
9925     player->is_moving = TRUE;
9926 #if 1
9927     player->is_snapping = FALSE;
9928 #endif
9929
9930 #if 1
9931     player->is_switching = FALSE;
9932 #endif
9933
9934     player->is_dropping = FALSE;
9935
9936
9937 #if 0
9938     /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9939
9940 #if 1
9941     if (game.engine_version < VERSION_IDENT(3,1,0,0))
9942 #endif
9943     {
9944       int move_direction = player->MovDir;
9945 #if 1
9946       int enter_side = MV_DIR_OPPOSITE(move_direction);
9947       int leave_side = move_direction;
9948 #else
9949       static int trigger_sides[4][2] =
9950       {
9951         /* enter side           leave side */
9952         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
9953         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
9954         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
9955         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
9956       };
9957       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9958       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9959 #endif
9960       int old_element = Feld[old_jx][old_jy];
9961       int new_element = Feld[jx][jy];
9962
9963 #if 1
9964       /* !!! TEST ONLY !!! */
9965       if (IS_CUSTOM_ELEMENT(old_element))
9966         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9967                                    CE_LEFT_BY_PLAYER,
9968                                    player->index_bit, leave_side);
9969
9970       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9971                                           CE_OTHER_GETS_LEFT,
9972                                           player->index_bit, leave_side);
9973
9974       if (IS_CUSTOM_ELEMENT(new_element))
9975         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9976                                    player->index_bit, enter_side);
9977
9978       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9979                                           CE_OTHER_GETS_ENTERED,
9980                                           player->index_bit, enter_side);
9981 #endif
9982
9983     }
9984 #endif
9985
9986
9987   }
9988   else
9989   {
9990     CheckGravityMovementWhenNotMoving(player);
9991
9992     /*
9993     player->last_move_dir = MV_NO_MOVING;
9994     */
9995     player->is_moving = FALSE;
9996
9997 #if USE_NEW_MOVE_STYLE
9998     /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
9999     /* ensure that the player is also allowed to move in the next frame */
10000     /* (currently, the player is forced to wait eight frames before he can try
10001        again!!!) */
10002
10003     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10004       player->move_delay = 0;   /* allow direct movement in the next frame */
10005 #endif
10006   }
10007
10008 #if USE_NEW_MOVE_DELAY
10009   if (player->move_delay == -1)         /* not yet initialized by DigField() */
10010     player->move_delay = player->move_delay_value;
10011 #endif
10012
10013   if (game.engine_version < VERSION_IDENT(3,0,7,0))
10014   {
10015     TestIfHeroTouchesBadThing(jx, jy);
10016     TestIfPlayerTouchesCustomElement(jx, jy);
10017   }
10018
10019   if (!player->active)
10020     RemoveHero(player);
10021
10022   return moved;
10023 }
10024
10025 void ScrollPlayer(struct PlayerInfo *player, int mode)
10026 {
10027   int jx = player->jx, jy = player->jy;
10028   int last_jx = player->last_jx, last_jy = player->last_jy;
10029   int move_stepsize = TILEX / player->move_delay_value;
10030
10031   if (!player->active || !player->MovPos)
10032     return;
10033
10034   if (mode == SCROLL_INIT)
10035   {
10036     player->actual_frame_counter = FrameCounter;
10037     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10038
10039 #if 0
10040     printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10041            FrameCounter,
10042            last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10043            player->block_delay);
10044 #endif
10045
10046 #if USE_NEW_BLOCK_STYLE
10047
10048 #if 0
10049     if (player->block_delay <= 0)
10050       printf("::: ALERT! block_delay == %d\n", player->block_delay);
10051 #endif
10052
10053     if (player->block_delay > 0 &&
10054         Feld[last_jx][last_jy] == EL_EMPTY)
10055     {
10056       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10057       MovDelay[last_jx][last_jy] = player->block_delay + 1;
10058     }
10059 #else
10060 #if USE_NEW_MOVE_STYLE
10061     if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10062          player->block_last_field) &&
10063         Feld[last_jx][last_jy] == EL_EMPTY)
10064       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10065 #else
10066     if (Feld[last_jx][last_jy] == EL_EMPTY)
10067       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10068 #endif
10069 #endif
10070
10071 #if 0
10072     DrawPlayer(player);
10073 #endif
10074
10075     return;
10076   }
10077   else if (!FrameReached(&player->actual_frame_counter, 1))
10078     return;
10079
10080   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10081   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10082
10083 #if USE_NEW_BLOCK_STYLE
10084 #else
10085   if (!player->block_last_field &&
10086       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10087 #if 1
10088     RemoveField(last_jx, last_jy);
10089 #else
10090     Feld[last_jx][last_jy] = EL_EMPTY;
10091 #endif
10092 #endif
10093
10094   /* before DrawPlayer() to draw correct player graphic for this case */
10095   if (player->MovPos == 0)
10096     CheckGravityMovement(player);
10097
10098 #if 0
10099   DrawPlayer(player);   /* needed here only to cleanup last field */
10100 #endif
10101
10102   if (player->MovPos == 0)      /* player reached destination field */
10103   {
10104 #if 1
10105     if (player->move_delay_reset_counter > 0)
10106     {
10107       player->move_delay_reset_counter--;
10108
10109       if (player->move_delay_reset_counter == 0)
10110       {
10111         /* continue with normal speed after quickly moving through gate */
10112         HALVE_PLAYER_SPEED(player);
10113
10114         /* be able to make the next move without delay */
10115         player->move_delay = 0;
10116       }
10117     }
10118 #else
10119     if (IS_PASSABLE(Feld[last_jx][last_jy]))
10120     {
10121       /* continue with normal speed after quickly moving through gate */
10122       HALVE_PLAYER_SPEED(player);
10123
10124       /* be able to make the next move without delay */
10125       player->move_delay = 0;
10126     }
10127 #endif
10128
10129 #if USE_NEW_BLOCK_STYLE
10130 #else
10131     if (player->block_last_field &&
10132         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10133 #if 1
10134       RemoveField(last_jx, last_jy);
10135 #else
10136       Feld[last_jx][last_jy] = EL_EMPTY;
10137 #endif
10138 #endif
10139
10140     player->last_jx = jx;
10141     player->last_jy = jy;
10142
10143     if (Feld[jx][jy] == EL_EXIT_OPEN ||
10144         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10145         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
10146     {
10147       DrawPlayer(player);       /* needed here only to cleanup last field */
10148       RemoveHero(player);
10149
10150       if (local_player->friends_still_needed == 0 ||
10151           IS_SP_ELEMENT(Feld[jx][jy]))
10152         player->LevelSolved = player->GameOver = TRUE;
10153     }
10154
10155 #if 1
10156     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10157     /* this breaks one level: "machine", level 000 */
10158 #if 0
10159     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10160 #endif
10161     {
10162       int move_direction = player->MovDir;
10163 #if 1
10164       int enter_side = MV_DIR_OPPOSITE(move_direction);
10165       int leave_side = move_direction;
10166 #else
10167       static int trigger_sides[4][2] =
10168       {
10169         /* enter side           leave side */
10170         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
10171         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
10172         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
10173         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
10174       };
10175       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10176       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10177 #endif
10178       int old_jx = last_jx;
10179       int old_jy = last_jy;
10180       int old_element = Feld[old_jx][old_jy];
10181       int new_element = Feld[jx][jy];
10182
10183 #if 1
10184       /* !!! TEST ONLY !!! */
10185       if (IS_CUSTOM_ELEMENT(old_element))
10186         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10187                                    CE_LEFT_BY_PLAYER,
10188                                    player->index_bit, leave_side);
10189
10190       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10191                                           CE_OTHER_GETS_LEFT,
10192                                           player->index_bit, leave_side);
10193
10194       if (IS_CUSTOM_ELEMENT(new_element))
10195         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10196                                    player->index_bit, enter_side);
10197
10198       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10199                                           CE_OTHER_GETS_ENTERED,
10200                                           player->index_bit, enter_side);
10201 #endif
10202
10203     }
10204 #endif
10205
10206     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10207     {
10208       TestIfHeroTouchesBadThing(jx, jy);
10209       TestIfPlayerTouchesCustomElement(jx, jy);
10210 #if 1
10211 #if 1
10212       /* needed because pushed element has not yet reached its destination,
10213          so it would trigger a change event at its previous field location */
10214       if (!player->is_pushing)
10215 #endif
10216         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
10217 #endif
10218
10219       if (!player->active)
10220         RemoveHero(player);
10221     }
10222
10223     if (level.use_step_counter)
10224     {
10225       int i;
10226
10227       TimePlayed++;
10228
10229       if (TimeLeft > 0)
10230       {
10231         TimeLeft--;
10232
10233         if (TimeLeft <= 10 && setup.time_limit)
10234           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10235
10236         DrawGameValue_Time(TimeLeft);
10237
10238         if (!TimeLeft && setup.time_limit)
10239           for (i = 0; i < MAX_PLAYERS; i++)
10240             KillHero(&stored_player[i]);
10241       }
10242       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10243         DrawGameValue_Time(TimePlayed);
10244     }
10245
10246     if (tape.single_step && tape.recording && !tape.pausing &&
10247         !player->programmed_action)
10248       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10249   }
10250 }
10251
10252 void ScrollScreen(struct PlayerInfo *player, int mode)
10253 {
10254   static unsigned long screen_frame_counter = 0;
10255
10256   if (mode == SCROLL_INIT)
10257   {
10258     /* set scrolling step size according to actual player's moving speed */
10259     ScrollStepSize = TILEX / player->move_delay_value;
10260
10261     screen_frame_counter = FrameCounter;
10262     ScreenMovDir = player->MovDir;
10263     ScreenMovPos = player->MovPos;
10264     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10265     return;
10266   }
10267   else if (!FrameReached(&screen_frame_counter, 1))
10268     return;
10269
10270   if (ScreenMovPos)
10271   {
10272     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10273     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10274     redraw_mask |= REDRAW_FIELD;
10275   }
10276   else
10277     ScreenMovDir = MV_NO_MOVING;
10278 }
10279
10280 void TestIfPlayerTouchesCustomElement(int x, int y)
10281 {
10282   static int xy[4][2] =
10283   {
10284     { 0, -1 },
10285     { -1, 0 },
10286     { +1, 0 },
10287     { 0, +1 }
10288   };
10289   static int trigger_sides[4][2] =
10290   {
10291     /* center side       border side */
10292     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10293     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10294     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10295     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10296   };
10297   static int touch_dir[4] =
10298   {
10299     MV_LEFT | MV_RIGHT,
10300     MV_UP   | MV_DOWN,
10301     MV_UP   | MV_DOWN,
10302     MV_LEFT | MV_RIGHT
10303   };
10304   int center_element = Feld[x][y];      /* should always be non-moving! */
10305   int i;
10306
10307   for (i = 0; i < NUM_DIRECTIONS; i++)
10308   {
10309     int xx = x + xy[i][0];
10310     int yy = y + xy[i][1];
10311     int center_side = trigger_sides[i][0];
10312     int border_side = trigger_sides[i][1];
10313     int border_element;
10314
10315     if (!IN_LEV_FIELD(xx, yy))
10316       continue;
10317
10318     if (IS_PLAYER(x, y))
10319     {
10320       struct PlayerInfo *player = PLAYERINFO(x, y);
10321
10322       if (game.engine_version < VERSION_IDENT(3,0,7,0))
10323         border_element = Feld[xx][yy];          /* may be moving! */
10324       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10325         border_element = Feld[xx][yy];
10326       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
10327         border_element = MovingOrBlocked2Element(xx, yy);
10328       else
10329         continue;               /* center and border element do not touch */
10330
10331 #if 1
10332       /* !!! TEST ONLY !!! */
10333       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10334                                  player->index_bit, border_side);
10335       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10336                                           CE_OTHER_GETS_TOUCHED,
10337                                           player->index_bit, border_side);
10338 #else
10339       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10340                                           CE_OTHER_GETS_TOUCHED,
10341                                           player->index_bit, border_side);
10342       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10343                                  player->index_bit, border_side);
10344 #endif
10345     }
10346     else if (IS_PLAYER(xx, yy))
10347     {
10348       struct PlayerInfo *player = PLAYERINFO(xx, yy);
10349
10350       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10351       {
10352         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10353           continue;             /* center and border element do not touch */
10354       }
10355
10356 #if 1
10357       /* !!! TEST ONLY !!! */
10358       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10359                                  player->index_bit, center_side);
10360       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10361                                           CE_OTHER_GETS_TOUCHED,
10362                                           player->index_bit, center_side);
10363 #else
10364       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10365                                           CE_OTHER_GETS_TOUCHED,
10366                                           player->index_bit, center_side);
10367       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10368                                  player->index_bit, center_side);
10369 #endif
10370
10371       break;
10372     }
10373   }
10374 }
10375
10376 void TestIfElementTouchesCustomElement(int x, int y)
10377 {
10378   static int xy[4][2] =
10379   {
10380     { 0, -1 },
10381     { -1, 0 },
10382     { +1, 0 },
10383     { 0, +1 }
10384   };
10385   static int trigger_sides[4][2] =
10386   {
10387     /* center side      border side */
10388     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10389     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10390     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10391     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10392   };
10393   static int touch_dir[4] =
10394   {
10395     MV_LEFT | MV_RIGHT,
10396     MV_UP   | MV_DOWN,
10397     MV_UP   | MV_DOWN,
10398     MV_LEFT | MV_RIGHT
10399   };
10400   boolean change_center_element = FALSE;
10401   int center_element_change_page = 0;
10402   int center_element = Feld[x][y];      /* should always be non-moving! */
10403   int border_trigger_element = EL_UNDEFINED;
10404   int i, j;
10405
10406   for (i = 0; i < NUM_DIRECTIONS; i++)
10407   {
10408     int xx = x + xy[i][0];
10409     int yy = y + xy[i][1];
10410     int center_side = trigger_sides[i][0];
10411     int border_side = trigger_sides[i][1];
10412     int border_element;
10413
10414     if (!IN_LEV_FIELD(xx, yy))
10415       continue;
10416
10417     if (game.engine_version < VERSION_IDENT(3,0,7,0))
10418       border_element = Feld[xx][yy];    /* may be moving! */
10419     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10420       border_element = Feld[xx][yy];
10421     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
10422       border_element = MovingOrBlocked2Element(xx, yy);
10423     else
10424       continue;                 /* center and border element do not touch */
10425
10426     /* check for change of center element (but change it only once) */
10427     if (IS_CUSTOM_ELEMENT(center_element) &&
10428         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10429         !change_center_element)
10430     {
10431       for (j = 0; j < element_info[center_element].num_change_pages; j++)
10432       {
10433         struct ElementChangeInfo *change =
10434           &element_info[center_element].change_page[j];
10435
10436         if (change->can_change &&
10437             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10438             change->trigger_side & border_side &&
10439 #if 1
10440             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10441 #else
10442             change->trigger_element == border_element
10443 #endif
10444             )
10445         {
10446           change_center_element = TRUE;
10447           center_element_change_page = j;
10448           border_trigger_element = border_element;
10449
10450           break;
10451         }
10452       }
10453     }
10454
10455     /* check for change of border element */
10456     if (IS_CUSTOM_ELEMENT(border_element) &&
10457         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10458     {
10459       for (j = 0; j < element_info[border_element].num_change_pages; j++)
10460       {
10461         struct ElementChangeInfo *change =
10462           &element_info[border_element].change_page[j];
10463
10464         if (change->can_change &&
10465             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10466             change->trigger_side & center_side &&
10467 #if 1
10468             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10469 #else
10470             change->trigger_element == center_element
10471 #endif
10472             )
10473         {
10474 #if 0
10475           printf("::: border_element %d, %d\n", x, y);
10476 #endif
10477
10478           CheckElementChangeByPage(xx, yy, border_element, center_element,
10479                                    CE_OTHER_IS_TOUCHING, j);
10480           break;
10481         }
10482       }
10483     }
10484   }
10485
10486   if (change_center_element)
10487   {
10488 #if 0
10489     printf("::: center_element %d, %d\n", x, y);
10490 #endif
10491
10492     CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10493                              CE_OTHER_IS_TOUCHING, center_element_change_page);
10494   }
10495 }
10496
10497 void TestIfElementHitsCustomElement(int x, int y, int direction)
10498 {
10499   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10500   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10501   int hitx = x + dx, hity = y + dy;
10502   int hitting_element = Feld[x][y];
10503   int touched_element;
10504 #if 0
10505   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10506                         !IS_FREE(hitx, hity) &&
10507                         (!IS_MOVING(hitx, hity) ||
10508                          MovDir[hitx][hity] != direction ||
10509                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10510 #endif
10511
10512   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10513     return;
10514
10515 #if 0
10516   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10517     return;
10518 #endif
10519
10520   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10521                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10522
10523   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10524                            CE_HITTING_SOMETHING, direction);
10525
10526   if (IN_LEV_FIELD(hitx, hity))
10527   {
10528     int opposite_direction = MV_DIR_OPPOSITE(direction);
10529     int hitting_side = direction;
10530     int touched_side = opposite_direction;
10531 #if 0
10532     int touched_element = MovingOrBlocked2Element(hitx, hity);
10533 #endif
10534 #if 1
10535     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10536                           MovDir[hitx][hity] != direction ||
10537                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10538
10539     object_hit = TRUE;
10540 #endif
10541
10542     if (object_hit)
10543     {
10544       int i;
10545
10546       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10547                                CE_HIT_BY_SOMETHING, opposite_direction);
10548
10549       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10550           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10551       {
10552         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10553         {
10554           struct ElementChangeInfo *change =
10555             &element_info[hitting_element].change_page[i];
10556
10557           if (change->can_change &&
10558               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10559               change->trigger_side & touched_side &&
10560           
10561 #if 1
10562               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10563 #else
10564               change->trigger_element == touched_element
10565 #endif
10566               )
10567           {
10568             CheckElementChangeByPage(x, y, hitting_element, touched_element,
10569                                      CE_OTHER_IS_HITTING, i);
10570             break;
10571           }
10572         }
10573       }
10574
10575       if (IS_CUSTOM_ELEMENT(touched_element) &&
10576           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10577       {
10578         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10579         {
10580           struct ElementChangeInfo *change =
10581             &element_info[touched_element].change_page[i];
10582
10583           if (change->can_change &&
10584               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10585               change->trigger_side & hitting_side &&
10586 #if 1
10587               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10588 #else
10589               change->trigger_element == hitting_element
10590 #endif
10591               )
10592           {
10593             CheckElementChangeByPage(hitx, hity, touched_element,
10594                                      hitting_element, CE_OTHER_GETS_HIT, i);
10595             break;
10596           }
10597         }
10598       }
10599     }
10600   }
10601 }
10602
10603 #if 0
10604 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10605 {
10606   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10607   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10608   int hitx = x + dx, hity = y + dy;
10609   int hitting_element = Feld[x][y];
10610   int touched_element;
10611 #if 0
10612   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10613                         !IS_FREE(hitx, hity) &&
10614                         (!IS_MOVING(hitx, hity) ||
10615                          MovDir[hitx][hity] != direction ||
10616                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10617 #endif
10618
10619   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10620     return;
10621
10622 #if 0
10623   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10624     return;
10625 #endif
10626
10627   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10628                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10629
10630   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10631                            EP_CAN_SMASH_EVERYTHING, direction);
10632
10633   if (IN_LEV_FIELD(hitx, hity))
10634   {
10635     int opposite_direction = MV_DIR_OPPOSITE(direction);
10636     int hitting_side = direction;
10637     int touched_side = opposite_direction;
10638 #if 0
10639     int touched_element = MovingOrBlocked2Element(hitx, hity);
10640 #endif
10641 #if 1
10642     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10643                           MovDir[hitx][hity] != direction ||
10644                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10645
10646     object_hit = TRUE;
10647 #endif
10648
10649     if (object_hit)
10650     {
10651       int i;
10652
10653       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10654                                CE_SMASHED_BY_SOMETHING, opposite_direction);
10655
10656       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10657           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10658       {
10659         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10660         {
10661           struct ElementChangeInfo *change =
10662             &element_info[hitting_element].change_page[i];
10663
10664           if (change->can_change &&
10665               change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10666               change->trigger_side & touched_side &&
10667           
10668 #if 1
10669               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10670 #else
10671               change->trigger_element == touched_element
10672 #endif
10673               )
10674           {
10675             CheckElementChangeByPage(x, y, hitting_element, touched_element,
10676                                      CE_OTHER_IS_SMASHING, i);
10677             break;
10678           }
10679         }
10680       }
10681
10682       if (IS_CUSTOM_ELEMENT(touched_element) &&
10683           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10684       {
10685         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10686         {
10687           struct ElementChangeInfo *change =
10688             &element_info[touched_element].change_page[i];
10689
10690           if (change->can_change &&
10691               change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10692               change->trigger_side & hitting_side &&
10693 #if 1
10694               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10695 #else
10696               change->trigger_element == hitting_element
10697 #endif
10698               )
10699           {
10700             CheckElementChangeByPage(hitx, hity, touched_element,
10701                                      hitting_element, CE_OTHER_GETS_SMASHED,i);
10702             break;
10703           }
10704         }
10705       }
10706     }
10707   }
10708 }
10709 #endif
10710
10711 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10712 {
10713   int i, kill_x = -1, kill_y = -1;
10714   int bad_element = -1;
10715   static int test_xy[4][2] =
10716   {
10717     { 0, -1 },
10718     { -1, 0 },
10719     { +1, 0 },
10720     { 0, +1 }
10721   };
10722   static int test_dir[4] =
10723   {
10724     MV_UP,
10725     MV_LEFT,
10726     MV_RIGHT,
10727     MV_DOWN
10728   };
10729
10730   for (i = 0; i < NUM_DIRECTIONS; i++)
10731   {
10732     int test_x, test_y, test_move_dir, test_element;
10733
10734     test_x = good_x + test_xy[i][0];
10735     test_y = good_y + test_xy[i][1];
10736
10737     if (!IN_LEV_FIELD(test_x, test_y))
10738       continue;
10739
10740     test_move_dir =
10741       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10742
10743 #if 0
10744     test_element = Feld[test_x][test_y];
10745 #else
10746     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10747 #endif
10748
10749     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10750        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10751     */
10752     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10753         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
10754     {
10755       kill_x = test_x;
10756       kill_y = test_y;
10757       bad_element = test_element;
10758
10759       break;
10760     }
10761   }
10762
10763   if (kill_x != -1 || kill_y != -1)
10764   {
10765     if (IS_PLAYER(good_x, good_y))
10766     {
10767       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10768
10769 #if 1
10770       if (player->shield_deadly_time_left > 0 &&
10771           !IS_INDESTRUCTIBLE(bad_element))
10772         Bang(kill_x, kill_y);
10773       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10774         KillHero(player);
10775 #else
10776       if (player->shield_deadly_time_left > 0)
10777         Bang(kill_x, kill_y);
10778       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10779         KillHero(player);
10780 #endif
10781     }
10782     else
10783       Bang(good_x, good_y);
10784   }
10785 }
10786
10787 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10788 {
10789   int i, kill_x = -1, kill_y = -1;
10790   int bad_element = Feld[bad_x][bad_y];
10791   static int test_xy[4][2] =
10792   {
10793     { 0, -1 },
10794     { -1, 0 },
10795     { +1, 0 },
10796     { 0, +1 }
10797   };
10798   static int touch_dir[4] =
10799   {
10800     MV_LEFT | MV_RIGHT,
10801     MV_UP   | MV_DOWN,
10802     MV_UP   | MV_DOWN,
10803     MV_LEFT | MV_RIGHT
10804   };
10805   static int test_dir[4] =
10806   {
10807     MV_UP,
10808     MV_LEFT,
10809     MV_RIGHT,
10810     MV_DOWN
10811   };
10812
10813   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
10814     return;
10815
10816   for (i = 0; i < NUM_DIRECTIONS; i++)
10817   {
10818     int test_x, test_y, test_move_dir, test_element;
10819
10820     test_x = bad_x + test_xy[i][0];
10821     test_y = bad_y + test_xy[i][1];
10822     if (!IN_LEV_FIELD(test_x, test_y))
10823       continue;
10824
10825     test_move_dir =
10826       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10827
10828     test_element = Feld[test_x][test_y];
10829
10830     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10831        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10832     */
10833     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
10834         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
10835     {
10836       /* good thing is player or penguin that does not move away */
10837       if (IS_PLAYER(test_x, test_y))
10838       {
10839         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10840
10841         if (bad_element == EL_ROBOT && player->is_moving)
10842           continue;     /* robot does not kill player if he is moving */
10843
10844         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10845         {
10846           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10847             continue;           /* center and border element do not touch */
10848         }
10849
10850         kill_x = test_x;
10851         kill_y = test_y;
10852         break;
10853       }
10854       else if (test_element == EL_PENGUIN)
10855       {
10856         kill_x = test_x;
10857         kill_y = test_y;
10858         break;
10859       }
10860     }
10861   }
10862
10863   if (kill_x != -1 || kill_y != -1)
10864   {
10865     if (IS_PLAYER(kill_x, kill_y))
10866     {
10867       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10868
10869 #if 1
10870       if (player->shield_deadly_time_left > 0 &&
10871           !IS_INDESTRUCTIBLE(bad_element))
10872         Bang(bad_x, bad_y);
10873       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10874         KillHero(player);
10875 #else
10876       if (player->shield_deadly_time_left > 0)
10877         Bang(bad_x, bad_y);
10878       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10879         KillHero(player);
10880 #endif
10881     }
10882     else
10883       Bang(kill_x, kill_y);
10884   }
10885 }
10886
10887 void TestIfHeroTouchesBadThing(int x, int y)
10888 {
10889   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10890 }
10891
10892 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10893 {
10894   TestIfGoodThingHitsBadThing(x, y, move_dir);
10895 }
10896
10897 void TestIfBadThingTouchesHero(int x, int y)
10898 {
10899   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10900 }
10901
10902 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10903 {
10904   TestIfBadThingHitsGoodThing(x, y, move_dir);
10905 }
10906
10907 void TestIfFriendTouchesBadThing(int x, int y)
10908 {
10909   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10910 }
10911
10912 void TestIfBadThingTouchesFriend(int x, int y)
10913 {
10914   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10915 }
10916
10917 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10918 {
10919   int i, kill_x = bad_x, kill_y = bad_y;
10920   static int xy[4][2] =
10921   {
10922     { 0, -1 },
10923     { -1, 0 },
10924     { +1, 0 },
10925     { 0, +1 }
10926   };
10927
10928   for (i = 0; i < NUM_DIRECTIONS; i++)
10929   {
10930     int x, y, element;
10931
10932     x = bad_x + xy[i][0];
10933     y = bad_y + xy[i][1];
10934     if (!IN_LEV_FIELD(x, y))
10935       continue;
10936
10937     element = Feld[x][y];
10938     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10939         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10940     {
10941       kill_x = x;
10942       kill_y = y;
10943       break;
10944     }
10945   }
10946
10947   if (kill_x != bad_x || kill_y != bad_y)
10948     Bang(bad_x, bad_y);
10949 }
10950
10951 void KillHero(struct PlayerInfo *player)
10952 {
10953   int jx = player->jx, jy = player->jy;
10954
10955   if (!player->active)
10956     return;
10957
10958   /* remove accessible field at the player's position */
10959   Feld[jx][jy] = EL_EMPTY;
10960
10961   /* deactivate shield (else Bang()/Explode() would not work right) */
10962   player->shield_normal_time_left = 0;
10963   player->shield_deadly_time_left = 0;
10964
10965   Bang(jx, jy);
10966   BuryHero(player);
10967 }
10968
10969 static void KillHeroUnlessEnemyProtected(int x, int y)
10970 {
10971   if (!PLAYER_ENEMY_PROTECTED(x, y))
10972     KillHero(PLAYERINFO(x, y));
10973 }
10974
10975 static void KillHeroUnlessExplosionProtected(int x, int y)
10976 {
10977   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10978     KillHero(PLAYERINFO(x, y));
10979 }
10980
10981 void BuryHero(struct PlayerInfo *player)
10982 {
10983   int jx = player->jx, jy = player->jy;
10984
10985   if (!player->active)
10986     return;
10987
10988 #if 1
10989   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10990 #else
10991   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10992 #endif
10993   PlayLevelSound(jx, jy, SND_GAME_LOSING);
10994
10995   player->GameOver = TRUE;
10996   RemoveHero(player);
10997 }
10998
10999 void RemoveHero(struct PlayerInfo *player)
11000 {
11001   int jx = player->jx, jy = player->jy;
11002   int i, found = FALSE;
11003
11004   player->present = FALSE;
11005   player->active = FALSE;
11006
11007   if (!ExplodeField[jx][jy])
11008     StorePlayer[jx][jy] = 0;
11009
11010   for (i = 0; i < MAX_PLAYERS; i++)
11011     if (stored_player[i].active)
11012       found = TRUE;
11013
11014   if (!found)
11015     AllPlayersGone = TRUE;
11016
11017   ExitX = ZX = jx;
11018   ExitY = ZY = jy;
11019 }
11020
11021 /*
11022   =============================================================================
11023   checkDiagonalPushing()
11024   -----------------------------------------------------------------------------
11025   check if diagonal input device direction results in pushing of object
11026   (by checking if the alternative direction is walkable, diggable, ...)
11027   =============================================================================
11028 */
11029
11030 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11031                                     int x, int y, int real_dx, int real_dy)
11032 {
11033   int jx, jy, dx, dy, xx, yy;
11034
11035   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11036     return TRUE;
11037
11038   /* diagonal direction: check alternative direction */
11039   jx = player->jx;
11040   jy = player->jy;
11041   dx = x - jx;
11042   dy = y - jy;
11043   xx = jx + (dx == 0 ? real_dx : 0);
11044   yy = jy + (dy == 0 ? real_dy : 0);
11045
11046   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11047 }
11048
11049 /*
11050   =============================================================================
11051   DigField()
11052   -----------------------------------------------------------------------------
11053   x, y:                 field next to player (non-diagonal) to try to dig to
11054   real_dx, real_dy:     direction as read from input device (can be diagonal)
11055   =============================================================================
11056 */
11057
11058 int DigField(struct PlayerInfo *player,
11059              int oldx, int oldy, int x, int y,
11060              int real_dx, int real_dy, int mode)
11061 {
11062 #if 0
11063   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11064 #endif
11065   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11066   boolean player_was_pushing = player->is_pushing;
11067   int jx = oldx, jy = oldy;
11068   int dx = x - jx, dy = y - jy;
11069   int nextx = x + dx, nexty = y + dy;
11070   int move_direction = (dx == -1 ? MV_LEFT :
11071                         dx == +1 ? MV_RIGHT :
11072                         dy == -1 ? MV_UP :
11073                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
11074   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11075 #if 1
11076   int dig_side = MV_DIR_OPPOSITE(move_direction);
11077 #else
11078   static int trigger_sides[4] =
11079   {
11080     CH_SIDE_RIGHT,      /* moving left  */
11081     CH_SIDE_LEFT,       /* moving right */
11082     CH_SIDE_BOTTOM,     /* moving up    */
11083     CH_SIDE_TOP,        /* moving down  */
11084   };
11085   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11086 #endif
11087   int old_element = Feld[jx][jy];
11088   int element;
11089
11090   if (is_player)                /* function can also be called by EL_PENGUIN */
11091   {
11092     if (player->MovPos == 0)
11093     {
11094       player->is_digging = FALSE;
11095       player->is_collecting = FALSE;
11096     }
11097
11098     if (player->MovPos == 0)    /* last pushing move finished */
11099       player->is_pushing = FALSE;
11100
11101     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11102     {
11103       player->is_switching = FALSE;
11104 #if USE_NEW_PUSH_DELAY
11105       player->push_delay = -1;
11106 #else
11107       player->push_delay = 0;
11108 #endif
11109
11110       return MF_NO_ACTION;
11111     }
11112   }
11113
11114   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11115     return MF_NO_ACTION;
11116
11117 #if 0
11118
11119 #if 0
11120   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11121 #else
11122   if (IS_TUBE(Feld[jx][jy]) ||
11123       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11124 #endif
11125   {
11126     int i = 0;
11127     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11128     int tube_leave_directions[][2] =
11129     {
11130       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11131       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
11132       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
11133       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
11134       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
11135       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
11136       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
11137       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
11138       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
11139       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
11140       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
11141       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11142     };
11143
11144     while (tube_leave_directions[i][0] != tube_element)
11145     {
11146       i++;
11147       if (tube_leave_directions[i][0] == -1)    /* should not happen */
11148         break;
11149     }
11150
11151     if (!(tube_leave_directions[i][1] & move_direction))
11152       return MF_NO_ACTION;      /* tube has no opening in this direction */
11153   }
11154
11155 #else
11156
11157   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11158     old_element = Back[jx][jy];
11159
11160 #endif
11161
11162   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11163     return MF_NO_ACTION;        /* field has no opening in this direction */
11164
11165   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11166     return MF_NO_ACTION;        /* field has no opening in this direction */
11167
11168   element = Feld[x][y];
11169
11170   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
11171     return MF_NO_ACTION;
11172
11173   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11174       game.engine_version >= VERSION_IDENT(2,2,0,0))
11175     return MF_NO_ACTION;
11176
11177 #if 1
11178   if (game.gravity && is_player && !player->is_auto_moving &&
11179       canFallDown(player) && move_direction != MV_DOWN &&
11180       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11181     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
11182 #endif
11183
11184 #if 0
11185   if (element == EL_EMPTY_SPACE &&
11186       game.gravity && !player->is_auto_moving &&
11187       canFallDown(player) && move_direction != MV_DOWN)
11188     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
11189 #endif
11190
11191   switch (element)
11192   {
11193 #if 0
11194     case EL_SP_PORT_LEFT:
11195     case EL_SP_PORT_RIGHT:
11196     case EL_SP_PORT_UP:
11197     case EL_SP_PORT_DOWN:
11198     case EL_SP_PORT_HORIZONTAL:
11199     case EL_SP_PORT_VERTICAL:
11200     case EL_SP_PORT_ANY:
11201     case EL_SP_GRAVITY_PORT_LEFT:
11202     case EL_SP_GRAVITY_PORT_RIGHT:
11203     case EL_SP_GRAVITY_PORT_UP:
11204     case EL_SP_GRAVITY_PORT_DOWN:
11205 #if 1
11206       if (!canEnterSupaplexPort(x, y, dx, dy))
11207         return MF_NO_ACTION;
11208 #else
11209       if ((dx == -1 &&
11210            element != EL_SP_PORT_LEFT &&
11211            element != EL_SP_GRAVITY_PORT_LEFT &&
11212            element != EL_SP_PORT_HORIZONTAL &&
11213            element != EL_SP_PORT_ANY) ||
11214           (dx == +1 &&
11215            element != EL_SP_PORT_RIGHT &&
11216            element != EL_SP_GRAVITY_PORT_RIGHT &&
11217            element != EL_SP_PORT_HORIZONTAL &&
11218            element != EL_SP_PORT_ANY) ||
11219           (dy == -1 &&
11220            element != EL_SP_PORT_UP &&
11221            element != EL_SP_GRAVITY_PORT_UP &&
11222            element != EL_SP_PORT_VERTICAL &&
11223            element != EL_SP_PORT_ANY) ||
11224           (dy == +1 &&
11225            element != EL_SP_PORT_DOWN &&
11226            element != EL_SP_GRAVITY_PORT_DOWN &&
11227            element != EL_SP_PORT_VERTICAL &&
11228            element != EL_SP_PORT_ANY) ||
11229           !IN_LEV_FIELD(nextx, nexty) ||
11230           !IS_FREE(nextx, nexty))
11231         return MF_NO_ACTION;
11232 #endif
11233
11234       if (element == EL_SP_GRAVITY_PORT_LEFT ||
11235           element == EL_SP_GRAVITY_PORT_RIGHT ||
11236           element == EL_SP_GRAVITY_PORT_UP ||
11237           element == EL_SP_GRAVITY_PORT_DOWN)
11238         game.gravity = !game.gravity;
11239
11240       /* automatically move to the next field with double speed */
11241       player->programmed_action = move_direction;
11242 #if 1
11243       if (player->move_delay_reset_counter == 0)
11244       {
11245         player->move_delay_reset_counter = 2;   /* two double speed steps */
11246
11247         DOUBLE_PLAYER_SPEED(player);
11248       }
11249 #else
11250       player->move_delay_reset_counter = 2;
11251
11252       DOUBLE_PLAYER_SPEED(player);
11253 #endif
11254
11255 #if 0
11256       printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11257 #endif
11258
11259       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11260       break;
11261 #endif
11262
11263 #if 0
11264     case EL_TUBE_ANY:
11265     case EL_TUBE_VERTICAL:
11266     case EL_TUBE_HORIZONTAL:
11267     case EL_TUBE_VERTICAL_LEFT:
11268     case EL_TUBE_VERTICAL_RIGHT:
11269     case EL_TUBE_HORIZONTAL_UP:
11270     case EL_TUBE_HORIZONTAL_DOWN:
11271     case EL_TUBE_LEFT_UP:
11272     case EL_TUBE_LEFT_DOWN:
11273     case EL_TUBE_RIGHT_UP:
11274     case EL_TUBE_RIGHT_DOWN:
11275       {
11276         int i = 0;
11277         int tube_enter_directions[][2] =
11278         {
11279           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11280           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
11281           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
11282           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
11283           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
11284           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
11285           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
11286           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
11287           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
11288           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
11289           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
11290           { -1,                         MV_NO_MOVING                         }
11291         };
11292
11293         while (tube_enter_directions[i][0] != element)
11294         {
11295           i++;
11296           if (tube_enter_directions[i][0] == -1)        /* should not happen */
11297             break;
11298         }
11299
11300         if (!(tube_enter_directions[i][1] & move_direction))
11301           return MF_NO_ACTION;  /* tube has no opening in this direction */
11302
11303         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11304       }
11305       break;
11306 #endif
11307
11308     default:
11309
11310 #if 1
11311       if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11312 #else
11313       if (IS_WALKABLE(element))
11314 #endif
11315       {
11316         int sound_element = SND_ELEMENT(element);
11317         int sound_action = ACTION_WALKING;
11318
11319 #if 0
11320         if (!ACCESS_FROM(element, opposite_direction))
11321           return MF_NO_ACTION;  /* field not accessible from this direction */
11322 #endif
11323
11324 #if 0
11325         if (element == EL_EMPTY_SPACE &&
11326             game.gravity && !player->is_auto_moving &&
11327             canFallDown(player) && move_direction != MV_DOWN)
11328           return MF_NO_ACTION;  /* player cannot walk here due to gravity */
11329 #endif
11330
11331         if (IS_GATE(element))
11332         {
11333           if (!player->key[element - EL_GATE_1])
11334             return MF_NO_ACTION;
11335         }
11336         else if (IS_GATE_GRAY(element))
11337         {
11338           if (!player->key[element - EL_GATE_1_GRAY])
11339             return MF_NO_ACTION;
11340         }
11341         else if (element == EL_EXIT_OPEN ||
11342                  element == EL_SP_EXIT_OPEN ||
11343                  element == EL_SP_EXIT_OPENING)
11344         {
11345           sound_action = ACTION_PASSING;        /* player is passing exit */
11346         }
11347         else if (element == EL_EMPTY)
11348         {
11349           sound_action = ACTION_MOVING;         /* nothing to walk on */
11350         }
11351
11352         /* play sound from background or player, whatever is available */
11353         if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11354           PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11355         else
11356           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11357
11358         break;
11359       }
11360 #if 1
11361       else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11362 #else
11363       else if (IS_PASSABLE(element))
11364 #endif
11365       {
11366 #if 0
11367         if (!canPassField(x, y, move_direction))
11368           return MF_NO_ACTION;
11369 #else
11370
11371 #if 0
11372 #if 1
11373         if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11374             !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11375             (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11376           return MF_NO_ACTION;
11377 #else
11378         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11379           return MF_NO_ACTION;
11380 #endif
11381 #endif
11382
11383 #if 1
11384         if (!ACCESS_FROM(element, opposite_direction))
11385           return MF_NO_ACTION;  /* field not accessible from this direction */
11386 #else
11387         if (IS_CUSTOM_ELEMENT(element) &&
11388             !ACCESS_FROM(element, opposite_direction))
11389           return MF_NO_ACTION;  /* field not accessible from this direction */
11390 #endif
11391
11392 #if 1
11393         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
11394           return MF_NO_ACTION;
11395 #endif
11396
11397 #endif
11398
11399         if (IS_EM_GATE(element))
11400         {
11401           if (!player->key[element - EL_EM_GATE_1])
11402             return MF_NO_ACTION;
11403         }
11404         else if (IS_EM_GATE_GRAY(element))
11405         {
11406           if (!player->key[element - EL_EM_GATE_1_GRAY])
11407             return MF_NO_ACTION;
11408         }
11409         else if (IS_SP_PORT(element))
11410         {
11411           if (element == EL_SP_GRAVITY_PORT_LEFT ||
11412               element == EL_SP_GRAVITY_PORT_RIGHT ||
11413               element == EL_SP_GRAVITY_PORT_UP ||
11414               element == EL_SP_GRAVITY_PORT_DOWN)
11415             game.gravity = !game.gravity;
11416           else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11417                    element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11418                    element == EL_SP_GRAVITY_ON_PORT_UP ||
11419                    element == EL_SP_GRAVITY_ON_PORT_DOWN)
11420             game.gravity = TRUE;
11421           else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11422                    element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11423                    element == EL_SP_GRAVITY_OFF_PORT_UP ||
11424                    element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11425             game.gravity = FALSE;
11426         }
11427
11428         /* automatically move to the next field with double speed */
11429         player->programmed_action = move_direction;
11430 #if 1
11431         if (player->move_delay_reset_counter == 0)
11432         {
11433           player->move_delay_reset_counter = 2; /* two double speed steps */
11434
11435           DOUBLE_PLAYER_SPEED(player);
11436         }
11437 #else
11438         player->move_delay_reset_counter = 2;
11439
11440         DOUBLE_PLAYER_SPEED(player);
11441 #endif
11442
11443         PlayLevelSoundAction(x, y, ACTION_PASSING);
11444
11445         break;
11446       }
11447       else if (IS_DIGGABLE(element))
11448       {
11449         RemoveField(x, y);
11450
11451         if (mode != DF_SNAP)
11452         {
11453 #if 1
11454           GfxElement[x][y] = GFX_ELEMENT(element);
11455 #else
11456           GfxElement[x][y] =
11457             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11458 #endif
11459           player->is_digging = TRUE;
11460         }
11461
11462         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11463
11464         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11465                                             player->index_bit, dig_side);
11466
11467 #if 1
11468         if (mode == DF_SNAP)
11469           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
11470 #endif
11471
11472         break;
11473       }
11474       else if (IS_COLLECTIBLE(element))
11475       {
11476         RemoveField(x, y);
11477
11478         if (is_player && mode != DF_SNAP)
11479         {
11480           GfxElement[x][y] = element;
11481           player->is_collecting = TRUE;
11482         }
11483
11484         if (element == EL_SPEED_PILL)
11485           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11486         else if (element == EL_EXTRA_TIME && level.time > 0)
11487         {
11488           TimeLeft += 10;
11489           DrawGameValue_Time(TimeLeft);
11490         }
11491         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11492         {
11493           player->shield_normal_time_left += 10;
11494           if (element == EL_SHIELD_DEADLY)
11495             player->shield_deadly_time_left += 10;
11496         }
11497         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11498         {
11499           if (player->inventory_size < MAX_INVENTORY_SIZE)
11500             player->inventory_element[player->inventory_size++] = element;
11501
11502           DrawGameValue_Dynamite(local_player->inventory_size);
11503         }
11504         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11505         {
11506           player->dynabomb_count++;
11507           player->dynabombs_left++;
11508         }
11509         else if (element == EL_DYNABOMB_INCREASE_SIZE)
11510         {
11511           player->dynabomb_size++;
11512         }
11513         else if (element == EL_DYNABOMB_INCREASE_POWER)
11514         {
11515           player->dynabomb_xl = TRUE;
11516         }
11517         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11518                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11519         {
11520           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11521                         element - EL_KEY_1 : element - EL_EM_KEY_1);
11522
11523           player->key[key_nr] = TRUE;
11524
11525           DrawGameValue_Keys(player);
11526
11527           redraw_mask |= REDRAW_DOOR_1;
11528         }
11529         else if (IS_ENVELOPE(element))
11530         {
11531 #if 1
11532           player->show_envelope = element;
11533 #else
11534           ShowEnvelope(element - EL_ENVELOPE_1);
11535 #endif
11536         }
11537         else if (IS_DROPPABLE(element) ||
11538                  IS_THROWABLE(element)) /* can be collected and dropped */
11539         {
11540           int i;
11541
11542           if (element_info[element].collect_count == 0)
11543             player->inventory_infinite_element = element;
11544           else
11545             for (i = 0; i < element_info[element].collect_count; i++)
11546               if (player->inventory_size < MAX_INVENTORY_SIZE)
11547                 player->inventory_element[player->inventory_size++] = element;
11548
11549           DrawGameValue_Dynamite(local_player->inventory_size);
11550         }
11551         else if (element_info[element].collect_count > 0)
11552         {
11553           local_player->gems_still_needed -=
11554             element_info[element].collect_count;
11555           if (local_player->gems_still_needed < 0)
11556             local_player->gems_still_needed = 0;
11557
11558           DrawGameValue_Emeralds(local_player->gems_still_needed);
11559         }
11560
11561         RaiseScoreElement(element);
11562         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11563
11564         if (is_player)
11565           CheckTriggeredElementChangeByPlayer(x, y, element,
11566                                               CE_OTHER_GETS_COLLECTED,
11567                                               player->index_bit, dig_side);
11568
11569 #if 1
11570         if (mode == DF_SNAP)
11571           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
11572 #endif
11573
11574         break;
11575       }
11576       else if (IS_PUSHABLE(element))
11577       {
11578         if (mode == DF_SNAP && element != EL_BD_ROCK)
11579           return MF_NO_ACTION;
11580
11581         if (CAN_FALL(element) && dy)
11582           return MF_NO_ACTION;
11583
11584         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11585             !(element == EL_SPRING && level.use_spring_bug))
11586           return MF_NO_ACTION;
11587
11588 #if 1
11589         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11590             ((move_direction & MV_VERTICAL &&
11591               ((element_info[element].move_pattern & MV_LEFT &&
11592                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11593                (element_info[element].move_pattern & MV_RIGHT &&
11594                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11595              (move_direction & MV_HORIZONTAL &&
11596               ((element_info[element].move_pattern & MV_UP &&
11597                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11598                (element_info[element].move_pattern & MV_DOWN &&
11599                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11600           return MF_NO_ACTION;
11601 #endif
11602
11603 #if 1
11604         /* do not push elements already moving away faster than player */
11605         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11606             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11607           return MF_NO_ACTION;
11608 #else
11609         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11610           return MF_NO_ACTION;
11611 #endif
11612
11613 #if 1
11614
11615 #if 1
11616         if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11617         {
11618           if (player->push_delay_value == -1 || !player_was_pushing)
11619             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11620         }
11621         else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11622         {
11623           if (player->push_delay_value == -1)
11624             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11625         }
11626 #else
11627         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11628         {
11629           if (player->push_delay_value == -1 || !player_was_pushing)
11630             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11631         }
11632 #endif
11633         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11634         {
11635           if (!player->is_pushing)
11636             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11637         }
11638
11639         /*
11640         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11641             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11642              !player_is_pushing))
11643           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11644         */
11645 #else
11646         if (!player->is_pushing &&
11647             game.engine_version >= VERSION_IDENT(2,2,0,7))
11648           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11649 #endif
11650
11651 #if 0
11652         printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11653                player->push_delay, player->push_delay_value,
11654                FrameCounter, game.engine_version,
11655                player_was_pushing, player->is_pushing,
11656                element, element_info[element].token_name,
11657                GET_NEW_PUSH_DELAY(element));
11658 #endif
11659
11660         player->is_pushing = TRUE;
11661
11662         if (!(IN_LEV_FIELD(nextx, nexty) &&
11663               (IS_FREE(nextx, nexty) ||
11664                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11665                 IS_SB_ELEMENT(element)))))
11666           return MF_NO_ACTION;
11667
11668         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11669           return MF_NO_ACTION;
11670
11671 #if USE_NEW_PUSH_DELAY
11672
11673 #if 0
11674         if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11675           printf("::: ALERT: %d, %d [%d / %d]\n",
11676                  player->push_delay, player->push_delay2,
11677                  FrameCounter, FrameCounter / 50);
11678 #endif
11679
11680         if (player->push_delay == -1)   /* new pushing; restart delay */
11681           player->push_delay = 0;
11682 #else
11683         if (player->push_delay == 0)    /* new pushing; restart delay */
11684           player->push_delay = FrameCounter;
11685 #endif
11686
11687 #if USE_NEW_PUSH_DELAY
11688 #if 0
11689         if ( (player->push_delay > 0) != (!xxx_fr) )
11690           printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11691                  player->push_delay,
11692                  xxx_pdv2, player->push_delay2, player->push_delay_value,
11693                  FrameCounter, FrameCounter / 50);
11694 #endif
11695
11696 #if 0
11697         if (player->push_delay > 0 &&
11698             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11699             element != EL_SPRING && element != EL_BALLOON)
11700 #else
11701         /* !!! */
11702         if (player->push_delay < player->push_delay_value &&
11703             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11704             element != EL_SPRING && element != EL_BALLOON)
11705 #endif
11706
11707 #else
11708         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11709             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11710             element != EL_SPRING && element != EL_BALLOON)
11711 #endif
11712         {
11713           /* make sure that there is no move delay before next try to push */
11714 #if USE_NEW_MOVE_DELAY
11715           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11716             player->move_delay = 0;
11717 #else
11718           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11719             player->move_delay = INITIAL_MOVE_DELAY_OFF;
11720 #endif
11721
11722           return MF_NO_ACTION;
11723         }
11724
11725 #if 0
11726         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11727 #endif
11728
11729         if (IS_SB_ELEMENT(element))
11730         {
11731           if (element == EL_SOKOBAN_FIELD_FULL)
11732           {
11733             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11734             local_player->sokobanfields_still_needed++;
11735           }
11736
11737           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11738           {
11739             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11740             local_player->sokobanfields_still_needed--;
11741           }
11742
11743           Feld[x][y] = EL_SOKOBAN_OBJECT;
11744
11745           if (Back[x][y] == Back[nextx][nexty])
11746             PlayLevelSoundAction(x, y, ACTION_PUSHING);
11747           else if (Back[x][y] != 0)
11748             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11749                                         ACTION_EMPTYING);
11750           else
11751             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11752                                         ACTION_FILLING);
11753
11754           if (local_player->sokobanfields_still_needed == 0 &&
11755               game.emulation == EMU_SOKOBAN)
11756           {
11757             player->LevelSolved = player->GameOver = TRUE;
11758             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11759           }
11760         }
11761         else
11762           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11763
11764         InitMovingField(x, y, move_direction);
11765         GfxAction[x][y] = ACTION_PUSHING;
11766
11767         if (mode == DF_SNAP)
11768           ContinueMoving(x, y);
11769         else
11770           MovPos[x][y] = (dx != 0 ? dx : dy);
11771
11772         Pushed[x][y] = TRUE;
11773         Pushed[nextx][nexty] = TRUE;
11774
11775         if (game.engine_version < VERSION_IDENT(2,2,0,7))
11776           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11777         else
11778           player->push_delay_value = -1;        /* get new value later */
11779
11780 #if USE_PUSH_BUGFIX
11781         /* now: check for element change _after_ element has been pushed! */
11782 #if 1
11783         if (game.use_bug_change_when_pushing)
11784 #else
11785         if (game.engine_version < VERSION_IDENT(3,1,0,0))
11786 #endif
11787         {
11788           CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11789                                      player->index_bit, dig_side);
11790           CheckTriggeredElementChangeByPlayer(x,y,element,CE_OTHER_GETS_PUSHED,
11791                                               player->index_bit, dig_side);
11792         }
11793
11794 #else
11795
11796 #if 1
11797         /* check for element change _after_ element has been pushed! */
11798 #else
11799
11800 #if 1
11801         /* !!! TEST ONLY !!! */
11802         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11803                                    player->index_bit, dig_side);
11804         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11805                                             player->index_bit, dig_side);
11806 #else
11807         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11808                                             player->index_bit, dig_side);
11809         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11810                                    player->index_bit, dig_side);
11811 #endif
11812 #endif
11813
11814 #endif
11815
11816         break;
11817       }
11818       else if (IS_SWITCHABLE(element))
11819       {
11820         if (PLAYER_SWITCHING(player, x, y))
11821         {
11822           CheckTriggeredElementChangeByPlayer(x,y, element,
11823                                               CE_OTHER_GETS_PRESSED,
11824                                               player->index_bit, dig_side);
11825
11826           return MF_ACTION;
11827         }
11828
11829         player->is_switching = TRUE;
11830         player->switch_x = x;
11831         player->switch_y = y;
11832
11833         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11834
11835         if (element == EL_ROBOT_WHEEL)
11836         {
11837           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11838           ZX = x;
11839           ZY = y;
11840
11841           DrawLevelField(x, y);
11842         }
11843         else if (element == EL_SP_TERMINAL)
11844         {
11845           int xx, yy;
11846
11847           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11848           {
11849             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11850               Bang(xx, yy);
11851             else if (Feld[xx][yy] == EL_SP_TERMINAL)
11852               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11853           }
11854         }
11855         else if (IS_BELT_SWITCH(element))
11856         {
11857           ToggleBeltSwitch(x, y);
11858         }
11859         else if (element == EL_SWITCHGATE_SWITCH_UP ||
11860                  element == EL_SWITCHGATE_SWITCH_DOWN)
11861         {
11862           ToggleSwitchgateSwitch(x, y);
11863         }
11864         else if (element == EL_LIGHT_SWITCH ||
11865                  element == EL_LIGHT_SWITCH_ACTIVE)
11866         {
11867           ToggleLightSwitch(x, y);
11868
11869 #if 0
11870           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11871                          SND_LIGHT_SWITCH_ACTIVATING :
11872                          SND_LIGHT_SWITCH_DEACTIVATING);
11873 #endif
11874         }
11875         else if (element == EL_TIMEGATE_SWITCH)
11876         {
11877           ActivateTimegateSwitch(x, y);
11878         }
11879         else if (element == EL_BALLOON_SWITCH_LEFT ||
11880                  element == EL_BALLOON_SWITCH_RIGHT ||
11881                  element == EL_BALLOON_SWITCH_UP ||
11882                  element == EL_BALLOON_SWITCH_DOWN ||
11883                  element == EL_BALLOON_SWITCH_ANY)
11884         {
11885           if (element == EL_BALLOON_SWITCH_ANY)
11886             game.balloon_dir = move_direction;
11887           else
11888             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
11889                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11890                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
11891                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
11892                                 MV_NO_MOVING);
11893         }
11894         else if (element == EL_LAMP)
11895         {
11896           Feld[x][y] = EL_LAMP_ACTIVE;
11897           local_player->lights_still_needed--;
11898
11899           DrawLevelField(x, y);
11900         }
11901         else if (element == EL_TIME_ORB_FULL)
11902         {
11903           Feld[x][y] = EL_TIME_ORB_EMPTY;
11904           TimeLeft += 10;
11905           DrawGameValue_Time(TimeLeft);
11906
11907           DrawLevelField(x, y);
11908
11909 #if 0
11910           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11911 #endif
11912         }
11913
11914         CheckTriggeredElementChangeByPlayer(x, y, element,
11915                                             CE_OTHER_IS_SWITCHING,
11916                                             player->index_bit, dig_side);
11917
11918         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11919                                             player->index_bit, dig_side);
11920
11921         return MF_ACTION;
11922       }
11923       else
11924       {
11925         if (!PLAYER_SWITCHING(player, x, y))
11926         {
11927           player->is_switching = TRUE;
11928           player->switch_x = x;
11929           player->switch_y = y;
11930
11931 #if 1
11932           /* !!! TEST ONLY !!! */
11933           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11934                                      player->index_bit, dig_side);
11935           CheckTriggeredElementChangeByPlayer(x, y, element,
11936                                               CE_OTHER_IS_SWITCHING,
11937                                               player->index_bit, dig_side);
11938 #else
11939           CheckTriggeredElementChangeByPlayer(x, y, element,
11940                                               CE_OTHER_IS_SWITCHING,
11941                                               player->index_bit, dig_side);
11942           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11943                                      player->index_bit, dig_side);
11944 #endif
11945         }
11946
11947 #if 1
11948         /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11949         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11950                                    player->index_bit, dig_side);
11951         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11952                                             player->index_bit, dig_side);
11953 #else
11954         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11955                                             player->index_bit, dig_side);
11956         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11957                                    player->index_bit, dig_side);
11958 #endif
11959       }
11960
11961       return MF_NO_ACTION;
11962   }
11963
11964 #if USE_NEW_PUSH_DELAY
11965   player->push_delay = -1;
11966 #else
11967   player->push_delay = 0;
11968 #endif
11969
11970   if (Feld[x][y] != element)            /* really digged/collected something */
11971     player->is_collecting = !player->is_digging;
11972
11973   return MF_MOVING;
11974 }
11975
11976 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11977 {
11978   int jx = player->jx, jy = player->jy;
11979   int x = jx + dx, y = jy + dy;
11980   int snap_direction = (dx == -1 ? MV_LEFT :
11981                         dx == +1 ? MV_RIGHT :
11982                         dy == -1 ? MV_UP :
11983                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
11984
11985 #if 0
11986   if (player->MovPos != 0)
11987     return FALSE;
11988 #else
11989   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11990     return FALSE;
11991 #endif
11992
11993   if (!player->active || !IN_LEV_FIELD(x, y))
11994     return FALSE;
11995
11996   if (dx && dy)
11997     return FALSE;
11998
11999   if (!dx && !dy)
12000   {
12001     if (player->MovPos == 0)
12002       player->is_pushing = FALSE;
12003
12004     player->is_snapping = FALSE;
12005
12006     if (player->MovPos == 0)
12007     {
12008       player->is_moving = FALSE;
12009       player->is_digging = FALSE;
12010       player->is_collecting = FALSE;
12011     }
12012
12013     return FALSE;
12014   }
12015
12016   if (player->is_snapping)
12017     return FALSE;
12018
12019   player->MovDir = snap_direction;
12020
12021 #if 1
12022   if (player->MovPos == 0)
12023 #endif
12024   {
12025     player->is_moving = FALSE;
12026     player->is_digging = FALSE;
12027     player->is_collecting = FALSE;
12028   }
12029
12030   player->is_dropping = FALSE;
12031
12032   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12033     return FALSE;
12034
12035   player->is_snapping = TRUE;
12036
12037 #if 1
12038   if (player->MovPos == 0)
12039 #endif
12040   {
12041     player->is_moving = FALSE;
12042     player->is_digging = FALSE;
12043     player->is_collecting = FALSE;
12044   }
12045
12046 #if 1
12047   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12048     DrawLevelField(player->last_jx, player->last_jy);
12049 #endif
12050
12051   DrawLevelField(x, y);
12052
12053 #if 0
12054   BackToFront();
12055 #endif
12056
12057   return TRUE;
12058 }
12059
12060 boolean DropElement(struct PlayerInfo *player)
12061 {
12062   int old_element, new_element;
12063   int dropx = player->jx, dropy = player->jy;
12064   int drop_direction = player->MovDir;
12065 #if 1
12066   int drop_side = drop_direction;
12067 #else
12068   static int trigger_sides[4] =
12069   {
12070     CH_SIDE_LEFT,       /* dropping left  */
12071     CH_SIDE_RIGHT,      /* dropping right */
12072     CH_SIDE_TOP,        /* dropping up    */
12073     CH_SIDE_BOTTOM,     /* dropping down  */
12074   };
12075   int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12076 #endif
12077   int drop_element = (player->inventory_size > 0 ?
12078                       player->inventory_element[player->inventory_size - 1] :
12079                       player->inventory_infinite_element != EL_UNDEFINED ?
12080                       player->inventory_infinite_element :
12081                       player->dynabombs_left > 0 ?
12082                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12083                       EL_UNDEFINED);
12084
12085   if (IS_THROWABLE(drop_element))
12086   {
12087     dropx += GET_DX_FROM_DIR(drop_direction);
12088     dropy += GET_DY_FROM_DIR(drop_direction);
12089
12090     if (!IN_LEV_FIELD(dropx, dropy))
12091       return FALSE;
12092   }
12093
12094   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12095   new_element = drop_element;           /* default: no change when dropping */
12096
12097   /* check if player is active, not moving and ready to drop */
12098   if (!player->active || player->MovPos || player->drop_delay > 0)
12099     return FALSE;
12100
12101   /* check if player has anything that can be dropped */
12102 #if 1
12103   if (new_element == EL_UNDEFINED)
12104     return FALSE;
12105 #else
12106   if (player->inventory_size == 0 &&
12107       player->inventory_infinite_element == EL_UNDEFINED &&
12108       player->dynabombs_left == 0)
12109     return FALSE;
12110 #endif
12111
12112   /* check if anything can be dropped at the current position */
12113   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12114     return FALSE;
12115
12116   /* collected custom elements can only be dropped on empty fields */
12117 #if 1
12118   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12119     return FALSE;
12120 #else
12121   if (player->inventory_size > 0 &&
12122       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12123       && old_element != EL_EMPTY)
12124     return FALSE;
12125 #endif
12126
12127   if (old_element != EL_EMPTY)
12128     Back[dropx][dropy] = old_element;   /* store old element on this field */
12129
12130   ResetGfxAnimation(dropx, dropy);
12131   ResetRandomAnimationValue(dropx, dropy);
12132
12133   if (player->inventory_size > 0 ||
12134       player->inventory_infinite_element != EL_UNDEFINED)
12135   {
12136     if (player->inventory_size > 0)
12137     {
12138       player->inventory_size--;
12139
12140 #if 0
12141       new_element = player->inventory_element[player->inventory_size];
12142 #endif
12143
12144       DrawGameValue_Dynamite(local_player->inventory_size);
12145
12146       if (new_element == EL_DYNAMITE)
12147         new_element = EL_DYNAMITE_ACTIVE;
12148       else if (new_element == EL_SP_DISK_RED)
12149         new_element = EL_SP_DISK_RED_ACTIVE;
12150     }
12151
12152     Feld[dropx][dropy] = new_element;
12153
12154     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12155       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12156                           el2img(Feld[dropx][dropy]), 0);
12157
12158     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12159
12160 #if 1
12161     /* needed if previous element just changed to "empty" in the last frame */
12162     Changed[dropx][dropy] = 0;          /* allow another change */
12163 #endif
12164
12165 #if 1
12166     /* !!! TEST ONLY !!! */
12167     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12168                                player->index_bit, drop_side);
12169     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12170                                         CE_OTHER_GETS_DROPPED,
12171                                         player->index_bit, drop_side);
12172 #else
12173     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12174                                         CE_OTHER_GETS_DROPPED,
12175                                         player->index_bit, drop_side);
12176     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12177                                player->index_bit, drop_side);
12178 #endif
12179
12180     TestIfElementTouchesCustomElement(dropx, dropy);
12181   }
12182   else          /* player is dropping a dyna bomb */
12183   {
12184     player->dynabombs_left--;
12185
12186 #if 0
12187     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12188 #endif
12189
12190     Feld[dropx][dropy] = new_element;
12191
12192     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12193       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12194                           el2img(Feld[dropx][dropy]), 0);
12195
12196     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12197   }
12198
12199
12200
12201 #if 1
12202
12203   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12204   {
12205 #if 1
12206     InitField_WithBug1(dropx, dropy, FALSE);
12207 #else
12208     InitField(dropx, dropy, FALSE);
12209     if (CAN_MOVE(Feld[dropx][dropy]))
12210       InitMovDir(dropx, dropy);
12211 #endif
12212   }
12213
12214   new_element = Feld[dropx][dropy];     /* element might have changed */
12215
12216   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12217       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12218   {
12219 #if 0
12220     int move_stepsize = element_info[new_element].move_stepsize;
12221 #endif
12222     int move_direction, nextx, nexty;
12223
12224     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12225       MovDir[dropx][dropy] = drop_direction;
12226
12227     move_direction = MovDir[dropx][dropy];
12228     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12229     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12230
12231 #if 1
12232     Changed[dropx][dropy] = 0;          /* allow another change */
12233     CheckCollision[dropx][dropy] = 2;
12234 #else
12235
12236     if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12237     {
12238 #if 0
12239       WasJustMoving[dropx][dropy] = 3;
12240 #else
12241 #if 1
12242       InitMovingField(dropx, dropy, move_direction);
12243       ContinueMoving(dropx, dropy);
12244 #endif
12245 #endif
12246     }
12247 #if 0
12248     /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12249     else
12250     {
12251       Changed[dropx][dropy] = 0;        /* allow another change */
12252
12253 #if 1
12254       TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12255 #else
12256       CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12257                                CE_HITTING_SOMETHING, move_direction);
12258 #endif
12259     }
12260 #endif
12261
12262 #endif
12263
12264 #if 0
12265     player->drop_delay = 2 * TILEX / move_stepsize + 1;
12266 #endif
12267   }
12268
12269 #if 0
12270   player->drop_delay = 8 + 8 + 8;
12271 #endif
12272
12273 #if 1
12274   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12275 #endif
12276
12277 #endif
12278
12279   player->is_dropping = TRUE;
12280
12281
12282   return TRUE;
12283 }
12284
12285 /* ------------------------------------------------------------------------- */
12286 /* game sound playing functions                                              */
12287 /* ------------------------------------------------------------------------- */
12288
12289 static int *loop_sound_frame = NULL;
12290 static int *loop_sound_volume = NULL;
12291
12292 void InitPlayLevelSound()
12293 {
12294   int num_sounds = getSoundListSize();
12295
12296   checked_free(loop_sound_frame);
12297   checked_free(loop_sound_volume);
12298
12299   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12300   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12301 }
12302
12303 static void PlayLevelSound(int x, int y, int nr)
12304 {
12305   int sx = SCREENX(x), sy = SCREENY(y);
12306   int volume, stereo_position;
12307   int max_distance = 8;
12308   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12309
12310   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12311       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12312     return;
12313
12314   if (!IN_LEV_FIELD(x, y) ||
12315       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12316       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12317     return;
12318
12319   volume = SOUND_MAX_VOLUME;
12320
12321   if (!IN_SCR_FIELD(sx, sy))
12322   {
12323     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12324     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12325
12326     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12327   }
12328
12329   stereo_position = (SOUND_MAX_LEFT +
12330                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12331                      (SCR_FIELDX + 2 * max_distance));
12332
12333   if (IS_LOOP_SOUND(nr))
12334   {
12335     /* This assures that quieter loop sounds do not overwrite louder ones,
12336        while restarting sound volume comparison with each new game frame. */
12337
12338     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12339       return;
12340
12341     loop_sound_volume[nr] = volume;
12342     loop_sound_frame[nr] = FrameCounter;
12343   }
12344
12345   PlaySoundExt(nr, volume, stereo_position, type);
12346 }
12347
12348 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12349 {
12350   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12351                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12352                  y < LEVELY(BY1) ? LEVELY(BY1) :
12353                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12354                  sound_action);
12355 }
12356
12357 static void PlayLevelSoundAction(int x, int y, int action)
12358 {
12359   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12360 }
12361
12362 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12363 {
12364   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12365
12366   if (sound_effect != SND_UNDEFINED)
12367     PlayLevelSound(x, y, sound_effect);
12368 }
12369
12370 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12371                                               int action)
12372 {
12373   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12374
12375   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12376     PlayLevelSound(x, y, sound_effect);
12377 }
12378
12379 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12380 {
12381   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12382
12383   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12384     PlayLevelSound(x, y, sound_effect);
12385 }
12386
12387 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12388 {
12389   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12390
12391   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12392     StopSound(sound_effect);
12393 }
12394
12395 static void PlayLevelMusic()
12396 {
12397   if (levelset.music[level_nr] != MUS_UNDEFINED)
12398     PlayMusic(levelset.music[level_nr]);        /* from config file */
12399   else
12400     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
12401 }
12402
12403 void RaiseScore(int value)
12404 {
12405   local_player->score += value;
12406
12407   DrawGameValue_Score(local_player->score);
12408 }
12409
12410 void RaiseScoreElement(int element)
12411 {
12412   switch(element)
12413   {
12414     case EL_EMERALD:
12415     case EL_BD_DIAMOND:
12416     case EL_EMERALD_YELLOW:
12417     case EL_EMERALD_RED:
12418     case EL_EMERALD_PURPLE:
12419     case EL_SP_INFOTRON:
12420       RaiseScore(level.score[SC_EMERALD]);
12421       break;
12422     case EL_DIAMOND:
12423       RaiseScore(level.score[SC_DIAMOND]);
12424       break;
12425     case EL_CRYSTAL:
12426       RaiseScore(level.score[SC_CRYSTAL]);
12427       break;
12428     case EL_PEARL:
12429       RaiseScore(level.score[SC_PEARL]);
12430       break;
12431     case EL_BUG:
12432     case EL_BD_BUTTERFLY:
12433     case EL_SP_ELECTRON:
12434       RaiseScore(level.score[SC_BUG]);
12435       break;
12436     case EL_SPACESHIP:
12437     case EL_BD_FIREFLY:
12438     case EL_SP_SNIKSNAK:
12439       RaiseScore(level.score[SC_SPACESHIP]);
12440       break;
12441     case EL_YAMYAM:
12442     case EL_DARK_YAMYAM:
12443       RaiseScore(level.score[SC_YAMYAM]);
12444       break;
12445     case EL_ROBOT:
12446       RaiseScore(level.score[SC_ROBOT]);
12447       break;
12448     case EL_PACMAN:
12449       RaiseScore(level.score[SC_PACMAN]);
12450       break;
12451     case EL_NUT:
12452       RaiseScore(level.score[SC_NUT]);
12453       break;
12454     case EL_DYNAMITE:
12455     case EL_SP_DISK_RED:
12456     case EL_DYNABOMB_INCREASE_NUMBER:
12457     case EL_DYNABOMB_INCREASE_SIZE:
12458     case EL_DYNABOMB_INCREASE_POWER:
12459       RaiseScore(level.score[SC_DYNAMITE]);
12460       break;
12461     case EL_SHIELD_NORMAL:
12462     case EL_SHIELD_DEADLY:
12463       RaiseScore(level.score[SC_SHIELD]);
12464       break;
12465     case EL_EXTRA_TIME:
12466       RaiseScore(level.score[SC_TIME_BONUS]);
12467       break;
12468     case EL_KEY_1:
12469     case EL_KEY_2:
12470     case EL_KEY_3:
12471     case EL_KEY_4:
12472       RaiseScore(level.score[SC_KEY]);
12473       break;
12474     default:
12475       RaiseScore(element_info[element].collect_score);
12476       break;
12477   }
12478 }
12479
12480 void RequestQuitGame(boolean ask_if_really_quit)
12481 {
12482   if (AllPlayersGone ||
12483       !ask_if_really_quit ||
12484       level_editor_test_game ||
12485       Request("Do you really want to quit the game ?",
12486               REQ_ASK | REQ_STAY_CLOSED))
12487   {
12488 #if defined(NETWORK_AVALIABLE)
12489     if (options.network)
12490       SendToServer_StopPlaying();
12491     else
12492 #endif
12493     {
12494       game_status = GAME_MODE_MAIN;
12495       DrawMainMenu();
12496     }
12497   }
12498   else
12499   {
12500
12501 #if 1
12502     if (tape.playing && tape.deactivate_display)
12503       TapeDeactivateDisplayOff(TRUE);
12504 #endif
12505
12506     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12507
12508 #if 1
12509     if (tape.playing && tape.deactivate_display)
12510       TapeDeactivateDisplayOn();
12511 #endif
12512
12513   }
12514 }
12515
12516
12517 /* ---------- new game button stuff ---------------------------------------- */
12518
12519 /* graphic position values for game buttons */
12520 #define GAME_BUTTON_XSIZE       30
12521 #define GAME_BUTTON_YSIZE       30
12522 #define GAME_BUTTON_XPOS        5
12523 #define GAME_BUTTON_YPOS        215
12524 #define SOUND_BUTTON_XPOS       5
12525 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12526
12527 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12528 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12529 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12530 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12531 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12532 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12533
12534 static struct
12535 {
12536   int x, y;
12537   int gadget_id;
12538   char *infotext;
12539 } gamebutton_info[NUM_GAME_BUTTONS] =
12540 {
12541   {
12542     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
12543     GAME_CTRL_ID_STOP,
12544     "stop game"
12545   },
12546   {
12547     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
12548     GAME_CTRL_ID_PAUSE,
12549     "pause game"
12550   },
12551   {
12552     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
12553     GAME_CTRL_ID_PLAY,
12554     "play game"
12555   },
12556   {
12557     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
12558     SOUND_CTRL_ID_MUSIC,
12559     "background music on/off"
12560   },
12561   {
12562     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
12563     SOUND_CTRL_ID_LOOPS,
12564     "sound loops on/off"
12565   },
12566   {
12567     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
12568     SOUND_CTRL_ID_SIMPLE,
12569     "normal sounds on/off"
12570   }
12571 };
12572
12573 void CreateGameButtons()
12574 {
12575   int i;
12576
12577   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12578   {
12579     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12580     struct GadgetInfo *gi;
12581     int button_type;
12582     boolean checked;
12583     unsigned long event_mask;
12584     int gd_xoffset, gd_yoffset;
12585     int gd_x1, gd_x2, gd_y1, gd_y2;
12586     int id = i;
12587
12588     gd_xoffset = gamebutton_info[i].x;
12589     gd_yoffset = gamebutton_info[i].y;
12590     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12591     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12592
12593     if (id == GAME_CTRL_ID_STOP ||
12594         id == GAME_CTRL_ID_PAUSE ||
12595         id == GAME_CTRL_ID_PLAY)
12596     {
12597       button_type = GD_TYPE_NORMAL_BUTTON;
12598       checked = FALSE;
12599       event_mask = GD_EVENT_RELEASED;
12600       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12601       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12602     }
12603     else
12604     {
12605       button_type = GD_TYPE_CHECK_BUTTON;
12606       checked =
12607         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12608          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12609          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12610       event_mask = GD_EVENT_PRESSED;
12611       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
12612       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12613     }
12614
12615     gi = CreateGadget(GDI_CUSTOM_ID, id,
12616                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
12617                       GDI_X, DX + gd_xoffset,
12618                       GDI_Y, DY + gd_yoffset,
12619                       GDI_WIDTH, GAME_BUTTON_XSIZE,
12620                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
12621                       GDI_TYPE, button_type,
12622                       GDI_STATE, GD_BUTTON_UNPRESSED,
12623                       GDI_CHECKED, checked,
12624                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12625                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12626                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12627                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12628                       GDI_EVENT_MASK, event_mask,
12629                       GDI_CALLBACK_ACTION, HandleGameButtons,
12630                       GDI_END);
12631
12632     if (gi == NULL)
12633       Error(ERR_EXIT, "cannot create gadget");
12634
12635     game_gadget[id] = gi;
12636   }
12637 }
12638
12639 void FreeGameButtons()
12640 {
12641   int i;
12642
12643   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12644     FreeGadget(game_gadget[i]);
12645 }
12646
12647 static void MapGameButtons()
12648 {
12649   int i;
12650
12651   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12652     MapGadget(game_gadget[i]);
12653 }
12654
12655 void UnmapGameButtons()
12656 {
12657   int i;
12658
12659   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12660     UnmapGadget(game_gadget[i]);
12661 }
12662
12663 static void HandleGameButtons(struct GadgetInfo *gi)
12664 {
12665   int id = gi->custom_id;
12666
12667   if (game_status != GAME_MODE_PLAYING)
12668     return;
12669
12670   switch (id)
12671   {
12672     case GAME_CTRL_ID_STOP:
12673       RequestQuitGame(TRUE);
12674       break;
12675
12676     case GAME_CTRL_ID_PAUSE:
12677       if (options.network)
12678       {
12679 #if defined(NETWORK_AVALIABLE)
12680         if (tape.pausing)
12681           SendToServer_ContinuePlaying();
12682         else
12683           SendToServer_PausePlaying();
12684 #endif
12685       }
12686       else
12687         TapeTogglePause(TAPE_TOGGLE_MANUAL);
12688       break;
12689
12690     case GAME_CTRL_ID_PLAY:
12691       if (tape.pausing)
12692       {
12693 #if defined(NETWORK_AVALIABLE)
12694         if (options.network)
12695           SendToServer_ContinuePlaying();
12696         else
12697 #endif
12698         {
12699           tape.pausing = FALSE;
12700           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12701         }
12702       }
12703       break;
12704
12705     case SOUND_CTRL_ID_MUSIC:
12706       if (setup.sound_music)
12707       { 
12708         setup.sound_music = FALSE;
12709         FadeMusic();
12710       }
12711       else if (audio.music_available)
12712       { 
12713         setup.sound = setup.sound_music = TRUE;
12714
12715         SetAudioMode(setup.sound);
12716
12717         PlayLevelMusic();
12718       }
12719       break;
12720
12721     case SOUND_CTRL_ID_LOOPS:
12722       if (setup.sound_loops)
12723         setup.sound_loops = FALSE;
12724       else if (audio.loops_available)
12725       {
12726         setup.sound = setup.sound_loops = TRUE;
12727         SetAudioMode(setup.sound);
12728       }
12729       break;
12730
12731     case SOUND_CTRL_ID_SIMPLE:
12732       if (setup.sound_simple)
12733         setup.sound_simple = FALSE;
12734       else if (audio.sound_available)
12735       {
12736         setup.sound = setup.sound_simple = TRUE;
12737         SetAudioMode(setup.sound);
12738       }
12739       break;
12740
12741     default:
12742       break;
12743   }
12744 }