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