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