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