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