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