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