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