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