rnd-20040612-4-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_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 #if 1
9163   /* use random number generator in every frame to make it less predictable */
9164   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9165     RND(1);
9166 #endif
9167 }
9168
9169 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9170 {
9171   int min_x = x, min_y = y, max_x = x, max_y = y;
9172   int i;
9173
9174   for (i = 0; i < MAX_PLAYERS; i++)
9175   {
9176     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9177
9178     if (!stored_player[i].active || &stored_player[i] == player)
9179       continue;
9180
9181     min_x = MIN(min_x, jx);
9182     min_y = MIN(min_y, jy);
9183     max_x = MAX(max_x, jx);
9184     max_y = MAX(max_y, jy);
9185   }
9186
9187   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9188 }
9189
9190 static boolean AllPlayersInVisibleScreen()
9191 {
9192   int i;
9193
9194   for (i = 0; i < MAX_PLAYERS; i++)
9195   {
9196     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9197
9198     if (!stored_player[i].active)
9199       continue;
9200
9201     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9202       return FALSE;
9203   }
9204
9205   return TRUE;
9206 }
9207
9208 void ScrollLevel(int dx, int dy)
9209 {
9210   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9211   int x, y;
9212
9213   BlitBitmap(drawto_field, drawto_field,
9214              FX + TILEX * (dx == -1) - softscroll_offset,
9215              FY + TILEY * (dy == -1) - softscroll_offset,
9216              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9217              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9218              FX + TILEX * (dx == 1) - softscroll_offset,
9219              FY + TILEY * (dy == 1) - softscroll_offset);
9220
9221   if (dx)
9222   {
9223     x = (dx == 1 ? BX1 : BX2);
9224     for (y = BY1; y <= BY2; y++)
9225       DrawScreenField(x, y);
9226   }
9227
9228   if (dy)
9229   {
9230     y = (dy == 1 ? BY1 : BY2);
9231     for (x = BX1; x <= BX2; x++)
9232       DrawScreenField(x, y);
9233   }
9234
9235   redraw_mask |= REDRAW_FIELD;
9236 }
9237
9238 #if 0
9239 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
9240 {
9241   int nextx = x + dx, nexty = y + dy;
9242   int element = Feld[x][y];
9243
9244   if ((dx == -1 &&
9245        element != EL_SP_PORT_LEFT &&
9246        element != EL_SP_GRAVITY_PORT_LEFT &&
9247        element != EL_SP_PORT_HORIZONTAL &&
9248        element != EL_SP_PORT_ANY) ||
9249       (dx == +1 &&
9250        element != EL_SP_PORT_RIGHT &&
9251        element != EL_SP_GRAVITY_PORT_RIGHT &&
9252        element != EL_SP_PORT_HORIZONTAL &&
9253        element != EL_SP_PORT_ANY) ||
9254       (dy == -1 &&
9255        element != EL_SP_PORT_UP &&
9256        element != EL_SP_GRAVITY_PORT_UP &&
9257        element != EL_SP_PORT_VERTICAL &&
9258        element != EL_SP_PORT_ANY) ||
9259       (dy == +1 &&
9260        element != EL_SP_PORT_DOWN &&
9261        element != EL_SP_GRAVITY_PORT_DOWN &&
9262        element != EL_SP_PORT_VERTICAL &&
9263        element != EL_SP_PORT_ANY) ||
9264       !IN_LEV_FIELD(nextx, nexty) ||
9265       !IS_FREE(nextx, nexty))
9266     return FALSE;
9267
9268   return TRUE;
9269 }
9270 #endif
9271
9272 static boolean canFallDown(struct PlayerInfo *player)
9273 {
9274   int jx = player->jx, jy = player->jy;
9275
9276   return (IN_LEV_FIELD(jx, jy + 1) &&
9277           (IS_FREE(jx, jy + 1) ||
9278            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9279           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9280           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9281 }
9282
9283 static boolean canPassField(int x, int y, int move_dir)
9284 {
9285   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9286   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9287   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9288   int nextx = x + dx;
9289   int nexty = y + dy;
9290   int element = Feld[x][y];
9291
9292   return (IS_PASSABLE_FROM(element, opposite_dir) &&
9293           !CAN_MOVE(element) &&
9294           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9295           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9296           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9297 }
9298
9299 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9300 {
9301   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9302   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9303   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9304   int newx = x + dx;
9305   int newy = y + dy;
9306 #if 0
9307   int nextx = newx + dx;
9308   int nexty = newy + dy;
9309 #endif
9310
9311 #if 1
9312   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9313           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9314 #if 0
9315           (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
9316 #endif
9317           (IS_DIGGABLE(Feld[newx][newy]) ||
9318            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9319            canPassField(newx, newy, move_dir)));
9320 #else
9321 #if 1
9322   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9323           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9324           (IS_DIGGABLE(Feld[newx][newy]) ||
9325            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9326            canPassField(newx, newy, move_dir)));
9327 #else
9328 #if 1
9329   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9330           (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9331            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9332            canPassField(newx, newy, move_dir)));
9333 #else
9334   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9335           (IS_DIGGABLE(Feld[newx][newy]) ||
9336            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9337            (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9338             !CAN_MOVE(Feld[newx][newy]) &&
9339             IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9340             IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9341             (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9342 #endif
9343 #endif
9344 #endif
9345 }
9346
9347 static void CheckGravityMovement(struct PlayerInfo *player)
9348 {
9349   if (game.gravity && !player->programmed_action)
9350   {
9351 #if 1
9352     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9353     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
9354 #else
9355     int move_dir_horizontal = player->action & MV_HORIZONTAL;
9356     int move_dir_vertical   = player->action & MV_VERTICAL;
9357 #endif
9358
9359 #if 1
9360     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9361 #else
9362     boolean player_is_snapping = player->action & JOY_BUTTON_1;
9363 #endif
9364
9365     int jx = player->jx, jy = player->jy;
9366
9367     boolean player_is_moving_to_valid_field =
9368       (!player_is_snapping &&
9369        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9370         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9371
9372 #if 0
9373     int move_dir =
9374       (player->last_move_dir & MV_HORIZONTAL ?
9375        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9376        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9377 #endif
9378
9379 #if 0
9380     int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9381     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9382     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9383     int new_jx = jx + dx, new_jy = jy + dy;
9384     int nextx = new_jx + dx, nexty = new_jy + dy;
9385 #endif
9386
9387 #if 1
9388
9389 #if 1
9390     boolean player_can_fall_down = canFallDown(player);
9391 #else
9392     boolean player_can_fall_down =
9393       (IN_LEV_FIELD(jx, jy + 1) &&
9394        (IS_FREE(jx, jy + 1) ||
9395         (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9396 #endif
9397
9398 #else
9399     boolean player_can_fall_down =
9400       (IN_LEV_FIELD(jx, jy + 1) &&
9401        (IS_FREE(jx, jy + 1)));
9402 #endif
9403
9404 #if 0
9405     boolean player_is_moving_to_valid_field =
9406       (
9407 #if 1
9408        !player_is_snapping &&
9409 #endif
9410
9411 #if 1
9412        IN_LEV_FIELD(new_jx, new_jy) &&
9413        (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9414         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9415          element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9416          IN_LEV_FIELD(nextx, nexty) &&
9417          element_info[Feld[nextx][nexty]].access_direction & move_dir))
9418 #else
9419        IN_LEV_FIELD(new_jx, new_jy) &&
9420        (Feld[new_jx][new_jy] == EL_SP_BASE ||
9421         Feld[new_jx][new_jy] == EL_SAND ||
9422         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9423          canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9424     /* !!! extend EL_SAND to anything diggable !!! */
9425 #endif
9426        );
9427 #endif
9428
9429 #if 0
9430     boolean player_is_standing_on_valid_field =
9431       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9432        (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9433 #endif
9434
9435 #if 0
9436     printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9437            player_can_fall_down,
9438            player_is_standing_on_valid_field,
9439            player_is_moving_to_valid_field,
9440            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9441            player->effective_action,
9442            player->can_fall_into_acid);
9443 #endif
9444
9445     if (player_can_fall_down &&
9446 #if 0
9447         !player_is_standing_on_valid_field &&
9448 #endif
9449         !player_is_moving_to_valid_field)
9450     {
9451 #if 0
9452       printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9453              jx, jy, FrameCounter);
9454 #endif
9455
9456       player->programmed_action = MV_DOWN;
9457     }
9458   }
9459 }
9460
9461 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9462 {
9463 #if 1
9464   return CheckGravityMovement(player);
9465 #endif
9466
9467   if (game.gravity && !player->programmed_action)
9468   {
9469     int jx = player->jx, jy = player->jy;
9470     boolean field_under_player_is_free =
9471       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9472     boolean player_is_standing_on_valid_field =
9473       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9474        (IS_WALKABLE(Feld[jx][jy]) &&
9475         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9476
9477     if (field_under_player_is_free && !player_is_standing_on_valid_field)
9478       player->programmed_action = MV_DOWN;
9479   }
9480 }
9481
9482 /*
9483   MovePlayerOneStep()
9484   -----------------------------------------------------------------------------
9485   dx, dy:               direction (non-diagonal) to try to move the player to
9486   real_dx, real_dy:     direction as read from input device (can be diagonal)
9487 */
9488
9489 boolean MovePlayerOneStep(struct PlayerInfo *player,
9490                           int dx, int dy, int real_dx, int real_dy)
9491 {
9492 #if 0
9493   static int trigger_sides[4][2] =
9494   {
9495     /* enter side        leave side */
9496     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
9497     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
9498     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
9499     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
9500   };
9501   int move_direction = (dx == -1 ? MV_LEFT :
9502                         dx == +1 ? MV_RIGHT :
9503                         dy == -1 ? MV_UP :
9504                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9505   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9506   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9507 #endif
9508   int jx = player->jx, jy = player->jy;
9509   int new_jx = jx + dx, new_jy = jy + dy;
9510   int element;
9511   int can_move;
9512
9513   if (!player->active || (!dx && !dy))
9514     return MF_NO_ACTION;
9515
9516   player->MovDir = (dx < 0 ? MV_LEFT :
9517                     dx > 0 ? MV_RIGHT :
9518                     dy < 0 ? MV_UP :
9519                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
9520
9521   if (!IN_LEV_FIELD(new_jx, new_jy))
9522     return MF_NO_ACTION;
9523
9524   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9525     return MF_NO_ACTION;
9526
9527 #if 0
9528   element = MovingOrBlocked2Element(new_jx, new_jy);
9529 #else
9530   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9531 #endif
9532
9533   if (DONT_RUN_INTO(element))
9534   {
9535     if (element == EL_ACID && dx == 0 && dy == 1)
9536     {
9537       SplashAcid(new_jx, new_jy);
9538       Feld[jx][jy] = EL_PLAYER_1;
9539       InitMovingField(jx, jy, MV_DOWN);
9540       Store[jx][jy] = EL_ACID;
9541       ContinueMoving(jx, jy);
9542       BuryHero(player);
9543     }
9544     else
9545       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9546
9547     return MF_MOVING;
9548   }
9549
9550   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9551   if (can_move != MF_MOVING)
9552     return can_move;
9553
9554   /* check if DigField() has caused relocation of the player */
9555   if (player->jx != jx || player->jy != jy)
9556     return MF_NO_ACTION;
9557
9558   StorePlayer[jx][jy] = 0;
9559   player->last_jx = jx;
9560   player->last_jy = jy;
9561   player->jx = new_jx;
9562   player->jy = new_jy;
9563   StorePlayer[new_jx][new_jy] = player->element_nr;
9564
9565   player->MovPos =
9566     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9567
9568   player->step_counter++;
9569
9570 #if 0
9571   player->drop_delay = 0;
9572 #endif
9573
9574   PlayerVisit[jx][jy] = FrameCounter;
9575
9576   ScrollPlayer(player, SCROLL_INIT);
9577
9578 #if 0
9579   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9580   {
9581     CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9582                                       leave_side);
9583     CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9584   }
9585
9586   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9587   {
9588     CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9589                                       CE_OTHER_GETS_ENTERED, enter_side);
9590     CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9591                              CE_ENTERED_BY_PLAYER, enter_side);
9592   }
9593 #endif
9594
9595   return MF_MOVING;
9596 }
9597
9598 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9599 {
9600   int jx = player->jx, jy = player->jy;
9601   int old_jx = jx, old_jy = jy;
9602   int moved = MF_NO_ACTION;
9603
9604 #if 1
9605   if (!player->active)
9606     return FALSE;
9607
9608   if (!dx && !dy)
9609   {
9610     if (player->MovPos == 0)
9611     {
9612       player->is_moving = FALSE;
9613       player->is_digging = FALSE;
9614       player->is_collecting = FALSE;
9615       player->is_snapping = FALSE;
9616       player->is_pushing = FALSE;
9617     }
9618
9619     return FALSE;
9620   }
9621 #else
9622   if (!player->active || (!dx && !dy))
9623     return FALSE;
9624 #endif
9625
9626 #if 0
9627   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9628       !tape.playing)
9629     return FALSE;
9630 #else
9631
9632 #if 1
9633
9634 #if 0
9635   printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
9636          player->move_delay + player->move_delay_value);
9637 #endif
9638
9639 #if USE_NEW_MOVE_DELAY
9640   if (player->move_delay > 0)
9641 #else
9642   if (!FrameReached(&player->move_delay, player->move_delay_value))
9643 #endif
9644   {
9645 #if 0
9646     printf("::: can NOT move\n");
9647 #endif
9648
9649     return FALSE;
9650   }
9651 #else
9652   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9653       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9654     return FALSE;
9655 #endif
9656
9657 #endif
9658
9659 #if 0
9660   printf("::: COULD move now\n");
9661 #endif
9662
9663 #if USE_NEW_MOVE_DELAY
9664   player->move_delay = -1;              /* set to "uninitialized" value */
9665 #endif
9666
9667   /* store if player is automatically moved to next field */
9668   player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9669
9670   /* remove the last programmed player action */
9671   player->programmed_action = 0;
9672
9673   if (player->MovPos)
9674   {
9675     /* should only happen if pre-1.2 tape recordings are played */
9676     /* this is only for backward compatibility */
9677
9678     int original_move_delay_value = player->move_delay_value;
9679
9680 #if DEBUG
9681     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9682            tape.counter);
9683 #endif
9684
9685     /* scroll remaining steps with finest movement resolution */
9686     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9687
9688     while (player->MovPos)
9689     {
9690       ScrollPlayer(player, SCROLL_GO_ON);
9691       ScrollScreen(NULL, SCROLL_GO_ON);
9692
9693 #if USE_NEW_MOVE_DELAY
9694       AdvanceFrameAndPlayerCounters(player->index_nr);
9695 #else
9696       FrameCounter++;
9697 #endif
9698
9699       DrawAllPlayers();
9700       BackToFront();
9701     }
9702
9703     player->move_delay_value = original_move_delay_value;
9704   }
9705
9706   if (player->last_move_dir & MV_HORIZONTAL)
9707   {
9708     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9709       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9710   }
9711   else
9712   {
9713     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9714       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9715   }
9716
9717   jx = player->jx;
9718   jy = player->jy;
9719
9720   if (moved & MF_MOVING && !ScreenMovPos &&
9721       (player == local_player || !options.network))
9722   {
9723     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9724     int offset = (setup.scroll_delay ? 3 : 0);
9725
9726     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9727     {
9728       /* actual player has left the screen -- scroll in that direction */
9729       if (jx != old_jx)         /* player has moved horizontally */
9730         scroll_x += (jx - old_jx);
9731       else                      /* player has moved vertically */
9732         scroll_y += (jy - old_jy);
9733     }
9734     else
9735     {
9736       if (jx != old_jx)         /* player has moved horizontally */
9737       {
9738         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
9739             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9740           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9741
9742         /* don't scroll over playfield boundaries */
9743         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9744           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9745
9746         /* don't scroll more than one field at a time */
9747         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9748
9749         /* don't scroll against the player's moving direction */
9750         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
9751             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9752           scroll_x = old_scroll_x;
9753       }
9754       else                      /* player has moved vertically */
9755       {
9756         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
9757             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9758           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9759
9760         /* don't scroll over playfield boundaries */
9761         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9762           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9763
9764         /* don't scroll more than one field at a time */
9765         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9766
9767         /* don't scroll against the player's moving direction */
9768         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
9769             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9770           scroll_y = old_scroll_y;
9771       }
9772     }
9773
9774     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9775     {
9776       if (!options.network && !AllPlayersInVisibleScreen())
9777       {
9778         scroll_x = old_scroll_x;
9779         scroll_y = old_scroll_y;
9780       }
9781       else
9782       {
9783         ScrollScreen(player, SCROLL_INIT);
9784         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9785       }
9786     }
9787   }
9788
9789 #if 0
9790 #if 1
9791   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9792 #else
9793   if (!(moved & MF_MOVING) && !player->is_pushing)
9794     player->Frame = 0;
9795 #endif
9796 #endif
9797
9798   player->StepFrame = 0;
9799
9800   if (moved & MF_MOVING)
9801   {
9802 #if 0
9803     printf("::: REALLY moves now\n");
9804 #endif
9805
9806     if (old_jx != jx && old_jy == jy)
9807       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9808     else if (old_jx == jx && old_jy != jy)
9809       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9810
9811     DrawLevelField(jx, jy);     /* for "crumbled sand" */
9812
9813     player->last_move_dir = player->MovDir;
9814     player->is_moving = TRUE;
9815 #if 1
9816     player->is_snapping = FALSE;
9817 #endif
9818
9819 #if 1
9820     player->is_switching = FALSE;
9821 #endif
9822
9823     player->is_dropping = FALSE;
9824
9825
9826 #if 0
9827     /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9828
9829 #if 1
9830     if (game.engine_version < VERSION_IDENT(3,1,0,0))
9831 #endif
9832     {
9833       int move_direction = player->MovDir;
9834 #if 1
9835       int enter_side = MV_DIR_OPPOSITE(move_direction);
9836       int leave_side = move_direction;
9837 #else
9838       static int trigger_sides[4][2] =
9839       {
9840         /* enter side           leave side */
9841         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
9842         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
9843         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
9844         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
9845       };
9846       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9847       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9848 #endif
9849       int old_element = Feld[old_jx][old_jy];
9850       int new_element = Feld[jx][jy];
9851
9852 #if 1
9853       /* !!! TEST ONLY !!! */
9854       if (IS_CUSTOM_ELEMENT(old_element))
9855         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9856                                    CE_LEFT_BY_PLAYER,
9857                                    player->index_bit, leave_side);
9858
9859       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9860                                           CE_OTHER_GETS_LEFT,
9861                                           player->index_bit, leave_side);
9862
9863       if (IS_CUSTOM_ELEMENT(new_element))
9864         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9865                                    player->index_bit, enter_side);
9866
9867       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9868                                           CE_OTHER_GETS_ENTERED,
9869                                           player->index_bit, enter_side);
9870 #endif
9871
9872     }
9873 #endif
9874
9875
9876   }
9877   else
9878   {
9879     CheckGravityMovementWhenNotMoving(player);
9880
9881     /*
9882     player->last_move_dir = MV_NO_MOVING;
9883     */
9884     player->is_moving = FALSE;
9885
9886 #if USE_NEW_MOVE_STYLE
9887     /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
9888     /* ensure that the player is also allowed to move in the next frame */
9889     /* (currently, the player is forced to wait eight frames before he can try
9890        again!!!) */
9891
9892     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9893       player->move_delay = 0;   /* allow direct movement in the next frame */
9894 #endif
9895   }
9896
9897 #if USE_NEW_MOVE_DELAY
9898   if (player->move_delay == -1)         /* not yet initialized by DigField() */
9899     player->move_delay = player->move_delay_value;
9900 #endif
9901
9902   if (game.engine_version < VERSION_IDENT(3,0,7,0))
9903   {
9904     TestIfHeroTouchesBadThing(jx, jy);
9905     TestIfPlayerTouchesCustomElement(jx, jy);
9906   }
9907
9908   if (!player->active)
9909     RemoveHero(player);
9910
9911   return moved;
9912 }
9913
9914 void ScrollPlayer(struct PlayerInfo *player, int mode)
9915 {
9916   int jx = player->jx, jy = player->jy;
9917   int last_jx = player->last_jx, last_jy = player->last_jy;
9918   int move_stepsize = TILEX / player->move_delay_value;
9919
9920   if (!player->active || !player->MovPos)
9921     return;
9922
9923   if (mode == SCROLL_INIT)
9924   {
9925     player->actual_frame_counter = FrameCounter;
9926     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9927
9928 #if USE_NEW_BLOCK_STYLE
9929     if (player->block_delay > 0 &&
9930         Feld[last_jx][last_jy] == EL_EMPTY)
9931     {
9932       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9933 #if 1
9934       MovDelay[last_jx][last_jy] = player->block_delay + 1;
9935 #else
9936       ChangeDelay[last_jx][last_jy] = player->block_last_field_delay;
9937 #endif
9938     }
9939 #else
9940 #if USE_NEW_MOVE_STYLE
9941     if (player->block_last_field &&
9942         Feld[last_jx][last_jy] == EL_EMPTY)
9943       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9944 #else
9945     if (Feld[last_jx][last_jy] == EL_EMPTY)
9946       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9947 #endif
9948 #endif
9949
9950 #if 0
9951     DrawPlayer(player);
9952 #endif
9953
9954     return;
9955   }
9956   else if (!FrameReached(&player->actual_frame_counter, 1))
9957     return;
9958
9959   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9960   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9961
9962 #if USE_NEW_BLOCK_STYLE
9963 #else
9964   if (!player->block_last_field &&
9965       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9966 #if 1
9967     RemoveField(last_jx, last_jy);
9968 #else
9969     Feld[last_jx][last_jy] = EL_EMPTY;
9970 #endif
9971 #endif
9972
9973   /* before DrawPlayer() to draw correct player graphic for this case */
9974   if (player->MovPos == 0)
9975     CheckGravityMovement(player);
9976
9977 #if 0
9978   DrawPlayer(player);   /* needed here only to cleanup last field */
9979 #endif
9980
9981   if (player->MovPos == 0)      /* player reached destination field */
9982   {
9983 #if 1
9984     if (player->move_delay_reset_counter > 0)
9985     {
9986       player->move_delay_reset_counter--;
9987
9988       if (player->move_delay_reset_counter == 0)
9989       {
9990         /* continue with normal speed after quickly moving through gate */
9991         HALVE_PLAYER_SPEED(player);
9992
9993         /* be able to make the next move without delay */
9994         player->move_delay = 0;
9995       }
9996     }
9997 #else
9998     if (IS_PASSABLE(Feld[last_jx][last_jy]))
9999     {
10000       /* continue with normal speed after quickly moving through gate */
10001       HALVE_PLAYER_SPEED(player);
10002
10003       /* be able to make the next move without delay */
10004       player->move_delay = 0;
10005     }
10006 #endif
10007
10008 #if USE_NEW_BLOCK_STYLE
10009 #else
10010     if (player->block_last_field &&
10011         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10012 #if 1
10013       RemoveField(last_jx, last_jy);
10014 #else
10015       Feld[last_jx][last_jy] = EL_EMPTY;
10016 #endif
10017 #endif
10018
10019     player->last_jx = jx;
10020     player->last_jy = jy;
10021
10022     if (Feld[jx][jy] == EL_EXIT_OPEN ||
10023         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10024         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
10025     {
10026       DrawPlayer(player);       /* needed here only to cleanup last field */
10027       RemoveHero(player);
10028
10029       if (local_player->friends_still_needed == 0 ||
10030           IS_SP_ELEMENT(Feld[jx][jy]))
10031         player->LevelSolved = player->GameOver = TRUE;
10032     }
10033
10034 #if 1
10035     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10036     /* this breaks one level: "machine", level 000 */
10037 #if 0
10038     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10039 #endif
10040     {
10041       int move_direction = player->MovDir;
10042 #if 1
10043       int enter_side = MV_DIR_OPPOSITE(move_direction);
10044       int leave_side = move_direction;
10045 #else
10046       static int trigger_sides[4][2] =
10047       {
10048         /* enter side           leave side */
10049         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
10050         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
10051         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
10052         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
10053       };
10054       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10055       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10056 #endif
10057       int old_jx = last_jx;
10058       int old_jy = last_jy;
10059       int old_element = Feld[old_jx][old_jy];
10060       int new_element = Feld[jx][jy];
10061
10062 #if 1
10063       /* !!! TEST ONLY !!! */
10064       if (IS_CUSTOM_ELEMENT(old_element))
10065         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10066                                    CE_LEFT_BY_PLAYER,
10067                                    player->index_bit, leave_side);
10068
10069       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10070                                           CE_OTHER_GETS_LEFT,
10071                                           player->index_bit, leave_side);
10072
10073       if (IS_CUSTOM_ELEMENT(new_element))
10074         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10075                                    player->index_bit, enter_side);
10076
10077       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10078                                           CE_OTHER_GETS_ENTERED,
10079                                           player->index_bit, enter_side);
10080 #endif
10081
10082     }
10083 #endif
10084
10085     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10086     {
10087       TestIfHeroTouchesBadThing(jx, jy);
10088       TestIfPlayerTouchesCustomElement(jx, jy);
10089 #if 1
10090 #if 1
10091       /* needed because pushed element has not yet reached its destination,
10092          so it would trigger a change event at its previous field location */
10093       if (!player->is_pushing)
10094 #endif
10095         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
10096 #endif
10097
10098       if (!player->active)
10099         RemoveHero(player);
10100     }
10101
10102     if (level.use_step_counter)
10103     {
10104       int i;
10105
10106       TimePlayed++;
10107
10108       if (TimeLeft > 0)
10109       {
10110         TimeLeft--;
10111
10112         if (TimeLeft <= 10 && setup.time_limit)
10113           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10114
10115         DrawGameValue_Time(TimeLeft);
10116
10117         if (!TimeLeft && setup.time_limit)
10118           for (i = 0; i < MAX_PLAYERS; i++)
10119             KillHero(&stored_player[i]);
10120       }
10121       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10122         DrawGameValue_Time(TimePlayed);
10123     }
10124
10125     if (tape.single_step && tape.recording && !tape.pausing &&
10126         !player->programmed_action)
10127       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10128   }
10129 }
10130
10131 void ScrollScreen(struct PlayerInfo *player, int mode)
10132 {
10133   static unsigned long screen_frame_counter = 0;
10134
10135   if (mode == SCROLL_INIT)
10136   {
10137     /* set scrolling step size according to actual player's moving speed */
10138     ScrollStepSize = TILEX / player->move_delay_value;
10139
10140     screen_frame_counter = FrameCounter;
10141     ScreenMovDir = player->MovDir;
10142     ScreenMovPos = player->MovPos;
10143     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10144     return;
10145   }
10146   else if (!FrameReached(&screen_frame_counter, 1))
10147     return;
10148
10149   if (ScreenMovPos)
10150   {
10151     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10152     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10153     redraw_mask |= REDRAW_FIELD;
10154   }
10155   else
10156     ScreenMovDir = MV_NO_MOVING;
10157 }
10158
10159 void TestIfPlayerTouchesCustomElement(int x, int y)
10160 {
10161   static int xy[4][2] =
10162   {
10163     { 0, -1 },
10164     { -1, 0 },
10165     { +1, 0 },
10166     { 0, +1 }
10167   };
10168   static int trigger_sides[4][2] =
10169   {
10170     /* center side       border side */
10171     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10172     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10173     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10174     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10175   };
10176   static int touch_dir[4] =
10177   {
10178     MV_LEFT | MV_RIGHT,
10179     MV_UP   | MV_DOWN,
10180     MV_UP   | MV_DOWN,
10181     MV_LEFT | MV_RIGHT
10182   };
10183   int center_element = Feld[x][y];      /* should always be non-moving! */
10184   int i;
10185
10186   for (i = 0; i < NUM_DIRECTIONS; i++)
10187   {
10188     int xx = x + xy[i][0];
10189     int yy = y + xy[i][1];
10190     int center_side = trigger_sides[i][0];
10191     int border_side = trigger_sides[i][1];
10192     int border_element;
10193
10194     if (!IN_LEV_FIELD(xx, yy))
10195       continue;
10196
10197     if (IS_PLAYER(x, y))
10198     {
10199       struct PlayerInfo *player = PLAYERINFO(x, y);
10200
10201       if (game.engine_version < VERSION_IDENT(3,0,7,0))
10202         border_element = Feld[xx][yy];          /* may be moving! */
10203       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10204         border_element = Feld[xx][yy];
10205       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
10206         border_element = MovingOrBlocked2Element(xx, yy);
10207       else
10208         continue;               /* center and border element do not touch */
10209
10210 #if 1
10211       /* !!! TEST ONLY !!! */
10212       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10213                                  player->index_bit, border_side);
10214       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10215                                           CE_OTHER_GETS_TOUCHED,
10216                                           player->index_bit, border_side);
10217 #else
10218       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10219                                           CE_OTHER_GETS_TOUCHED,
10220                                           player->index_bit, border_side);
10221       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10222                                  player->index_bit, border_side);
10223 #endif
10224     }
10225     else if (IS_PLAYER(xx, yy))
10226     {
10227       struct PlayerInfo *player = PLAYERINFO(xx, yy);
10228
10229       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10230       {
10231         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10232           continue;             /* center and border element do not touch */
10233       }
10234
10235 #if 1
10236       /* !!! TEST ONLY !!! */
10237       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10238                                  player->index_bit, center_side);
10239       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10240                                           CE_OTHER_GETS_TOUCHED,
10241                                           player->index_bit, center_side);
10242 #else
10243       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10244                                           CE_OTHER_GETS_TOUCHED,
10245                                           player->index_bit, center_side);
10246       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10247                                  player->index_bit, center_side);
10248 #endif
10249
10250       break;
10251     }
10252   }
10253 }
10254
10255 void TestIfElementTouchesCustomElement(int x, int y)
10256 {
10257   static int xy[4][2] =
10258   {
10259     { 0, -1 },
10260     { -1, 0 },
10261     { +1, 0 },
10262     { 0, +1 }
10263   };
10264   static int trigger_sides[4][2] =
10265   {
10266     /* center side      border side */
10267     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10268     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10269     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10270     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10271   };
10272   static int touch_dir[4] =
10273   {
10274     MV_LEFT | MV_RIGHT,
10275     MV_UP   | MV_DOWN,
10276     MV_UP   | MV_DOWN,
10277     MV_LEFT | MV_RIGHT
10278   };
10279   boolean change_center_element = FALSE;
10280   int center_element_change_page = 0;
10281   int center_element = Feld[x][y];      /* should always be non-moving! */
10282   int border_trigger_element = EL_UNDEFINED;
10283   int i, j;
10284
10285   for (i = 0; i < NUM_DIRECTIONS; i++)
10286   {
10287     int xx = x + xy[i][0];
10288     int yy = y + xy[i][1];
10289     int center_side = trigger_sides[i][0];
10290     int border_side = trigger_sides[i][1];
10291     int border_element;
10292
10293     if (!IN_LEV_FIELD(xx, yy))
10294       continue;
10295
10296     if (game.engine_version < VERSION_IDENT(3,0,7,0))
10297       border_element = Feld[xx][yy];    /* may be moving! */
10298     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10299       border_element = Feld[xx][yy];
10300     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
10301       border_element = MovingOrBlocked2Element(xx, yy);
10302     else
10303       continue;                 /* center and border element do not touch */
10304
10305     /* check for change of center element (but change it only once) */
10306     if (IS_CUSTOM_ELEMENT(center_element) &&
10307         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
10308         !change_center_element)
10309     {
10310       for (j = 0; j < element_info[center_element].num_change_pages; j++)
10311       {
10312         struct ElementChangeInfo *change =
10313           &element_info[center_element].change_page[j];
10314
10315         if (change->can_change &&
10316             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10317             change->trigger_side & border_side &&
10318 #if 1
10319             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
10320 #else
10321             change->trigger_element == border_element
10322 #endif
10323             )
10324         {
10325           change_center_element = TRUE;
10326           center_element_change_page = j;
10327           border_trigger_element = border_element;
10328
10329           break;
10330         }
10331       }
10332     }
10333
10334     /* check for change of border element */
10335     if (IS_CUSTOM_ELEMENT(border_element) &&
10336         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
10337     {
10338       for (j = 0; j < element_info[border_element].num_change_pages; j++)
10339       {
10340         struct ElementChangeInfo *change =
10341           &element_info[border_element].change_page[j];
10342
10343         if (change->can_change &&
10344             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
10345             change->trigger_side & center_side &&
10346 #if 1
10347             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
10348 #else
10349             change->trigger_element == center_element
10350 #endif
10351             )
10352         {
10353 #if 0
10354           printf("::: border_element %d, %d\n", x, y);
10355 #endif
10356
10357           CheckElementChangeByPage(xx, yy, border_element, center_element,
10358                                    CE_OTHER_IS_TOUCHING, j);
10359           break;
10360         }
10361       }
10362     }
10363   }
10364
10365   if (change_center_element)
10366   {
10367 #if 0
10368     printf("::: center_element %d, %d\n", x, y);
10369 #endif
10370
10371     CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10372                              CE_OTHER_IS_TOUCHING, center_element_change_page);
10373   }
10374 }
10375
10376 void TestIfElementHitsCustomElement(int x, int y, int direction)
10377 {
10378   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10379   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10380   int hitx = x + dx, hity = y + dy;
10381   int hitting_element = Feld[x][y];
10382   int touched_element;
10383 #if 0
10384   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10385                         !IS_FREE(hitx, hity) &&
10386                         (!IS_MOVING(hitx, hity) ||
10387                          MovDir[hitx][hity] != direction ||
10388                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10389 #endif
10390
10391   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10392     return;
10393
10394 #if 0
10395   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10396     return;
10397 #endif
10398
10399   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10400                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10401
10402   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10403                            CE_HITTING_SOMETHING, direction);
10404
10405   if (IN_LEV_FIELD(hitx, hity))
10406   {
10407     int opposite_direction = MV_DIR_OPPOSITE(direction);
10408     int hitting_side = direction;
10409     int touched_side = opposite_direction;
10410 #if 0
10411     int touched_element = MovingOrBlocked2Element(hitx, hity);
10412 #endif
10413 #if 1
10414     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10415                           MovDir[hitx][hity] != direction ||
10416                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10417
10418     object_hit = TRUE;
10419 #endif
10420
10421     if (object_hit)
10422     {
10423       int i;
10424
10425       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10426                                CE_HIT_BY_SOMETHING, opposite_direction);
10427
10428       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10429           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10430       {
10431         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10432         {
10433           struct ElementChangeInfo *change =
10434             &element_info[hitting_element].change_page[i];
10435
10436           if (change->can_change &&
10437               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10438               change->trigger_side & touched_side &&
10439           
10440 #if 1
10441               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10442 #else
10443               change->trigger_element == touched_element
10444 #endif
10445               )
10446           {
10447             CheckElementChangeByPage(x, y, hitting_element, touched_element,
10448                                      CE_OTHER_IS_HITTING, i);
10449             break;
10450           }
10451         }
10452       }
10453
10454       if (IS_CUSTOM_ELEMENT(touched_element) &&
10455           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10456       {
10457         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10458         {
10459           struct ElementChangeInfo *change =
10460             &element_info[touched_element].change_page[i];
10461
10462           if (change->can_change &&
10463               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10464               change->trigger_side & hitting_side &&
10465 #if 1
10466               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10467 #else
10468               change->trigger_element == hitting_element
10469 #endif
10470               )
10471           {
10472             CheckElementChangeByPage(hitx, hity, touched_element,
10473                                      hitting_element, CE_OTHER_GETS_HIT, i);
10474             break;
10475           }
10476         }
10477       }
10478     }
10479   }
10480 }
10481
10482 #if 0
10483 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10484 {
10485   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10486   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10487   int hitx = x + dx, hity = y + dy;
10488   int hitting_element = Feld[x][y];
10489   int touched_element;
10490 #if 0
10491   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10492                         !IS_FREE(hitx, hity) &&
10493                         (!IS_MOVING(hitx, hity) ||
10494                          MovDir[hitx][hity] != direction ||
10495                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10496 #endif
10497
10498   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10499     return;
10500
10501 #if 0
10502   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10503     return;
10504 #endif
10505
10506   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10507                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10508
10509   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10510                            EP_CAN_SMASH_EVERYTHING, direction);
10511
10512   if (IN_LEV_FIELD(hitx, hity))
10513   {
10514     int opposite_direction = MV_DIR_OPPOSITE(direction);
10515     int hitting_side = direction;
10516     int touched_side = opposite_direction;
10517 #if 0
10518     int touched_element = MovingOrBlocked2Element(hitx, hity);
10519 #endif
10520 #if 1
10521     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10522                           MovDir[hitx][hity] != direction ||
10523                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10524
10525     object_hit = TRUE;
10526 #endif
10527
10528     if (object_hit)
10529     {
10530       int i;
10531
10532       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10533                                CE_SMASHED_BY_SOMETHING, opposite_direction);
10534
10535       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10536           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10537       {
10538         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10539         {
10540           struct ElementChangeInfo *change =
10541             &element_info[hitting_element].change_page[i];
10542
10543           if (change->can_change &&
10544               change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10545               change->trigger_side & touched_side &&
10546           
10547 #if 1
10548               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10549 #else
10550               change->trigger_element == touched_element
10551 #endif
10552               )
10553           {
10554             CheckElementChangeByPage(x, y, hitting_element, touched_element,
10555                                      CE_OTHER_IS_SMASHING, i);
10556             break;
10557           }
10558         }
10559       }
10560
10561       if (IS_CUSTOM_ELEMENT(touched_element) &&
10562           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10563       {
10564         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10565         {
10566           struct ElementChangeInfo *change =
10567             &element_info[touched_element].change_page[i];
10568
10569           if (change->can_change &&
10570               change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10571               change->trigger_side & hitting_side &&
10572 #if 1
10573               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10574 #else
10575               change->trigger_element == hitting_element
10576 #endif
10577               )
10578           {
10579             CheckElementChangeByPage(hitx, hity, touched_element,
10580                                      hitting_element, CE_OTHER_GETS_SMASHED,i);
10581             break;
10582           }
10583         }
10584       }
10585     }
10586   }
10587 }
10588 #endif
10589
10590 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10591 {
10592   int i, kill_x = -1, kill_y = -1;
10593   int bad_element = -1;
10594   static int test_xy[4][2] =
10595   {
10596     { 0, -1 },
10597     { -1, 0 },
10598     { +1, 0 },
10599     { 0, +1 }
10600   };
10601   static int test_dir[4] =
10602   {
10603     MV_UP,
10604     MV_LEFT,
10605     MV_RIGHT,
10606     MV_DOWN
10607   };
10608
10609   for (i = 0; i < NUM_DIRECTIONS; i++)
10610   {
10611     int test_x, test_y, test_move_dir, test_element;
10612
10613     test_x = good_x + test_xy[i][0];
10614     test_y = good_y + test_xy[i][1];
10615
10616     if (!IN_LEV_FIELD(test_x, test_y))
10617       continue;
10618
10619     test_move_dir =
10620       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10621
10622 #if 0
10623     test_element = Feld[test_x][test_y];
10624 #else
10625     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10626 #endif
10627
10628     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10629        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10630     */
10631     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10632         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
10633     {
10634       kill_x = test_x;
10635       kill_y = test_y;
10636       bad_element = test_element;
10637
10638       break;
10639     }
10640   }
10641
10642   if (kill_x != -1 || kill_y != -1)
10643   {
10644     if (IS_PLAYER(good_x, good_y))
10645     {
10646       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10647
10648 #if 1
10649       if (player->shield_deadly_time_left > 0 &&
10650           !IS_INDESTRUCTIBLE(bad_element))
10651         Bang(kill_x, kill_y);
10652       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10653         KillHero(player);
10654 #else
10655       if (player->shield_deadly_time_left > 0)
10656         Bang(kill_x, kill_y);
10657       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10658         KillHero(player);
10659 #endif
10660     }
10661     else
10662       Bang(good_x, good_y);
10663   }
10664 }
10665
10666 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10667 {
10668   int i, kill_x = -1, kill_y = -1;
10669   int bad_element = Feld[bad_x][bad_y];
10670   static int test_xy[4][2] =
10671   {
10672     { 0, -1 },
10673     { -1, 0 },
10674     { +1, 0 },
10675     { 0, +1 }
10676   };
10677   static int touch_dir[4] =
10678   {
10679     MV_LEFT | MV_RIGHT,
10680     MV_UP   | MV_DOWN,
10681     MV_UP   | MV_DOWN,
10682     MV_LEFT | MV_RIGHT
10683   };
10684   static int test_dir[4] =
10685   {
10686     MV_UP,
10687     MV_LEFT,
10688     MV_RIGHT,
10689     MV_DOWN
10690   };
10691
10692   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
10693     return;
10694
10695   for (i = 0; i < NUM_DIRECTIONS; i++)
10696   {
10697     int test_x, test_y, test_move_dir, test_element;
10698
10699     test_x = bad_x + test_xy[i][0];
10700     test_y = bad_y + test_xy[i][1];
10701     if (!IN_LEV_FIELD(test_x, test_y))
10702       continue;
10703
10704     test_move_dir =
10705       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10706
10707     test_element = Feld[test_x][test_y];
10708
10709     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10710        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10711     */
10712     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
10713         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
10714     {
10715       /* good thing is player or penguin that does not move away */
10716       if (IS_PLAYER(test_x, test_y))
10717       {
10718         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10719
10720         if (bad_element == EL_ROBOT && player->is_moving)
10721           continue;     /* robot does not kill player if he is moving */
10722
10723         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10724         {
10725           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10726             continue;           /* center and border element do not touch */
10727         }
10728
10729         kill_x = test_x;
10730         kill_y = test_y;
10731         break;
10732       }
10733       else if (test_element == EL_PENGUIN)
10734       {
10735         kill_x = test_x;
10736         kill_y = test_y;
10737         break;
10738       }
10739     }
10740   }
10741
10742   if (kill_x != -1 || kill_y != -1)
10743   {
10744     if (IS_PLAYER(kill_x, kill_y))
10745     {
10746       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10747
10748 #if 1
10749       if (player->shield_deadly_time_left > 0 &&
10750           !IS_INDESTRUCTIBLE(bad_element))
10751         Bang(bad_x, bad_y);
10752       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10753         KillHero(player);
10754 #else
10755       if (player->shield_deadly_time_left > 0)
10756         Bang(bad_x, bad_y);
10757       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10758         KillHero(player);
10759 #endif
10760     }
10761     else
10762       Bang(kill_x, kill_y);
10763   }
10764 }
10765
10766 void TestIfHeroTouchesBadThing(int x, int y)
10767 {
10768   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10769 }
10770
10771 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10772 {
10773   TestIfGoodThingHitsBadThing(x, y, move_dir);
10774 }
10775
10776 void TestIfBadThingTouchesHero(int x, int y)
10777 {
10778   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10779 }
10780
10781 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10782 {
10783   TestIfBadThingHitsGoodThing(x, y, move_dir);
10784 }
10785
10786 void TestIfFriendTouchesBadThing(int x, int y)
10787 {
10788   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10789 }
10790
10791 void TestIfBadThingTouchesFriend(int x, int y)
10792 {
10793   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10794 }
10795
10796 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10797 {
10798   int i, kill_x = bad_x, kill_y = bad_y;
10799   static int xy[4][2] =
10800   {
10801     { 0, -1 },
10802     { -1, 0 },
10803     { +1, 0 },
10804     { 0, +1 }
10805   };
10806
10807   for (i = 0; i < NUM_DIRECTIONS; i++)
10808   {
10809     int x, y, element;
10810
10811     x = bad_x + xy[i][0];
10812     y = bad_y + xy[i][1];
10813     if (!IN_LEV_FIELD(x, y))
10814       continue;
10815
10816     element = Feld[x][y];
10817     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10818         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10819     {
10820       kill_x = x;
10821       kill_y = y;
10822       break;
10823     }
10824   }
10825
10826   if (kill_x != bad_x || kill_y != bad_y)
10827     Bang(bad_x, bad_y);
10828 }
10829
10830 void KillHero(struct PlayerInfo *player)
10831 {
10832   int jx = player->jx, jy = player->jy;
10833
10834   if (!player->active)
10835     return;
10836
10837   /* remove accessible field at the player's position */
10838   Feld[jx][jy] = EL_EMPTY;
10839
10840   /* deactivate shield (else Bang()/Explode() would not work right) */
10841   player->shield_normal_time_left = 0;
10842   player->shield_deadly_time_left = 0;
10843
10844   Bang(jx, jy);
10845   BuryHero(player);
10846 }
10847
10848 static void KillHeroUnlessEnemyProtected(int x, int y)
10849 {
10850   if (!PLAYER_ENEMY_PROTECTED(x, y))
10851     KillHero(PLAYERINFO(x, y));
10852 }
10853
10854 static void KillHeroUnlessExplosionProtected(int x, int y)
10855 {
10856   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10857     KillHero(PLAYERINFO(x, y));
10858 }
10859
10860 void BuryHero(struct PlayerInfo *player)
10861 {
10862   int jx = player->jx, jy = player->jy;
10863
10864   if (!player->active)
10865     return;
10866
10867 #if 1
10868   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10869 #else
10870   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10871 #endif
10872   PlayLevelSound(jx, jy, SND_GAME_LOSING);
10873
10874   player->GameOver = TRUE;
10875   RemoveHero(player);
10876 }
10877
10878 void RemoveHero(struct PlayerInfo *player)
10879 {
10880   int jx = player->jx, jy = player->jy;
10881   int i, found = FALSE;
10882
10883   player->present = FALSE;
10884   player->active = FALSE;
10885
10886   if (!ExplodeField[jx][jy])
10887     StorePlayer[jx][jy] = 0;
10888
10889   for (i = 0; i < MAX_PLAYERS; i++)
10890     if (stored_player[i].active)
10891       found = TRUE;
10892
10893   if (!found)
10894     AllPlayersGone = TRUE;
10895
10896   ExitX = ZX = jx;
10897   ExitY = ZY = jy;
10898 }
10899
10900 /*
10901   =============================================================================
10902   checkDiagonalPushing()
10903   -----------------------------------------------------------------------------
10904   check if diagonal input device direction results in pushing of object
10905   (by checking if the alternative direction is walkable, diggable, ...)
10906   =============================================================================
10907 */
10908
10909 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10910                                     int x, int y, int real_dx, int real_dy)
10911 {
10912   int jx, jy, dx, dy, xx, yy;
10913
10914   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
10915     return TRUE;
10916
10917   /* diagonal direction: check alternative direction */
10918   jx = player->jx;
10919   jy = player->jy;
10920   dx = x - jx;
10921   dy = y - jy;
10922   xx = jx + (dx == 0 ? real_dx : 0);
10923   yy = jy + (dy == 0 ? real_dy : 0);
10924
10925   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10926 }
10927
10928 /*
10929   =============================================================================
10930   DigField()
10931   -----------------------------------------------------------------------------
10932   x, y:                 field next to player (non-diagonal) to try to dig to
10933   real_dx, real_dy:     direction as read from input device (can be diagonal)
10934   =============================================================================
10935 */
10936
10937 int DigField(struct PlayerInfo *player,
10938              int oldx, int oldy, int x, int y,
10939              int real_dx, int real_dy, int mode)
10940 {
10941 #if 0
10942   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10943 #endif
10944   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10945   boolean player_was_pushing = player->is_pushing;
10946   int jx = oldx, jy = oldy;
10947   int dx = x - jx, dy = y - jy;
10948   int nextx = x + dx, nexty = y + dy;
10949   int move_direction = (dx == -1 ? MV_LEFT :
10950                         dx == +1 ? MV_RIGHT :
10951                         dy == -1 ? MV_UP :
10952                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
10953   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10954 #if 1
10955   int dig_side = MV_DIR_OPPOSITE(move_direction);
10956 #else
10957   static int trigger_sides[4] =
10958   {
10959     CH_SIDE_RIGHT,      /* moving left  */
10960     CH_SIDE_LEFT,       /* moving right */
10961     CH_SIDE_BOTTOM,     /* moving up    */
10962     CH_SIDE_TOP,        /* moving down  */
10963   };
10964   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10965 #endif
10966   int old_element = Feld[jx][jy];
10967   int element;
10968
10969   if (is_player)                /* function can also be called by EL_PENGUIN */
10970   {
10971     if (player->MovPos == 0)
10972     {
10973       player->is_digging = FALSE;
10974       player->is_collecting = FALSE;
10975     }
10976
10977     if (player->MovPos == 0)    /* last pushing move finished */
10978       player->is_pushing = FALSE;
10979
10980     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
10981     {
10982       player->is_switching = FALSE;
10983 #if USE_NEW_PUSH_DELAY
10984       player->push_delay = -1;
10985 #else
10986       player->push_delay = 0;
10987 #endif
10988
10989       return MF_NO_ACTION;
10990     }
10991   }
10992
10993   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10994     return MF_NO_ACTION;
10995
10996 #if 0
10997
10998 #if 0
10999   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11000 #else
11001   if (IS_TUBE(Feld[jx][jy]) ||
11002       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11003 #endif
11004   {
11005     int i = 0;
11006     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11007     int tube_leave_directions[][2] =
11008     {
11009       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11010       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
11011       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
11012       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
11013       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
11014       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
11015       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
11016       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
11017       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
11018       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
11019       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
11020       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11021     };
11022
11023     while (tube_leave_directions[i][0] != tube_element)
11024     {
11025       i++;
11026       if (tube_leave_directions[i][0] == -1)    /* should not happen */
11027         break;
11028     }
11029
11030     if (!(tube_leave_directions[i][1] & move_direction))
11031       return MF_NO_ACTION;      /* tube has no opening in this direction */
11032   }
11033
11034 #else
11035
11036   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11037     old_element = Back[jx][jy];
11038
11039 #endif
11040
11041   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11042     return MF_NO_ACTION;        /* field has no opening in this direction */
11043
11044   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11045     return MF_NO_ACTION;        /* field has no opening in this direction */
11046
11047   element = Feld[x][y];
11048
11049   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
11050     return MF_NO_ACTION;
11051
11052   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11053       game.engine_version >= VERSION_IDENT(2,2,0,0))
11054     return MF_NO_ACTION;
11055
11056 #if 1
11057   if (game.gravity && is_player && !player->is_auto_moving &&
11058       canFallDown(player) && move_direction != MV_DOWN &&
11059       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11060     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
11061 #endif
11062
11063 #if 0
11064   if (element == EL_EMPTY_SPACE &&
11065       game.gravity && !player->is_auto_moving &&
11066       canFallDown(player) && move_direction != MV_DOWN)
11067     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
11068 #endif
11069
11070   switch (element)
11071   {
11072 #if 0
11073     case EL_SP_PORT_LEFT:
11074     case EL_SP_PORT_RIGHT:
11075     case EL_SP_PORT_UP:
11076     case EL_SP_PORT_DOWN:
11077     case EL_SP_PORT_HORIZONTAL:
11078     case EL_SP_PORT_VERTICAL:
11079     case EL_SP_PORT_ANY:
11080     case EL_SP_GRAVITY_PORT_LEFT:
11081     case EL_SP_GRAVITY_PORT_RIGHT:
11082     case EL_SP_GRAVITY_PORT_UP:
11083     case EL_SP_GRAVITY_PORT_DOWN:
11084 #if 1
11085       if (!canEnterSupaplexPort(x, y, dx, dy))
11086         return MF_NO_ACTION;
11087 #else
11088       if ((dx == -1 &&
11089            element != EL_SP_PORT_LEFT &&
11090            element != EL_SP_GRAVITY_PORT_LEFT &&
11091            element != EL_SP_PORT_HORIZONTAL &&
11092            element != EL_SP_PORT_ANY) ||
11093           (dx == +1 &&
11094            element != EL_SP_PORT_RIGHT &&
11095            element != EL_SP_GRAVITY_PORT_RIGHT &&
11096            element != EL_SP_PORT_HORIZONTAL &&
11097            element != EL_SP_PORT_ANY) ||
11098           (dy == -1 &&
11099            element != EL_SP_PORT_UP &&
11100            element != EL_SP_GRAVITY_PORT_UP &&
11101            element != EL_SP_PORT_VERTICAL &&
11102            element != EL_SP_PORT_ANY) ||
11103           (dy == +1 &&
11104            element != EL_SP_PORT_DOWN &&
11105            element != EL_SP_GRAVITY_PORT_DOWN &&
11106            element != EL_SP_PORT_VERTICAL &&
11107            element != EL_SP_PORT_ANY) ||
11108           !IN_LEV_FIELD(nextx, nexty) ||
11109           !IS_FREE(nextx, nexty))
11110         return MF_NO_ACTION;
11111 #endif
11112
11113       if (element == EL_SP_GRAVITY_PORT_LEFT ||
11114           element == EL_SP_GRAVITY_PORT_RIGHT ||
11115           element == EL_SP_GRAVITY_PORT_UP ||
11116           element == EL_SP_GRAVITY_PORT_DOWN)
11117         game.gravity = !game.gravity;
11118
11119       /* automatically move to the next field with double speed */
11120       player->programmed_action = move_direction;
11121 #if 1
11122       if (player->move_delay_reset_counter == 0)
11123       {
11124         player->move_delay_reset_counter = 2;   /* two double speed steps */
11125
11126         DOUBLE_PLAYER_SPEED(player);
11127       }
11128 #else
11129       player->move_delay_reset_counter = 2;
11130
11131       DOUBLE_PLAYER_SPEED(player);
11132 #endif
11133
11134 #if 0
11135       printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
11136 #endif
11137
11138       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
11139       break;
11140 #endif
11141
11142 #if 0
11143     case EL_TUBE_ANY:
11144     case EL_TUBE_VERTICAL:
11145     case EL_TUBE_HORIZONTAL:
11146     case EL_TUBE_VERTICAL_LEFT:
11147     case EL_TUBE_VERTICAL_RIGHT:
11148     case EL_TUBE_HORIZONTAL_UP:
11149     case EL_TUBE_HORIZONTAL_DOWN:
11150     case EL_TUBE_LEFT_UP:
11151     case EL_TUBE_LEFT_DOWN:
11152     case EL_TUBE_RIGHT_UP:
11153     case EL_TUBE_RIGHT_DOWN:
11154       {
11155         int i = 0;
11156         int tube_enter_directions[][2] =
11157         {
11158           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11159           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
11160           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
11161           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
11162           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
11163           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
11164           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
11165           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
11166           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
11167           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
11168           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
11169           { -1,                         MV_NO_MOVING                         }
11170         };
11171
11172         while (tube_enter_directions[i][0] != element)
11173         {
11174           i++;
11175           if (tube_enter_directions[i][0] == -1)        /* should not happen */
11176             break;
11177         }
11178
11179         if (!(tube_enter_directions[i][1] & move_direction))
11180           return MF_NO_ACTION;  /* tube has no opening in this direction */
11181
11182         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
11183       }
11184       break;
11185 #endif
11186
11187     default:
11188
11189 #if 1
11190       if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11191 #else
11192       if (IS_WALKABLE(element))
11193 #endif
11194       {
11195         int sound_element = SND_ELEMENT(element);
11196         int sound_action = ACTION_WALKING;
11197
11198 #if 0
11199         if (!ACCESS_FROM(element, opposite_direction))
11200           return MF_NO_ACTION;  /* field not accessible from this direction */
11201 #endif
11202
11203 #if 0
11204         if (element == EL_EMPTY_SPACE &&
11205             game.gravity && !player->is_auto_moving &&
11206             canFallDown(player) && move_direction != MV_DOWN)
11207           return MF_NO_ACTION;  /* player cannot walk here due to gravity */
11208 #endif
11209
11210         if (IS_GATE(element))
11211         {
11212           if (!player->key[element - EL_GATE_1])
11213             return MF_NO_ACTION;
11214         }
11215         else if (IS_GATE_GRAY(element))
11216         {
11217           if (!player->key[element - EL_GATE_1_GRAY])
11218             return MF_NO_ACTION;
11219         }
11220         else if (element == EL_EXIT_OPEN ||
11221                  element == EL_SP_EXIT_OPEN ||
11222                  element == EL_SP_EXIT_OPENING)
11223         {
11224           sound_action = ACTION_PASSING;        /* player is passing exit */
11225         }
11226         else if (element == EL_EMPTY)
11227         {
11228           sound_action = ACTION_MOVING;         /* nothing to walk on */
11229         }
11230
11231         /* play sound from background or player, whatever is available */
11232         if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11233           PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11234         else
11235           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
11236
11237         break;
11238       }
11239 #if 1
11240       else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
11241 #else
11242       else if (IS_PASSABLE(element))
11243 #endif
11244       {
11245 #if 0
11246         if (!canPassField(x, y, move_direction))
11247           return MF_NO_ACTION;
11248 #else
11249
11250 #if 0
11251 #if 1
11252         if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
11253             !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
11254             (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
11255           return MF_NO_ACTION;
11256 #else
11257         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
11258           return MF_NO_ACTION;
11259 #endif
11260 #endif
11261
11262 #if 1
11263         if (!ACCESS_FROM(element, opposite_direction))
11264           return MF_NO_ACTION;  /* field not accessible from this direction */
11265 #else
11266         if (IS_CUSTOM_ELEMENT(element) &&
11267             !ACCESS_FROM(element, opposite_direction))
11268           return MF_NO_ACTION;  /* field not accessible from this direction */
11269 #endif
11270
11271 #if 1
11272         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
11273           return MF_NO_ACTION;
11274 #endif
11275
11276 #endif
11277
11278         if (IS_EM_GATE(element))
11279         {
11280           if (!player->key[element - EL_EM_GATE_1])
11281             return MF_NO_ACTION;
11282         }
11283         else if (IS_EM_GATE_GRAY(element))
11284         {
11285           if (!player->key[element - EL_EM_GATE_1_GRAY])
11286             return MF_NO_ACTION;
11287         }
11288         else if (IS_SP_PORT(element))
11289         {
11290           if (element == EL_SP_GRAVITY_PORT_LEFT ||
11291               element == EL_SP_GRAVITY_PORT_RIGHT ||
11292               element == EL_SP_GRAVITY_PORT_UP ||
11293               element == EL_SP_GRAVITY_PORT_DOWN)
11294             game.gravity = !game.gravity;
11295           else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11296                    element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11297                    element == EL_SP_GRAVITY_ON_PORT_UP ||
11298                    element == EL_SP_GRAVITY_ON_PORT_DOWN)
11299             game.gravity = TRUE;
11300           else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11301                    element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11302                    element == EL_SP_GRAVITY_OFF_PORT_UP ||
11303                    element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11304             game.gravity = FALSE;
11305         }
11306
11307         /* automatically move to the next field with double speed */
11308         player->programmed_action = move_direction;
11309 #if 1
11310         if (player->move_delay_reset_counter == 0)
11311         {
11312           player->move_delay_reset_counter = 2; /* two double speed steps */
11313
11314           DOUBLE_PLAYER_SPEED(player);
11315         }
11316 #else
11317         player->move_delay_reset_counter = 2;
11318
11319         DOUBLE_PLAYER_SPEED(player);
11320 #endif
11321
11322         PlayLevelSoundAction(x, y, ACTION_PASSING);
11323
11324         break;
11325       }
11326       else if (IS_DIGGABLE(element))
11327       {
11328         RemoveField(x, y);
11329
11330         if (mode != DF_SNAP)
11331         {
11332 #if 1
11333           GfxElement[x][y] = GFX_ELEMENT(element);
11334 #else
11335           GfxElement[x][y] =
11336             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
11337 #endif
11338           player->is_digging = TRUE;
11339         }
11340
11341         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11342
11343         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
11344                                             player->index_bit, dig_side);
11345
11346 #if 1
11347         if (mode == DF_SNAP)
11348           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
11349 #endif
11350
11351         break;
11352       }
11353       else if (IS_COLLECTIBLE(element))
11354       {
11355         RemoveField(x, y);
11356
11357         if (is_player && mode != DF_SNAP)
11358         {
11359           GfxElement[x][y] = element;
11360           player->is_collecting = TRUE;
11361         }
11362
11363         if (element == EL_SPEED_PILL)
11364           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11365         else if (element == EL_EXTRA_TIME && level.time > 0)
11366         {
11367           TimeLeft += 10;
11368           DrawGameValue_Time(TimeLeft);
11369         }
11370         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11371         {
11372           player->shield_normal_time_left += 10;
11373           if (element == EL_SHIELD_DEADLY)
11374             player->shield_deadly_time_left += 10;
11375         }
11376         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
11377         {
11378           if (player->inventory_size < MAX_INVENTORY_SIZE)
11379             player->inventory_element[player->inventory_size++] = element;
11380
11381           DrawGameValue_Dynamite(local_player->inventory_size);
11382         }
11383         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11384         {
11385           player->dynabomb_count++;
11386           player->dynabombs_left++;
11387         }
11388         else if (element == EL_DYNABOMB_INCREASE_SIZE)
11389         {
11390           player->dynabomb_size++;
11391         }
11392         else if (element == EL_DYNABOMB_INCREASE_POWER)
11393         {
11394           player->dynabomb_xl = TRUE;
11395         }
11396         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11397                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11398         {
11399           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11400                         element - EL_KEY_1 : element - EL_EM_KEY_1);
11401
11402           player->key[key_nr] = TRUE;
11403
11404           DrawGameValue_Keys(player);
11405
11406           redraw_mask |= REDRAW_DOOR_1;
11407         }
11408         else if (IS_ENVELOPE(element))
11409         {
11410 #if 1
11411           player->show_envelope = element;
11412 #else
11413           ShowEnvelope(element - EL_ENVELOPE_1);
11414 #endif
11415         }
11416         else if (IS_DROPPABLE(element) ||
11417                  IS_THROWABLE(element)) /* can be collected and dropped */
11418         {
11419           int i;
11420
11421           if (element_info[element].collect_count == 0)
11422             player->inventory_infinite_element = element;
11423           else
11424             for (i = 0; i < element_info[element].collect_count; i++)
11425               if (player->inventory_size < MAX_INVENTORY_SIZE)
11426                 player->inventory_element[player->inventory_size++] = element;
11427
11428           DrawGameValue_Dynamite(local_player->inventory_size);
11429         }
11430         else if (element_info[element].collect_count > 0)
11431         {
11432           local_player->gems_still_needed -=
11433             element_info[element].collect_count;
11434           if (local_player->gems_still_needed < 0)
11435             local_player->gems_still_needed = 0;
11436
11437           DrawGameValue_Emeralds(local_player->gems_still_needed);
11438         }
11439
11440         RaiseScoreElement(element);
11441         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11442
11443         if (is_player)
11444           CheckTriggeredElementChangeByPlayer(x, y, element,
11445                                               CE_OTHER_GETS_COLLECTED,
11446                                               player->index_bit, dig_side);
11447
11448 #if 1
11449         if (mode == DF_SNAP)
11450           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
11451 #endif
11452
11453         break;
11454       }
11455       else if (IS_PUSHABLE(element))
11456       {
11457         if (mode == DF_SNAP && element != EL_BD_ROCK)
11458           return MF_NO_ACTION;
11459
11460         if (CAN_FALL(element) && dy)
11461           return MF_NO_ACTION;
11462
11463         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11464             !(element == EL_SPRING && level.use_spring_bug))
11465           return MF_NO_ACTION;
11466
11467 #if 1
11468         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11469             ((move_direction & MV_VERTICAL &&
11470               ((element_info[element].move_pattern & MV_LEFT &&
11471                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11472                (element_info[element].move_pattern & MV_RIGHT &&
11473                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11474              (move_direction & MV_HORIZONTAL &&
11475               ((element_info[element].move_pattern & MV_UP &&
11476                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11477                (element_info[element].move_pattern & MV_DOWN &&
11478                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11479           return MF_NO_ACTION;
11480 #endif
11481
11482 #if 1
11483         /* do not push elements already moving away faster than player */
11484         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11485             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11486           return MF_NO_ACTION;
11487 #else
11488         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11489           return MF_NO_ACTION;
11490 #endif
11491
11492 #if 1
11493
11494 #if 1
11495         if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11496         {
11497           if (player->push_delay_value == -1 || !player_was_pushing)
11498             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11499         }
11500         else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11501         {
11502           if (player->push_delay_value == -1)
11503             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11504         }
11505 #else
11506         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11507         {
11508           if (player->push_delay_value == -1 || !player_was_pushing)
11509             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11510         }
11511 #endif
11512         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11513         {
11514           if (!player->is_pushing)
11515             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11516         }
11517
11518         /*
11519         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11520             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11521              !player_is_pushing))
11522           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11523         */
11524 #else
11525         if (!player->is_pushing &&
11526             game.engine_version >= VERSION_IDENT(2,2,0,7))
11527           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11528 #endif
11529
11530 #if 0
11531         printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11532                player->push_delay, player->push_delay_value,
11533                FrameCounter, game.engine_version,
11534                player_was_pushing, player->is_pushing,
11535                element, element_info[element].token_name,
11536                GET_NEW_PUSH_DELAY(element));
11537 #endif
11538
11539         player->is_pushing = TRUE;
11540
11541         if (!(IN_LEV_FIELD(nextx, nexty) &&
11542               (IS_FREE(nextx, nexty) ||
11543                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11544                 IS_SB_ELEMENT(element)))))
11545           return MF_NO_ACTION;
11546
11547         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11548           return MF_NO_ACTION;
11549
11550 #if USE_NEW_PUSH_DELAY
11551
11552 #if 0
11553         if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
11554           printf("::: ALERT: %d, %d [%d / %d]\n",
11555                  player->push_delay, player->push_delay2,
11556                  FrameCounter, FrameCounter / 50);
11557 #endif
11558
11559         if (player->push_delay == -1)   /* new pushing; restart delay */
11560           player->push_delay = 0;
11561 #else
11562         if (player->push_delay == 0)    /* new pushing; restart delay */
11563           player->push_delay = FrameCounter;
11564 #endif
11565
11566 #if USE_NEW_PUSH_DELAY
11567 #if 0
11568         if ( (player->push_delay > 0) != (!xxx_fr) )
11569           printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
11570                  player->push_delay,
11571                  xxx_pdv2, player->push_delay2, player->push_delay_value,
11572                  FrameCounter, FrameCounter / 50);
11573 #endif
11574
11575 #if 0
11576         if (player->push_delay > 0 &&
11577             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11578             element != EL_SPRING && element != EL_BALLOON)
11579 #else
11580         /* !!! */
11581         if (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 #else
11587         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11588             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11589             element != EL_SPRING && element != EL_BALLOON)
11590 #endif
11591         {
11592           /* make sure that there is no move delay before next try to push */
11593 #if USE_NEW_MOVE_DELAY
11594           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11595             player->move_delay = 0;
11596 #else
11597           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11598             player->move_delay = INITIAL_MOVE_DELAY_OFF;
11599 #endif
11600
11601           return MF_NO_ACTION;
11602         }
11603
11604 #if 0
11605         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11606 #endif
11607
11608         if (IS_SB_ELEMENT(element))
11609         {
11610           if (element == EL_SOKOBAN_FIELD_FULL)
11611           {
11612             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11613             local_player->sokobanfields_still_needed++;
11614           }
11615
11616           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11617           {
11618             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11619             local_player->sokobanfields_still_needed--;
11620           }
11621
11622           Feld[x][y] = EL_SOKOBAN_OBJECT;
11623
11624           if (Back[x][y] == Back[nextx][nexty])
11625             PlayLevelSoundAction(x, y, ACTION_PUSHING);
11626           else if (Back[x][y] != 0)
11627             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11628                                         ACTION_EMPTYING);
11629           else
11630             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11631                                         ACTION_FILLING);
11632
11633           if (local_player->sokobanfields_still_needed == 0 &&
11634               game.emulation == EMU_SOKOBAN)
11635           {
11636             player->LevelSolved = player->GameOver = TRUE;
11637             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11638           }
11639         }
11640         else
11641           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11642
11643         InitMovingField(x, y, move_direction);
11644         GfxAction[x][y] = ACTION_PUSHING;
11645
11646         if (mode == DF_SNAP)
11647           ContinueMoving(x, y);
11648         else
11649           MovPos[x][y] = (dx != 0 ? dx : dy);
11650
11651         Pushed[x][y] = TRUE;
11652         Pushed[nextx][nexty] = TRUE;
11653
11654         if (game.engine_version < VERSION_IDENT(2,2,0,7))
11655           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11656         else
11657           player->push_delay_value = -1;        /* get new value later */
11658
11659 #if 1
11660         /* check for element change _after_ element has been pushed! */
11661 #else
11662
11663 #if 1
11664       /* !!! TEST ONLY !!! */
11665         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11666                                    player->index_bit, dig_side);
11667         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11668                                             player->index_bit, dig_side);
11669 #else
11670         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11671                                             player->index_bit, dig_side);
11672         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11673                                    player->index_bit, dig_side);
11674 #endif
11675 #endif
11676
11677         break;
11678       }
11679       else if (IS_SWITCHABLE(element))
11680       {
11681         if (PLAYER_SWITCHING(player, x, y))
11682         {
11683           CheckTriggeredElementChangeByPlayer(x,y, element,
11684                                               CE_OTHER_GETS_PRESSED,
11685                                               player->index_bit, dig_side);
11686
11687           return MF_ACTION;
11688         }
11689
11690         player->is_switching = TRUE;
11691         player->switch_x = x;
11692         player->switch_y = y;
11693
11694         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11695
11696         if (element == EL_ROBOT_WHEEL)
11697         {
11698           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11699           ZX = x;
11700           ZY = y;
11701
11702           DrawLevelField(x, y);
11703         }
11704         else if (element == EL_SP_TERMINAL)
11705         {
11706           int xx, yy;
11707
11708           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11709           {
11710             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11711               Bang(xx, yy);
11712             else if (Feld[xx][yy] == EL_SP_TERMINAL)
11713               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11714           }
11715         }
11716         else if (IS_BELT_SWITCH(element))
11717         {
11718           ToggleBeltSwitch(x, y);
11719         }
11720         else if (element == EL_SWITCHGATE_SWITCH_UP ||
11721                  element == EL_SWITCHGATE_SWITCH_DOWN)
11722         {
11723           ToggleSwitchgateSwitch(x, y);
11724         }
11725         else if (element == EL_LIGHT_SWITCH ||
11726                  element == EL_LIGHT_SWITCH_ACTIVE)
11727         {
11728           ToggleLightSwitch(x, y);
11729
11730 #if 0
11731           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11732                          SND_LIGHT_SWITCH_ACTIVATING :
11733                          SND_LIGHT_SWITCH_DEACTIVATING);
11734 #endif
11735         }
11736         else if (element == EL_TIMEGATE_SWITCH)
11737         {
11738           ActivateTimegateSwitch(x, y);
11739         }
11740         else if (element == EL_BALLOON_SWITCH_LEFT ||
11741                  element == EL_BALLOON_SWITCH_RIGHT ||
11742                  element == EL_BALLOON_SWITCH_UP ||
11743                  element == EL_BALLOON_SWITCH_DOWN ||
11744                  element == EL_BALLOON_SWITCH_ANY)
11745         {
11746           if (element == EL_BALLOON_SWITCH_ANY)
11747             game.balloon_dir = move_direction;
11748           else
11749             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
11750                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11751                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
11752                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
11753                                 MV_NO_MOVING);
11754         }
11755         else if (element == EL_LAMP)
11756         {
11757           Feld[x][y] = EL_LAMP_ACTIVE;
11758           local_player->lights_still_needed--;
11759
11760           DrawLevelField(x, y);
11761         }
11762         else if (element == EL_TIME_ORB_FULL)
11763         {
11764           Feld[x][y] = EL_TIME_ORB_EMPTY;
11765           TimeLeft += 10;
11766           DrawGameValue_Time(TimeLeft);
11767
11768           DrawLevelField(x, y);
11769
11770 #if 0
11771           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11772 #endif
11773         }
11774
11775         CheckTriggeredElementChangeByPlayer(x, y, element,
11776                                             CE_OTHER_IS_SWITCHING,
11777                                             player->index_bit, dig_side);
11778
11779         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11780                                             player->index_bit, dig_side);
11781
11782         return MF_ACTION;
11783       }
11784       else
11785       {
11786         if (!PLAYER_SWITCHING(player, x, y))
11787         {
11788           player->is_switching = TRUE;
11789           player->switch_x = x;
11790           player->switch_y = y;
11791
11792 #if 1
11793           /* !!! TEST ONLY !!! */
11794           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11795                                      player->index_bit, dig_side);
11796           CheckTriggeredElementChangeByPlayer(x, y, element,
11797                                               CE_OTHER_IS_SWITCHING,
11798                                               player->index_bit, dig_side);
11799 #else
11800           CheckTriggeredElementChangeByPlayer(x, y, element,
11801                                               CE_OTHER_IS_SWITCHING,
11802                                               player->index_bit, dig_side);
11803           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11804                                      player->index_bit, dig_side);
11805 #endif
11806         }
11807
11808 #if 1
11809         /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11810         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11811                                    player->index_bit, dig_side);
11812         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11813                                             player->index_bit, dig_side);
11814 #else
11815         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11816                                             player->index_bit, dig_side);
11817         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11818                                    player->index_bit, dig_side);
11819 #endif
11820       }
11821
11822       return MF_NO_ACTION;
11823   }
11824
11825 #if USE_NEW_PUSH_DELAY
11826   player->push_delay = -1;
11827 #else
11828   player->push_delay = 0;
11829 #endif
11830
11831   if (Feld[x][y] != element)            /* really digged/collected something */
11832     player->is_collecting = !player->is_digging;
11833
11834   return MF_MOVING;
11835 }
11836
11837 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11838 {
11839   int jx = player->jx, jy = player->jy;
11840   int x = jx + dx, y = jy + dy;
11841   int snap_direction = (dx == -1 ? MV_LEFT :
11842                         dx == +1 ? MV_RIGHT :
11843                         dy == -1 ? MV_UP :
11844                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
11845
11846 #if 0
11847   if (player->MovPos != 0)
11848     return FALSE;
11849 #else
11850   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11851     return FALSE;
11852 #endif
11853
11854   if (!player->active || !IN_LEV_FIELD(x, y))
11855     return FALSE;
11856
11857   if (dx && dy)
11858     return FALSE;
11859
11860   if (!dx && !dy)
11861   {
11862     if (player->MovPos == 0)
11863       player->is_pushing = FALSE;
11864
11865     player->is_snapping = FALSE;
11866
11867     if (player->MovPos == 0)
11868     {
11869       player->is_moving = FALSE;
11870       player->is_digging = FALSE;
11871       player->is_collecting = FALSE;
11872     }
11873
11874     return FALSE;
11875   }
11876
11877   if (player->is_snapping)
11878     return FALSE;
11879
11880   player->MovDir = snap_direction;
11881
11882 #if 1
11883   if (player->MovPos == 0)
11884 #endif
11885   {
11886     player->is_moving = FALSE;
11887     player->is_digging = FALSE;
11888     player->is_collecting = FALSE;
11889   }
11890
11891   player->is_dropping = FALSE;
11892
11893   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11894     return FALSE;
11895
11896   player->is_snapping = TRUE;
11897
11898 #if 1
11899   if (player->MovPos == 0)
11900 #endif
11901   {
11902     player->is_moving = FALSE;
11903     player->is_digging = FALSE;
11904     player->is_collecting = FALSE;
11905   }
11906
11907 #if 1
11908   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
11909     DrawLevelField(player->last_jx, player->last_jy);
11910 #endif
11911
11912   DrawLevelField(x, y);
11913
11914 #if 0
11915   BackToFront();
11916 #endif
11917
11918   return TRUE;
11919 }
11920
11921 boolean DropElement(struct PlayerInfo *player)
11922 {
11923   int old_element, new_element;
11924   int dropx = player->jx, dropy = player->jy;
11925   int drop_direction = player->MovDir;
11926 #if 1
11927   int drop_side = drop_direction;
11928 #else
11929   static int trigger_sides[4] =
11930   {
11931     CH_SIDE_LEFT,       /* dropping left  */
11932     CH_SIDE_RIGHT,      /* dropping right */
11933     CH_SIDE_TOP,        /* dropping up    */
11934     CH_SIDE_BOTTOM,     /* dropping down  */
11935   };
11936   int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11937 #endif
11938   int drop_element = (player->inventory_size > 0 ?
11939                       player->inventory_element[player->inventory_size - 1] :
11940                       player->inventory_infinite_element != EL_UNDEFINED ?
11941                       player->inventory_infinite_element :
11942                       player->dynabombs_left > 0 ?
11943                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11944                       EL_UNDEFINED);
11945
11946   if (IS_THROWABLE(drop_element))
11947   {
11948     dropx += GET_DX_FROM_DIR(drop_direction);
11949     dropy += GET_DY_FROM_DIR(drop_direction);
11950
11951     if (!IN_LEV_FIELD(dropx, dropy))
11952       return FALSE;
11953   }
11954
11955   old_element = Feld[dropx][dropy];     /* old element at dropping position */
11956   new_element = drop_element;           /* default: no change when dropping */
11957
11958   /* check if player is active, not moving and ready to drop */
11959   if (!player->active || player->MovPos || player->drop_delay > 0)
11960     return FALSE;
11961
11962   /* check if player has anything that can be dropped */
11963 #if 1
11964   if (new_element == EL_UNDEFINED)
11965     return FALSE;
11966 #else
11967   if (player->inventory_size == 0 &&
11968       player->inventory_infinite_element == EL_UNDEFINED &&
11969       player->dynabombs_left == 0)
11970     return FALSE;
11971 #endif
11972
11973   /* check if anything can be dropped at the current position */
11974   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11975     return FALSE;
11976
11977   /* collected custom elements can only be dropped on empty fields */
11978 #if 1
11979   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11980     return FALSE;
11981 #else
11982   if (player->inventory_size > 0 &&
11983       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11984       && old_element != EL_EMPTY)
11985     return FALSE;
11986 #endif
11987
11988   if (old_element != EL_EMPTY)
11989     Back[dropx][dropy] = old_element;   /* store old element on this field */
11990
11991   ResetGfxAnimation(dropx, dropy);
11992   ResetRandomAnimationValue(dropx, dropy);
11993
11994   if (player->inventory_size > 0 ||
11995       player->inventory_infinite_element != EL_UNDEFINED)
11996   {
11997     if (player->inventory_size > 0)
11998     {
11999       player->inventory_size--;
12000
12001 #if 0
12002       new_element = player->inventory_element[player->inventory_size];
12003 #endif
12004
12005       DrawGameValue_Dynamite(local_player->inventory_size);
12006
12007       if (new_element == EL_DYNAMITE)
12008         new_element = EL_DYNAMITE_ACTIVE;
12009       else if (new_element == EL_SP_DISK_RED)
12010         new_element = EL_SP_DISK_RED_ACTIVE;
12011     }
12012
12013     Feld[dropx][dropy] = new_element;
12014
12015     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12016       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12017                           el2img(Feld[dropx][dropy]), 0);
12018
12019     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12020
12021 #if 1
12022     /* needed if previous element just changed to "empty" in the last frame */
12023     Changed[dropx][dropy] = 0;          /* allow another change */
12024 #endif
12025
12026 #if 1
12027     /* !!! TEST ONLY !!! */
12028     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12029                                player->index_bit, drop_side);
12030     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12031                                         CE_OTHER_GETS_DROPPED,
12032                                         player->index_bit, drop_side);
12033 #else
12034     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12035                                         CE_OTHER_GETS_DROPPED,
12036                                         player->index_bit, drop_side);
12037     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12038                                player->index_bit, drop_side);
12039 #endif
12040
12041     TestIfElementTouchesCustomElement(dropx, dropy);
12042   }
12043   else          /* player is dropping a dyna bomb */
12044   {
12045     player->dynabombs_left--;
12046
12047 #if 0
12048     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
12049 #endif
12050
12051     Feld[dropx][dropy] = new_element;
12052
12053     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12054       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12055                           el2img(Feld[dropx][dropy]), 0);
12056
12057     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12058   }
12059
12060
12061
12062 #if 1
12063
12064   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12065   {
12066 #if 1
12067     InitField_WithBug1(dropx, dropy, FALSE);
12068 #else
12069     InitField(dropx, dropy, FALSE);
12070     if (CAN_MOVE(Feld[dropx][dropy]))
12071       InitMovDir(dropx, dropy);
12072 #endif
12073   }
12074
12075   new_element = Feld[dropx][dropy];     /* element might have changed */
12076
12077   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12078       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12079   {
12080 #if 0
12081     int move_stepsize = element_info[new_element].move_stepsize;
12082 #endif
12083     int move_direction, nextx, nexty;
12084
12085     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12086       MovDir[dropx][dropy] = drop_direction;
12087
12088     move_direction = MovDir[dropx][dropy];
12089     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12090     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12091
12092 #if 1
12093     Changed[dropx][dropy] = 0;          /* allow another change */
12094     CheckCollision[dropx][dropy] = 2;
12095 #else
12096
12097     if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
12098     {
12099 #if 0
12100       WasJustMoving[dropx][dropy] = 3;
12101 #else
12102 #if 1
12103       InitMovingField(dropx, dropy, move_direction);
12104       ContinueMoving(dropx, dropy);
12105 #endif
12106 #endif
12107     }
12108 #if 0
12109     /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
12110     else
12111     {
12112       Changed[dropx][dropy] = 0;        /* allow another change */
12113
12114 #if 1
12115       TestIfElementHitsCustomElement(dropx, dropy, move_direction);
12116 #else
12117       CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
12118                                CE_HITTING_SOMETHING, move_direction);
12119 #endif
12120     }
12121 #endif
12122
12123 #endif
12124
12125 #if 0
12126     player->drop_delay = 2 * TILEX / move_stepsize + 1;
12127 #endif
12128   }
12129
12130 #if 0
12131   player->drop_delay = 8 + 8 + 8;
12132 #endif
12133
12134 #if 1
12135   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12136 #endif
12137
12138 #endif
12139
12140   player->is_dropping = TRUE;
12141
12142
12143   return TRUE;
12144 }
12145
12146 /* ------------------------------------------------------------------------- */
12147 /* game sound playing functions                                              */
12148 /* ------------------------------------------------------------------------- */
12149
12150 static int *loop_sound_frame = NULL;
12151 static int *loop_sound_volume = NULL;
12152
12153 void InitPlayLevelSound()
12154 {
12155   int num_sounds = getSoundListSize();
12156
12157   checked_free(loop_sound_frame);
12158   checked_free(loop_sound_volume);
12159
12160   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12161   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12162 }
12163
12164 static void PlayLevelSound(int x, int y, int nr)
12165 {
12166   int sx = SCREENX(x), sy = SCREENY(y);
12167   int volume, stereo_position;
12168   int max_distance = 8;
12169   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12170
12171   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12172       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12173     return;
12174
12175   if (!IN_LEV_FIELD(x, y) ||
12176       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12177       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12178     return;
12179
12180   volume = SOUND_MAX_VOLUME;
12181
12182   if (!IN_SCR_FIELD(sx, sy))
12183   {
12184     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12185     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12186
12187     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12188   }
12189
12190   stereo_position = (SOUND_MAX_LEFT +
12191                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12192                      (SCR_FIELDX + 2 * max_distance));
12193
12194   if (IS_LOOP_SOUND(nr))
12195   {
12196     /* This assures that quieter loop sounds do not overwrite louder ones,
12197        while restarting sound volume comparison with each new game frame. */
12198
12199     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12200       return;
12201
12202     loop_sound_volume[nr] = volume;
12203     loop_sound_frame[nr] = FrameCounter;
12204   }
12205
12206   PlaySoundExt(nr, volume, stereo_position, type);
12207 }
12208
12209 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12210 {
12211   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12212                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12213                  y < LEVELY(BY1) ? LEVELY(BY1) :
12214                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12215                  sound_action);
12216 }
12217
12218 static void PlayLevelSoundAction(int x, int y, int action)
12219 {
12220   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12221 }
12222
12223 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12224 {
12225   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12226
12227   if (sound_effect != SND_UNDEFINED)
12228     PlayLevelSound(x, y, sound_effect);
12229 }
12230
12231 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12232                                               int action)
12233 {
12234   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12235
12236   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12237     PlayLevelSound(x, y, sound_effect);
12238 }
12239
12240 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12241 {
12242   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12243
12244   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12245     PlayLevelSound(x, y, sound_effect);
12246 }
12247
12248 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12249 {
12250   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12251
12252   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12253     StopSound(sound_effect);
12254 }
12255
12256 static void PlayLevelMusic()
12257 {
12258   if (levelset.music[level_nr] != MUS_UNDEFINED)
12259     PlayMusic(levelset.music[level_nr]);        /* from config file */
12260   else
12261     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
12262 }
12263
12264 void RaiseScore(int value)
12265 {
12266   local_player->score += value;
12267
12268   DrawGameValue_Score(local_player->score);
12269 }
12270
12271 void RaiseScoreElement(int element)
12272 {
12273   switch(element)
12274   {
12275     case EL_EMERALD:
12276     case EL_BD_DIAMOND:
12277     case EL_EMERALD_YELLOW:
12278     case EL_EMERALD_RED:
12279     case EL_EMERALD_PURPLE:
12280     case EL_SP_INFOTRON:
12281       RaiseScore(level.score[SC_EMERALD]);
12282       break;
12283     case EL_DIAMOND:
12284       RaiseScore(level.score[SC_DIAMOND]);
12285       break;
12286     case EL_CRYSTAL:
12287       RaiseScore(level.score[SC_CRYSTAL]);
12288       break;
12289     case EL_PEARL:
12290       RaiseScore(level.score[SC_PEARL]);
12291       break;
12292     case EL_BUG:
12293     case EL_BD_BUTTERFLY:
12294     case EL_SP_ELECTRON:
12295       RaiseScore(level.score[SC_BUG]);
12296       break;
12297     case EL_SPACESHIP:
12298     case EL_BD_FIREFLY:
12299     case EL_SP_SNIKSNAK:
12300       RaiseScore(level.score[SC_SPACESHIP]);
12301       break;
12302     case EL_YAMYAM:
12303     case EL_DARK_YAMYAM:
12304       RaiseScore(level.score[SC_YAMYAM]);
12305       break;
12306     case EL_ROBOT:
12307       RaiseScore(level.score[SC_ROBOT]);
12308       break;
12309     case EL_PACMAN:
12310       RaiseScore(level.score[SC_PACMAN]);
12311       break;
12312     case EL_NUT:
12313       RaiseScore(level.score[SC_NUT]);
12314       break;
12315     case EL_DYNAMITE:
12316     case EL_SP_DISK_RED:
12317     case EL_DYNABOMB_INCREASE_NUMBER:
12318     case EL_DYNABOMB_INCREASE_SIZE:
12319     case EL_DYNABOMB_INCREASE_POWER:
12320       RaiseScore(level.score[SC_DYNAMITE]);
12321       break;
12322     case EL_SHIELD_NORMAL:
12323     case EL_SHIELD_DEADLY:
12324       RaiseScore(level.score[SC_SHIELD]);
12325       break;
12326     case EL_EXTRA_TIME:
12327       RaiseScore(level.score[SC_TIME_BONUS]);
12328       break;
12329     case EL_KEY_1:
12330     case EL_KEY_2:
12331     case EL_KEY_3:
12332     case EL_KEY_4:
12333       RaiseScore(level.score[SC_KEY]);
12334       break;
12335     default:
12336       RaiseScore(element_info[element].collect_score);
12337       break;
12338   }
12339 }
12340
12341 void RequestQuitGame(boolean ask_if_really_quit)
12342 {
12343   if (AllPlayersGone ||
12344       !ask_if_really_quit ||
12345       level_editor_test_game ||
12346       Request("Do you really want to quit the game ?",
12347               REQ_ASK | REQ_STAY_CLOSED))
12348   {
12349 #if defined(NETWORK_AVALIABLE)
12350     if (options.network)
12351       SendToServer_StopPlaying();
12352     else
12353 #endif
12354     {
12355       game_status = GAME_MODE_MAIN;
12356       DrawMainMenu();
12357     }
12358   }
12359   else
12360   {
12361
12362 #if 1
12363     if (tape.playing && tape.deactivate_display)
12364       TapeDeactivateDisplayOff(TRUE);
12365 #endif
12366
12367     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12368
12369 #if 1
12370     if (tape.playing && tape.deactivate_display)
12371       TapeDeactivateDisplayOn();
12372 #endif
12373
12374   }
12375 }
12376
12377
12378 /* ---------- new game button stuff ---------------------------------------- */
12379
12380 /* graphic position values for game buttons */
12381 #define GAME_BUTTON_XSIZE       30
12382 #define GAME_BUTTON_YSIZE       30
12383 #define GAME_BUTTON_XPOS        5
12384 #define GAME_BUTTON_YPOS        215
12385 #define SOUND_BUTTON_XPOS       5
12386 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12387
12388 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12389 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12390 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12391 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12392 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12393 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12394
12395 static struct
12396 {
12397   int x, y;
12398   int gadget_id;
12399   char *infotext;
12400 } gamebutton_info[NUM_GAME_BUTTONS] =
12401 {
12402   {
12403     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
12404     GAME_CTRL_ID_STOP,
12405     "stop game"
12406   },
12407   {
12408     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
12409     GAME_CTRL_ID_PAUSE,
12410     "pause game"
12411   },
12412   {
12413     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
12414     GAME_CTRL_ID_PLAY,
12415     "play game"
12416   },
12417   {
12418     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
12419     SOUND_CTRL_ID_MUSIC,
12420     "background music on/off"
12421   },
12422   {
12423     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
12424     SOUND_CTRL_ID_LOOPS,
12425     "sound loops on/off"
12426   },
12427   {
12428     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
12429     SOUND_CTRL_ID_SIMPLE,
12430     "normal sounds on/off"
12431   }
12432 };
12433
12434 void CreateGameButtons()
12435 {
12436   int i;
12437
12438   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12439   {
12440     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12441     struct GadgetInfo *gi;
12442     int button_type;
12443     boolean checked;
12444     unsigned long event_mask;
12445     int gd_xoffset, gd_yoffset;
12446     int gd_x1, gd_x2, gd_y1, gd_y2;
12447     int id = i;
12448
12449     gd_xoffset = gamebutton_info[i].x;
12450     gd_yoffset = gamebutton_info[i].y;
12451     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12452     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12453
12454     if (id == GAME_CTRL_ID_STOP ||
12455         id == GAME_CTRL_ID_PAUSE ||
12456         id == GAME_CTRL_ID_PLAY)
12457     {
12458       button_type = GD_TYPE_NORMAL_BUTTON;
12459       checked = FALSE;
12460       event_mask = GD_EVENT_RELEASED;
12461       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12462       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12463     }
12464     else
12465     {
12466       button_type = GD_TYPE_CHECK_BUTTON;
12467       checked =
12468         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12469          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12470          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12471       event_mask = GD_EVENT_PRESSED;
12472       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
12473       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12474     }
12475
12476     gi = CreateGadget(GDI_CUSTOM_ID, id,
12477                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
12478                       GDI_X, DX + gd_xoffset,
12479                       GDI_Y, DY + gd_yoffset,
12480                       GDI_WIDTH, GAME_BUTTON_XSIZE,
12481                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
12482                       GDI_TYPE, button_type,
12483                       GDI_STATE, GD_BUTTON_UNPRESSED,
12484                       GDI_CHECKED, checked,
12485                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12486                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12487                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12488                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12489                       GDI_EVENT_MASK, event_mask,
12490                       GDI_CALLBACK_ACTION, HandleGameButtons,
12491                       GDI_END);
12492
12493     if (gi == NULL)
12494       Error(ERR_EXIT, "cannot create gadget");
12495
12496     game_gadget[id] = gi;
12497   }
12498 }
12499
12500 void FreeGameButtons()
12501 {
12502   int i;
12503
12504   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12505     FreeGadget(game_gadget[i]);
12506 }
12507
12508 static void MapGameButtons()
12509 {
12510   int i;
12511
12512   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12513     MapGadget(game_gadget[i]);
12514 }
12515
12516 void UnmapGameButtons()
12517 {
12518   int i;
12519
12520   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12521     UnmapGadget(game_gadget[i]);
12522 }
12523
12524 static void HandleGameButtons(struct GadgetInfo *gi)
12525 {
12526   int id = gi->custom_id;
12527
12528   if (game_status != GAME_MODE_PLAYING)
12529     return;
12530
12531   switch (id)
12532   {
12533     case GAME_CTRL_ID_STOP:
12534       RequestQuitGame(TRUE);
12535       break;
12536
12537     case GAME_CTRL_ID_PAUSE:
12538       if (options.network)
12539       {
12540 #if defined(NETWORK_AVALIABLE)
12541         if (tape.pausing)
12542           SendToServer_ContinuePlaying();
12543         else
12544           SendToServer_PausePlaying();
12545 #endif
12546       }
12547       else
12548         TapeTogglePause(TAPE_TOGGLE_MANUAL);
12549       break;
12550
12551     case GAME_CTRL_ID_PLAY:
12552       if (tape.pausing)
12553       {
12554 #if defined(NETWORK_AVALIABLE)
12555         if (options.network)
12556           SendToServer_ContinuePlaying();
12557         else
12558 #endif
12559         {
12560           tape.pausing = FALSE;
12561           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12562         }
12563       }
12564       break;
12565
12566     case SOUND_CTRL_ID_MUSIC:
12567       if (setup.sound_music)
12568       { 
12569         setup.sound_music = FALSE;
12570         FadeMusic();
12571       }
12572       else if (audio.music_available)
12573       { 
12574         setup.sound = setup.sound_music = TRUE;
12575
12576         SetAudioMode(setup.sound);
12577
12578         PlayLevelMusic();
12579       }
12580       break;
12581
12582     case SOUND_CTRL_ID_LOOPS:
12583       if (setup.sound_loops)
12584         setup.sound_loops = FALSE;
12585       else if (audio.loops_available)
12586       {
12587         setup.sound = setup.sound_loops = TRUE;
12588         SetAudioMode(setup.sound);
12589       }
12590       break;
12591
12592     case SOUND_CTRL_ID_SIMPLE:
12593       if (setup.sound_simple)
12594         setup.sound_simple = FALSE;
12595       else if (audio.sound_available)
12596       {
12597         setup.sound = setup.sound_simple = TRUE;
12598         SetAudioMode(setup.sound);
12599       }
12600       break;
12601
12602     default:
12603       break;
12604   }
12605 }