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