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