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