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