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