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