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