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