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