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