rnd-20040430-2-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* for DigField() */
31 #define DF_NO_PUSH              0
32 #define DF_DIG                  1
33 #define DF_SNAP                 2
34
35 /* for MovePlayer() */
36 #define MF_NO_ACTION            0
37 #define MF_MOVING               1
38 #define MF_ACTION               2
39
40 /* for ScrollPlayer() */
41 #define SCROLL_INIT             0
42 #define SCROLL_GO_ON            1
43
44 /* for Explode() */
45 #define EX_PHASE_START          0
46 #define EX_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 1
3054       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3055           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3056            (x == ex && y == ey)))
3057 #else
3058       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3059 #endif
3060       {
3061         if (IS_ACTIVE_BOMB(element))
3062         {
3063           /* re-activate things under the bomb like gate or penguin */
3064 #if 1
3065           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3066           Back[x][y] = 0;
3067 #else
3068           Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3069           Store[x][y] = 0;
3070 #endif
3071         }
3072
3073         continue;
3074       }
3075
3076       /* save walkable background elements while explosion on same tile */
3077 #if 0
3078       if (IS_INDESTRUCTIBLE(element))
3079         Back[x][y] = element;
3080 #else
3081 #if 1
3082       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3083           (x != ex || y != ey))
3084         Back[x][y] = element;
3085 #else
3086       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3087         Back[x][y] = element;
3088 #endif
3089 #endif
3090
3091       /* ignite explodable elements reached by other explosion */
3092       if (element == EL_EXPLOSION)
3093         element = Store2[x][y];
3094
3095 #if 1
3096       if (AmoebaNr[x][y] &&
3097           (element == EL_AMOEBA_FULL ||
3098            element == EL_BD_AMOEBA ||
3099            element == EL_AMOEBA_GROWING))
3100       {
3101         AmoebaCnt[AmoebaNr[x][y]]--;
3102         AmoebaCnt2[AmoebaNr[x][y]]--;
3103       }
3104
3105       RemoveField(x, y);
3106 #endif
3107
3108       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3109       {
3110         switch(StorePlayer[ex][ey])
3111         {
3112           case EL_PLAYER_2:
3113             Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3114             break;
3115           case EL_PLAYER_3:
3116             Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3117             break;
3118           case EL_PLAYER_4:
3119             Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3120             break;
3121           case EL_PLAYER_1:
3122           default:
3123             Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3124             break;
3125         }
3126
3127 #if 1
3128         if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3129           Store[x][y] = EL_EMPTY;
3130 #else
3131         if (game.emulation == EMU_SUPAPLEX)
3132           Store[x][y] = EL_EMPTY;
3133 #endif
3134       }
3135       else if (center_element == EL_MOLE)
3136         Store[x][y] = EL_EMERALD_RED;
3137       else if (center_element == EL_PENGUIN)
3138         Store[x][y] = EL_EMERALD_PURPLE;
3139       else if (center_element == EL_BUG)
3140         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3141       else if (center_element == EL_BD_BUTTERFLY)
3142         Store[x][y] = EL_BD_DIAMOND;
3143       else if (center_element == EL_SP_ELECTRON)
3144         Store[x][y] = EL_SP_INFOTRON;
3145       else if (center_element == EL_AMOEBA_TO_DIAMOND)
3146         Store[x][y] = level.amoeba_content;
3147       else if (center_element == EL_YAMYAM)
3148         Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3149       else if (IS_CUSTOM_ELEMENT(center_element) &&
3150                element_info[center_element].content[xx][yy] != EL_EMPTY)
3151         Store[x][y] = element_info[center_element].content[xx][yy];
3152       else if (element == EL_WALL_EMERALD)
3153         Store[x][y] = EL_EMERALD;
3154       else if (element == EL_WALL_DIAMOND)
3155         Store[x][y] = EL_DIAMOND;
3156       else if (element == EL_WALL_BD_DIAMOND)
3157         Store[x][y] = EL_BD_DIAMOND;
3158       else if (element == EL_WALL_EMERALD_YELLOW)
3159         Store[x][y] = EL_EMERALD_YELLOW;
3160       else if (element == EL_WALL_EMERALD_RED)
3161         Store[x][y] = EL_EMERALD_RED;
3162       else if (element == EL_WALL_EMERALD_PURPLE)
3163         Store[x][y] = EL_EMERALD_PURPLE;
3164       else if (element == EL_WALL_PEARL)
3165         Store[x][y] = EL_PEARL;
3166       else if (element == EL_WALL_CRYSTAL)
3167         Store[x][y] = EL_CRYSTAL;
3168       else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3169         Store[x][y] = element_info[element].content[1][1];
3170       else
3171         Store[x][y] = EL_EMPTY;
3172
3173       if (x != ex || y != ey ||
3174           center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_TYPE_BORDER)
3175         Store2[x][y] = element;
3176
3177 #if 0
3178       if (AmoebaNr[x][y] &&
3179           (element == EL_AMOEBA_FULL ||
3180            element == EL_BD_AMOEBA ||
3181            element == EL_AMOEBA_GROWING))
3182       {
3183         AmoebaCnt[AmoebaNr[x][y]]--;
3184         AmoebaCnt2[AmoebaNr[x][y]]--;
3185       }
3186
3187 #if 1
3188       RemoveField(x, y);
3189 #else
3190       MovDir[x][y] = MovPos[x][y] = 0;
3191       GfxDir[x][y] = MovDir[x][y];
3192       AmoebaNr[x][y] = 0;
3193 #endif
3194 #endif
3195
3196       Feld[x][y] = EL_EXPLOSION;
3197 #if 1
3198       GfxElement[x][y] = center_element;
3199 #else
3200       GfxElement[x][y] = EL_UNDEFINED;
3201 #endif
3202
3203       ExplodePhase[x][y] = 1;
3204 #if 1
3205       ExplodeDelay[x][y] = last_phase;
3206 #endif
3207
3208 #if 0
3209 #if 1
3210       GfxFrame[x][y] = 0;       /* animation does not start until next frame */
3211 #else
3212       GfxFrame[x][y] = -1;      /* animation does not start until next frame */
3213 #endif
3214 #endif
3215
3216       Stop[x][y] = TRUE;
3217     }
3218
3219     if (center_element == EL_YAMYAM)
3220       game.yamyam_content_nr =
3221         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3222
3223     return;
3224   }
3225
3226   if (Stop[ex][ey])
3227     return;
3228
3229   x = ex;
3230   y = ey;
3231
3232 #if 1
3233   if (phase == 1)
3234     GfxFrame[x][y] = 0;         /* restart explosion animation */
3235 #endif
3236
3237 #if 0
3238   printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3239 #endif
3240
3241 #if 1
3242   last_phase = ExplodeDelay[x][y];
3243 #endif
3244
3245   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3246
3247 #ifdef DEBUG
3248
3249   /* activate this even in non-DEBUG version until cause for crash in
3250      getGraphicAnimationFrame() (see below) is found and eliminated */
3251 #endif
3252 #if 1
3253
3254   if (GfxElement[x][y] == EL_UNDEFINED)
3255   {
3256     printf("\n\n");
3257     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3258     printf("Explode(): This should never happen!\n");
3259     printf("\n\n");
3260
3261     GfxElement[x][y] = EL_EMPTY;
3262   }
3263 #endif
3264
3265 #if 1
3266
3267   border_element = Store2[x][y];
3268 #if 1
3269   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3270     border_element = StorePlayer[x][y];
3271 #else
3272   if (IS_PLAYER(x, y))
3273     border_element = StorePlayer[x][y];
3274 #endif
3275
3276 #if 0
3277   printf("::: phase == %d\n", phase);
3278 #endif
3279
3280   if (phase == element_info[border_element].ignition_delay ||
3281       phase == last_phase)
3282   {
3283     boolean border_explosion = FALSE;
3284
3285 #if 1
3286 #if 1
3287     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3288         !PLAYER_EXPLOSION_PROTECTED(x, y))
3289 #else
3290     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3291 #endif
3292 #else
3293     if (IS_PLAYER(x, y))
3294 #endif
3295     {
3296       KillHeroUnlessExplosionProtected(x, y);
3297       border_explosion = TRUE;
3298
3299 #if 0
3300       if (phase == last_phase)
3301         printf("::: IS_PLAYER\n");
3302 #endif
3303     }
3304     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3305     {
3306       Feld[x][y] = Store2[x][y];
3307       Store2[x][y] = 0;
3308       Bang(x, y);
3309       border_explosion = TRUE;
3310
3311 #if 0
3312       if (phase == last_phase)
3313         printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3314 #endif
3315     }
3316     else if (border_element == EL_AMOEBA_TO_DIAMOND)
3317     {
3318       AmoebeUmwandeln(x, y);
3319       Store2[x][y] = 0;
3320       border_explosion = TRUE;
3321
3322 #if 0
3323       if (phase == last_phase)
3324         printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3325                element_info[border_element].explosion_delay,
3326                element_info[border_element].ignition_delay,
3327                phase);
3328 #endif
3329     }
3330
3331 #if 1
3332     /* if an element just explodes due to another explosion (chain-reaction),
3333        do not immediately end the new explosion when it was the last frame of
3334        the explosion (as it would be done in the following "if"-statement!) */
3335     if (border_explosion && phase == last_phase)
3336       return;
3337 #endif
3338   }
3339
3340 #else
3341
3342   if (phase == first_phase_after_start)
3343   {
3344     int element = Store2[x][y];
3345
3346     if (element == EL_BLACK_ORB)
3347     {
3348       Feld[x][y] = Store2[x][y];
3349       Store2[x][y] = 0;
3350       Bang(x, y);
3351     }
3352   }
3353   else if (phase == half_phase)
3354   {
3355     int element = Store2[x][y];
3356
3357     if (IS_PLAYER(x, y))
3358       KillHeroUnlessExplosionProtected(x, y);
3359     else if (CAN_EXPLODE_BY_EXPLOSION(element))
3360     {
3361       Feld[x][y] = Store2[x][y];
3362       Store2[x][y] = 0;
3363       Bang(x, y);
3364     }
3365     else if (element == EL_AMOEBA_TO_DIAMOND)
3366       AmoebeUmwandeln(x, y);
3367   }
3368 #endif
3369
3370   if (phase == last_phase)
3371   {
3372     int element;
3373
3374 #if 0
3375   printf("::: done: phase == %d\n", phase);
3376 #endif
3377
3378 #if 0
3379     printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3380 #endif
3381
3382     element = Feld[x][y] = Store[x][y];
3383     Store[x][y] = Store2[x][y] = 0;
3384     GfxElement[x][y] = EL_UNDEFINED;
3385
3386     /* player can escape from explosions and might therefore be still alive */
3387     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3388         element <= EL_PLAYER_IS_EXPLODING_4)
3389       Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3390                     EL_EMPTY :
3391                     element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3392                     element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3393                     element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3394                     EL_EMERALD_PURPLE);
3395
3396     /* restore probably existing indestructible background element */
3397     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3398       element = Feld[x][y] = Back[x][y];
3399     Back[x][y] = 0;
3400
3401     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3402     GfxDir[x][y] = MV_NO_MOVING;
3403     ChangeDelay[x][y] = 0;
3404     ChangePage[x][y] = -1;
3405
3406 #if 1
3407     InitField_WithBug2(x, y, FALSE);
3408 #else
3409     InitField(x, y, FALSE);
3410 #if 1
3411     /* !!! not needed !!! */
3412 #if 1
3413     if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3414         CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3415       InitMovDir(x, y);
3416 #else
3417     if (CAN_MOVE(element))
3418       InitMovDir(x, y);
3419 #endif
3420 #endif
3421 #endif
3422     DrawLevelField(x, y);
3423
3424     TestIfElementTouchesCustomElement(x, y);
3425
3426     if (GFX_CRUMBLED(element))
3427       DrawLevelFieldCrumbledSandNeighbours(x, y);
3428
3429     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3430       StorePlayer[x][y] = 0;
3431
3432     if (ELEM_IS_PLAYER(element))
3433       RelocatePlayer(x, y, element);
3434   }
3435 #if 1
3436   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3437 #else
3438   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3439 #endif
3440   {
3441 #if 1
3442     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3443 #else
3444     int stored = Store[x][y];
3445     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3446                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3447                    IMG_SP_EXPLOSION);
3448 #endif
3449 #if 1
3450     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3451 #else
3452     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3453 #endif
3454
3455 #if 0
3456   printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3457 #endif
3458
3459 #if 0
3460     printf("::: %d / %d [%d - %d]\n",
3461            GfxFrame[x][y], phase - delay, phase, delay);
3462 #endif
3463
3464 #if 0
3465     printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3466            element_info[GfxElement[x][y]].token_name,
3467            graphic);
3468 #endif
3469
3470     if (phase == delay)
3471       DrawLevelFieldCrumbledSand(x, y);
3472
3473     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3474     {
3475       DrawLevelElement(x, y, Back[x][y]);
3476       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3477     }
3478     else if (IS_WALKABLE_UNDER(Back[x][y]))
3479     {
3480       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3481       DrawLevelElementThruMask(x, y, Back[x][y]);
3482     }
3483     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3484       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3485   }
3486 }
3487
3488 void DynaExplode(int ex, int ey)
3489 {
3490   int i, j;
3491   int dynabomb_element = Feld[ex][ey];
3492   int dynabomb_size = 1;
3493   boolean dynabomb_xl = FALSE;
3494   struct PlayerInfo *player;
3495   static int xy[4][2] =
3496   {
3497     { 0, -1 },
3498     { -1, 0 },
3499     { +1, 0 },
3500     { 0, +1 }
3501   };
3502
3503   if (IS_ACTIVE_BOMB(dynabomb_element))
3504   {
3505     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3506     dynabomb_size = player->dynabomb_size;
3507     dynabomb_xl = player->dynabomb_xl;
3508     player->dynabombs_left++;
3509   }
3510
3511   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3512
3513   for (i = 0; i < NUM_DIRECTIONS; i++)
3514   {
3515     for (j = 1; j <= dynabomb_size; j++)
3516     {
3517       int x = ex + j * xy[i][0];
3518       int y = ey + j * xy[i][1];
3519       int element;
3520
3521       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3522         break;
3523
3524       element = Feld[x][y];
3525
3526       /* do not restart explosions of fields with active bombs */
3527       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3528         continue;
3529
3530       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3531
3532 #if 1
3533 #if 1
3534       if (element != EL_EMPTY && element != EL_EXPLOSION &&
3535           !IS_DIGGABLE(element) && !dynabomb_xl)
3536         break;
3537 #else
3538       if (element != EL_EMPTY && element != EL_EXPLOSION &&
3539           !CAN_GROW_INTO(element) && !dynabomb_xl)
3540         break;
3541 #endif
3542 #else
3543       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3544       if (element != EL_EMPTY && element != EL_EXPLOSION &&
3545           element != EL_SAND && !dynabomb_xl)
3546         break;
3547 #endif
3548     }
3549   }
3550 }
3551
3552 void Bang(int x, int y)
3553 {
3554 #if 1
3555   int element = MovingOrBlocked2Element(x, y);
3556 #else
3557   int element = Feld[x][y];
3558 #endif
3559
3560 #if 1
3561   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3562 #else
3563   if (IS_PLAYER(x, y))
3564 #endif
3565   {
3566     struct PlayerInfo *player = PLAYERINFO(x, y);
3567
3568     element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3569                             player->element_nr);
3570   }
3571
3572 #if 0
3573 #if 1
3574   PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3575 #else
3576   if (game.emulation == EMU_SUPAPLEX)
3577     PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3578   else
3579     PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3580 #endif
3581 #endif
3582
3583 #if 0
3584   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
3585     element = EL_EMPTY;
3586 #endif
3587
3588   switch(element)
3589   {
3590     case EL_BUG:
3591     case EL_SPACESHIP:
3592     case EL_BD_BUTTERFLY:
3593     case EL_BD_FIREFLY:
3594     case EL_YAMYAM:
3595     case EL_DARK_YAMYAM:
3596     case EL_ROBOT:
3597     case EL_PACMAN:
3598     case EL_MOLE:
3599       RaiseScoreElement(element);
3600       Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3601       break;
3602     case EL_DYNABOMB_PLAYER_1_ACTIVE:
3603     case EL_DYNABOMB_PLAYER_2_ACTIVE:
3604     case EL_DYNABOMB_PLAYER_3_ACTIVE:
3605     case EL_DYNABOMB_PLAYER_4_ACTIVE:
3606     case EL_DYNABOMB_INCREASE_NUMBER:
3607     case EL_DYNABOMB_INCREASE_SIZE:
3608     case EL_DYNABOMB_INCREASE_POWER:
3609       DynaExplode(x, y);
3610       break;
3611     case EL_PENGUIN:
3612     case EL_LAMP:
3613     case EL_LAMP_ACTIVE:
3614 #if 1
3615     case EL_AMOEBA_TO_DIAMOND:
3616 #endif
3617       if (IS_PLAYER(x, y))
3618         Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3619       else
3620         Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3621       break;
3622     default:
3623 #if 1
3624       if (element_info[element].explosion_type == EXPLODES_CROSS)
3625 #else
3626       if (CAN_EXPLODE_CROSS(element))
3627 #endif
3628 #if 1
3629         Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
3630 #else
3631         DynaExplode(x, y);
3632 #endif
3633 #if 1
3634       else if (element_info[element].explosion_type == EXPLODES_1X1)
3635 #else
3636       else if (CAN_EXPLODE_1X1(element))
3637 #endif
3638         Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
3639       else
3640         Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
3641       break;
3642   }
3643
3644   CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3645 }
3646
3647 void SplashAcid(int x, int y)
3648 {
3649 #if 1
3650   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
3651       (!IN_LEV_FIELD(x - 1, y - 2) ||
3652        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
3653     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
3654
3655   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
3656       (!IN_LEV_FIELD(x + 1, y - 2) ||
3657        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
3658     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
3659
3660   PlayLevelSound(x, y, SND_ACID_SPLASHING);
3661 #else
3662   /* input: position of element entering acid (obsolete) */
3663
3664   int element = Feld[x][y];
3665
3666   if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
3667     return;
3668
3669   if (element != EL_ACID_SPLASH_LEFT &&
3670       element != EL_ACID_SPLASH_RIGHT)
3671   {
3672     PlayLevelSound(x, y, SND_ACID_SPLASHING);
3673
3674     if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
3675         (!IN_LEV_FIELD(x - 1, y - 1) ||
3676          !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
3677       Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
3678
3679     if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
3680         (!IN_LEV_FIELD(x + 1, y - 1) ||
3681          !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
3682       Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
3683   }
3684 #endif
3685 }
3686
3687 static void InitBeltMovement()
3688 {
3689   static int belt_base_element[4] =
3690   {
3691     EL_CONVEYOR_BELT_1_LEFT,
3692     EL_CONVEYOR_BELT_2_LEFT,
3693     EL_CONVEYOR_BELT_3_LEFT,
3694     EL_CONVEYOR_BELT_4_LEFT
3695   };
3696   static int belt_base_active_element[4] =
3697   {
3698     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3699     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3700     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3701     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3702   };
3703
3704   int x, y, i, j;
3705
3706   /* set frame order for belt animation graphic according to belt direction */
3707   for (i = 0; i < NUM_BELTS; i++)
3708   {
3709     int belt_nr = i;
3710
3711     for (j = 0; j < NUM_BELT_PARTS; j++)
3712     {
3713       int element = belt_base_active_element[belt_nr] + j;
3714       int graphic = el2img(element);
3715
3716       if (game.belt_dir[i] == MV_LEFT)
3717         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3718       else
3719         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3720     }
3721   }
3722
3723   for (y = 0; y < lev_fieldy; y++)
3724   {
3725     for (x = 0; x < lev_fieldx; x++)
3726     {
3727       int element = Feld[x][y];
3728
3729       for (i = 0; i < NUM_BELTS; i++)
3730       {
3731         if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3732         {
3733           int e_belt_nr = getBeltNrFromBeltElement(element);
3734           int belt_nr = i;
3735
3736           if (e_belt_nr == belt_nr)
3737           {
3738             int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3739
3740             Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3741           }
3742         }
3743       }
3744     }
3745   }
3746 }
3747
3748 static void ToggleBeltSwitch(int x, int y)
3749 {
3750   static int belt_base_element[4] =
3751   {
3752     EL_CONVEYOR_BELT_1_LEFT,
3753     EL_CONVEYOR_BELT_2_LEFT,
3754     EL_CONVEYOR_BELT_3_LEFT,
3755     EL_CONVEYOR_BELT_4_LEFT
3756   };
3757   static int belt_base_active_element[4] =
3758   {
3759     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3760     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3761     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3762     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3763   };
3764   static int belt_base_switch_element[4] =
3765   {
3766     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3767     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3768     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3769     EL_CONVEYOR_BELT_4_SWITCH_LEFT
3770   };
3771   static int belt_move_dir[4] =
3772   {
3773     MV_LEFT,
3774     MV_NO_MOVING,
3775     MV_RIGHT,
3776     MV_NO_MOVING,
3777   };
3778
3779   int element = Feld[x][y];
3780   int belt_nr = getBeltNrFromBeltSwitchElement(element);
3781   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3782   int belt_dir = belt_move_dir[belt_dir_nr];
3783   int xx, yy, i;
3784
3785   if (!IS_BELT_SWITCH(element))
3786     return;
3787
3788   game.belt_dir_nr[belt_nr] = belt_dir_nr;
3789   game.belt_dir[belt_nr] = belt_dir;
3790
3791   if (belt_dir_nr == 3)
3792     belt_dir_nr = 1;
3793
3794   /* set frame order for belt animation graphic according to belt direction */
3795   for (i = 0; i < NUM_BELT_PARTS; i++)
3796   {
3797     int element = belt_base_active_element[belt_nr] + i;
3798     int graphic = el2img(element);
3799
3800     if (belt_dir == MV_LEFT)
3801       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3802     else
3803       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3804   }
3805
3806   for (yy = 0; yy < lev_fieldy; yy++)
3807   {
3808     for (xx = 0; xx < lev_fieldx; xx++)
3809     {
3810       int element = Feld[xx][yy];
3811
3812       if (IS_BELT_SWITCH(element))
3813       {
3814         int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3815
3816         if (e_belt_nr == belt_nr)
3817         {
3818           Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3819           DrawLevelField(xx, yy);
3820         }
3821       }
3822       else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3823       {
3824         int e_belt_nr = getBeltNrFromBeltElement(element);
3825
3826         if (e_belt_nr == belt_nr)
3827         {
3828           int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3829
3830           Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3831           DrawLevelField(xx, yy);
3832         }
3833       }
3834       else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3835       {
3836         int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3837
3838         if (e_belt_nr == belt_nr)
3839         {
3840           int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3841
3842           Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3843           DrawLevelField(xx, yy);
3844         }
3845       }
3846     }
3847   }
3848 }
3849
3850 static void ToggleSwitchgateSwitch(int x, int y)
3851 {
3852   int xx, yy;
3853
3854   game.switchgate_pos = !game.switchgate_pos;
3855
3856   for (yy = 0; yy < lev_fieldy; yy++)
3857   {
3858     for (xx = 0; xx < lev_fieldx; xx++)
3859     {
3860       int element = Feld[xx][yy];
3861
3862       if (element == EL_SWITCHGATE_SWITCH_UP ||
3863           element == EL_SWITCHGATE_SWITCH_DOWN)
3864       {
3865         Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3866         DrawLevelField(xx, yy);
3867       }
3868       else if (element == EL_SWITCHGATE_OPEN ||
3869                element == EL_SWITCHGATE_OPENING)
3870       {
3871         Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3872 #if 1
3873         PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3874 #else
3875         PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3876 #endif
3877       }
3878       else if (element == EL_SWITCHGATE_CLOSED ||
3879                element == EL_SWITCHGATE_CLOSING)
3880       {
3881         Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3882 #if 1
3883         PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3884 #else
3885         PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3886 #endif
3887       }
3888     }
3889   }
3890 }
3891
3892 static int getInvisibleActiveFromInvisibleElement(int element)
3893 {
3894   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3895           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
3896           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
3897           element);
3898 }
3899
3900 static int getInvisibleFromInvisibleActiveElement(int element)
3901 {
3902   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3903           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
3904           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
3905           element);
3906 }
3907
3908 static void RedrawAllLightSwitchesAndInvisibleElements()
3909 {
3910   int x, y;
3911
3912   for (y = 0; y < lev_fieldy; y++)
3913   {
3914     for (x = 0; x < lev_fieldx; x++)
3915     {
3916       int element = Feld[x][y];
3917
3918       if (element == EL_LIGHT_SWITCH &&
3919           game.light_time_left > 0)
3920       {
3921         Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3922         DrawLevelField(x, y);
3923       }
3924       else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3925                game.light_time_left == 0)
3926       {
3927         Feld[x][y] = EL_LIGHT_SWITCH;
3928         DrawLevelField(x, y);
3929       }
3930       else if (element == EL_INVISIBLE_STEELWALL ||
3931                element == EL_INVISIBLE_WALL ||
3932                element == EL_INVISIBLE_SAND)
3933       {
3934         if (game.light_time_left > 0)
3935           Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3936
3937         DrawLevelField(x, y);
3938       }
3939       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3940                element == EL_INVISIBLE_WALL_ACTIVE ||
3941                element == EL_INVISIBLE_SAND_ACTIVE)
3942       {
3943         if (game.light_time_left == 0)
3944           Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3945
3946         DrawLevelField(x, y);
3947       }
3948     }
3949   }
3950 }
3951
3952 static void ToggleLightSwitch(int x, int y)
3953 {
3954   int element = Feld[x][y];
3955
3956   game.light_time_left =
3957     (element == EL_LIGHT_SWITCH ?
3958      level.time_light * FRAMES_PER_SECOND : 0);
3959
3960   RedrawAllLightSwitchesAndInvisibleElements();
3961 }
3962
3963 static void ActivateTimegateSwitch(int x, int y)
3964 {
3965   int xx, yy;
3966
3967   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3968
3969   for (yy = 0; yy < lev_fieldy; yy++)
3970   {
3971     for (xx = 0; xx < lev_fieldx; xx++)
3972     {
3973       int element = Feld[xx][yy];
3974
3975       if (element == EL_TIMEGATE_CLOSED ||
3976           element == EL_TIMEGATE_CLOSING)
3977       {
3978         Feld[xx][yy] = EL_TIMEGATE_OPENING;
3979         PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3980       }
3981
3982       /*
3983       else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3984       {
3985         Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3986         DrawLevelField(xx, yy);
3987       }
3988       */
3989
3990     }
3991   }
3992
3993   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3994 }
3995
3996 inline static int getElementMoveStepsize(int x, int y)
3997 {
3998   int element = Feld[x][y];
3999   int direction = MovDir[x][y];
4000   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4001   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4002   int horiz_move = (dx != 0);
4003   int sign = (horiz_move ? dx : dy);
4004   int step = sign * element_info[element].move_stepsize;
4005
4006   /* special values for move stepsize for spring and things on conveyor belt */
4007   if (horiz_move)
4008   {
4009 #if 0
4010     if (element == EL_SPRING)
4011       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4012     else if (CAN_FALL(element) && !CAN_MOVE(element) &&
4013              y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4014       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4015 #else
4016     if (CAN_FALL(element) &&
4017         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4018       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4019     else if (element == EL_SPRING)
4020       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4021 #endif
4022   }
4023
4024   return step;
4025 }
4026
4027 void Impact(int x, int y)
4028 {
4029   boolean lastline = (y == lev_fieldy-1);
4030   boolean object_hit = FALSE;
4031   boolean impact = (lastline || object_hit);
4032   int element = Feld[x][y];
4033   int smashed = EL_STEELWALL;
4034
4035   if (!lastline)        /* check if element below was hit */
4036   {
4037     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4038       return;
4039
4040     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4041                                          MovDir[x][y + 1] != MV_DOWN ||
4042                                          MovPos[x][y + 1] <= TILEY / 2));
4043
4044 #if 0
4045     object_hit = !IS_FREE(x, y + 1);
4046 #endif
4047
4048     /* do not smash moving elements that left the smashed field in time */
4049     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4050         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4051       object_hit = FALSE;
4052
4053     if (object_hit)
4054       smashed = MovingOrBlocked2Element(x, y + 1);
4055
4056     impact = (lastline || object_hit);
4057   }
4058
4059   if (!lastline && smashed == EL_ACID)  /* element falls into acid */
4060   {
4061     SplashAcid(x, y + 1);
4062     return;
4063   }
4064
4065   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4066   /* only reset graphic animation if graphic really changes after impact */
4067   if (impact &&
4068       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4069   {
4070     ResetGfxAnimation(x, y);
4071     DrawLevelField(x, y);
4072   }
4073
4074   if (impact && CAN_EXPLODE_IMPACT(element))
4075   {
4076     Bang(x, y);
4077     return;
4078   }
4079   else if (impact && element == EL_PEARL)
4080   {
4081     ResetGfxAnimation(x, y);
4082
4083     Feld[x][y] = EL_PEARL_BREAKING;
4084     PlayLevelSound(x, y, SND_PEARL_BREAKING);
4085     return;
4086   }
4087   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4088   {
4089     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4090
4091     return;
4092   }
4093
4094   if (impact && element == EL_AMOEBA_DROP)
4095   {
4096     if (object_hit && IS_PLAYER(x, y + 1))
4097       KillHeroUnlessEnemyProtected(x, y + 1);
4098     else if (object_hit && smashed == EL_PENGUIN)
4099       Bang(x, y + 1);
4100     else
4101     {
4102       Feld[x][y] = EL_AMOEBA_GROWING;
4103       Store[x][y] = EL_AMOEBA_WET;
4104
4105       ResetRandomAnimationValue(x, y);
4106     }
4107     return;
4108   }
4109
4110   if (object_hit)               /* check which object was hit */
4111   {
4112     if (CAN_PASS_MAGIC_WALL(element) && 
4113         (smashed == EL_MAGIC_WALL ||
4114          smashed == EL_BD_MAGIC_WALL))
4115     {
4116       int xx, yy;
4117       int activated_magic_wall =
4118         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4119          EL_BD_MAGIC_WALL_ACTIVE);
4120
4121       /* activate magic wall / mill */
4122       for (yy = 0; yy < lev_fieldy; yy++)
4123         for (xx = 0; xx < lev_fieldx; xx++)
4124           if (Feld[xx][yy] == smashed)
4125             Feld[xx][yy] = activated_magic_wall;
4126
4127       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4128       game.magic_wall_active = TRUE;
4129
4130       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4131                             SND_MAGIC_WALL_ACTIVATING :
4132                             SND_BD_MAGIC_WALL_ACTIVATING));
4133     }
4134
4135     if (IS_PLAYER(x, y + 1))
4136     {
4137       if (CAN_SMASH_PLAYER(element))
4138       {
4139         KillHeroUnlessEnemyProtected(x, y + 1);
4140         return;
4141       }
4142     }
4143     else if (smashed == EL_PENGUIN)
4144     {
4145       if (CAN_SMASH_PLAYER(element))
4146       {
4147         Bang(x, y + 1);
4148         return;
4149       }
4150     }
4151     else if (element == EL_BD_DIAMOND)
4152     {
4153       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4154       {
4155         Bang(x, y + 1);
4156         return;
4157       }
4158     }
4159     else if (((element == EL_SP_INFOTRON ||
4160                element == EL_SP_ZONK) &&
4161               (smashed == EL_SP_SNIKSNAK ||
4162                smashed == EL_SP_ELECTRON ||
4163                smashed == EL_SP_DISK_ORANGE)) ||
4164              (element == EL_SP_INFOTRON &&
4165               smashed == EL_SP_DISK_YELLOW))
4166     {
4167       Bang(x, y + 1);
4168       return;
4169     }
4170 #if 0
4171     else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4172     {
4173       Bang(x, y + 1);
4174       return;
4175     }
4176 #endif
4177     else if (CAN_SMASH_EVERYTHING(element))
4178     {
4179       if (IS_CLASSIC_ENEMY(smashed) ||
4180           CAN_EXPLODE_SMASHED(smashed))
4181       {
4182         Bang(x, y + 1);
4183         return;
4184       }
4185       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4186       {
4187         if (smashed == EL_LAMP ||
4188             smashed == EL_LAMP_ACTIVE)
4189         {
4190           Bang(x, y + 1);
4191           return;
4192         }
4193         else if (smashed == EL_NUT)
4194         {
4195           Feld[x][y + 1] = EL_NUT_BREAKING;
4196           PlayLevelSound(x, y, SND_NUT_BREAKING);
4197           RaiseScoreElement(EL_NUT);
4198           return;
4199         }
4200         else if (smashed == EL_PEARL)
4201         {
4202           ResetGfxAnimation(x, y);
4203
4204           Feld[x][y + 1] = EL_PEARL_BREAKING;
4205           PlayLevelSound(x, y, SND_PEARL_BREAKING);
4206           return;
4207         }
4208         else if (smashed == EL_DIAMOND)
4209         {
4210           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4211           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4212           return;
4213         }
4214         else if (IS_BELT_SWITCH(smashed))
4215         {
4216           ToggleBeltSwitch(x, y + 1);
4217         }
4218         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4219                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
4220         {
4221           ToggleSwitchgateSwitch(x, y + 1);
4222         }
4223         else if (smashed == EL_LIGHT_SWITCH ||
4224                  smashed == EL_LIGHT_SWITCH_ACTIVE)
4225         {
4226           ToggleLightSwitch(x, y + 1);
4227         }
4228         else
4229         {
4230 #if 0
4231           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4232 #endif
4233
4234           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4235
4236 #if 1
4237           /* !!! TEST ONLY !!! */
4238           CheckElementChangeBySide(x, y + 1, smashed, element,
4239                                    CE_SWITCHED, CH_SIDE_TOP);
4240           CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4241                                             CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4242 #else
4243           CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4244                                             CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
4245           CheckElementChangeBySide(x, y + 1, smashed, element,
4246                                    CE_SWITCHED, CH_SIDE_TOP);
4247 #endif
4248         }
4249       }
4250       else
4251       {
4252         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4253       }
4254     }
4255   }
4256
4257   /* play sound of magic wall / mill */
4258   if (!lastline &&
4259       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4260        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4261   {
4262     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4263       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4264     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4265       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4266
4267     return;
4268   }
4269
4270   /* play sound of object that hits the ground */
4271   if (lastline || object_hit)
4272     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4273 }
4274
4275 inline static void TurnRoundExt(int x, int y)
4276 {
4277   static struct
4278   {
4279     int x, y;
4280   } move_xy[] =
4281   {
4282     {  0,  0 },
4283     { -1,  0 },
4284     { +1,  0 },
4285     {  0,  0 },
4286     {  0, -1 },
4287     {  0,  0 }, { 0, 0 }, { 0, 0 },
4288     {  0, +1 }
4289   };
4290   static struct
4291   {
4292     int left, right, back;
4293   } turn[] =
4294   {
4295     { 0,        0,              0        },
4296     { MV_DOWN,  MV_UP,          MV_RIGHT },
4297     { MV_UP,    MV_DOWN,        MV_LEFT  },
4298     { 0,        0,              0        },
4299     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
4300     { 0,        0,              0        },
4301     { 0,        0,              0        },
4302     { 0,        0,              0        },
4303     { MV_RIGHT, MV_LEFT,        MV_UP    }
4304   };
4305
4306   int element = Feld[x][y];
4307   int move_pattern = element_info[element].move_pattern;
4308
4309   int old_move_dir = MovDir[x][y];
4310   int left_dir  = turn[old_move_dir].left;
4311   int right_dir = turn[old_move_dir].right;
4312   int back_dir  = turn[old_move_dir].back;
4313
4314   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
4315   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
4316   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
4317   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
4318
4319   int left_x  = x + left_dx,  left_y  = y + left_dy;
4320   int right_x = x + right_dx, right_y = y + right_dy;
4321   int move_x  = x + move_dx,  move_y  = y + move_dy;
4322
4323   int xx, yy;
4324
4325   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4326   {
4327     TestIfBadThingTouchesOtherBadThing(x, y);
4328
4329     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4330       MovDir[x][y] = right_dir;
4331     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4332       MovDir[x][y] = left_dir;
4333
4334     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4335       MovDelay[x][y] = 9;
4336     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
4337       MovDelay[x][y] = 1;
4338   }
4339 #if 0
4340   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4341            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4342   {
4343     TestIfBadThingTouchesOtherBadThing(x, y);
4344
4345     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4346       MovDir[x][y] = left_dir;
4347     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4348       MovDir[x][y] = right_dir;
4349
4350     if ((element == EL_SPACESHIP ||
4351          element == EL_SP_SNIKSNAK ||
4352          element == EL_SP_ELECTRON)
4353         && MovDir[x][y] != old_move_dir)
4354       MovDelay[x][y] = 9;
4355     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
4356       MovDelay[x][y] = 1;
4357   }
4358 #else
4359   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4360   {
4361     TestIfBadThingTouchesOtherBadThing(x, y);
4362
4363     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4364       MovDir[x][y] = left_dir;
4365     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4366       MovDir[x][y] = right_dir;
4367
4368     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4369       MovDelay[x][y] = 9;
4370     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
4371       MovDelay[x][y] = 1;
4372   }
4373   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4374   {
4375     TestIfBadThingTouchesOtherBadThing(x, y);
4376
4377     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4378       MovDir[x][y] = left_dir;
4379     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4380       MovDir[x][y] = right_dir;
4381
4382     if (MovDir[x][y] != old_move_dir)
4383       MovDelay[x][y] = 9;
4384   }
4385 #endif
4386   else if (element == EL_YAMYAM)
4387   {
4388     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4389     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4390
4391     if (can_turn_left && can_turn_right)
4392       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4393     else if (can_turn_left)
4394       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4395     else if (can_turn_right)
4396       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4397     else
4398       MovDir[x][y] = back_dir;
4399
4400     MovDelay[x][y] = 16 + 16 * RND(3);
4401   }
4402   else if (element == EL_DARK_YAMYAM)
4403   {
4404     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4405                                                          left_x, left_y);
4406     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4407                                                          right_x, right_y);
4408
4409     if (can_turn_left && can_turn_right)
4410       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4411     else if (can_turn_left)
4412       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4413     else if (can_turn_right)
4414       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4415     else
4416       MovDir[x][y] = back_dir;
4417
4418     MovDelay[x][y] = 16 + 16 * RND(3);
4419   }
4420   else if (element == EL_PACMAN)
4421   {
4422     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4423     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4424
4425     if (can_turn_left && can_turn_right)
4426       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4427     else if (can_turn_left)
4428       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4429     else if (can_turn_right)
4430       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4431     else
4432       MovDir[x][y] = back_dir;
4433
4434     MovDelay[x][y] = 6 + RND(40);
4435   }
4436   else if (element == EL_PIG)
4437   {
4438     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4439     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4440     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4441     boolean should_turn_left, should_turn_right, should_move_on;
4442     int rnd_value = 24;
4443     int rnd = RND(rnd_value);
4444
4445     should_turn_left = (can_turn_left &&
4446                         (!can_move_on ||
4447                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4448                                                    y + back_dy + left_dy)));
4449     should_turn_right = (can_turn_right &&
4450                          (!can_move_on ||
4451                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4452                                                     y + back_dy + right_dy)));
4453     should_move_on = (can_move_on &&
4454                       (!can_turn_left ||
4455                        !can_turn_right ||
4456                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4457                                                  y + move_dy + left_dy) ||
4458                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4459                                                  y + move_dy + right_dy)));
4460
4461     if (should_turn_left || should_turn_right || should_move_on)
4462     {
4463       if (should_turn_left && should_turn_right && should_move_on)
4464         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
4465                         rnd < 2 * rnd_value / 3 ? right_dir :
4466                         old_move_dir);
4467       else if (should_turn_left && should_turn_right)
4468         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4469       else if (should_turn_left && should_move_on)
4470         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4471       else if (should_turn_right && should_move_on)
4472         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4473       else if (should_turn_left)
4474         MovDir[x][y] = left_dir;
4475       else if (should_turn_right)
4476         MovDir[x][y] = right_dir;
4477       else if (should_move_on)
4478         MovDir[x][y] = old_move_dir;
4479     }
4480     else if (can_move_on && rnd > rnd_value / 8)
4481       MovDir[x][y] = old_move_dir;
4482     else if (can_turn_left && can_turn_right)
4483       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4484     else if (can_turn_left && rnd > rnd_value / 8)
4485       MovDir[x][y] = left_dir;
4486     else if (can_turn_right && rnd > rnd_value/8)
4487       MovDir[x][y] = right_dir;
4488     else
4489       MovDir[x][y] = back_dir;
4490
4491     xx = x + move_xy[MovDir[x][y]].x;
4492     yy = y + move_xy[MovDir[x][y]].y;
4493
4494     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4495       MovDir[x][y] = old_move_dir;
4496
4497     MovDelay[x][y] = 0;
4498   }
4499   else if (element == EL_DRAGON)
4500   {
4501     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4502     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4503     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4504     int rnd_value = 24;
4505     int rnd = RND(rnd_value);
4506
4507 #if 0
4508     if (FrameCounter < 1 && x == 0 && y == 29)
4509       printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4510 #endif
4511
4512     if (can_move_on && rnd > rnd_value / 8)
4513       MovDir[x][y] = old_move_dir;
4514     else if (can_turn_left && can_turn_right)
4515       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4516     else if (can_turn_left && rnd > rnd_value / 8)
4517       MovDir[x][y] = left_dir;
4518     else if (can_turn_right && rnd > rnd_value / 8)
4519       MovDir[x][y] = right_dir;
4520     else
4521       MovDir[x][y] = back_dir;
4522
4523     xx = x + move_xy[MovDir[x][y]].x;
4524     yy = y + move_xy[MovDir[x][y]].y;
4525
4526 #if 0
4527     if (FrameCounter < 1 && x == 0 && y == 29)
4528       printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4529              xx, yy, Feld[xx][yy],
4530              FrameCounter);
4531 #endif
4532
4533 #if 1
4534     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4535       MovDir[x][y] = old_move_dir;
4536 #else
4537     if (!IS_FREE(xx, yy))
4538       MovDir[x][y] = old_move_dir;
4539 #endif
4540
4541 #if 0
4542     if (FrameCounter < 1 && x == 0 && y == 29)
4543       printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4544 #endif
4545
4546     MovDelay[x][y] = 0;
4547   }
4548   else if (element == EL_MOLE)
4549   {
4550     boolean can_move_on =
4551       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4552                             IS_AMOEBOID(Feld[move_x][move_y]) ||
4553                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4554     if (!can_move_on)
4555     {
4556       boolean can_turn_left =
4557         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4558                               IS_AMOEBOID(Feld[left_x][left_y])));
4559
4560       boolean can_turn_right =
4561         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4562                               IS_AMOEBOID(Feld[right_x][right_y])));
4563
4564       if (can_turn_left && can_turn_right)
4565         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4566       else if (can_turn_left)
4567         MovDir[x][y] = left_dir;
4568       else
4569         MovDir[x][y] = right_dir;
4570     }
4571
4572     if (MovDir[x][y] != old_move_dir)
4573       MovDelay[x][y] = 9;
4574   }
4575   else if (element == EL_BALLOON)
4576   {
4577     MovDir[x][y] = game.balloon_dir;
4578     MovDelay[x][y] = 0;
4579   }
4580   else if (element == EL_SPRING)
4581   {
4582 #if 0
4583     if (MovDir[x][y] & MV_HORIZONTAL &&
4584         !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
4585       MovDir[x][y] = MV_NO_MOVING;
4586 #else
4587     if (MovDir[x][y] & MV_HORIZONTAL &&
4588         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
4589          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
4590       MovDir[x][y] = MV_NO_MOVING;
4591 #endif
4592
4593     MovDelay[x][y] = 0;
4594   }
4595   else if (element == EL_ROBOT ||
4596            element == EL_SATELLITE ||
4597            element == EL_PENGUIN)
4598   {
4599     int attr_x = -1, attr_y = -1;
4600
4601     if (AllPlayersGone)
4602     {
4603       attr_x = ExitX;
4604       attr_y = ExitY;
4605     }
4606     else
4607     {
4608       int i;
4609
4610       for (i = 0; i < MAX_PLAYERS; i++)
4611       {
4612         struct PlayerInfo *player = &stored_player[i];
4613         int jx = player->jx, jy = player->jy;
4614
4615         if (!player->active)
4616           continue;
4617
4618         if (attr_x == -1 ||
4619             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4620         {
4621           attr_x = jx;
4622           attr_y = jy;
4623         }
4624       }
4625     }
4626
4627 #if 1
4628     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
4629         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
4630          game.engine_version < VERSION_IDENT(3,1,0,0)))
4631 #else
4632     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
4633 #endif
4634     {
4635       attr_x = ZX;
4636       attr_y = ZY;
4637     }
4638
4639     if (element == EL_PENGUIN)
4640     {
4641       int i;
4642       static int xy[4][2] =
4643       {
4644         { 0, -1 },
4645         { -1, 0 },
4646         { +1, 0 },
4647         { 0, +1 }
4648       };
4649
4650       for (i = 0; i < NUM_DIRECTIONS; i++)
4651       {
4652         int ex = x + xy[i][0];
4653         int ey = y + xy[i][1];
4654
4655         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
4656         {
4657           attr_x = ex;
4658           attr_y = ey;
4659           break;
4660         }
4661       }
4662     }
4663
4664     MovDir[x][y] = MV_NO_MOVING;
4665     if (attr_x < x)
4666       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4667     else if (attr_x > x)
4668       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4669     if (attr_y < y)
4670       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4671     else if (attr_y > y)
4672       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4673
4674     if (element == EL_ROBOT)
4675     {
4676       int newx, newy;
4677
4678       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4679         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4680       Moving2Blocked(x, y, &newx, &newy);
4681
4682       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4683         MovDelay[x][y] = 8 + 8 * !RND(3);
4684       else
4685         MovDelay[x][y] = 16;
4686     }
4687     else if (element == EL_PENGUIN)
4688     {
4689       int newx, newy;
4690
4691       MovDelay[x][y] = 1;
4692
4693       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4694       {
4695         boolean first_horiz = RND(2);
4696         int new_move_dir = MovDir[x][y];
4697
4698         MovDir[x][y] =
4699           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4700         Moving2Blocked(x, y, &newx, &newy);
4701
4702         if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4703           return;
4704
4705         MovDir[x][y] =
4706           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4707         Moving2Blocked(x, y, &newx, &newy);
4708
4709         if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
4710           return;
4711
4712         MovDir[x][y] = old_move_dir;
4713         return;
4714       }
4715     }
4716     else        /* (element == EL_SATELLITE) */
4717     {
4718       int newx, newy;
4719
4720       MovDelay[x][y] = 1;
4721
4722       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4723       {
4724         boolean first_horiz = RND(2);
4725         int new_move_dir = MovDir[x][y];
4726
4727         MovDir[x][y] =
4728           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4729         Moving2Blocked(x, y, &newx, &newy);
4730
4731         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4732           return;
4733
4734         MovDir[x][y] =
4735           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4736         Moving2Blocked(x, y, &newx, &newy);
4737
4738         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
4739           return;
4740
4741         MovDir[x][y] = old_move_dir;
4742         return;
4743       }
4744     }
4745   }
4746   else if (move_pattern == MV_TURNING_LEFT ||
4747            move_pattern == MV_TURNING_RIGHT ||
4748            move_pattern == MV_TURNING_LEFT_RIGHT ||
4749            move_pattern == MV_TURNING_RIGHT_LEFT ||
4750            move_pattern == MV_TURNING_RANDOM ||
4751            move_pattern == MV_ALL_DIRECTIONS)
4752   {
4753     boolean can_turn_left =
4754       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4755     boolean can_turn_right =
4756       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4757
4758     if (move_pattern == MV_TURNING_LEFT)
4759       MovDir[x][y] = left_dir;
4760     else if (move_pattern == MV_TURNING_RIGHT)
4761       MovDir[x][y] = right_dir;
4762     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4763       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4764     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4765       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4766     else if (move_pattern == MV_TURNING_RANDOM)
4767       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4768                       can_turn_right && !can_turn_left ? right_dir :
4769                       RND(2) ? left_dir : right_dir);
4770     else if (can_turn_left && can_turn_right)
4771       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4772     else if (can_turn_left)
4773       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4774     else if (can_turn_right)
4775       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4776     else
4777       MovDir[x][y] = back_dir;
4778
4779     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4780   }
4781   else if (move_pattern == MV_HORIZONTAL ||
4782            move_pattern == MV_VERTICAL)
4783   {
4784     if (move_pattern & old_move_dir)
4785       MovDir[x][y] = back_dir;
4786     else if (move_pattern == MV_HORIZONTAL)
4787       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4788     else if (move_pattern == MV_VERTICAL)
4789       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4790
4791     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4792   }
4793   else if (move_pattern & MV_ANY_DIRECTION)
4794   {
4795     MovDir[x][y] = move_pattern;
4796     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4797   }
4798   else if (move_pattern == MV_ALONG_LEFT_SIDE)
4799   {
4800     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4801       MovDir[x][y] = left_dir;
4802     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4803       MovDir[x][y] = right_dir;
4804
4805     if (MovDir[x][y] != old_move_dir)
4806       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4807   }
4808   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4809   {
4810     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4811       MovDir[x][y] = right_dir;
4812     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4813       MovDir[x][y] = left_dir;
4814
4815     if (MovDir[x][y] != old_move_dir)
4816       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4817   }
4818   else if (move_pattern == MV_TOWARDS_PLAYER ||
4819            move_pattern == MV_AWAY_FROM_PLAYER)
4820   {
4821     int attr_x = -1, attr_y = -1;
4822     int newx, newy;
4823     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4824
4825     if (AllPlayersGone)
4826     {
4827       attr_x = ExitX;
4828       attr_y = ExitY;
4829     }
4830     else
4831     {
4832       int i;
4833
4834       for (i = 0; i < MAX_PLAYERS; i++)
4835       {
4836         struct PlayerInfo *player = &stored_player[i];
4837         int jx = player->jx, jy = player->jy;
4838
4839         if (!player->active)
4840           continue;
4841
4842         if (attr_x == -1 ||
4843             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4844         {
4845           attr_x = jx;
4846           attr_y = jy;
4847         }
4848       }
4849     }
4850
4851     MovDir[x][y] = MV_NO_MOVING;
4852     if (attr_x < x)
4853       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4854     else if (attr_x > x)
4855       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4856     if (attr_y < y)
4857       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4858     else if (attr_y > y)
4859       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4860
4861     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4862
4863     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4864     {
4865       boolean first_horiz = RND(2);
4866       int new_move_dir = MovDir[x][y];
4867
4868       MovDir[x][y] =
4869         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4870       Moving2Blocked(x, y, &newx, &newy);
4871
4872       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4873         return;
4874
4875       MovDir[x][y] =
4876         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4877       Moving2Blocked(x, y, &newx, &newy);
4878
4879       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4880         return;
4881
4882       MovDir[x][y] = old_move_dir;
4883     }
4884   }
4885   else if (move_pattern == MV_WHEN_PUSHED ||
4886            move_pattern == MV_WHEN_DROPPED)
4887   {
4888     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4889       MovDir[x][y] = MV_NO_MOVING;
4890
4891     MovDelay[x][y] = 0;
4892   }
4893   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4894   {
4895     static int test_xy[7][2] =
4896     {
4897       { 0, -1 },
4898       { -1, 0 },
4899       { +1, 0 },
4900       { 0, +1 },
4901       { 0, -1 },
4902       { -1, 0 },
4903       { +1, 0 },
4904     };
4905     static int test_dir[7] =
4906     {
4907       MV_UP,
4908       MV_LEFT,
4909       MV_RIGHT,
4910       MV_DOWN,
4911       MV_UP,
4912       MV_LEFT,
4913       MV_RIGHT,
4914     };
4915     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4916     int move_preference = -1000000;     /* start with very low preference */
4917     int new_move_dir = MV_NO_MOVING;
4918     int start_test = RND(4);
4919     int i;
4920
4921     for (i = 0; i < NUM_DIRECTIONS; i++)
4922     {
4923       int move_dir = test_dir[start_test + i];
4924       int move_dir_preference;
4925
4926       xx = x + test_xy[start_test + i][0];
4927       yy = y + test_xy[start_test + i][1];
4928
4929       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4930           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4931       {
4932         new_move_dir = move_dir;
4933
4934         break;
4935       }
4936
4937       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4938         continue;
4939
4940       move_dir_preference = -1 * RunnerVisit[xx][yy];
4941       if (hunter_mode && PlayerVisit[xx][yy] > 0)
4942         move_dir_preference = PlayerVisit[xx][yy];
4943
4944       if (move_dir_preference > move_preference)
4945       {
4946         /* prefer field that has not been visited for the longest time */
4947         move_preference = move_dir_preference;
4948         new_move_dir = move_dir;
4949       }
4950       else if (move_dir_preference == move_preference &&
4951                move_dir == old_move_dir)
4952       {
4953         /* prefer last direction when all directions are preferred equally */
4954         move_preference = move_dir_preference;
4955         new_move_dir = move_dir;
4956       }
4957     }
4958
4959     MovDir[x][y] = new_move_dir;
4960     if (old_move_dir != new_move_dir)
4961       MovDelay[x][y] = 9;
4962   }
4963 }
4964
4965 static void TurnRound(int x, int y)
4966 {
4967   int direction = MovDir[x][y];
4968
4969 #if 0
4970   GfxDir[x][y] = MovDir[x][y];
4971 #endif
4972
4973   TurnRoundExt(x, y);
4974
4975 #if 1
4976   GfxDir[x][y] = MovDir[x][y];
4977 #endif
4978
4979   if (direction != MovDir[x][y])
4980     GfxFrame[x][y] = 0;
4981
4982 #if 1
4983   if (MovDelay[x][y])
4984     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4985 #else
4986   if (MovDelay[x][y])
4987     GfxAction[x][y] = ACTION_WAITING;
4988 #endif
4989 }
4990
4991 static boolean JustBeingPushed(int x, int y)
4992 {
4993   int i;
4994
4995   for (i = 0; i < MAX_PLAYERS; i++)
4996   {
4997     struct PlayerInfo *player = &stored_player[i];
4998
4999     if (player->active && player->is_pushing && player->MovPos)
5000     {
5001       int next_jx = player->jx + (player->jx - player->last_jx);
5002       int next_jy = player->jy + (player->jy - player->last_jy);
5003
5004       if (x == next_jx && y == next_jy)
5005         return TRUE;
5006     }
5007   }
5008
5009   return FALSE;
5010 }
5011
5012 void StartMoving(int x, int y)
5013 {
5014 #if 0
5015   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5016 #endif
5017   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
5018   int element = Feld[x][y];
5019
5020   if (Stop[x][y])
5021     return;
5022
5023 #if 1
5024   if (MovDelay[x][y] == 0)
5025     GfxAction[x][y] = ACTION_DEFAULT;
5026 #else
5027   /* !!! this should be handled more generic (not only for mole) !!! */
5028   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5029     GfxAction[x][y] = ACTION_DEFAULT;
5030 #endif
5031
5032   if (CAN_FALL(element) && y < lev_fieldy - 1)
5033   {
5034     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5035         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5036       if (JustBeingPushed(x, y))
5037         return;
5038
5039     if (element == EL_QUICKSAND_FULL)
5040     {
5041       if (IS_FREE(x, y + 1))
5042       {
5043         InitMovingField(x, y, MV_DOWN);
5044         started_moving = TRUE;
5045
5046         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5047         Store[x][y] = EL_ROCK;
5048 #if 1
5049         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5050 #else
5051         PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5052 #endif
5053       }
5054       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5055       {
5056         if (!MovDelay[x][y])
5057           MovDelay[x][y] = TILEY + 1;
5058
5059         if (MovDelay[x][y])
5060         {
5061           MovDelay[x][y]--;
5062           if (MovDelay[x][y])
5063             return;
5064         }
5065
5066         Feld[x][y] = EL_QUICKSAND_EMPTY;
5067         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5068         Store[x][y + 1] = Store[x][y];
5069         Store[x][y] = 0;
5070 #if 1
5071         PlayLevelSoundAction(x, y, ACTION_FILLING);
5072 #else
5073         PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5074 #endif
5075       }
5076     }
5077     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5078              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5079     {
5080       InitMovingField(x, y, MV_DOWN);
5081       started_moving = TRUE;
5082
5083       Feld[x][y] = EL_QUICKSAND_FILLING;
5084       Store[x][y] = element;
5085 #if 1
5086       PlayLevelSoundAction(x, y, ACTION_FILLING);
5087 #else
5088       PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5089 #endif
5090     }
5091     else if (element == EL_MAGIC_WALL_FULL)
5092     {
5093       if (IS_FREE(x, y + 1))
5094       {
5095         InitMovingField(x, y, MV_DOWN);
5096         started_moving = TRUE;
5097
5098         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5099         Store[x][y] = EL_CHANGED(Store[x][y]);
5100       }
5101       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5102       {
5103         if (!MovDelay[x][y])
5104           MovDelay[x][y] = TILEY/4 + 1;
5105
5106         if (MovDelay[x][y])
5107         {
5108           MovDelay[x][y]--;
5109           if (MovDelay[x][y])
5110             return;
5111         }
5112
5113         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5114         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5115         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5116         Store[x][y] = 0;
5117       }
5118     }
5119     else if (element == EL_BD_MAGIC_WALL_FULL)
5120     {
5121       if (IS_FREE(x, y + 1))
5122       {
5123         InitMovingField(x, y, MV_DOWN);
5124         started_moving = TRUE;
5125
5126         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5127         Store[x][y] = EL_CHANGED2(Store[x][y]);
5128       }
5129       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5130       {
5131         if (!MovDelay[x][y])
5132           MovDelay[x][y] = TILEY/4 + 1;
5133
5134         if (MovDelay[x][y])
5135         {
5136           MovDelay[x][y]--;
5137           if (MovDelay[x][y])
5138             return;
5139         }
5140
5141         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5142         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5143         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5144         Store[x][y] = 0;
5145       }
5146     }
5147     else if (CAN_PASS_MAGIC_WALL(element) &&
5148              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5149               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5150     {
5151       InitMovingField(x, y, MV_DOWN);
5152       started_moving = TRUE;
5153
5154       Feld[x][y] =
5155         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5156          EL_BD_MAGIC_WALL_FILLING);
5157       Store[x][y] = element;
5158     }
5159 #if 0
5160     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5161 #else
5162     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5163 #endif
5164     {
5165       SplashAcid(x, y + 1);
5166
5167       InitMovingField(x, y, MV_DOWN);
5168       started_moving = TRUE;
5169
5170       Store[x][y] = EL_ACID;
5171 #if 0
5172       /* !!! TEST !!! better use "_FALLING" etc. !!! */
5173       GfxAction[x][y + 1] = ACTION_ACTIVE;
5174 #endif
5175     }
5176 #if 1
5177     else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5178               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5179
5180              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5181               CAN_SMASH(element) && WasJustFalling[x][y] &&
5182               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5183
5184              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5185               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5186               (Feld[x][y + 1] == EL_BLOCKED)))
5187
5188 #else
5189 #if 1
5190     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5191              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5192              WasJustMoving[x][y] && !Pushed[x][y + 1])
5193 #else
5194     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5195              WasJustMoving[x][y])
5196 #endif
5197 #endif
5198
5199     {
5200       /* this is needed for a special case not covered by calling "Impact()"
5201          from "ContinueMoving()": if an element moves to a tile directly below
5202          another element which was just falling on that tile (which was empty
5203          in the previous frame), the falling element above would just stop
5204          instead of smashing the element below (in previous version, the above
5205          element was just checked for "moving" instead of "falling", resulting
5206          in incorrect smashes caused by horizontal movement of the above
5207          element; also, the case of the player being the element to smash was
5208          simply not covered here... :-/ ) */
5209
5210 #if 0
5211       WasJustMoving[x][y] = 0;
5212       WasJustFalling[x][y] = 0;
5213 #endif
5214
5215       CheckCollision[x][y] = 0;
5216
5217       Impact(x, y);
5218     }
5219     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5220     {
5221       if (MovDir[x][y] == MV_NO_MOVING)
5222       {
5223         InitMovingField(x, y, MV_DOWN);
5224         started_moving = TRUE;
5225       }
5226     }
5227     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5228     {
5229       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5230         MovDir[x][y] = MV_DOWN;
5231
5232       InitMovingField(x, y, MV_DOWN);
5233       started_moving = TRUE;
5234     }
5235     else if (element == EL_AMOEBA_DROP)
5236     {
5237       Feld[x][y] = EL_AMOEBA_GROWING;
5238       Store[x][y] = EL_AMOEBA_WET;
5239     }
5240     /* Store[x][y + 1] must be zero, because:
5241        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5242     */
5243 #if 0
5244 #if OLD_GAME_BEHAVIOUR
5245     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5246 #else
5247     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5248              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5249              element != EL_DX_SUPABOMB)
5250 #endif
5251 #else
5252     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5253               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5254              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5255              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5256 #endif
5257     {
5258       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
5259                                 (IS_FREE(x - 1, y + 1) ||
5260                                  Feld[x - 1][y + 1] == EL_ACID));
5261       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5262                                 (IS_FREE(x + 1, y + 1) ||
5263                                  Feld[x + 1][y + 1] == EL_ACID));
5264       boolean can_fall_any  = (can_fall_left || can_fall_right);
5265       boolean can_fall_both = (can_fall_left && can_fall_right);
5266
5267       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5268       {
5269         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5270
5271         if (slippery_type == SLIPPERY_ONLY_LEFT)
5272           can_fall_right = FALSE;
5273         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5274           can_fall_left = FALSE;
5275         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5276           can_fall_right = FALSE;
5277         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5278           can_fall_left = FALSE;
5279
5280         can_fall_any  = (can_fall_left || can_fall_right);
5281         can_fall_both = (can_fall_left && can_fall_right);
5282       }
5283
5284       if (can_fall_any)
5285       {
5286         if (can_fall_both &&
5287             (game.emulation != EMU_BOULDERDASH &&
5288              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5289           can_fall_left = !(can_fall_right = RND(2));
5290
5291         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5292         started_moving = TRUE;
5293       }
5294     }
5295 #if 0
5296     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5297 #else
5298     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5299 #endif
5300     {
5301       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
5302       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5303       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5304       int belt_dir = game.belt_dir[belt_nr];
5305
5306       if ((belt_dir == MV_LEFT  && left_is_free) ||
5307           (belt_dir == MV_RIGHT && right_is_free))
5308       {
5309 #if 1
5310         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5311 #endif
5312
5313         InitMovingField(x, y, belt_dir);
5314         started_moving = TRUE;
5315
5316 #if 1
5317         Pushed[x][y] = TRUE;
5318         Pushed[nextx][y] = TRUE;
5319 #endif
5320
5321         GfxAction[x][y] = ACTION_DEFAULT;
5322       }
5323       else
5324       {
5325         MovDir[x][y] = 0;       /* if element was moving, stop it */
5326       }
5327     }
5328   }
5329
5330   /* not "else if" because of elements that can fall and move (EL_SPRING) */
5331 #if 0
5332   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5333 #else
5334   if (CAN_MOVE(element) && !started_moving)
5335 #endif
5336   {
5337     int move_pattern = element_info[element].move_pattern;
5338     int newx, newy;
5339
5340 #if 0
5341 #if DEBUG
5342     if (MovDir[x][y] == MV_NO_MOVING)
5343     {
5344       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5345              x, y, element, element_info[element].token_name);
5346       printf("StartMoving(): This should never happen!\n");
5347     }
5348 #endif
5349 #endif
5350
5351     Moving2Blocked(x, y, &newx, &newy);
5352
5353 #if 1
5354     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5355       return;
5356 #else
5357     if ((element == EL_SATELLITE ||
5358          element == EL_BALLOON ||
5359          element == EL_SPRING)
5360         && JustBeingPushed(x, y))
5361       return;
5362 #endif
5363
5364 #if 1
5365
5366 #if 1
5367     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5368         CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
5369 #else
5370     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5371         WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5372         (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5373 #endif
5374     {
5375 #if 0
5376       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5377              element, element_info[element].token_name,
5378              WasJustMoving[x][y],
5379              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5380              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5381              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
5382              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
5383 #endif
5384
5385 #if 1
5386       WasJustMoving[x][y] = 0;
5387 #endif
5388
5389       CheckCollision[x][y] = 0;
5390
5391       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5392
5393 #if 0
5394       if (Feld[x][y] != element)        /* element has changed */
5395       {
5396         element = Feld[x][y];
5397         move_pattern = element_info[element].move_pattern;
5398
5399         if (!CAN_MOVE(element))
5400           return;
5401       }
5402 #else
5403       if (Feld[x][y] != element)        /* element has changed */
5404         return;
5405 #endif
5406     }
5407 #endif
5408
5409 #if 0
5410 #if 0
5411     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5412       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
5413 #else
5414     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5415     {
5416       Moving2Blocked(x, y, &newx, &newy);
5417       if (Feld[newx][newy] == EL_BLOCKED)
5418         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
5419     }
5420 #endif
5421 #endif
5422
5423 #if 0
5424     if (FrameCounter < 1 && x == 0 && y == 29)
5425       printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5426 #endif
5427
5428     if (!MovDelay[x][y])        /* start new movement phase */
5429     {
5430       /* all objects that can change their move direction after each step
5431          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5432
5433       if (element != EL_YAMYAM &&
5434           element != EL_DARK_YAMYAM &&
5435           element != EL_PACMAN &&
5436           !(move_pattern & MV_ANY_DIRECTION) &&
5437           move_pattern != MV_TURNING_LEFT &&
5438           move_pattern != MV_TURNING_RIGHT &&
5439           move_pattern != MV_TURNING_LEFT_RIGHT &&
5440           move_pattern != MV_TURNING_RIGHT_LEFT &&
5441           move_pattern != MV_TURNING_RANDOM)
5442       {
5443         TurnRound(x, y);
5444
5445 #if 0
5446         if (FrameCounter < 1 && x == 0 && y == 29)
5447           printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5448 #endif
5449
5450         if (MovDelay[x][y] && (element == EL_BUG ||
5451                                element == EL_SPACESHIP ||
5452                                element == EL_SP_SNIKSNAK ||
5453                                element == EL_SP_ELECTRON ||
5454                                element == EL_MOLE))
5455           DrawLevelField(x, y);
5456       }
5457     }
5458
5459     if (MovDelay[x][y])         /* wait some time before next movement */
5460     {
5461       MovDelay[x][y]--;
5462
5463 #if 0
5464       if (element == EL_YAMYAM)
5465       {
5466         printf("::: %d\n",
5467                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5468         DrawLevelElementAnimation(x, y, element);
5469       }
5470 #endif
5471
5472       if (MovDelay[x][y])       /* element still has to wait some time */
5473       {
5474 #if 0
5475         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5476         ResetGfxAnimation(x, y);
5477 #endif
5478
5479 #if 0
5480         if (GfxAction[x][y] != ACTION_WAITING)
5481           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5482
5483         GfxAction[x][y] = ACTION_WAITING;
5484 #endif
5485       }
5486
5487       if (element == EL_ROBOT ||
5488 #if 0
5489           element == EL_PACMAN ||
5490 #endif
5491           element == EL_YAMYAM ||
5492           element == EL_DARK_YAMYAM)
5493       {
5494 #if 0
5495         DrawLevelElementAnimation(x, y, element);
5496 #else
5497         DrawLevelElementAnimationIfNeeded(x, y, element);
5498 #endif
5499         PlayLevelSoundAction(x, y, ACTION_WAITING);
5500       }
5501       else if (element == EL_SP_ELECTRON)
5502         DrawLevelElementAnimationIfNeeded(x, y, element);
5503       else if (element == EL_DRAGON)
5504       {
5505         int i;
5506         int dir = MovDir[x][y];
5507         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5508         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
5509         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
5510                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
5511                        dir == MV_UP     ? IMG_FLAMES_1_UP :
5512                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5513         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5514
5515 #if 0
5516         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
5517 #endif
5518
5519         GfxAction[x][y] = ACTION_ATTACKING;
5520
5521         if (IS_PLAYER(x, y))
5522           DrawPlayerField(x, y);
5523         else
5524           DrawLevelField(x, y);
5525
5526         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
5527
5528         for (i = 1; i <= 3; i++)
5529         {
5530           int xx = x + i * dx;
5531           int yy = y + i * dy;
5532           int sx = SCREENX(xx);
5533           int sy = SCREENY(yy);
5534           int flame_graphic = graphic + (i - 1);
5535
5536           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
5537             break;
5538
5539           if (MovDelay[x][y])
5540           {
5541             int flamed = MovingOrBlocked2Element(xx, yy);
5542
5543             /* !!! */
5544 #if 0
5545             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5546               Bang(xx, yy);
5547             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
5548               RemoveMovingField(xx, yy);
5549             else
5550               RemoveField(xx, yy);
5551 #else
5552             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
5553               Bang(xx, yy);
5554             else
5555               RemoveMovingField(xx, yy);
5556 #endif
5557
5558 #if 0
5559             if (ChangeDelay[xx][yy])
5560               printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
5561                                         Feld[xx][yy] == EL_BLOCKED));
5562 #endif
5563
5564 #if 1
5565             ChangeDelay[xx][yy] = 0;
5566 #endif
5567             Feld[xx][yy] = EL_FLAMES;
5568             if (IN_SCR_FIELD(sx, sy))
5569             {
5570               DrawLevelFieldCrumbledSand(xx, yy);
5571               DrawGraphic(sx, sy, flame_graphic, frame);
5572             }
5573           }
5574           else
5575           {
5576             if (Feld[xx][yy] == EL_FLAMES)
5577               Feld[xx][yy] = EL_EMPTY;
5578             DrawLevelField(xx, yy);
5579           }
5580         }
5581       }
5582
5583       if (MovDelay[x][y])       /* element still has to wait some time */
5584       {
5585         PlayLevelSoundAction(x, y, ACTION_WAITING);
5586
5587         return;
5588       }
5589
5590 #if 0
5591       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
5592          for all other elements GfxAction will be set by InitMovingField() */
5593       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
5594         GfxAction[x][y] = ACTION_MOVING;
5595 #endif
5596     }
5597
5598     /* now make next step */
5599
5600     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
5601
5602     if (DONT_COLLIDE_WITH(element) &&
5603         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
5604         !PLAYER_ENEMY_PROTECTED(newx, newy))
5605     {
5606 #if 1
5607       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
5608
5609       return;
5610 #else
5611       /* player killed by element which is deadly when colliding with */
5612       MovDir[x][y] = 0;
5613       KillHero(PLAYERINFO(newx, newy));
5614       return;
5615 #endif
5616
5617     }
5618 #if 1
5619 #if 1
5620     else if (CAN_MOVE_INTO_ACID(element) &&
5621              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
5622              (MovDir[x][y] == MV_DOWN ||
5623               game.engine_version >= VERSION_IDENT(3,1,0,0)))
5624 #else
5625     else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
5626              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
5627 #endif
5628 #else
5629
5630     else if ((element == EL_PENGUIN ||
5631               element == EL_ROBOT ||
5632               element == EL_SATELLITE ||
5633               element == EL_BALLOON ||
5634               IS_CUSTOM_ELEMENT(element)) &&
5635              IN_LEV_FIELD(newx, newy) &&
5636              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
5637 #endif
5638     {
5639       SplashAcid(newx, newy);
5640       Store[x][y] = EL_ACID;
5641     }
5642     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
5643     {
5644       if (Feld[newx][newy] == EL_EXIT_OPEN)
5645       {
5646 #if 1
5647         RemoveField(x, y);
5648         DrawLevelField(x, y);
5649 #else
5650         Feld[x][y] = EL_EMPTY;
5651         DrawLevelField(x, y);
5652 #endif
5653
5654         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
5655         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
5656           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
5657
5658         local_player->friends_still_needed--;
5659         if (!local_player->friends_still_needed &&
5660             !local_player->GameOver && AllPlayersGone)
5661           local_player->LevelSolved = local_player->GameOver = TRUE;
5662
5663         return;
5664       }
5665       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
5666       {
5667         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
5668           DrawLevelField(newx, newy);
5669         else
5670           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
5671       }
5672       else if (!IS_FREE(newx, newy))
5673       {
5674         GfxAction[x][y] = ACTION_WAITING;
5675
5676         if (IS_PLAYER(x, y))
5677           DrawPlayerField(x, y);
5678         else
5679           DrawLevelField(x, y);
5680
5681         return;
5682       }
5683     }
5684     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
5685     {
5686       if (IS_FOOD_PIG(Feld[newx][newy]))
5687       {
5688         if (IS_MOVING(newx, newy))
5689           RemoveMovingField(newx, newy);
5690         else
5691         {
5692           Feld[newx][newy] = EL_EMPTY;
5693           DrawLevelField(newx, newy);
5694         }
5695
5696         PlayLevelSound(x, y, SND_PIG_DIGGING);
5697       }
5698       else if (!IS_FREE(newx, newy))
5699       {
5700         if (IS_PLAYER(x, y))
5701           DrawPlayerField(x, y);
5702         else
5703           DrawLevelField(x, y);
5704
5705         return;
5706       }
5707     }
5708
5709 #if 1
5710
5711     /*
5712     else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
5713     */
5714
5715     else if (IS_CUSTOM_ELEMENT(element) &&
5716              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
5717
5718 #if 0
5719  &&
5720              !IS_FREE(newx, newy)
5721 #endif
5722
5723 )
5724     {
5725       int new_element = Feld[newx][newy];
5726
5727 #if 0
5728       printf("::: '%s' digs '%s' [%d]\n",
5729              element_info[element].token_name,
5730              element_info[Feld[newx][newy]].token_name,
5731              StorePlayer[newx][newy]);
5732 #endif
5733
5734       if (!IS_FREE(newx, newy))
5735       {
5736         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
5737                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
5738                       ACTION_BREAKING);
5739
5740         /* no element can dig solid indestructible elements */
5741         if (IS_INDESTRUCTIBLE(new_element) &&
5742             !IS_DIGGABLE(new_element) &&
5743             !IS_COLLECTIBLE(new_element))
5744           return;
5745
5746         if (AmoebaNr[newx][newy] &&
5747             (new_element == EL_AMOEBA_FULL ||
5748              new_element == EL_BD_AMOEBA ||
5749              new_element == EL_AMOEBA_GROWING))
5750         {
5751           AmoebaCnt[AmoebaNr[newx][newy]]--;
5752           AmoebaCnt2[AmoebaNr[newx][newy]]--;
5753         }
5754
5755         if (IS_MOVING(newx, newy))
5756           RemoveMovingField(newx, newy);
5757         else
5758         {
5759           RemoveField(newx, newy);
5760           DrawLevelField(newx, newy);
5761         }
5762
5763         /* if digged element was about to explode, prevent the explosion */
5764         ExplodeField[newx][newy] = EX_TYPE_NONE;
5765
5766         PlayLevelSoundAction(x, y, action);
5767       }
5768
5769 #if 1
5770 #if 1
5771       Store[newx][newy] = EL_EMPTY;
5772       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5773         Store[newx][newy] = element_info[element].move_leave_element;
5774 #else
5775       Store[newx][newy] = EL_EMPTY;
5776       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
5777           element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
5778         Store[newx][newy] = element_info[element].move_leave_element;
5779 #endif
5780 #else
5781       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
5782         element_info[element].can_leave_element = TRUE;
5783 #endif
5784
5785       if (move_pattern & MV_MAZE_RUNNER_STYLE)
5786       {
5787         RunnerVisit[x][y] = FrameCounter;
5788         PlayerVisit[x][y] /= 8;         /* expire player visit path */
5789       }
5790     }
5791
5792 #endif
5793
5794     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5795     {
5796       if (!IS_FREE(newx, newy))
5797       {
5798         if (IS_PLAYER(x, y))
5799           DrawPlayerField(x, y);
5800         else
5801           DrawLevelField(x, y);
5802
5803         return;
5804       }
5805       else
5806       {
5807         boolean wanna_flame = !RND(10);
5808         int dx = newx - x, dy = newy - y;
5809         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5810         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5811         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5812                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5813         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5814                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5815
5816         if ((wanna_flame ||
5817              IS_CLASSIC_ENEMY(element1) ||
5818              IS_CLASSIC_ENEMY(element2)) &&
5819             element1 != EL_DRAGON && element2 != EL_DRAGON &&
5820             element1 != EL_FLAMES && element2 != EL_FLAMES)
5821         {
5822 #if 1
5823           ResetGfxAnimation(x, y);
5824           GfxAction[x][y] = ACTION_ATTACKING;
5825 #endif
5826
5827           if (IS_PLAYER(x, y))
5828             DrawPlayerField(x, y);
5829           else
5830             DrawLevelField(x, y);
5831
5832           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5833
5834           MovDelay[x][y] = 50;
5835
5836           /* !!! */
5837 #if 0
5838           RemoveField(newx, newy);
5839 #endif
5840           Feld[newx][newy] = EL_FLAMES;
5841           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5842           {
5843 #if 0
5844             RemoveField(newx1, newy1);
5845 #endif
5846             Feld[newx1][newy1] = EL_FLAMES;
5847           }
5848           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5849           {
5850 #if 0
5851             RemoveField(newx2, newy2);
5852 #endif
5853             Feld[newx2][newy2] = EL_FLAMES;
5854           }
5855
5856           return;
5857         }
5858       }
5859     }
5860     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5861              Feld[newx][newy] == EL_DIAMOND)
5862     {
5863       if (IS_MOVING(newx, newy))
5864         RemoveMovingField(newx, newy);
5865       else
5866       {
5867         Feld[newx][newy] = EL_EMPTY;
5868         DrawLevelField(newx, newy);
5869       }
5870
5871       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5872     }
5873     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5874              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5875     {
5876       if (AmoebaNr[newx][newy])
5877       {
5878         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5879         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5880             Feld[newx][newy] == EL_BD_AMOEBA)
5881           AmoebaCnt[AmoebaNr[newx][newy]]--;
5882       }
5883
5884 #if 0
5885       /* !!! test !!! */
5886       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
5887 #else
5888       if (IS_MOVING(newx, newy))
5889 #endif
5890       {
5891         RemoveMovingField(newx, newy);
5892       }
5893       else
5894       {
5895         Feld[newx][newy] = EL_EMPTY;
5896         DrawLevelField(newx, newy);
5897       }
5898
5899       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5900     }
5901     else if ((element == EL_PACMAN || element == EL_MOLE)
5902              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5903     {
5904       if (AmoebaNr[newx][newy])
5905       {
5906         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5907         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5908             Feld[newx][newy] == EL_BD_AMOEBA)
5909           AmoebaCnt[AmoebaNr[newx][newy]]--;
5910       }
5911
5912       if (element == EL_MOLE)
5913       {
5914         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5915         PlayLevelSound(x, y, SND_MOLE_DIGGING);
5916
5917         ResetGfxAnimation(x, y);
5918         GfxAction[x][y] = ACTION_DIGGING;
5919         DrawLevelField(x, y);
5920
5921         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
5922
5923         return;                         /* wait for shrinking amoeba */
5924       }
5925       else      /* element == EL_PACMAN */
5926       {
5927         Feld[newx][newy] = EL_EMPTY;
5928         DrawLevelField(newx, newy);
5929         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5930       }
5931     }
5932     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5933              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5934               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5935     {
5936       /* wait for shrinking amoeba to completely disappear */
5937       return;
5938     }
5939     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5940     {
5941       /* object was running against a wall */
5942
5943       TurnRound(x, y);
5944
5945 #if 0
5946       if (move_pattern & MV_ANY_DIRECTION &&
5947           move_pattern == MovDir[x][y])
5948       {
5949         int blocking_element =
5950           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
5951
5952 #if 0
5953         printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
5954                element_info[element].token_name,
5955                element_info[blocking_element].token_name,
5956                x, y, newx, newy);
5957 #endif
5958
5959         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
5960                                  MovDir[x][y]);
5961
5962         element = Feld[x][y];   /* element might have changed */
5963       }
5964 #endif
5965
5966 #if 1
5967       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
5968         DrawLevelElementAnimation(x, y, element);
5969 #else
5970       if (element == EL_BUG ||
5971           element == EL_SPACESHIP ||
5972           element == EL_SP_SNIKSNAK)
5973         DrawLevelField(x, y);
5974       else if (element == EL_MOLE)
5975         DrawLevelField(x, y);
5976       else if (element == EL_BD_BUTTERFLY ||
5977                element == EL_BD_FIREFLY)
5978         DrawLevelElementAnimationIfNeeded(x, y, element);
5979       else if (element == EL_SATELLITE)
5980         DrawLevelElementAnimationIfNeeded(x, y, element);
5981       else if (element == EL_SP_ELECTRON)
5982         DrawLevelElementAnimationIfNeeded(x, y, element);
5983 #endif
5984
5985       if (DONT_TOUCH(element))
5986         TestIfBadThingTouchesHero(x, y);
5987
5988 #if 0
5989       PlayLevelSoundAction(x, y, ACTION_WAITING);
5990 #endif
5991
5992       return;
5993     }
5994
5995     InitMovingField(x, y, MovDir[x][y]);
5996
5997     PlayLevelSoundAction(x, y, ACTION_MOVING);
5998   }
5999
6000   if (MovDir[x][y])
6001     ContinueMoving(x, y);
6002 }
6003
6004 void ContinueMoving(int x, int y)
6005 {
6006   int element = Feld[x][y];
6007   int stored = Store[x][y];
6008   struct ElementInfo *ei = &element_info[element];
6009   int direction = MovDir[x][y];
6010   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6011   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
6012   int newx = x + dx, newy = y + dy;
6013 #if 0
6014   int nextx = newx + dx, nexty = newy + dy;
6015 #endif
6016 #if 1
6017   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
6018   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6019 #else
6020   boolean pushed_by_player = Pushed[x][y];
6021 #endif
6022
6023   MovPos[x][y] += getElementMoveStepsize(x, y);
6024
6025 #if 0
6026   if (pushed_by_player && IS_PLAYER(x, y))
6027   {
6028     /* special case: moving object pushed by player */
6029     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6030   }
6031 #else
6032   if (pushed_by_player) /* special case: moving object pushed by player */
6033     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6034 #endif
6035
6036   if (ABS(MovPos[x][y]) < TILEX)
6037   {
6038     DrawLevelField(x, y);
6039
6040     return;     /* element is still moving */
6041   }
6042
6043   /* element reached destination field */
6044
6045   Feld[x][y] = EL_EMPTY;
6046   Feld[newx][newy] = element;
6047   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6048
6049 #if 1
6050   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6051   {
6052     element = Feld[newx][newy] = EL_ACID;
6053   }
6054 #endif
6055   else if (element == EL_MOLE)
6056   {
6057     Feld[x][y] = EL_SAND;
6058
6059     DrawLevelFieldCrumbledSandNeighbours(x, y);
6060   }
6061   else if (element == EL_QUICKSAND_FILLING)
6062   {
6063     element = Feld[newx][newy] = get_next_element(element);
6064     Store[newx][newy] = Store[x][y];
6065   }
6066   else if (element == EL_QUICKSAND_EMPTYING)
6067   {
6068     Feld[x][y] = get_next_element(element);
6069     element = Feld[newx][newy] = Store[x][y];
6070   }
6071   else if (element == EL_MAGIC_WALL_FILLING)
6072   {
6073     element = Feld[newx][newy] = get_next_element(element);
6074     if (!game.magic_wall_active)
6075       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6076     Store[newx][newy] = Store[x][y];
6077   }
6078   else if (element == EL_MAGIC_WALL_EMPTYING)
6079   {
6080     Feld[x][y] = get_next_element(element);
6081     if (!game.magic_wall_active)
6082       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6083     element = Feld[newx][newy] = Store[x][y];
6084   }
6085   else if (element == EL_BD_MAGIC_WALL_FILLING)
6086   {
6087     element = Feld[newx][newy] = get_next_element(element);
6088     if (!game.magic_wall_active)
6089       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6090     Store[newx][newy] = Store[x][y];
6091   }
6092   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6093   {
6094     Feld[x][y] = get_next_element(element);
6095     if (!game.magic_wall_active)
6096       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6097     element = Feld[newx][newy] = Store[x][y];
6098   }
6099   else if (element == EL_AMOEBA_DROPPING)
6100   {
6101     Feld[x][y] = get_next_element(element);
6102     element = Feld[newx][newy] = Store[x][y];
6103   }
6104   else if (element == EL_SOKOBAN_OBJECT)
6105   {
6106     if (Back[x][y])
6107       Feld[x][y] = Back[x][y];
6108
6109     if (Back[newx][newy])
6110       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6111
6112     Back[x][y] = Back[newx][newy] = 0;
6113   }
6114 #if 0
6115   else if (Store[x][y] == EL_ACID)
6116   {
6117     element = Feld[newx][newy] = EL_ACID;
6118   }
6119 #endif
6120 #if 0
6121   else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6122            ei->move_leave_element != EL_EMPTY &&
6123            (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6124             Store[x][y] != EL_EMPTY))
6125   {
6126     /* some elements can leave other elements behind after moving */
6127
6128     Feld[x][y] = ei->move_leave_element;
6129     InitField(x, y, FALSE);
6130
6131     if (GFX_CRUMBLED(Feld[x][y]))
6132       DrawLevelFieldCrumbledSandNeighbours(x, y);
6133   }
6134 #endif
6135
6136   Store[x][y] = EL_EMPTY;
6137   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
6138   MovDelay[newx][newy] = 0;
6139
6140   if (CAN_CHANGE(element))
6141   {
6142     /* copy element change control values to new field */
6143     ChangeDelay[newx][newy] = ChangeDelay[x][y];
6144     ChangePage[newx][newy]  = ChangePage[x][y];
6145     Changed[newx][newy]     = Changed[x][y];
6146     ChangeEvent[newx][newy] = ChangeEvent[x][y];
6147   }
6148
6149   ChangeDelay[x][y] = 0;
6150   ChangePage[x][y] = -1;
6151   Changed[x][y] = CE_BITMASK_DEFAULT;
6152   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
6153
6154   /* copy animation control values to new field */
6155   GfxFrame[newx][newy]  = GfxFrame[x][y];
6156   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
6157   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
6158   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
6159
6160   Pushed[x][y] = Pushed[newx][newy] = FALSE;
6161
6162   ResetGfxAnimation(x, y);      /* reset animation values for old field */
6163
6164 #if 1
6165   /* some elements can leave other elements behind after moving */
6166 #if 1
6167   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6168       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6169       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6170 #else
6171   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6172       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6173       !IS_PLAYER(x, y))
6174 #endif
6175   {
6176     int move_leave_element = ei->move_leave_element;
6177
6178     Feld[x][y] = move_leave_element;
6179     InitField(x, y, FALSE);
6180
6181     if (GFX_CRUMBLED(Feld[x][y]))
6182       DrawLevelFieldCrumbledSandNeighbours(x, y);
6183
6184     if (ELEM_IS_PLAYER(move_leave_element))
6185       RelocatePlayer(x, y, move_leave_element);
6186   }
6187 #endif
6188
6189 #if 0
6190   /* some elements can leave other elements behind after moving */
6191   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6192       ei->move_leave_element != EL_EMPTY &&
6193       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6194        ei->can_leave_element_last))
6195   {
6196     Feld[x][y] = ei->move_leave_element;
6197     InitField(x, y, FALSE);
6198
6199     if (GFX_CRUMBLED(Feld[x][y]))
6200       DrawLevelFieldCrumbledSandNeighbours(x, y);
6201   }
6202
6203   ei->can_leave_element_last = ei->can_leave_element;
6204   ei->can_leave_element = FALSE;
6205 #endif
6206
6207 #if 0
6208   /* 2.1.1 (does not work correctly for spring) */
6209   if (!CAN_MOVE(element))
6210     MovDir[newx][newy] = 0;
6211 #else
6212
6213 #if 0
6214   /* (does not work for falling objects that slide horizontally) */
6215   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6216     MovDir[newx][newy] = 0;
6217 #else
6218   /*
6219   if (!CAN_MOVE(element) ||
6220       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6221     MovDir[newx][newy] = 0;
6222   */
6223
6224 #if 0
6225   if (!CAN_MOVE(element) ||
6226       (CAN_FALL(element) && direction == MV_DOWN))
6227     GfxDir[x][y] = MovDir[newx][newy] = 0;
6228 #else
6229   if (!CAN_MOVE(element) ||
6230       (CAN_FALL(element) && direction == MV_DOWN &&
6231        (element == EL_SPRING ||
6232         element_info[element].move_pattern == MV_WHEN_PUSHED ||
6233         element_info[element].move_pattern == MV_WHEN_DROPPED)))
6234     GfxDir[x][y] = MovDir[newx][newy] = 0;
6235 #endif
6236
6237 #endif
6238 #endif
6239
6240   DrawLevelField(x, y);
6241   DrawLevelField(newx, newy);
6242
6243   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
6244
6245   /* prevent pushed element from moving on in pushed direction */
6246   if (pushed_by_player && CAN_MOVE(element) &&
6247       element_info[element].move_pattern & MV_ANY_DIRECTION &&
6248       !(element_info[element].move_pattern & direction))
6249     TurnRound(newx, newy);
6250
6251 #if 1
6252   /* prevent elements on conveyor belt from moving on in last direction */
6253   if (pushed_by_conveyor && CAN_FALL(element) &&
6254       direction & MV_HORIZONTAL)
6255   {
6256 #if 0
6257     if (CAN_MOVE(element))
6258       InitMovDir(newx, newy);
6259     else
6260       MovDir[newx][newy] = 0;
6261 #else
6262     MovDir[newx][newy] = 0;
6263 #endif
6264   }
6265 #endif
6266
6267   if (!pushed_by_player)
6268   {
6269     int nextx = newx + dx, nexty = newy + dy;
6270     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6271
6272     WasJustMoving[newx][newy] = 3;
6273
6274     if (CAN_FALL(element) && direction == MV_DOWN)
6275       WasJustFalling[newx][newy] = 3;
6276
6277     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6278       CheckCollision[newx][newy] = 2;
6279   }
6280
6281   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
6282   {
6283     TestIfBadThingTouchesHero(newx, newy);
6284     TestIfBadThingTouchesFriend(newx, newy);
6285
6286     if (!IS_CUSTOM_ELEMENT(element))
6287       TestIfBadThingTouchesOtherBadThing(newx, newy);
6288   }
6289   else if (element == EL_PENGUIN)
6290     TestIfFriendTouchesBadThing(newx, newy);
6291
6292   if (CAN_FALL(element) && direction == MV_DOWN &&
6293       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
6294     Impact(x, newy);
6295
6296 #if 1
6297   if (pushed_by_player)
6298   {
6299     static int trigger_sides[4] =
6300     {
6301       CH_SIDE_RIGHT,    /* moving left  */
6302       CH_SIDE_LEFT,     /* moving right */
6303       CH_SIDE_BOTTOM,   /* moving up    */
6304       CH_SIDE_TOP,      /* moving down  */
6305     };
6306     int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6307     struct PlayerInfo *player = PLAYERINFO(x, y);
6308
6309     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6310                                player->index_bit, dig_side);
6311     CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
6312                                         player->index_bit, dig_side);
6313   }
6314 #endif
6315
6316 #if 1
6317   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
6318 #endif
6319
6320 #if 0
6321   if (ChangePage[newx][newy] != -1)                     /* delayed change */
6322     ChangeElement(newx, newy, ChangePage[newx][newy]);
6323 #endif
6324
6325 #if 1
6326
6327   TestIfElementHitsCustomElement(newx, newy, direction);
6328
6329 #else
6330
6331   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6332   {
6333     int hitting_element = Feld[newx][newy];
6334
6335     /* !!! fix side (direction) orientation here and elsewhere !!! */
6336     CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6337                              direction);
6338
6339 #if 0
6340     if (IN_LEV_FIELD(nextx, nexty))
6341     {
6342       int opposite_direction = MV_DIR_OPPOSITE(direction);
6343       int hitting_side = direction;
6344       int touched_side = opposite_direction;
6345       int touched_element = MovingOrBlocked2Element(nextx, nexty);
6346       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6347                             MovDir[nextx][nexty] != direction ||
6348                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6349
6350       if (object_hit)
6351       {
6352         int i;
6353
6354         CheckElementChangeBySide(nextx, nexty, touched_element,
6355                                  CE_HIT_BY_SOMETHING, opposite_direction);
6356
6357         if (IS_CUSTOM_ELEMENT(hitting_element) &&
6358             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
6359         {
6360           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6361           {
6362             struct ElementChangeInfo *change =
6363               &element_info[hitting_element].change_page[i];
6364
6365             if (change->can_change &&
6366                 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
6367                 change->trigger_side & touched_side &&
6368                 change->trigger_element == touched_element)
6369             {
6370               CheckElementChangeByPage(newx, newy, hitting_element,
6371                                        touched_element, CE_OTHER_IS_HITTING,i);
6372               break;
6373             }
6374           }
6375         }
6376
6377         if (IS_CUSTOM_ELEMENT(touched_element) &&
6378             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
6379         {
6380           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6381           {
6382             struct ElementChangeInfo *change =
6383               &element_info[touched_element].change_page[i];
6384
6385             if (change->can_change &&
6386                 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
6387                 change->trigger_side & hitting_side &&
6388                 change->trigger_element == hitting_element)
6389             {
6390               CheckElementChangeByPage(nextx, nexty, touched_element,
6391                                        hitting_element, CE_OTHER_GETS_HIT, i);
6392               break;
6393             }
6394           }
6395         }
6396       }
6397     }
6398 #endif
6399   }
6400 #endif
6401
6402   TestIfPlayerTouchesCustomElement(newx, newy);
6403   TestIfElementTouchesCustomElement(newx, newy);
6404 }
6405
6406 int AmoebeNachbarNr(int ax, int ay)
6407 {
6408   int i;
6409   int element = Feld[ax][ay];
6410   int group_nr = 0;
6411   static int xy[4][2] =
6412   {
6413     { 0, -1 },
6414     { -1, 0 },
6415     { +1, 0 },
6416     { 0, +1 }
6417   };
6418
6419   for (i = 0; i < NUM_DIRECTIONS; i++)
6420   {
6421     int x = ax + xy[i][0];
6422     int y = ay + xy[i][1];
6423
6424     if (!IN_LEV_FIELD(x, y))
6425       continue;
6426
6427     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6428       group_nr = AmoebaNr[x][y];
6429   }
6430
6431   return group_nr;
6432 }
6433
6434 void AmoebenVereinigen(int ax, int ay)
6435 {
6436   int i, x, y, xx, yy;
6437   int new_group_nr = AmoebaNr[ax][ay];
6438   static int xy[4][2] =
6439   {
6440     { 0, -1 },
6441     { -1, 0 },
6442     { +1, 0 },
6443     { 0, +1 }
6444   };
6445
6446   if (new_group_nr == 0)
6447     return;
6448
6449   for (i = 0; i < NUM_DIRECTIONS; i++)
6450   {
6451     x = ax + xy[i][0];
6452     y = ay + xy[i][1];
6453
6454     if (!IN_LEV_FIELD(x, y))
6455       continue;
6456
6457     if ((Feld[x][y] == EL_AMOEBA_FULL ||
6458          Feld[x][y] == EL_BD_AMOEBA ||
6459          Feld[x][y] == EL_AMOEBA_DEAD) &&
6460         AmoebaNr[x][y] != new_group_nr)
6461     {
6462       int old_group_nr = AmoebaNr[x][y];
6463
6464       if (old_group_nr == 0)
6465         return;
6466
6467       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6468       AmoebaCnt[old_group_nr] = 0;
6469       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
6470       AmoebaCnt2[old_group_nr] = 0;
6471
6472       for (yy = 0; yy < lev_fieldy; yy++)
6473       {
6474         for (xx = 0; xx < lev_fieldx; xx++)
6475         {
6476           if (AmoebaNr[xx][yy] == old_group_nr)
6477             AmoebaNr[xx][yy] = new_group_nr;
6478         }
6479       }
6480     }
6481   }
6482 }
6483
6484 void AmoebeUmwandeln(int ax, int ay)
6485 {
6486   int i, x, y;
6487
6488   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
6489   {
6490     int group_nr = AmoebaNr[ax][ay];
6491
6492 #ifdef DEBUG
6493     if (group_nr == 0)
6494     {
6495       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
6496       printf("AmoebeUmwandeln(): This should never happen!\n");
6497       return;
6498     }
6499 #endif
6500
6501     for (y = 0; y < lev_fieldy; y++)
6502     {
6503       for (x = 0; x < lev_fieldx; x++)
6504       {
6505         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
6506         {
6507           AmoebaNr[x][y] = 0;
6508           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
6509         }
6510       }
6511     }
6512     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
6513                             SND_AMOEBA_TURNING_TO_GEM :
6514                             SND_AMOEBA_TURNING_TO_ROCK));
6515     Bang(ax, ay);
6516   }
6517   else
6518   {
6519     static int xy[4][2] =
6520     {
6521       { 0, -1 },
6522       { -1, 0 },
6523       { +1, 0 },
6524       { 0, +1 }
6525     };
6526
6527     for (i = 0; i < NUM_DIRECTIONS; i++)
6528     {
6529       x = ax + xy[i][0];
6530       y = ay + xy[i][1];
6531
6532       if (!IN_LEV_FIELD(x, y))
6533         continue;
6534
6535       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
6536       {
6537         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
6538                               SND_AMOEBA_TURNING_TO_GEM :
6539                               SND_AMOEBA_TURNING_TO_ROCK));
6540         Bang(x, y);
6541       }
6542     }
6543   }
6544 }
6545
6546 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
6547 {
6548   int x, y;
6549   int group_nr = AmoebaNr[ax][ay];
6550   boolean done = FALSE;
6551
6552 #ifdef DEBUG
6553   if (group_nr == 0)
6554   {
6555     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
6556     printf("AmoebeUmwandelnBD(): This should never happen!\n");
6557     return;
6558   }
6559 #endif
6560
6561   for (y = 0; y < lev_fieldy; y++)
6562   {
6563     for (x = 0; x < lev_fieldx; x++)
6564     {
6565       if (AmoebaNr[x][y] == group_nr &&
6566           (Feld[x][y] == EL_AMOEBA_DEAD ||
6567            Feld[x][y] == EL_BD_AMOEBA ||
6568            Feld[x][y] == EL_AMOEBA_GROWING))
6569       {
6570         AmoebaNr[x][y] = 0;
6571         Feld[x][y] = new_element;
6572         InitField(x, y, FALSE);
6573         DrawLevelField(x, y);
6574         done = TRUE;
6575       }
6576     }
6577   }
6578
6579   if (done)
6580     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
6581                             SND_BD_AMOEBA_TURNING_TO_ROCK :
6582                             SND_BD_AMOEBA_TURNING_TO_GEM));
6583 }
6584
6585 void AmoebeWaechst(int x, int y)
6586 {
6587   static unsigned long sound_delay = 0;
6588   static unsigned long sound_delay_value = 0;
6589
6590   if (!MovDelay[x][y])          /* start new growing cycle */
6591   {
6592     MovDelay[x][y] = 7;
6593
6594     if (DelayReached(&sound_delay, sound_delay_value))
6595     {
6596 #if 1
6597       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
6598 #else
6599       if (Store[x][y] == EL_BD_AMOEBA)
6600         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
6601       else
6602         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
6603 #endif
6604       sound_delay_value = 30;
6605     }
6606   }
6607
6608   if (MovDelay[x][y])           /* wait some time before growing bigger */
6609   {
6610     MovDelay[x][y]--;
6611     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6612     {
6613       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
6614                                            6 - MovDelay[x][y]);
6615
6616       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
6617     }
6618
6619     if (!MovDelay[x][y])
6620     {
6621       Feld[x][y] = Store[x][y];
6622       Store[x][y] = 0;
6623       DrawLevelField(x, y);
6624     }
6625   }
6626 }
6627
6628 void AmoebaDisappearing(int x, int y)
6629 {
6630   static unsigned long sound_delay = 0;
6631   static unsigned long sound_delay_value = 0;
6632
6633   if (!MovDelay[x][y])          /* start new shrinking cycle */
6634   {
6635     MovDelay[x][y] = 7;
6636
6637     if (DelayReached(&sound_delay, sound_delay_value))
6638       sound_delay_value = 30;
6639   }
6640
6641   if (MovDelay[x][y])           /* wait some time before shrinking */
6642   {
6643     MovDelay[x][y]--;
6644     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6645     {
6646       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
6647                                            6 - MovDelay[x][y]);
6648
6649       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
6650     }
6651
6652     if (!MovDelay[x][y])
6653     {
6654       Feld[x][y] = EL_EMPTY;
6655       DrawLevelField(x, y);
6656
6657       /* don't let mole enter this field in this cycle;
6658          (give priority to objects falling to this field from above) */
6659       Stop[x][y] = TRUE;
6660     }
6661   }
6662 }
6663
6664 void AmoebeAbleger(int ax, int ay)
6665 {
6666   int i;
6667   int element = Feld[ax][ay];
6668   int graphic = el2img(element);
6669   int newax = ax, neway = ay;
6670   static int xy[4][2] =
6671   {
6672     { 0, -1 },
6673     { -1, 0 },
6674     { +1, 0 },
6675     { 0, +1 }
6676   };
6677
6678   if (!level.amoeba_speed)
6679   {
6680     Feld[ax][ay] = EL_AMOEBA_DEAD;
6681     DrawLevelField(ax, ay);
6682     return;
6683   }
6684
6685   if (IS_ANIMATED(graphic))
6686     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6687
6688   if (!MovDelay[ax][ay])        /* start making new amoeba field */
6689     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
6690
6691   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
6692   {
6693     MovDelay[ax][ay]--;
6694     if (MovDelay[ax][ay])
6695       return;
6696   }
6697
6698   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
6699   {
6700     int start = RND(4);
6701     int x = ax + xy[start][0];
6702     int y = ay + xy[start][1];
6703
6704     if (!IN_LEV_FIELD(x, y))
6705       return;
6706
6707 #if 1
6708     if (IS_FREE(x, y) ||
6709         CAN_GROW_INTO(Feld[x][y]) ||
6710         Feld[x][y] == EL_QUICKSAND_EMPTY)
6711     {
6712       newax = x;
6713       neway = y;
6714     }
6715 #else
6716     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6717     if (IS_FREE(x, y) ||
6718         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6719     {
6720       newax = x;
6721       neway = y;
6722     }
6723 #endif
6724
6725     if (newax == ax && neway == ay)
6726       return;
6727   }
6728   else                          /* normal or "filled" (BD style) amoeba */
6729   {
6730     int start = RND(4);
6731     boolean waiting_for_player = FALSE;
6732
6733     for (i = 0; i < NUM_DIRECTIONS; i++)
6734     {
6735       int j = (start + i) % 4;
6736       int x = ax + xy[j][0];
6737       int y = ay + xy[j][1];
6738
6739       if (!IN_LEV_FIELD(x, y))
6740         continue;
6741
6742 #if 1
6743       if (IS_FREE(x, y) ||
6744           CAN_GROW_INTO(Feld[x][y]) ||
6745           Feld[x][y] == EL_QUICKSAND_EMPTY)
6746       {
6747         newax = x;
6748         neway = y;
6749         break;
6750       }
6751 #else
6752       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6753       if (IS_FREE(x, y) ||
6754           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
6755       {
6756         newax = x;
6757         neway = y;
6758         break;
6759       }
6760 #endif
6761       else if (IS_PLAYER(x, y))
6762         waiting_for_player = TRUE;
6763     }
6764
6765     if (newax == ax && neway == ay)             /* amoeba cannot grow */
6766     {
6767 #if 1
6768       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
6769 #else
6770       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
6771 #endif
6772       {
6773         Feld[ax][ay] = EL_AMOEBA_DEAD;
6774         DrawLevelField(ax, ay);
6775         AmoebaCnt[AmoebaNr[ax][ay]]--;
6776
6777         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
6778         {
6779           if (element == EL_AMOEBA_FULL)
6780             AmoebeUmwandeln(ax, ay);
6781           else if (element == EL_BD_AMOEBA)
6782             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
6783         }
6784       }
6785       return;
6786     }
6787     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
6788     {
6789       /* amoeba gets larger by growing in some direction */
6790
6791       int new_group_nr = AmoebaNr[ax][ay];
6792
6793 #ifdef DEBUG
6794   if (new_group_nr == 0)
6795   {
6796     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
6797     printf("AmoebeAbleger(): This should never happen!\n");
6798     return;
6799   }
6800 #endif
6801
6802       AmoebaNr[newax][neway] = new_group_nr;
6803       AmoebaCnt[new_group_nr]++;
6804       AmoebaCnt2[new_group_nr]++;
6805
6806       /* if amoeba touches other amoeba(s) after growing, unify them */
6807       AmoebenVereinigen(newax, neway);
6808
6809       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
6810       {
6811         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
6812         return;
6813       }
6814     }
6815   }
6816
6817   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
6818       (neway == lev_fieldy - 1 && newax != ax))
6819   {
6820     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
6821     Store[newax][neway] = element;
6822   }
6823   else if (neway == ay)
6824   {
6825     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
6826 #if 1
6827     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
6828 #else
6829     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
6830 #endif
6831   }
6832   else
6833   {
6834     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
6835     Feld[ax][ay] = EL_AMOEBA_DROPPING;
6836     Store[ax][ay] = EL_AMOEBA_DROP;
6837     ContinueMoving(ax, ay);
6838     return;
6839   }
6840
6841   DrawLevelField(newax, neway);
6842 }
6843
6844 void Life(int ax, int ay)
6845 {
6846   int x1, y1, x2, y2;
6847   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
6848   int life_time = 40;
6849   int element = Feld[ax][ay];
6850   int graphic = el2img(element);
6851   boolean changed = FALSE;
6852
6853   if (IS_ANIMATED(graphic))
6854     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6855
6856   if (Stop[ax][ay])
6857     return;
6858
6859   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
6860     MovDelay[ax][ay] = life_time;
6861
6862   if (MovDelay[ax][ay])         /* wait some time before next cycle */
6863   {
6864     MovDelay[ax][ay]--;
6865     if (MovDelay[ax][ay])
6866       return;
6867   }
6868
6869   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
6870   {
6871     int xx = ax+x1, yy = ay+y1;
6872     int nachbarn = 0;
6873
6874     if (!IN_LEV_FIELD(xx, yy))
6875       continue;
6876
6877     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
6878     {
6879       int x = xx+x2, y = yy+y2;
6880
6881       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
6882         continue;
6883
6884       if (((Feld[x][y] == element ||
6885             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
6886            !Stop[x][y]) ||
6887           (IS_FREE(x, y) && Stop[x][y]))
6888         nachbarn++;
6889     }
6890
6891     if (xx == ax && yy == ay)           /* field in the middle */
6892     {
6893       if (nachbarn < life[0] || nachbarn > life[1])
6894       {
6895         Feld[xx][yy] = EL_EMPTY;
6896         if (!Stop[xx][yy])
6897           DrawLevelField(xx, yy);
6898         Stop[xx][yy] = TRUE;
6899         changed = TRUE;
6900       }
6901     }
6902 #if 1
6903     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
6904     {                                   /* free border field */
6905       if (nachbarn >= life[2] && nachbarn <= life[3])
6906       {
6907         Feld[xx][yy] = element;
6908         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6909         if (!Stop[xx][yy])
6910           DrawLevelField(xx, yy);
6911         Stop[xx][yy] = TRUE;
6912         changed = TRUE;
6913       }
6914     }
6915 #else
6916     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
6917     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
6918     {                                   /* free border field */
6919       if (nachbarn >= life[2] && nachbarn <= life[3])
6920       {
6921         Feld[xx][yy] = element;
6922         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
6923         if (!Stop[xx][yy])
6924           DrawLevelField(xx, yy);
6925         Stop[xx][yy] = TRUE;
6926         changed = TRUE;
6927       }
6928     }
6929 #endif
6930   }
6931
6932   if (changed)
6933     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
6934                    SND_GAME_OF_LIFE_GROWING);
6935 }
6936
6937 static void InitRobotWheel(int x, int y)
6938 {
6939   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6940 }
6941
6942 static void RunRobotWheel(int x, int y)
6943 {
6944   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
6945 }
6946
6947 static void StopRobotWheel(int x, int y)
6948 {
6949   if (ZX == x && ZY == y)
6950     ZX = ZY = -1;
6951 }
6952
6953 static void InitTimegateWheel(int x, int y)
6954 {
6955 #if 1
6956   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
6957 #else
6958   /* another brainless, "type style" bug ... :-( */
6959   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6960 #endif
6961 }
6962
6963 static void RunTimegateWheel(int x, int y)
6964 {
6965   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6966 }
6967
6968 void CheckExit(int x, int y)
6969 {
6970   if (local_player->gems_still_needed > 0 ||
6971       local_player->sokobanfields_still_needed > 0 ||
6972       local_player->lights_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_EXIT_OPENING;
6987
6988   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6989 }
6990
6991 void CheckExitSP(int x, int y)
6992 {
6993   if (local_player->gems_still_needed > 0)
6994   {
6995     int element = Feld[x][y];
6996     int graphic = el2img(element);
6997
6998     if (IS_ANIMATED(graphic))
6999       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7000
7001     return;
7002   }
7003
7004   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7005     return;
7006
7007   Feld[x][y] = EL_SP_EXIT_OPENING;
7008
7009   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7010 }
7011
7012 static void CloseAllOpenTimegates()
7013 {
7014   int x, y;
7015
7016   for (y = 0; y < lev_fieldy; y++)
7017   {
7018     for (x = 0; x < lev_fieldx; x++)
7019     {
7020       int element = Feld[x][y];
7021
7022       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7023       {
7024         Feld[x][y] = EL_TIMEGATE_CLOSING;
7025 #if 1
7026         PlayLevelSoundAction(x, y, ACTION_CLOSING);
7027 #else
7028         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7029 #endif
7030       }
7031     }
7032   }
7033 }
7034
7035 void EdelsteinFunkeln(int x, int y)
7036 {
7037   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7038     return;
7039
7040   if (Feld[x][y] == EL_BD_DIAMOND)
7041     return;
7042
7043   if (MovDelay[x][y] == 0)      /* next animation frame */
7044     MovDelay[x][y] = 11 * !SimpleRND(500);
7045
7046   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7047   {
7048     MovDelay[x][y]--;
7049
7050     if (setup.direct_draw && MovDelay[x][y])
7051       SetDrawtoField(DRAW_BUFFERED);
7052
7053     DrawLevelElementAnimation(x, y, Feld[x][y]);
7054
7055     if (MovDelay[x][y] != 0)
7056     {
7057       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7058                                            10 - MovDelay[x][y]);
7059
7060       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7061
7062       if (setup.direct_draw)
7063       {
7064         int dest_x, dest_y;
7065
7066         dest_x = FX + SCREENX(x) * TILEX;
7067         dest_y = FY + SCREENY(y) * TILEY;
7068
7069         BlitBitmap(drawto_field, window,
7070                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7071         SetDrawtoField(DRAW_DIRECT);
7072       }
7073     }
7074   }
7075 }
7076
7077 void MauerWaechst(int x, int y)
7078 {
7079   int delay = 6;
7080
7081   if (!MovDelay[x][y])          /* next animation frame */
7082     MovDelay[x][y] = 3 * delay;
7083
7084   if (MovDelay[x][y])           /* wait some time before next frame */
7085   {
7086     MovDelay[x][y]--;
7087
7088     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7089     {
7090       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7091       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7092
7093       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7094     }
7095
7096     if (!MovDelay[x][y])
7097     {
7098       if (MovDir[x][y] == MV_LEFT)
7099       {
7100         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7101           DrawLevelField(x - 1, y);
7102       }
7103       else if (MovDir[x][y] == MV_RIGHT)
7104       {
7105         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7106           DrawLevelField(x + 1, y);
7107       }
7108       else if (MovDir[x][y] == MV_UP)
7109       {
7110         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7111           DrawLevelField(x, y - 1);
7112       }
7113       else
7114       {
7115         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7116           DrawLevelField(x, y + 1);
7117       }
7118
7119       Feld[x][y] = Store[x][y];
7120       Store[x][y] = 0;
7121       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7122       DrawLevelField(x, y);
7123     }
7124   }
7125 }
7126
7127 void MauerAbleger(int ax, int ay)
7128 {
7129   int element = Feld[ax][ay];
7130   int graphic = el2img(element);
7131   boolean oben_frei = FALSE, unten_frei = FALSE;
7132   boolean links_frei = FALSE, rechts_frei = FALSE;
7133   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7134   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7135   boolean new_wall = FALSE;
7136
7137   if (IS_ANIMATED(graphic))
7138     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7139
7140   if (!MovDelay[ax][ay])        /* start building new wall */
7141     MovDelay[ax][ay] = 6;
7142
7143   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7144   {
7145     MovDelay[ax][ay]--;
7146     if (MovDelay[ax][ay])
7147       return;
7148   }
7149
7150   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7151     oben_frei = TRUE;
7152   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7153     unten_frei = TRUE;
7154   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7155     links_frei = TRUE;
7156   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7157     rechts_frei = TRUE;
7158
7159   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7160       element == EL_EXPANDABLE_WALL_ANY)
7161   {
7162     if (oben_frei)
7163     {
7164       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7165       Store[ax][ay-1] = element;
7166       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7167       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7168         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7169                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7170       new_wall = TRUE;
7171     }
7172     if (unten_frei)
7173     {
7174       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7175       Store[ax][ay+1] = element;
7176       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7177       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7178         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7179                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7180       new_wall = TRUE;
7181     }
7182   }
7183
7184   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7185       element == EL_EXPANDABLE_WALL_ANY ||
7186       element == EL_EXPANDABLE_WALL)
7187   {
7188     if (links_frei)
7189     {
7190       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7191       Store[ax-1][ay] = element;
7192       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7193       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7194         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7195                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7196       new_wall = TRUE;
7197     }
7198
7199     if (rechts_frei)
7200     {
7201       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7202       Store[ax+1][ay] = element;
7203       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7204       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7205         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7206                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7207       new_wall = TRUE;
7208     }
7209   }
7210
7211   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7212     DrawLevelField(ax, ay);
7213
7214   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7215     oben_massiv = TRUE;
7216   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7217     unten_massiv = TRUE;
7218   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7219     links_massiv = TRUE;
7220   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7221     rechts_massiv = TRUE;
7222
7223   if (((oben_massiv && unten_massiv) ||
7224        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7225        element == EL_EXPANDABLE_WALL) &&
7226       ((links_massiv && rechts_massiv) ||
7227        element == EL_EXPANDABLE_WALL_VERTICAL))
7228     Feld[ax][ay] = EL_WALL;
7229
7230   if (new_wall)
7231 #if 1
7232     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7233 #else
7234     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7235 #endif
7236 }
7237
7238 void CheckForDragon(int x, int y)
7239 {
7240   int i, j;
7241   boolean dragon_found = FALSE;
7242   static int xy[4][2] =
7243   {
7244     { 0, -1 },
7245     { -1, 0 },
7246     { +1, 0 },
7247     { 0, +1 }
7248   };
7249
7250   for (i = 0; i < NUM_DIRECTIONS; i++)
7251   {
7252     for (j = 0; j < 4; j++)
7253     {
7254       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7255
7256       if (IN_LEV_FIELD(xx, yy) &&
7257           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7258       {
7259         if (Feld[xx][yy] == EL_DRAGON)
7260           dragon_found = TRUE;
7261       }
7262       else
7263         break;
7264     }
7265   }
7266
7267   if (!dragon_found)
7268   {
7269     for (i = 0; i < NUM_DIRECTIONS; i++)
7270     {
7271       for (j = 0; j < 3; j++)
7272       {
7273         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7274   
7275         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7276         {
7277           Feld[xx][yy] = EL_EMPTY;
7278           DrawLevelField(xx, yy);
7279         }
7280         else
7281           break;
7282       }
7283     }
7284   }
7285 }
7286
7287 static void InitBuggyBase(int x, int y)
7288 {
7289   int element = Feld[x][y];
7290   int activating_delay = FRAMES_PER_SECOND / 4;
7291
7292   ChangeDelay[x][y] =
7293     (element == EL_SP_BUGGY_BASE ?
7294      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7295      element == EL_SP_BUGGY_BASE_ACTIVATING ?
7296      activating_delay :
7297      element == EL_SP_BUGGY_BASE_ACTIVE ?
7298      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7299 }
7300
7301 static void WarnBuggyBase(int x, int y)
7302 {
7303   int i;
7304   static int xy[4][2] =
7305   {
7306     { 0, -1 },
7307     { -1, 0 },
7308     { +1, 0 },
7309     { 0, +1 }
7310   };
7311
7312   for (i = 0; i < NUM_DIRECTIONS; i++)
7313   {
7314     int xx = x + xy[i][0], yy = y + xy[i][1];
7315
7316     if (IS_PLAYER(xx, yy))
7317     {
7318       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7319
7320       break;
7321     }
7322   }
7323 }
7324
7325 static void InitTrap(int x, int y)
7326 {
7327   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7328 }
7329
7330 static void ActivateTrap(int x, int y)
7331 {
7332   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7333 }
7334
7335 static void ChangeActiveTrap(int x, int y)
7336 {
7337   int graphic = IMG_TRAP_ACTIVE;
7338
7339   /* if new animation frame was drawn, correct crumbled sand border */
7340   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7341     DrawLevelFieldCrumbledSand(x, y);
7342 }
7343
7344 static void ChangeElementNowExt(int x, int y, int target_element)
7345 {
7346   int previous_move_direction = MovDir[x][y];
7347 #if 1
7348   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7349                         IS_WALKABLE(Feld[x][y]));
7350 #else
7351   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
7352                         IS_WALKABLE(Feld[x][y]) &&
7353                         !IS_MOVING(x, y));
7354 #endif
7355
7356   /* check if element under player changes from accessible to unaccessible
7357      (needed for special case of dropping element which then changes) */
7358   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
7359       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
7360   {
7361     Bang(x, y);
7362     return;
7363   }
7364
7365 #if 1
7366   if (!add_player)
7367 #endif
7368   {
7369 #if 1
7370     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
7371       RemoveMovingField(x, y);
7372     else
7373       RemoveField(x, y);
7374
7375     Feld[x][y] = target_element;
7376 #else
7377     RemoveField(x, y);
7378     Feld[x][y] = target_element;
7379 #endif
7380
7381     ResetGfxAnimation(x, y);
7382     ResetRandomAnimationValue(x, y);
7383
7384     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
7385       MovDir[x][y] = previous_move_direction;
7386
7387 #if 1
7388     InitField_WithBug1(x, y, FALSE);
7389 #else
7390     InitField(x, y, FALSE);
7391     if (CAN_MOVE(Feld[x][y]))
7392       InitMovDir(x, y);
7393 #endif
7394
7395     DrawLevelField(x, y);
7396
7397     if (GFX_CRUMBLED(Feld[x][y]))
7398       DrawLevelFieldCrumbledSandNeighbours(x, y);
7399   }
7400
7401   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7402
7403 #if 0
7404   TestIfBadThingTouchesHero(x, y);
7405   TestIfPlayerTouchesCustomElement(x, y);
7406   TestIfElementTouchesCustomElement(x, y);
7407 #endif
7408
7409   if (ELEM_IS_PLAYER(target_element))
7410     RelocatePlayer(x, y, target_element);
7411
7412 #if 1
7413   TestIfBadThingTouchesHero(x, y);
7414   TestIfPlayerTouchesCustomElement(x, y);
7415   TestIfElementTouchesCustomElement(x, y);
7416 #endif
7417 }
7418
7419 static boolean ChangeElementNow(int x, int y, int element, int page)
7420 {
7421   struct ElementChangeInfo *change = &element_info[element].change_page[page];
7422   int target_element;
7423   int old_element = Feld[x][y];
7424
7425   /* always use default change event to prevent running into a loop */
7426   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
7427     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
7428
7429   if (ChangeEvent[x][y] == CH_EVENT_BIT(CE_DELAY))
7430   {
7431     /* reset actual trigger element and player */
7432     change->actual_trigger_element = EL_EMPTY;
7433     change->actual_trigger_player = EL_PLAYER_1;
7434   }
7435
7436   /* do not change already changed elements with same change event */
7437 #if 0
7438   if (Changed[x][y] & ChangeEvent[x][y])
7439     return FALSE;
7440 #else
7441   if (Changed[x][y])
7442     return FALSE;
7443 #endif
7444
7445   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
7446
7447 #if 0
7448   /* !!! indirect change before direct change !!! */
7449   CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
7450 #endif
7451
7452   if (change->explode)
7453   {
7454     Bang(x, y);
7455
7456     return TRUE;
7457   }
7458
7459   if (change->use_target_content)
7460   {
7461     boolean complete_replace = TRUE;
7462     boolean can_replace[3][3];
7463     int xx, yy;
7464
7465     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7466     {
7467       boolean is_empty;
7468       boolean is_walkable;
7469       boolean is_diggable;
7470       boolean is_collectible;
7471       boolean is_removable;
7472       boolean is_destructible;
7473       int ex = x + xx - 1;
7474       int ey = y + yy - 1;
7475       int content_element = change->target_content[xx][yy];
7476       int e;
7477
7478       can_replace[xx][yy] = TRUE;
7479
7480       if (ex == x && ey == y)   /* do not check changing element itself */
7481         continue;
7482
7483       if (content_element == EL_EMPTY_SPACE)
7484       {
7485         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
7486
7487         continue;
7488       }
7489
7490       if (!IN_LEV_FIELD(ex, ey))
7491       {
7492         can_replace[xx][yy] = FALSE;
7493         complete_replace = FALSE;
7494
7495         continue;
7496       }
7497
7498       e = Feld[ex][ey];
7499
7500       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7501         e = MovingOrBlocked2Element(ex, ey);
7502
7503 #if 1
7504
7505 #if 0
7506       is_empty = (IS_FREE(ex, ey) ||
7507                   (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
7508                   (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
7509                    !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
7510 #else
7511       is_empty = (IS_FREE(ex, ey) ||
7512                   (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
7513 #endif
7514       is_walkable     = (is_empty || IS_WALKABLE(e));
7515       is_diggable     = (is_empty || IS_DIGGABLE(e));
7516       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
7517       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
7518       is_removable    = (is_diggable || is_collectible);
7519
7520       can_replace[xx][yy] =
7521         ((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
7522          (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
7523          (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
7524          (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
7525          (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
7526          (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
7527
7528       if (!can_replace[xx][yy])
7529         complete_replace = FALSE;
7530 #else
7531       empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
7532                                                IS_WALKABLE(content_element)));
7533 #if 1
7534       half_destructible = (empty_for_element || IS_DIGGABLE(e));
7535 #else
7536       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
7537 #endif
7538
7539       if ((change->replace_when <= CP_WHEN_EMPTY  && !empty_for_element) ||
7540           (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
7541           (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
7542       {
7543         can_replace[xx][yy] = FALSE;
7544         complete_replace = FALSE;
7545       }
7546 #endif
7547     }
7548
7549     if (!change->only_if_complete || complete_replace)
7550     {
7551       boolean something_has_changed = FALSE;
7552
7553       if (change->only_if_complete && change->use_random_replace &&
7554           RND(100) < change->random_percentage)
7555         return FALSE;
7556
7557       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
7558       {
7559         int ex = x + xx - 1;
7560         int ey = y + yy - 1;
7561         int content_element;
7562
7563         if (can_replace[xx][yy] && (!change->use_random_replace ||
7564                                     RND(100) < change->random_percentage))
7565         {
7566           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
7567             RemoveMovingField(ex, ey);
7568
7569           ChangeEvent[ex][ey] = ChangeEvent[x][y];
7570
7571           content_element = change->target_content[xx][yy];
7572           target_element = GET_TARGET_ELEMENT(content_element, change);
7573
7574           ChangeElementNowExt(ex, ey, target_element);
7575
7576           something_has_changed = TRUE;
7577
7578           /* for symmetry reasons, freeze newly created border elements */
7579           if (ex != x || ey != y)
7580             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
7581         }
7582       }
7583
7584       if (something_has_changed)
7585         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7586     }
7587   }
7588   else
7589   {
7590     target_element = GET_TARGET_ELEMENT(change->target_element, change);
7591
7592     ChangeElementNowExt(x, y, target_element);
7593
7594     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
7595   }
7596
7597 #if 1
7598   /* this uses direct change before indirect change */
7599   CheckTriggeredElementChangeByPage(x,y,old_element,CE_OTHER_IS_CHANGING,page);
7600 #endif
7601
7602   return TRUE;
7603 }
7604
7605 static void ChangeElement(int x, int y, int page)
7606 {
7607   int element = MovingOrBlocked2Element(x, y);
7608   struct ElementInfo *ei = &element_info[element];
7609   struct ElementChangeInfo *change = &ei->change_page[page];
7610
7611 #ifdef DEBUG
7612   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
7613   {
7614     printf("\n\n");
7615     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
7616            x, y, element, element_info[element].token_name);
7617     printf("ChangeElement(): This should never happen!\n");
7618     printf("\n\n");
7619   }
7620 #endif
7621
7622   /* this can happen with classic bombs on walkable, changing elements */
7623   if (!CAN_CHANGE(element))
7624   {
7625 #if 0
7626     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
7627       ChangeDelay[x][y] = 0;
7628 #endif
7629
7630     return;
7631   }
7632
7633   if (ChangeDelay[x][y] == 0)           /* initialize element change */
7634   {
7635     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
7636                          RND(change->delay_random * change->delay_frames)) + 1;
7637
7638     ResetGfxAnimation(x, y);
7639     ResetRandomAnimationValue(x, y);
7640
7641     if (change->pre_change_function)
7642       change->pre_change_function(x, y);
7643   }
7644
7645   ChangeDelay[x][y]--;
7646
7647   if (ChangeDelay[x][y] != 0)           /* continue element change */
7648   {
7649     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7650
7651     if (IS_ANIMATED(graphic))
7652       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7653
7654     if (change->change_function)
7655       change->change_function(x, y);
7656   }
7657   else                                  /* finish element change */
7658   {
7659     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
7660     {
7661       page = ChangePage[x][y];
7662       ChangePage[x][y] = -1;
7663
7664       change = &ei->change_page[page];
7665     }
7666
7667 #if 0
7668     if (IS_MOVING(x, y) && !change->explode)
7669 #else
7670     if (IS_MOVING(x, y))                /* never change a running system ;-) */
7671 #endif
7672     {
7673       ChangeDelay[x][y] = 1;            /* try change after next move step */
7674       ChangePage[x][y] = page;          /* remember page to use for change */
7675
7676       return;
7677     }
7678
7679     if (ChangeElementNow(x, y, element, page))
7680     {
7681       if (change->post_change_function)
7682         change->post_change_function(x, y);
7683     }
7684   }
7685 }
7686
7687 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
7688                                               int trigger_element,
7689                                               int trigger_event,
7690                                               int trigger_player,
7691                                               int trigger_side,
7692                                               int trigger_page)
7693 {
7694   int i, j, x, y;
7695   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
7696
7697   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
7698     return FALSE;
7699
7700   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7701   {
7702     int element = EL_CUSTOM_START + i;
7703
7704     boolean change_element = FALSE;
7705     int page = 0;
7706
7707     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7708       continue;
7709
7710     for (j = 0; j < element_info[element].num_change_pages; j++)
7711     {
7712       struct ElementChangeInfo *change = &element_info[element].change_page[j];
7713
7714       if (change->can_change &&
7715           change->events & CH_EVENT_BIT(trigger_event) &&
7716           change->trigger_side & trigger_side &&
7717           change->trigger_player & trigger_player &&
7718           change->trigger_page & trigger_page_bits &&
7719           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
7720       {
7721 #if 0
7722         if (!(change->events & CH_EVENT_BIT(trigger_event)))
7723           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
7724                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
7725 #endif
7726
7727         change_element = TRUE;
7728         page = j;
7729
7730         change->actual_trigger_element = trigger_element;
7731         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7732
7733         break;
7734       }
7735     }
7736
7737     if (!change_element)
7738       continue;
7739
7740     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7741     {
7742 #if 0
7743       if (x == lx && y == ly)   /* do not change trigger element itself */
7744         continue;
7745 #endif
7746
7747       if (Feld[x][y] == element)
7748       {
7749         ChangeDelay[x][y] = 1;
7750         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7751         ChangeElement(x, y, page);
7752       }
7753     }
7754   }
7755
7756   return TRUE;
7757 }
7758
7759 static boolean CheckElementChangeExt(int x, int y,
7760                                      int element,
7761                                      int trigger_element,
7762                                      int trigger_event,
7763                                      int trigger_player,
7764                                      int trigger_side,
7765                                      int trigger_page)
7766 {
7767   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
7768     return FALSE;
7769
7770   if (Feld[x][y] == EL_BLOCKED)
7771   {
7772     Blocked2Moving(x, y, &x, &y);
7773     element = Feld[x][y];
7774   }
7775
7776 #if 1
7777   if (Feld[x][y] != element)    /* check if element has already changed */
7778   {
7779 #if 0
7780     printf("::: %d ('%s') != %d ('%s') [%d]\n",
7781            Feld[x][y], element_info[Feld[x][y]].token_name,
7782            element, element_info[element].token_name,
7783            trigger_event);
7784 #endif
7785
7786     return FALSE;
7787   }
7788 #endif
7789
7790 #if 1
7791   if (trigger_page < 0)
7792   {
7793     boolean change_element = FALSE;
7794     int i;
7795
7796     for (i = 0; i < element_info[element].num_change_pages; i++)
7797     {
7798       struct ElementChangeInfo *change = &element_info[element].change_page[i];
7799
7800       if (change->can_change &&
7801           change->events & CH_EVENT_BIT(trigger_event) &&
7802           change->trigger_side & trigger_side &&
7803           change->trigger_player & trigger_player)
7804       {
7805         change_element = TRUE;
7806         trigger_page = i;
7807
7808         change->actual_trigger_element = trigger_element;
7809         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
7810
7811         break;
7812       }
7813     }
7814
7815     if (!change_element)
7816       return FALSE;
7817   }
7818   else
7819   {
7820     struct ElementInfo *ei = &element_info[element];
7821     struct ElementChangeInfo *change = &ei->change_page[trigger_page];
7822
7823     change->actual_trigger_element = trigger_element;
7824     change->actual_trigger_player = EL_PLAYER_1;        /* unused */
7825   }
7826
7827 #else
7828
7829   /* !!! this check misses pages with same event, but different side !!! */
7830
7831   if (trigger_page < 0)
7832     trigger_page = element_info[element].event_page_nr[trigger_event];
7833
7834   if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
7835     return FALSE;
7836 #endif
7837
7838   ChangeDelay[x][y] = 1;
7839   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
7840   ChangeElement(x, y, trigger_page);
7841
7842   return TRUE;
7843 }
7844
7845 static void PlayPlayerSound(struct PlayerInfo *player)
7846 {
7847   int jx = player->jx, jy = player->jy;
7848   int element = player->element_nr;
7849   int last_action = player->last_action_waiting;
7850   int action = player->action_waiting;
7851
7852   if (player->is_waiting)
7853   {
7854     if (action != last_action)
7855       PlayLevelSoundElementAction(jx, jy, element, action);
7856     else
7857       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
7858   }
7859   else
7860   {
7861     if (action != last_action)
7862       StopSound(element_info[element].sound[last_action]);
7863
7864     if (last_action == ACTION_SLEEPING)
7865       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
7866   }
7867 }
7868
7869 static void PlayAllPlayersSound()
7870 {
7871   int i;
7872
7873   for (i = 0; i < MAX_PLAYERS; i++)
7874     if (stored_player[i].active)
7875       PlayPlayerSound(&stored_player[i]);
7876 }
7877
7878 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
7879 {
7880   boolean last_waiting = player->is_waiting;
7881   int move_dir = player->MovDir;
7882
7883   player->last_action_waiting = player->action_waiting;
7884
7885   if (is_waiting)
7886   {
7887     if (!last_waiting)          /* not waiting -> waiting */
7888     {
7889       player->is_waiting = TRUE;
7890
7891       player->frame_counter_bored =
7892         FrameCounter +
7893         game.player_boring_delay_fixed +
7894         SimpleRND(game.player_boring_delay_random);
7895       player->frame_counter_sleeping =
7896         FrameCounter +
7897         game.player_sleeping_delay_fixed +
7898         SimpleRND(game.player_sleeping_delay_random);
7899
7900       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
7901     }
7902
7903     if (game.player_sleeping_delay_fixed +
7904         game.player_sleeping_delay_random > 0 &&
7905         player->anim_delay_counter == 0 &&
7906         player->post_delay_counter == 0 &&
7907         FrameCounter >= player->frame_counter_sleeping)
7908       player->is_sleeping = TRUE;
7909     else if (game.player_boring_delay_fixed +
7910              game.player_boring_delay_random > 0 &&
7911              FrameCounter >= player->frame_counter_bored)
7912       player->is_bored = TRUE;
7913
7914     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
7915                               player->is_bored ? ACTION_BORING :
7916                               ACTION_WAITING);
7917
7918     if (player->is_sleeping)
7919     {
7920       if (player->num_special_action_sleeping > 0)
7921       {
7922         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7923         {
7924           int last_special_action = player->special_action_sleeping;
7925           int num_special_action = player->num_special_action_sleeping;
7926           int special_action =
7927             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
7928              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
7929              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
7930              last_special_action + 1 : ACTION_SLEEPING);
7931           int special_graphic =
7932             el_act_dir2img(player->element_nr, special_action, move_dir);
7933
7934           player->anim_delay_counter =
7935             graphic_info[special_graphic].anim_delay_fixed +
7936             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7937           player->post_delay_counter =
7938             graphic_info[special_graphic].post_delay_fixed +
7939             SimpleRND(graphic_info[special_graphic].post_delay_random);
7940
7941           player->special_action_sleeping = special_action;
7942         }
7943
7944         if (player->anim_delay_counter > 0)
7945         {
7946           player->action_waiting = player->special_action_sleeping;
7947           player->anim_delay_counter--;
7948         }
7949         else if (player->post_delay_counter > 0)
7950         {
7951           player->post_delay_counter--;
7952         }
7953       }
7954     }
7955     else if (player->is_bored)
7956     {
7957       if (player->num_special_action_bored > 0)
7958       {
7959         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
7960         {
7961           int special_action =
7962             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
7963           int special_graphic =
7964             el_act_dir2img(player->element_nr, special_action, move_dir);
7965
7966           player->anim_delay_counter =
7967             graphic_info[special_graphic].anim_delay_fixed +
7968             SimpleRND(graphic_info[special_graphic].anim_delay_random);
7969           player->post_delay_counter =
7970             graphic_info[special_graphic].post_delay_fixed +
7971             SimpleRND(graphic_info[special_graphic].post_delay_random);
7972
7973           player->special_action_bored = special_action;
7974         }
7975
7976         if (player->anim_delay_counter > 0)
7977         {
7978           player->action_waiting = player->special_action_bored;
7979           player->anim_delay_counter--;
7980         }
7981         else if (player->post_delay_counter > 0)
7982         {
7983           player->post_delay_counter--;
7984         }
7985       }
7986     }
7987   }
7988   else if (last_waiting)        /* waiting -> not waiting */
7989   {
7990     player->is_waiting = FALSE;
7991     player->is_bored = FALSE;
7992     player->is_sleeping = FALSE;
7993
7994     player->frame_counter_bored = -1;
7995     player->frame_counter_sleeping = -1;
7996
7997     player->anim_delay_counter = 0;
7998     player->post_delay_counter = 0;
7999
8000     player->action_waiting = ACTION_DEFAULT;
8001
8002     player->special_action_bored = ACTION_DEFAULT;
8003     player->special_action_sleeping = ACTION_DEFAULT;
8004   }
8005 }
8006
8007 #if 1
8008 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
8009 {
8010 #if 0
8011   static byte stored_player_action[MAX_PLAYERS];
8012   static int num_stored_actions = 0;
8013 #endif
8014   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8015   int left      = player_action & JOY_LEFT;
8016   int right     = player_action & JOY_RIGHT;
8017   int up        = player_action & JOY_UP;
8018   int down      = player_action & JOY_DOWN;
8019   int button1   = player_action & JOY_BUTTON_1;
8020   int button2   = player_action & JOY_BUTTON_2;
8021   int dx        = (left ? -1    : right ? 1     : 0);
8022   int dy        = (up   ? -1    : down  ? 1     : 0);
8023
8024 #if 0
8025   stored_player_action[player->index_nr] = 0;
8026   num_stored_actions++;
8027 #endif
8028
8029 #if 0
8030   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8031 #endif
8032
8033   if (!player->active || tape.pausing)
8034     return 0;
8035
8036 #if 0
8037   printf("::: [%d %d %d %d] [%d %d]\n",
8038          left, right, up, down, button1, button2);
8039 #endif
8040
8041   if (player_action)
8042   {
8043 #if 0
8044     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8045 #endif
8046
8047 #if 0
8048     /* !!! TEST !!! */
8049     if (player->MovPos == 0)
8050       CheckGravityMovement(player);
8051 #endif
8052     if (button1)
8053       snapped = SnapField(player, dx, dy);
8054     else
8055     {
8056       if (button2)
8057         dropped = DropElement(player);
8058
8059       moved = MovePlayer(player, dx, dy);
8060     }
8061
8062     if (tape.single_step && tape.recording && !tape.pausing)
8063     {
8064       if (button1 || (dropped && !moved))
8065       {
8066         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8067         SnapField(player, 0, 0);                /* stop snapping */
8068       }
8069     }
8070
8071     SetPlayerWaiting(player, FALSE);
8072
8073 #if 1
8074     return player_action;
8075 #else
8076     stored_player_action[player->index_nr] = player_action;
8077 #endif
8078   }
8079   else
8080   {
8081 #if 0
8082     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8083 #endif
8084
8085     /* no actions for this player (no input at player's configured device) */
8086
8087     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8088     SnapField(player, 0, 0);
8089     CheckGravityMovementWhenNotMoving(player);
8090
8091     if (player->MovPos == 0)
8092       SetPlayerWaiting(player, TRUE);
8093
8094     if (player->MovPos == 0)    /* needed for tape.playing */
8095       player->is_moving = FALSE;
8096
8097     player->is_dropping = FALSE;
8098
8099     return 0;
8100   }
8101
8102 #if 0
8103   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8104   {
8105     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8106
8107     TapeRecordAction(stored_player_action);
8108     num_stored_actions = 0;
8109   }
8110 #endif
8111 }
8112
8113 #else
8114
8115 static void PlayerActions(struct PlayerInfo *player, byte player_action)
8116 {
8117   static byte stored_player_action[MAX_PLAYERS];
8118   static int num_stored_actions = 0;
8119   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
8120   int left      = player_action & JOY_LEFT;
8121   int right     = player_action & JOY_RIGHT;
8122   int up        = player_action & JOY_UP;
8123   int down      = player_action & JOY_DOWN;
8124   int button1   = player_action & JOY_BUTTON_1;
8125   int button2   = player_action & JOY_BUTTON_2;
8126   int dx        = (left ? -1    : right ? 1     : 0);
8127   int dy        = (up   ? -1    : down  ? 1     : 0);
8128
8129   stored_player_action[player->index_nr] = 0;
8130   num_stored_actions++;
8131
8132   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
8133
8134   if (!player->active || tape.pausing)
8135     return;
8136
8137   if (player_action)
8138   {
8139     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
8140
8141     if (button1)
8142       snapped = SnapField(player, dx, dy);
8143     else
8144     {
8145       if (button2)
8146         dropped = DropElement(player);
8147
8148       moved = MovePlayer(player, dx, dy);
8149     }
8150
8151     if (tape.single_step && tape.recording && !tape.pausing)
8152     {
8153       if (button1 || (dropped && !moved))
8154       {
8155         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8156         SnapField(player, 0, 0);                /* stop snapping */
8157       }
8158     }
8159
8160     stored_player_action[player->index_nr] = player_action;
8161   }
8162   else
8163   {
8164     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
8165
8166     /* no actions for this player (no input at player's configured device) */
8167
8168     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
8169     SnapField(player, 0, 0);
8170     CheckGravityMovementWhenNotMoving(player);
8171
8172     if (player->MovPos == 0)
8173       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
8174
8175     if (player->MovPos == 0)    /* needed for tape.playing */
8176       player->is_moving = FALSE;
8177   }
8178
8179   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
8180   {
8181     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
8182
8183     TapeRecordAction(stored_player_action);
8184     num_stored_actions = 0;
8185   }
8186 }
8187 #endif
8188
8189 void GameActions()
8190 {
8191   static unsigned long action_delay = 0;
8192   unsigned long action_delay_value;
8193   int magic_wall_x = 0, magic_wall_y = 0;
8194   int i, x, y, element, graphic;
8195   byte *recorded_player_action;
8196   byte summarized_player_action = 0;
8197 #if 1
8198   byte tape_action[MAX_PLAYERS];
8199 #endif
8200
8201   if (game_status != GAME_MODE_PLAYING)
8202     return;
8203
8204   action_delay_value =
8205     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
8206
8207   if (tape.playing && tape.warp_forward && !tape.pausing)
8208     action_delay_value = 0;
8209
8210   /* ---------- main game synchronization point ---------- */
8211
8212   WaitUntilDelayReached(&action_delay, action_delay_value);
8213
8214   if (network_playing && !network_player_action_received)
8215   {
8216     /*
8217 #ifdef DEBUG
8218     printf("DEBUG: try to get network player actions in time\n");
8219 #endif
8220     */
8221
8222 #if defined(NETWORK_AVALIABLE)
8223     /* last chance to get network player actions without main loop delay */
8224     HandleNetworking();
8225 #endif
8226
8227     if (game_status != GAME_MODE_PLAYING)
8228       return;
8229
8230     if (!network_player_action_received)
8231     {
8232       /*
8233 #ifdef DEBUG
8234       printf("DEBUG: failed to get network player actions in time\n");
8235 #endif
8236       */
8237       return;
8238     }
8239   }
8240
8241   if (tape.pausing)
8242     return;
8243
8244 #if 0
8245   printf("::: getting new tape action [%d]\n", FrameCounter);
8246 #endif
8247
8248   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
8249
8250 #if 1
8251   if (recorded_player_action == NULL && tape.pausing)
8252     return;
8253 #endif
8254
8255 #if 0
8256   printf("::: %d\n", stored_player[0].action);
8257 #endif
8258
8259 #if 0
8260   if (recorded_player_action != NULL)
8261     for (i = 0; i < MAX_PLAYERS; i++)
8262       stored_player[i].action = recorded_player_action[i];
8263 #endif
8264
8265   for (i = 0; i < MAX_PLAYERS; i++)
8266   {
8267     summarized_player_action |= stored_player[i].action;
8268
8269     if (!network_playing)
8270       stored_player[i].effective_action = stored_player[i].action;
8271   }
8272
8273 #if defined(NETWORK_AVALIABLE)
8274   if (network_playing)
8275     SendToServer_MovePlayer(summarized_player_action);
8276 #endif
8277
8278   if (!options.network && !setup.team_mode)
8279     local_player->effective_action = summarized_player_action;
8280
8281 #if 1
8282   if (recorded_player_action != NULL)
8283     for (i = 0; i < MAX_PLAYERS; i++)
8284       stored_player[i].effective_action = recorded_player_action[i];
8285 #endif
8286
8287 #if 1
8288   for (i = 0; i < MAX_PLAYERS; i++)
8289   {
8290     tape_action[i] = stored_player[i].effective_action;
8291
8292     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8293       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8294   }
8295
8296   /* only save actions from input devices, but not programmed actions */
8297   if (tape.recording)
8298     TapeRecordAction(tape_action);
8299 #endif
8300
8301   for (i = 0; i < MAX_PLAYERS; i++)
8302   {
8303     int actual_player_action = stored_player[i].effective_action;
8304
8305 #if 1
8306     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
8307        - rnd_equinox_tetrachloride 048
8308        - rnd_equinox_tetrachloride_ii 096
8309        - rnd_emanuel_schmieg 002
8310        - doctor_sloan_ww 001, 020
8311     */
8312     if (stored_player[i].MovPos == 0)
8313       CheckGravityMovement(&stored_player[i]);
8314 #endif
8315
8316 #if 1
8317     /* overwrite programmed action with tape action */
8318     if (stored_player[i].programmed_action)
8319       actual_player_action = stored_player[i].programmed_action;
8320 #endif
8321
8322 #if 0
8323     if (stored_player[i].programmed_action)
8324       printf("::: %d\n", stored_player[i].programmed_action);
8325 #endif
8326
8327     if (recorded_player_action)
8328     {
8329 #if 0
8330       if (stored_player[i].programmed_action &&
8331           stored_player[i].programmed_action != recorded_player_action[i])
8332         printf("::: %d: %d <-> %d\n", i,
8333                stored_player[i].programmed_action, recorded_player_action[i]);
8334 #endif
8335
8336 #if 0
8337       actual_player_action = recorded_player_action[i];
8338 #endif
8339     }
8340
8341 #if 0
8342     /* overwrite tape action with programmed action */
8343     if (stored_player[i].programmed_action)
8344       actual_player_action = stored_player[i].programmed_action;
8345 #endif
8346
8347 #if 0
8348     if (i == 0)
8349       printf("::: action: %d: %x [%d]\n",
8350              stored_player[i].MovPos, actual_player_action, FrameCounter);
8351 #endif
8352
8353 #if 1
8354     PlayerActions(&stored_player[i], actual_player_action);
8355 #else
8356     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
8357
8358     if (tape.recording && tape_action[i] && !tape.player_participates[i])
8359       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
8360 #endif
8361
8362     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
8363   }
8364
8365 #if 0
8366   if (tape.recording)
8367     TapeRecordAction(tape_action);
8368 #endif
8369
8370   network_player_action_received = FALSE;
8371
8372   ScrollScreen(NULL, SCROLL_GO_ON);
8373
8374 #if 0
8375   FrameCounter++;
8376   TimeFrames++;
8377
8378   for (i = 0; i < MAX_PLAYERS; i++)
8379     stored_player[i].Frame++;
8380 #endif
8381
8382 #if 1
8383   /* for downwards compatibility, the following code emulates a fixed bug that
8384      occured when pushing elements (causing elements that just made their last
8385      pushing step to already (if possible) make their first falling step in the
8386      same game frame, which is bad); this code is also needed to use the famous
8387      "spring push bug" which is used in older levels and might be wanted to be
8388      used also in newer levels, but in this case the buggy pushing code is only
8389      affecting the "spring" element and no other elements */
8390
8391 #if 1
8392   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
8393 #else
8394   if (game.engine_version < VERSION_IDENT(2,2,0,7))
8395 #endif
8396   {
8397     for (i = 0; i < MAX_PLAYERS; i++)
8398     {
8399       struct PlayerInfo *player = &stored_player[i];
8400       int x = player->jx;
8401       int y = player->jy;
8402
8403 #if 1
8404       if (player->active && player->is_pushing && player->is_moving &&
8405           IS_MOVING(x, y) &&
8406           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
8407            Feld[x][y] == EL_SPRING))
8408 #else
8409       if (player->active && player->is_pushing && player->is_moving &&
8410           IS_MOVING(x, y))
8411 #endif
8412       {
8413         ContinueMoving(x, y);
8414
8415         /* continue moving after pushing (this is actually a bug) */
8416         if (!IS_MOVING(x, y))
8417         {
8418           Stop[x][y] = FALSE;
8419         }
8420       }
8421     }
8422   }
8423 #endif
8424
8425   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8426   {
8427     Changed[x][y] = CE_BITMASK_DEFAULT;
8428     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
8429
8430 #if DEBUG
8431     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
8432     {
8433       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
8434       printf("GameActions(): This should never happen!\n");
8435
8436       ChangePage[x][y] = -1;
8437     }
8438 #endif
8439
8440     Stop[x][y] = FALSE;
8441     if (WasJustMoving[x][y] > 0)
8442       WasJustMoving[x][y]--;
8443     if (WasJustFalling[x][y] > 0)
8444       WasJustFalling[x][y]--;
8445     if (CheckCollision[x][y] > 0)
8446       CheckCollision[x][y]--;
8447
8448     GfxFrame[x][y]++;
8449
8450 #if 1
8451     /* reset finished pushing action (not done in ContinueMoving() to allow
8452        continous pushing animation for elements with zero push delay) */
8453     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
8454     {
8455       ResetGfxAnimation(x, y);
8456       DrawLevelField(x, y);
8457     }
8458 #endif
8459
8460 #if DEBUG
8461     if (IS_BLOCKED(x, y))
8462     {
8463       int oldx, oldy;
8464
8465       Blocked2Moving(x, y, &oldx, &oldy);
8466       if (!IS_MOVING(oldx, oldy))
8467       {
8468         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
8469         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
8470         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
8471         printf("GameActions(): This should never happen!\n");
8472       }
8473     }
8474 #endif
8475   }
8476
8477   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8478   {
8479     element = Feld[x][y];
8480 #if 1
8481     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8482 #else
8483     graphic = el2img(element);
8484 #endif
8485
8486 #if 0
8487     if (element == -1)
8488     {
8489       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
8490
8491       element = graphic = 0;
8492     }
8493 #endif
8494
8495     if (graphic_info[graphic].anim_global_sync)
8496       GfxFrame[x][y] = FrameCounter;
8497
8498     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
8499         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
8500       ResetRandomAnimationValue(x, y);
8501
8502     SetRandomAnimationValue(x, y);
8503
8504 #if 1
8505     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
8506 #endif
8507
8508     if (IS_INACTIVE(element))
8509     {
8510       if (IS_ANIMATED(graphic))
8511         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8512
8513       continue;
8514     }
8515
8516 #if 1
8517     /* this may take place after moving, so 'element' may have changed */
8518 #if 0
8519     if (IS_CHANGING(x, y))
8520 #else
8521     if (IS_CHANGING(x, y) &&
8522         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
8523 #endif
8524     {
8525 #if 0
8526       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
8527                     element_info[element].event_page_nr[CE_DELAY]);
8528 #else
8529       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
8530 #endif
8531
8532       element = Feld[x][y];
8533       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8534     }
8535 #endif
8536
8537     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
8538     {
8539       StartMoving(x, y);
8540
8541 #if 1
8542       element = Feld[x][y];
8543       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8544 #if 0
8545       if (element == EL_MOLE)
8546         printf("::: %d, %d, %d [%d]\n",
8547                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
8548                GfxAction[x][y]);
8549 #endif
8550 #if 0
8551       if (element == EL_YAMYAM)
8552         printf("::: %d, %d, %d\n",
8553                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
8554 #endif
8555 #endif
8556
8557       if (IS_ANIMATED(graphic) &&
8558           !IS_MOVING(x, y) &&
8559           !Stop[x][y])
8560       {
8561         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8562
8563 #if 0
8564         if (element == EL_BUG)
8565           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8566 #endif
8567
8568 #if 0
8569         if (element == EL_MOLE)
8570           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
8571 #endif
8572       }
8573
8574       if (IS_GEM(element) || element == EL_SP_INFOTRON)
8575         EdelsteinFunkeln(x, y);
8576     }
8577     else if ((element == EL_ACID ||
8578               element == EL_EXIT_OPEN ||
8579               element == EL_SP_EXIT_OPEN ||
8580               element == EL_SP_TERMINAL ||
8581               element == EL_SP_TERMINAL_ACTIVE ||
8582               element == EL_EXTRA_TIME ||
8583               element == EL_SHIELD_NORMAL ||
8584               element == EL_SHIELD_DEADLY) &&
8585              IS_ANIMATED(graphic))
8586       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8587     else if (IS_MOVING(x, y))
8588       ContinueMoving(x, y);
8589     else if (IS_ACTIVE_BOMB(element))
8590       CheckDynamite(x, y);
8591 #if 0
8592     else if (element == EL_EXPLOSION && !game.explosions_delayed)
8593       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8594 #endif
8595     else if (element == EL_AMOEBA_GROWING)
8596       AmoebeWaechst(x, y);
8597     else if (element == EL_AMOEBA_SHRINKING)
8598       AmoebaDisappearing(x, y);
8599
8600 #if !USE_NEW_AMOEBA_CODE
8601     else if (IS_AMOEBALIVE(element))
8602       AmoebeAbleger(x, y);
8603 #endif
8604
8605     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
8606       Life(x, y);
8607     else if (element == EL_EXIT_CLOSED)
8608       CheckExit(x, y);
8609     else if (element == EL_SP_EXIT_CLOSED)
8610       CheckExitSP(x, y);
8611     else if (element == EL_EXPANDABLE_WALL_GROWING)
8612       MauerWaechst(x, y);
8613     else if (element == EL_EXPANDABLE_WALL ||
8614              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
8615              element == EL_EXPANDABLE_WALL_VERTICAL ||
8616              element == EL_EXPANDABLE_WALL_ANY)
8617       MauerAbleger(x, y);
8618     else if (element == EL_FLAMES)
8619       CheckForDragon(x, y);
8620 #if 0
8621     else if (IS_AUTO_CHANGING(element))
8622       ChangeElement(x, y);
8623 #endif
8624     else if (element == EL_EXPLOSION)
8625       ; /* drawing of correct explosion animation is handled separately */
8626     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
8627       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8628
8629 #if 0
8630     /* this may take place after moving, so 'element' may have changed */
8631     if (IS_AUTO_CHANGING(Feld[x][y]))
8632       ChangeElement(x, y);
8633 #endif
8634
8635     if (IS_BELT_ACTIVE(element))
8636       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
8637
8638     if (game.magic_wall_active)
8639     {
8640       int jx = local_player->jx, jy = local_player->jy;
8641
8642       /* play the element sound at the position nearest to the player */
8643       if ((element == EL_MAGIC_WALL_FULL ||
8644            element == EL_MAGIC_WALL_ACTIVE ||
8645            element == EL_MAGIC_WALL_EMPTYING ||
8646            element == EL_BD_MAGIC_WALL_FULL ||
8647            element == EL_BD_MAGIC_WALL_ACTIVE ||
8648            element == EL_BD_MAGIC_WALL_EMPTYING) &&
8649           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
8650       {
8651         magic_wall_x = x;
8652         magic_wall_y = y;
8653       }
8654     }
8655   }
8656
8657 #if USE_NEW_AMOEBA_CODE
8658   /* new experimental amoeba growth stuff */
8659 #if 1
8660   if (!(FrameCounter % 8))
8661 #endif
8662   {
8663     static unsigned long random = 1684108901;
8664
8665     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
8666     {
8667 #if 0
8668       x = (random >> 10) % lev_fieldx;
8669       y = (random >> 20) % lev_fieldy;
8670 #else
8671       x = RND(lev_fieldx);
8672       y = RND(lev_fieldy);
8673 #endif
8674       element = Feld[x][y];
8675
8676 #if 1
8677       if (!IS_PLAYER(x,y) &&
8678           (element == EL_EMPTY ||
8679            CAN_GROW_INTO(element) ||
8680            element == EL_QUICKSAND_EMPTY ||
8681            element == EL_ACID_SPLASH_LEFT ||
8682            element == EL_ACID_SPLASH_RIGHT))
8683       {
8684         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8685             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8686             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8687             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8688           Feld[x][y] = EL_AMOEBA_DROP;
8689       }
8690 #else
8691       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
8692       if (!IS_PLAYER(x,y) &&
8693           (element == EL_EMPTY ||
8694            element == EL_SAND ||
8695            element == EL_QUICKSAND_EMPTY ||
8696            element == EL_ACID_SPLASH_LEFT ||
8697            element == EL_ACID_SPLASH_RIGHT))
8698       {
8699         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
8700             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
8701             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
8702             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
8703           Feld[x][y] = EL_AMOEBA_DROP;
8704       }
8705 #endif
8706
8707       random = random * 129 + 1;
8708     }
8709   }
8710 #endif
8711
8712 #if 0
8713   if (game.explosions_delayed)
8714 #endif
8715   {
8716     game.explosions_delayed = FALSE;
8717
8718     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8719     {
8720       element = Feld[x][y];
8721
8722       if (ExplodeField[x][y])
8723         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
8724       else if (element == EL_EXPLOSION)
8725         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
8726
8727       ExplodeField[x][y] = EX_TYPE_NONE;
8728     }
8729
8730     game.explosions_delayed = TRUE;
8731   }
8732
8733   if (game.magic_wall_active)
8734   {
8735     if (!(game.magic_wall_time_left % 4))
8736     {
8737       int element = Feld[magic_wall_x][magic_wall_y];
8738
8739       if (element == EL_BD_MAGIC_WALL_FULL ||
8740           element == EL_BD_MAGIC_WALL_ACTIVE ||
8741           element == EL_BD_MAGIC_WALL_EMPTYING)
8742         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
8743       else
8744         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
8745     }
8746
8747     if (game.magic_wall_time_left > 0)
8748     {
8749       game.magic_wall_time_left--;
8750       if (!game.magic_wall_time_left)
8751       {
8752         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8753         {
8754           element = Feld[x][y];
8755
8756           if (element == EL_MAGIC_WALL_ACTIVE ||
8757               element == EL_MAGIC_WALL_FULL)
8758           {
8759             Feld[x][y] = EL_MAGIC_WALL_DEAD;
8760             DrawLevelField(x, y);
8761           }
8762           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
8763                    element == EL_BD_MAGIC_WALL_FULL)
8764           {
8765             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8766             DrawLevelField(x, y);
8767           }
8768         }
8769
8770         game.magic_wall_active = FALSE;
8771       }
8772     }
8773   }
8774
8775   if (game.light_time_left > 0)
8776   {
8777     game.light_time_left--;
8778
8779     if (game.light_time_left == 0)
8780       RedrawAllLightSwitchesAndInvisibleElements();
8781   }
8782
8783   if (game.timegate_time_left > 0)
8784   {
8785     game.timegate_time_left--;
8786
8787     if (game.timegate_time_left == 0)
8788       CloseAllOpenTimegates();
8789   }
8790
8791   for (i = 0; i < MAX_PLAYERS; i++)
8792   {
8793     struct PlayerInfo *player = &stored_player[i];
8794
8795     if (SHIELD_ON(player))
8796     {
8797       if (player->shield_deadly_time_left)
8798         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
8799       else if (player->shield_normal_time_left)
8800         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
8801     }
8802   }
8803
8804   if (TimeFrames >= FRAMES_PER_SECOND)
8805   {
8806     TimeFrames = 0;
8807     TapeTime++;
8808
8809     for (i = 0; i < MAX_PLAYERS; i++)
8810     {
8811       struct PlayerInfo *player = &stored_player[i];
8812
8813       if (SHIELD_ON(player))
8814       {
8815         player->shield_normal_time_left--;
8816
8817         if (player->shield_deadly_time_left > 0)
8818           player->shield_deadly_time_left--;
8819       }
8820     }
8821
8822     if (!level.use_step_counter)
8823     {
8824       TimePlayed++;
8825
8826       if (TimeLeft > 0)
8827       {
8828         TimeLeft--;
8829
8830         if (TimeLeft <= 10 && setup.time_limit)
8831           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
8832
8833         DrawGameValue_Time(TimeLeft);
8834
8835         if (!TimeLeft && setup.time_limit)
8836           for (i = 0; i < MAX_PLAYERS; i++)
8837             KillHero(&stored_player[i]);
8838       }
8839       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
8840         DrawGameValue_Time(TimePlayed);
8841     }
8842
8843     if (tape.recording || tape.playing)
8844       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
8845   }
8846
8847   DrawAllPlayers();
8848   PlayAllPlayersSound();
8849
8850   if (options.debug)                    /* calculate frames per second */
8851   {
8852     static unsigned long fps_counter = 0;
8853     static int fps_frames = 0;
8854     unsigned long fps_delay_ms = Counter() - fps_counter;
8855
8856     fps_frames++;
8857
8858     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
8859     {
8860       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
8861
8862       fps_frames = 0;
8863       fps_counter = Counter();
8864     }
8865
8866     redraw_mask |= REDRAW_FPS;
8867   }
8868
8869 #if 0
8870   if (stored_player[0].jx != stored_player[0].last_jx ||
8871       stored_player[0].jy != stored_player[0].last_jy)
8872     printf("::: %d, %d, %d, %d, %d\n",
8873            stored_player[0].MovDir,
8874            stored_player[0].MovPos,
8875            stored_player[0].GfxPos,
8876            stored_player[0].Frame,
8877            stored_player[0].StepFrame);
8878 #endif
8879
8880 #if 1
8881   FrameCounter++;
8882   TimeFrames++;
8883
8884   for (i = 0; i < MAX_PLAYERS; i++)
8885   {
8886     int move_frames =
8887       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
8888
8889     stored_player[i].Frame += move_frames;
8890
8891     if (stored_player[i].MovPos != 0)
8892       stored_player[i].StepFrame += move_frames;
8893
8894     if (stored_player[i].drop_delay > 0)
8895       stored_player[i].drop_delay--;
8896   }
8897 #endif
8898
8899 #if 1
8900   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
8901   {
8902     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
8903
8904     local_player->show_envelope = 0;
8905   }
8906 #endif
8907 }
8908
8909 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
8910 {
8911   int min_x = x, min_y = y, max_x = x, max_y = y;
8912   int i;
8913
8914   for (i = 0; i < MAX_PLAYERS; i++)
8915   {
8916     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8917
8918     if (!stored_player[i].active || &stored_player[i] == player)
8919       continue;
8920
8921     min_x = MIN(min_x, jx);
8922     min_y = MIN(min_y, jy);
8923     max_x = MAX(max_x, jx);
8924     max_y = MAX(max_y, jy);
8925   }
8926
8927   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
8928 }
8929
8930 static boolean AllPlayersInVisibleScreen()
8931 {
8932   int i;
8933
8934   for (i = 0; i < MAX_PLAYERS; i++)
8935   {
8936     int jx = stored_player[i].jx, jy = stored_player[i].jy;
8937
8938     if (!stored_player[i].active)
8939       continue;
8940
8941     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
8942       return FALSE;
8943   }
8944
8945   return TRUE;
8946 }
8947
8948 void ScrollLevel(int dx, int dy)
8949 {
8950   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
8951   int x, y;
8952
8953   BlitBitmap(drawto_field, drawto_field,
8954              FX + TILEX * (dx == -1) - softscroll_offset,
8955              FY + TILEY * (dy == -1) - softscroll_offset,
8956              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
8957              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
8958              FX + TILEX * (dx == 1) - softscroll_offset,
8959              FY + TILEY * (dy == 1) - softscroll_offset);
8960
8961   if (dx)
8962   {
8963     x = (dx == 1 ? BX1 : BX2);
8964     for (y = BY1; y <= BY2; y++)
8965       DrawScreenField(x, y);
8966   }
8967
8968   if (dy)
8969   {
8970     y = (dy == 1 ? BY1 : BY2);
8971     for (x = BX1; x <= BX2; x++)
8972       DrawScreenField(x, y);
8973   }
8974
8975   redraw_mask |= REDRAW_FIELD;
8976 }
8977
8978 #if 0
8979 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
8980 {
8981   int nextx = x + dx, nexty = y + dy;
8982   int element = Feld[x][y];
8983
8984   if ((dx == -1 &&
8985        element != EL_SP_PORT_LEFT &&
8986        element != EL_SP_GRAVITY_PORT_LEFT &&
8987        element != EL_SP_PORT_HORIZONTAL &&
8988        element != EL_SP_PORT_ANY) ||
8989       (dx == +1 &&
8990        element != EL_SP_PORT_RIGHT &&
8991        element != EL_SP_GRAVITY_PORT_RIGHT &&
8992        element != EL_SP_PORT_HORIZONTAL &&
8993        element != EL_SP_PORT_ANY) ||
8994       (dy == -1 &&
8995        element != EL_SP_PORT_UP &&
8996        element != EL_SP_GRAVITY_PORT_UP &&
8997        element != EL_SP_PORT_VERTICAL &&
8998        element != EL_SP_PORT_ANY) ||
8999       (dy == +1 &&
9000        element != EL_SP_PORT_DOWN &&
9001        element != EL_SP_GRAVITY_PORT_DOWN &&
9002        element != EL_SP_PORT_VERTICAL &&
9003        element != EL_SP_PORT_ANY) ||
9004       !IN_LEV_FIELD(nextx, nexty) ||
9005       !IS_FREE(nextx, nexty))
9006     return FALSE;
9007
9008   return TRUE;
9009 }
9010 #endif
9011
9012 static boolean canFallDown(struct PlayerInfo *player)
9013 {
9014   int jx = player->jx, jy = player->jy;
9015
9016   return (IN_LEV_FIELD(jx, jy + 1) &&
9017           (IS_FREE(jx, jy + 1) ||
9018            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
9019           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
9020           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
9021 }
9022
9023 static boolean canPassField(int x, int y, int move_dir)
9024 {
9025   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9026   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9027   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9028   int nextx = x + dx;
9029   int nexty = y + dy;
9030   int element = Feld[x][y];
9031
9032   return (IS_PASSABLE_FROM(element, opposite_dir) &&
9033           !CAN_MOVE(element) &&
9034           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9035           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9036           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
9037 }
9038
9039 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
9040 {
9041   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9042   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9043   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
9044   int newx = x + dx;
9045   int newy = y + dy;
9046 #if 0
9047   int nextx = newx + dx;
9048   int nexty = newy + dy;
9049 #endif
9050
9051 #if 1
9052   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9053           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
9054           (IS_DIGGABLE(Feld[newx][newy]) ||
9055            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9056            canPassField(newx, newy, move_dir)));
9057 #else
9058 #if 1
9059   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9060           (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
9061            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9062            canPassField(newx, newy, move_dir)));
9063 #else
9064   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
9065           (IS_DIGGABLE(Feld[newx][newy]) ||
9066            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
9067            (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
9068             !CAN_MOVE(Feld[newx][newy]) &&
9069             IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
9070             IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
9071             (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
9072 #endif
9073 #endif
9074 }
9075
9076 static void CheckGravityMovement(struct PlayerInfo *player)
9077 {
9078   if (game.gravity && !player->programmed_action)
9079   {
9080 #if 1
9081     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
9082     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
9083 #else
9084     int move_dir_horizontal = player->action & MV_HORIZONTAL;
9085     int move_dir_vertical   = player->action & MV_VERTICAL;
9086 #endif
9087
9088 #if 1
9089     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
9090 #else
9091     boolean player_is_snapping = player->action & JOY_BUTTON_1;
9092 #endif
9093
9094     int jx = player->jx, jy = player->jy;
9095
9096     boolean player_is_moving_to_valid_field =
9097       (!player_is_snapping &&
9098        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
9099         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
9100
9101 #if 0
9102     int move_dir =
9103       (player->last_move_dir & MV_HORIZONTAL ?
9104        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
9105        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
9106 #endif
9107
9108 #if 0
9109     int opposite_dir = MV_DIR_OPPOSITE(move_dir);
9110     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
9111     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
9112     int new_jx = jx + dx, new_jy = jy + dy;
9113     int nextx = new_jx + dx, nexty = new_jy + dy;
9114 #endif
9115
9116 #if 1
9117
9118 #if 1
9119     boolean player_can_fall_down = canFallDown(player);
9120 #else
9121     boolean player_can_fall_down =
9122       (IN_LEV_FIELD(jx, jy + 1) &&
9123        (IS_FREE(jx, jy + 1) ||
9124         (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
9125 #endif
9126
9127 #else
9128     boolean player_can_fall_down =
9129       (IN_LEV_FIELD(jx, jy + 1) &&
9130        (IS_FREE(jx, jy + 1)));
9131 #endif
9132
9133 #if 0
9134     boolean player_is_moving_to_valid_field =
9135       (
9136 #if 1
9137        !player_is_snapping &&
9138 #endif
9139
9140 #if 1
9141        IN_LEV_FIELD(new_jx, new_jy) &&
9142        (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
9143         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9144          element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
9145          IN_LEV_FIELD(nextx, nexty) &&
9146          element_info[Feld[nextx][nexty]].access_direction & move_dir))
9147 #else
9148        IN_LEV_FIELD(new_jx, new_jy) &&
9149        (Feld[new_jx][new_jy] == EL_SP_BASE ||
9150         Feld[new_jx][new_jy] == EL_SAND ||
9151         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
9152          canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
9153     /* !!! extend EL_SAND to anything diggable !!! */
9154 #endif
9155        );
9156 #endif
9157
9158 #if 0
9159     boolean player_is_standing_on_valid_field =
9160       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9161        (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
9162 #endif
9163
9164 #if 0
9165     printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
9166            player_can_fall_down,
9167            player_is_standing_on_valid_field,
9168            player_is_moving_to_valid_field,
9169            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
9170            player->effective_action,
9171            player->can_fall_into_acid);
9172 #endif
9173
9174     if (player_can_fall_down &&
9175 #if 0
9176         !player_is_standing_on_valid_field &&
9177 #endif
9178         !player_is_moving_to_valid_field)
9179     {
9180 #if 0
9181       printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
9182              jx, jy, FrameCounter);
9183 #endif
9184
9185       player->programmed_action = MV_DOWN;
9186     }
9187   }
9188 }
9189
9190 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
9191 {
9192 #if 1
9193   return CheckGravityMovement(player);
9194 #endif
9195
9196   if (game.gravity && !player->programmed_action)
9197   {
9198     int jx = player->jx, jy = player->jy;
9199     boolean field_under_player_is_free =
9200       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
9201     boolean player_is_standing_on_valid_field =
9202       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
9203        (IS_WALKABLE(Feld[jx][jy]) &&
9204         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
9205
9206     if (field_under_player_is_free && !player_is_standing_on_valid_field)
9207       player->programmed_action = MV_DOWN;
9208   }
9209 }
9210
9211 /*
9212   MovePlayerOneStep()
9213   -----------------------------------------------------------------------------
9214   dx, dy:               direction (non-diagonal) to try to move the player to
9215   real_dx, real_dy:     direction as read from input device (can be diagonal)
9216 */
9217
9218 boolean MovePlayerOneStep(struct PlayerInfo *player,
9219                           int dx, int dy, int real_dx, int real_dy)
9220 {
9221 #if 0
9222   static int trigger_sides[4][2] =
9223   {
9224     /* enter side        leave side */
9225     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
9226     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
9227     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
9228     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
9229   };
9230   int move_direction = (dx == -1 ? MV_LEFT :
9231                         dx == +1 ? MV_RIGHT :
9232                         dy == -1 ? MV_UP :
9233                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9234   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9235   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9236 #endif
9237   int jx = player->jx, jy = player->jy;
9238   int new_jx = jx + dx, new_jy = jy + dy;
9239   int element;
9240   int can_move;
9241
9242   if (!player->active || (!dx && !dy))
9243     return MF_NO_ACTION;
9244
9245   player->MovDir = (dx < 0 ? MV_LEFT :
9246                     dx > 0 ? MV_RIGHT :
9247                     dy < 0 ? MV_UP :
9248                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
9249
9250   if (!IN_LEV_FIELD(new_jx, new_jy))
9251     return MF_NO_ACTION;
9252
9253   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
9254     return MF_NO_ACTION;
9255
9256 #if 0
9257   element = MovingOrBlocked2Element(new_jx, new_jy);
9258 #else
9259   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
9260 #endif
9261
9262   if (DONT_RUN_INTO(element))
9263   {
9264     if (element == EL_ACID && dx == 0 && dy == 1)
9265     {
9266       SplashAcid(new_jx, new_jy);
9267       Feld[jx][jy] = EL_PLAYER_1;
9268       InitMovingField(jx, jy, MV_DOWN);
9269       Store[jx][jy] = EL_ACID;
9270       ContinueMoving(jx, jy);
9271       BuryHero(player);
9272     }
9273     else
9274       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
9275
9276     return MF_MOVING;
9277   }
9278
9279   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
9280   if (can_move != MF_MOVING)
9281     return can_move;
9282
9283   /* check if DigField() has caused relocation of the player */
9284   if (player->jx != jx || player->jy != jy)
9285     return MF_NO_ACTION;
9286
9287   StorePlayer[jx][jy] = 0;
9288   player->last_jx = jx;
9289   player->last_jy = jy;
9290   player->jx = new_jx;
9291   player->jy = new_jy;
9292   StorePlayer[new_jx][new_jy] = player->element_nr;
9293
9294   player->MovPos =
9295     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
9296
9297   player->step_counter++;
9298
9299 #if 0
9300   player->drop_delay = 0;
9301 #endif
9302
9303   PlayerVisit[jx][jy] = FrameCounter;
9304
9305   ScrollPlayer(player, SCROLL_INIT);
9306
9307 #if 0
9308   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
9309   {
9310     CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
9311                                       leave_side);
9312     CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
9313   }
9314
9315   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
9316   {
9317     CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9318                                       CE_OTHER_GETS_ENTERED, enter_side);
9319     CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
9320                              CE_ENTERED_BY_PLAYER, enter_side);
9321   }
9322 #endif
9323
9324   return MF_MOVING;
9325 }
9326
9327 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
9328 {
9329   int jx = player->jx, jy = player->jy;
9330   int old_jx = jx, old_jy = jy;
9331   int moved = MF_NO_ACTION;
9332
9333 #if 1
9334   if (!player->active)
9335     return FALSE;
9336
9337   if (!dx && !dy)
9338   {
9339     if (player->MovPos == 0)
9340     {
9341       player->is_moving = FALSE;
9342       player->is_digging = FALSE;
9343       player->is_collecting = FALSE;
9344       player->is_snapping = FALSE;
9345       player->is_pushing = FALSE;
9346     }
9347
9348     return FALSE;
9349   }
9350 #else
9351   if (!player->active || (!dx && !dy))
9352     return FALSE;
9353 #endif
9354
9355 #if 0
9356   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9357       !tape.playing)
9358     return FALSE;
9359 #else
9360
9361 #if 1
9362   if (!FrameReached(&player->move_delay, player->move_delay_value))
9363     return FALSE;
9364 #else
9365   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
9366       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
9367     return FALSE;
9368 #endif
9369
9370 #endif
9371
9372   /* store if player is automatically moved to next field */
9373   player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
9374
9375   /* remove the last programmed player action */
9376   player->programmed_action = 0;
9377
9378   if (player->MovPos)
9379   {
9380     /* should only happen if pre-1.2 tape recordings are played */
9381     /* this is only for backward compatibility */
9382
9383     int original_move_delay_value = player->move_delay_value;
9384
9385 #if DEBUG
9386     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
9387            tape.counter);
9388 #endif
9389
9390     /* scroll remaining steps with finest movement resolution */
9391     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
9392
9393     while (player->MovPos)
9394     {
9395       ScrollPlayer(player, SCROLL_GO_ON);
9396       ScrollScreen(NULL, SCROLL_GO_ON);
9397       FrameCounter++;
9398       DrawAllPlayers();
9399       BackToFront();
9400     }
9401
9402     player->move_delay_value = original_move_delay_value;
9403   }
9404
9405   if (player->last_move_dir & MV_HORIZONTAL)
9406   {
9407     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
9408       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
9409   }
9410   else
9411   {
9412     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
9413       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
9414   }
9415
9416   jx = player->jx;
9417   jy = player->jy;
9418
9419   if (moved & MF_MOVING && !ScreenMovPos &&
9420       (player == local_player || !options.network))
9421   {
9422     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
9423     int offset = (setup.scroll_delay ? 3 : 0);
9424
9425     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9426     {
9427       /* actual player has left the screen -- scroll in that direction */
9428       if (jx != old_jx)         /* player has moved horizontally */
9429         scroll_x += (jx - old_jx);
9430       else                      /* player has moved vertically */
9431         scroll_y += (jy - old_jy);
9432     }
9433     else
9434     {
9435       if (jx != old_jx)         /* player has moved horizontally */
9436       {
9437         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
9438             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
9439           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
9440
9441         /* don't scroll over playfield boundaries */
9442         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
9443           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
9444
9445         /* don't scroll more than one field at a time */
9446         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
9447
9448         /* don't scroll against the player's moving direction */
9449         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
9450             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
9451           scroll_x = old_scroll_x;
9452       }
9453       else                      /* player has moved vertically */
9454       {
9455         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
9456             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
9457           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
9458
9459         /* don't scroll over playfield boundaries */
9460         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
9461           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
9462
9463         /* don't scroll more than one field at a time */
9464         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
9465
9466         /* don't scroll against the player's moving direction */
9467         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
9468             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
9469           scroll_y = old_scroll_y;
9470       }
9471     }
9472
9473     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
9474     {
9475       if (!options.network && !AllPlayersInVisibleScreen())
9476       {
9477         scroll_x = old_scroll_x;
9478         scroll_y = old_scroll_y;
9479       }
9480       else
9481       {
9482         ScrollScreen(player, SCROLL_INIT);
9483         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
9484       }
9485     }
9486   }
9487
9488 #if 0
9489 #if 1
9490   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
9491 #else
9492   if (!(moved & MF_MOVING) && !player->is_pushing)
9493     player->Frame = 0;
9494 #endif
9495 #endif
9496
9497   player->StepFrame = 0;
9498
9499   if (moved & MF_MOVING)
9500   {
9501     if (old_jx != jx && old_jy == jy)
9502       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
9503     else if (old_jx == jx && old_jy != jy)
9504       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
9505
9506     DrawLevelField(jx, jy);     /* for "crumbled sand" */
9507
9508     player->last_move_dir = player->MovDir;
9509     player->is_moving = TRUE;
9510 #if 1
9511     player->is_snapping = FALSE;
9512 #endif
9513
9514 #if 1
9515     player->is_switching = FALSE;
9516 #endif
9517
9518     player->is_dropping = FALSE;
9519
9520
9521 #if 0
9522     /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
9523
9524 #if 1
9525     if (game.engine_version < VERSION_IDENT(3,1,0,0))
9526 #endif
9527     {
9528       static int trigger_sides[4][2] =
9529       {
9530         /* enter side           leave side */
9531         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
9532         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
9533         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
9534         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
9535       };
9536       int move_direction = player->MovDir;
9537       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9538       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9539       int old_element = Feld[old_jx][old_jy];
9540       int new_element = Feld[jx][jy];
9541
9542 #if 1
9543       /* !!! TEST ONLY !!! */
9544       if (IS_CUSTOM_ELEMENT(old_element))
9545         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9546                                    CE_LEFT_BY_PLAYER,
9547                                    player->index_bit, leave_side);
9548
9549       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9550                                           CE_OTHER_GETS_LEFT,
9551                                           player->index_bit, leave_side);
9552
9553       if (IS_CUSTOM_ELEMENT(new_element))
9554         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9555                                    player->index_bit, enter_side);
9556
9557       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9558                                           CE_OTHER_GETS_ENTERED,
9559                                           player->index_bit, enter_side);
9560 #endif
9561
9562     }
9563 #endif
9564
9565
9566   }
9567   else
9568   {
9569     CheckGravityMovementWhenNotMoving(player);
9570
9571     /*
9572     player->last_move_dir = MV_NO_MOVING;
9573     */
9574     player->is_moving = FALSE;
9575   }
9576
9577   if (game.engine_version < VERSION_IDENT(3,0,7,0))
9578   {
9579     TestIfHeroTouchesBadThing(jx, jy);
9580     TestIfPlayerTouchesCustomElement(jx, jy);
9581   }
9582
9583   if (!player->active)
9584     RemoveHero(player);
9585
9586   return moved;
9587 }
9588
9589 void ScrollPlayer(struct PlayerInfo *player, int mode)
9590 {
9591   int jx = player->jx, jy = player->jy;
9592   int last_jx = player->last_jx, last_jy = player->last_jy;
9593   int move_stepsize = TILEX / player->move_delay_value;
9594
9595   if (!player->active || !player->MovPos)
9596     return;
9597
9598   if (mode == SCROLL_INIT)
9599   {
9600     player->actual_frame_counter = FrameCounter;
9601     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9602
9603     if (Feld[last_jx][last_jy] == EL_EMPTY)
9604       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
9605
9606 #if 0
9607     DrawPlayer(player);
9608 #endif
9609
9610     return;
9611   }
9612   else if (!FrameReached(&player->actual_frame_counter, 1))
9613     return;
9614
9615   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
9616   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
9617
9618   if (!player->block_last_field &&
9619       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9620     Feld[last_jx][last_jy] = EL_EMPTY;
9621
9622   /* before DrawPlayer() to draw correct player graphic for this case */
9623   if (player->MovPos == 0)
9624     CheckGravityMovement(player);
9625
9626 #if 0
9627   DrawPlayer(player);   /* needed here only to cleanup last field */
9628 #endif
9629
9630   if (player->MovPos == 0)      /* player reached destination field */
9631   {
9632 #if 1
9633     if (player->move_delay_reset_counter > 0)
9634     {
9635       player->move_delay_reset_counter--;
9636
9637       if (player->move_delay_reset_counter == 0)
9638       {
9639         /* continue with normal speed after quickly moving through gate */
9640         HALVE_PLAYER_SPEED(player);
9641
9642         /* be able to make the next move without delay */
9643         player->move_delay = 0;
9644       }
9645     }
9646 #else
9647     if (IS_PASSABLE(Feld[last_jx][last_jy]))
9648     {
9649       /* continue with normal speed after quickly moving through gate */
9650       HALVE_PLAYER_SPEED(player);
9651
9652       /* be able to make the next move without delay */
9653       player->move_delay = 0;
9654     }
9655 #endif
9656
9657     if (player->block_last_field &&
9658         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
9659       Feld[last_jx][last_jy] = EL_EMPTY;
9660
9661     player->last_jx = jx;
9662     player->last_jy = jy;
9663
9664     if (Feld[jx][jy] == EL_EXIT_OPEN ||
9665         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
9666         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
9667     {
9668       DrawPlayer(player);       /* needed here only to cleanup last field */
9669       RemoveHero(player);
9670
9671       if (local_player->friends_still_needed == 0 ||
9672           IS_SP_ELEMENT(Feld[jx][jy]))
9673         player->LevelSolved = player->GameOver = TRUE;
9674     }
9675
9676 #if 1
9677     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
9678     /* this breaks one level: "machine", level 000 */
9679 #if 0
9680     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
9681 #endif
9682     {
9683       static int trigger_sides[4][2] =
9684       {
9685         /* enter side           leave side */
9686         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
9687         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
9688         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
9689         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
9690       };
9691       int move_direction = player->MovDir;
9692       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
9693       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
9694       int old_jx = last_jx;
9695       int old_jy = last_jy;
9696       int old_element = Feld[old_jx][old_jy];
9697       int new_element = Feld[jx][jy];
9698
9699 #if 1
9700       /* !!! TEST ONLY !!! */
9701       if (IS_CUSTOM_ELEMENT(old_element))
9702         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
9703                                    CE_LEFT_BY_PLAYER,
9704                                    player->index_bit, leave_side);
9705
9706       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
9707                                           CE_OTHER_GETS_LEFT,
9708                                           player->index_bit, leave_side);
9709
9710       if (IS_CUSTOM_ELEMENT(new_element))
9711         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
9712                                    player->index_bit, enter_side);
9713
9714       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
9715                                           CE_OTHER_GETS_ENTERED,
9716                                           player->index_bit, enter_side);
9717 #endif
9718
9719     }
9720 #endif
9721
9722     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9723     {
9724       TestIfHeroTouchesBadThing(jx, jy);
9725       TestIfPlayerTouchesCustomElement(jx, jy);
9726 #if 1
9727       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
9728 #endif
9729
9730       if (!player->active)
9731         RemoveHero(player);
9732     }
9733
9734     if (level.use_step_counter)
9735     {
9736       int i;
9737
9738       TimePlayed++;
9739
9740       if (TimeLeft > 0)
9741       {
9742         TimeLeft--;
9743
9744         if (TimeLeft <= 10 && setup.time_limit)
9745           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9746
9747         DrawGameValue_Time(TimeLeft);
9748
9749         if (!TimeLeft && setup.time_limit)
9750           for (i = 0; i < MAX_PLAYERS; i++)
9751             KillHero(&stored_player[i]);
9752       }
9753       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9754         DrawGameValue_Time(TimePlayed);
9755     }
9756
9757     if (tape.single_step && tape.recording && !tape.pausing &&
9758         !player->programmed_action)
9759       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9760   }
9761 }
9762
9763 void ScrollScreen(struct PlayerInfo *player, int mode)
9764 {
9765   static unsigned long screen_frame_counter = 0;
9766
9767   if (mode == SCROLL_INIT)
9768   {
9769     /* set scrolling step size according to actual player's moving speed */
9770     ScrollStepSize = TILEX / player->move_delay_value;
9771
9772     screen_frame_counter = FrameCounter;
9773     ScreenMovDir = player->MovDir;
9774     ScreenMovPos = player->MovPos;
9775     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9776     return;
9777   }
9778   else if (!FrameReached(&screen_frame_counter, 1))
9779     return;
9780
9781   if (ScreenMovPos)
9782   {
9783     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
9784     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
9785     redraw_mask |= REDRAW_FIELD;
9786   }
9787   else
9788     ScreenMovDir = MV_NO_MOVING;
9789 }
9790
9791 void TestIfPlayerTouchesCustomElement(int x, int y)
9792 {
9793   static int xy[4][2] =
9794   {
9795     { 0, -1 },
9796     { -1, 0 },
9797     { +1, 0 },
9798     { 0, +1 }
9799   };
9800   static int trigger_sides[4][2] =
9801   {
9802     /* center side       border side */
9803     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9804     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9805     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9806     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9807   };
9808   static int touch_dir[4] =
9809   {
9810     MV_LEFT | MV_RIGHT,
9811     MV_UP   | MV_DOWN,
9812     MV_UP   | MV_DOWN,
9813     MV_LEFT | MV_RIGHT
9814   };
9815   int center_element = Feld[x][y];      /* should always be non-moving! */
9816   int i;
9817
9818   for (i = 0; i < NUM_DIRECTIONS; i++)
9819   {
9820     int xx = x + xy[i][0];
9821     int yy = y + xy[i][1];
9822     int center_side = trigger_sides[i][0];
9823     int border_side = trigger_sides[i][1];
9824     int border_element;
9825
9826     if (!IN_LEV_FIELD(xx, yy))
9827       continue;
9828
9829     if (IS_PLAYER(x, y))
9830     {
9831       struct PlayerInfo *player = PLAYERINFO(x, y);
9832
9833       if (game.engine_version < VERSION_IDENT(3,0,7,0))
9834         border_element = Feld[xx][yy];          /* may be moving! */
9835       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9836         border_element = Feld[xx][yy];
9837       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
9838         border_element = MovingOrBlocked2Element(xx, yy);
9839       else
9840         continue;               /* center and border element do not touch */
9841
9842 #if 1
9843       /* !!! TEST ONLY !!! */
9844       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9845                                  player->index_bit, border_side);
9846       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9847                                           CE_OTHER_GETS_TOUCHED,
9848                                           player->index_bit, border_side);
9849 #else
9850       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
9851                                           CE_OTHER_GETS_TOUCHED,
9852                                           player->index_bit, border_side);
9853       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
9854                                  player->index_bit, border_side);
9855 #endif
9856     }
9857     else if (IS_PLAYER(xx, yy))
9858     {
9859       struct PlayerInfo *player = PLAYERINFO(xx, yy);
9860
9861       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
9862       {
9863         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
9864           continue;             /* center and border element do not touch */
9865       }
9866
9867 #if 1
9868       /* !!! TEST ONLY !!! */
9869       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9870                                  player->index_bit, center_side);
9871       CheckTriggeredElementChangeByPlayer(x, y, center_element,
9872                                           CE_OTHER_GETS_TOUCHED,
9873                                           player->index_bit, center_side);
9874 #else
9875       CheckTriggeredElementChangeByPlayer(x, y, center_element,
9876                                           CE_OTHER_GETS_TOUCHED,
9877                                           player->index_bit, center_side);
9878       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
9879                                  player->index_bit, center_side);
9880 #endif
9881
9882       break;
9883     }
9884   }
9885 }
9886
9887 void TestIfElementTouchesCustomElement(int x, int y)
9888 {
9889   static int xy[4][2] =
9890   {
9891     { 0, -1 },
9892     { -1, 0 },
9893     { +1, 0 },
9894     { 0, +1 }
9895   };
9896   static int trigger_sides[4][2] =
9897   {
9898     /* center side      border side */
9899     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
9900     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
9901     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
9902     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
9903   };
9904   static int touch_dir[4] =
9905   {
9906     MV_LEFT | MV_RIGHT,
9907     MV_UP   | MV_DOWN,
9908     MV_UP   | MV_DOWN,
9909     MV_LEFT | MV_RIGHT
9910   };
9911   boolean change_center_element = FALSE;
9912   int center_element_change_page = 0;
9913   int center_element = Feld[x][y];      /* should always be non-moving! */
9914   int border_trigger_element;
9915   int i, j;
9916
9917   for (i = 0; i < NUM_DIRECTIONS; i++)
9918   {
9919     int xx = x + xy[i][0];
9920     int yy = y + xy[i][1];
9921     int center_side = trigger_sides[i][0];
9922     int border_side = trigger_sides[i][1];
9923     int border_element;
9924
9925     if (!IN_LEV_FIELD(xx, yy))
9926       continue;
9927
9928     if (game.engine_version < VERSION_IDENT(3,0,7,0))
9929       border_element = Feld[xx][yy];    /* may be moving! */
9930     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
9931       border_element = Feld[xx][yy];
9932     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
9933       border_element = MovingOrBlocked2Element(xx, yy);
9934     else
9935       continue;                 /* center and border element do not touch */
9936
9937     /* check for change of center element (but change it only once) */
9938     if (IS_CUSTOM_ELEMENT(center_element) &&
9939         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
9940         !change_center_element)
9941     {
9942       for (j = 0; j < element_info[center_element].num_change_pages; j++)
9943       {
9944         struct ElementChangeInfo *change =
9945           &element_info[center_element].change_page[j];
9946
9947         if (change->can_change &&
9948             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9949             change->trigger_side & border_side &&
9950 #if 1
9951             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
9952 #else
9953             change->trigger_element == border_element
9954 #endif
9955             )
9956         {
9957           change_center_element = TRUE;
9958           center_element_change_page = j;
9959           border_trigger_element = border_element;
9960
9961           break;
9962         }
9963       }
9964     }
9965
9966     /* check for change of border element */
9967     if (IS_CUSTOM_ELEMENT(border_element) &&
9968         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
9969     {
9970       for (j = 0; j < element_info[border_element].num_change_pages; j++)
9971       {
9972         struct ElementChangeInfo *change =
9973           &element_info[border_element].change_page[j];
9974
9975         if (change->can_change &&
9976             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
9977             change->trigger_side & center_side &&
9978 #if 1
9979             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
9980 #else
9981             change->trigger_element == center_element
9982 #endif
9983             )
9984         {
9985 #if 0
9986           printf("::: border_element %d, %d\n", x, y);
9987 #endif
9988
9989           CheckElementChangeByPage(xx, yy, border_element, center_element,
9990                                    CE_OTHER_IS_TOUCHING, j);
9991           break;
9992         }
9993       }
9994     }
9995   }
9996
9997   if (change_center_element)
9998   {
9999 #if 0
10000     printf("::: center_element %d, %d\n", x, y);
10001 #endif
10002
10003     CheckElementChangeByPage(x, y, center_element, border_trigger_element,
10004                              CE_OTHER_IS_TOUCHING, center_element_change_page);
10005   }
10006 }
10007
10008 void TestIfElementHitsCustomElement(int x, int y, int direction)
10009 {
10010   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10011   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10012   int hitx = x + dx, hity = y + dy;
10013   int hitting_element = Feld[x][y];
10014   int touched_element;
10015 #if 0
10016   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10017                         !IS_FREE(hitx, hity) &&
10018                         (!IS_MOVING(hitx, hity) ||
10019                          MovDir[hitx][hity] != direction ||
10020                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10021 #endif
10022
10023   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10024     return;
10025
10026 #if 0
10027   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10028     return;
10029 #endif
10030
10031   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10032                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10033
10034   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10035                            CE_HITTING_SOMETHING, direction);
10036
10037   if (IN_LEV_FIELD(hitx, hity))
10038   {
10039     int opposite_direction = MV_DIR_OPPOSITE(direction);
10040     int hitting_side = direction;
10041     int touched_side = opposite_direction;
10042 #if 0
10043     int touched_element = MovingOrBlocked2Element(hitx, hity);
10044 #endif
10045 #if 1
10046     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10047                           MovDir[hitx][hity] != direction ||
10048                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10049
10050     object_hit = TRUE;
10051 #endif
10052
10053     if (object_hit)
10054     {
10055       int i;
10056
10057       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10058                                CE_HIT_BY_SOMETHING, opposite_direction);
10059
10060       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10061           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
10062       {
10063         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10064         {
10065           struct ElementChangeInfo *change =
10066             &element_info[hitting_element].change_page[i];
10067
10068           if (change->can_change &&
10069               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
10070               change->trigger_side & touched_side &&
10071           
10072 #if 1
10073               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10074 #else
10075               change->trigger_element == touched_element
10076 #endif
10077               )
10078           {
10079             CheckElementChangeByPage(x, y, hitting_element, touched_element,
10080                                      CE_OTHER_IS_HITTING, i);
10081             break;
10082           }
10083         }
10084       }
10085
10086       if (IS_CUSTOM_ELEMENT(touched_element) &&
10087           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
10088       {
10089         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10090         {
10091           struct ElementChangeInfo *change =
10092             &element_info[touched_element].change_page[i];
10093
10094           if (change->can_change &&
10095               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
10096               change->trigger_side & hitting_side &&
10097 #if 1
10098               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10099 #else
10100               change->trigger_element == hitting_element
10101 #endif
10102               )
10103           {
10104             CheckElementChangeByPage(hitx, hity, touched_element,
10105                                      hitting_element, CE_OTHER_GETS_HIT, i);
10106             break;
10107           }
10108         }
10109       }
10110     }
10111   }
10112 }
10113
10114 #if 0
10115 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10116 {
10117   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10118   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10119   int hitx = x + dx, hity = y + dy;
10120   int hitting_element = Feld[x][y];
10121   int touched_element;
10122 #if 0
10123   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10124                         !IS_FREE(hitx, hity) &&
10125                         (!IS_MOVING(hitx, hity) ||
10126                          MovDir[hitx][hity] != direction ||
10127                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10128 #endif
10129
10130   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10131     return;
10132
10133 #if 0
10134   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10135     return;
10136 #endif
10137
10138   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10139                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10140
10141   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10142                            EP_CAN_SMASH_EVERYTHING, direction);
10143
10144   if (IN_LEV_FIELD(hitx, hity))
10145   {
10146     int opposite_direction = MV_DIR_OPPOSITE(direction);
10147     int hitting_side = direction;
10148     int touched_side = opposite_direction;
10149 #if 0
10150     int touched_element = MovingOrBlocked2Element(hitx, hity);
10151 #endif
10152 #if 1
10153     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10154                           MovDir[hitx][hity] != direction ||
10155                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10156
10157     object_hit = TRUE;
10158 #endif
10159
10160     if (object_hit)
10161     {
10162       int i;
10163
10164       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10165                                CE_SMASHED_BY_SOMETHING, opposite_direction);
10166
10167       if (IS_CUSTOM_ELEMENT(hitting_element) &&
10168           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
10169       {
10170         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
10171         {
10172           struct ElementChangeInfo *change =
10173             &element_info[hitting_element].change_page[i];
10174
10175           if (change->can_change &&
10176               change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
10177               change->trigger_side & touched_side &&
10178           
10179 #if 1
10180               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
10181 #else
10182               change->trigger_element == touched_element
10183 #endif
10184               )
10185           {
10186             CheckElementChangeByPage(x, y, hitting_element, touched_element,
10187                                      CE_OTHER_IS_SMASHING, i);
10188             break;
10189           }
10190         }
10191       }
10192
10193       if (IS_CUSTOM_ELEMENT(touched_element) &&
10194           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
10195       {
10196         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
10197         {
10198           struct ElementChangeInfo *change =
10199             &element_info[touched_element].change_page[i];
10200
10201           if (change->can_change &&
10202               change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
10203               change->trigger_side & hitting_side &&
10204 #if 1
10205               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
10206 #else
10207               change->trigger_element == hitting_element
10208 #endif
10209               )
10210           {
10211             CheckElementChangeByPage(hitx, hity, touched_element,
10212                                      hitting_element, CE_OTHER_GETS_SMASHED,i);
10213             break;
10214           }
10215         }
10216       }
10217     }
10218   }
10219 }
10220 #endif
10221
10222 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10223 {
10224   int i, kill_x = -1, kill_y = -1;
10225   int bad_element = -1;
10226   static int test_xy[4][2] =
10227   {
10228     { 0, -1 },
10229     { -1, 0 },
10230     { +1, 0 },
10231     { 0, +1 }
10232   };
10233   static int test_dir[4] =
10234   {
10235     MV_UP,
10236     MV_LEFT,
10237     MV_RIGHT,
10238     MV_DOWN
10239   };
10240
10241   for (i = 0; i < NUM_DIRECTIONS; i++)
10242   {
10243     int test_x, test_y, test_move_dir, test_element;
10244
10245     test_x = good_x + test_xy[i][0];
10246     test_y = good_y + test_xy[i][1];
10247
10248     if (!IN_LEV_FIELD(test_x, test_y))
10249       continue;
10250
10251     test_move_dir =
10252       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10253
10254 #if 0
10255     test_element = Feld[test_x][test_y];
10256 #else
10257     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10258 #endif
10259
10260     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10261        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10262     */
10263     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
10264         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
10265     {
10266       kill_x = test_x;
10267       kill_y = test_y;
10268       bad_element = test_element;
10269
10270       break;
10271     }
10272   }
10273
10274   if (kill_x != -1 || kill_y != -1)
10275   {
10276     if (IS_PLAYER(good_x, good_y))
10277     {
10278       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
10279
10280 #if 1
10281       if (player->shield_deadly_time_left > 0 &&
10282           !IS_INDESTRUCTIBLE(bad_element))
10283         Bang(kill_x, kill_y);
10284       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10285         KillHero(player);
10286 #else
10287       if (player->shield_deadly_time_left > 0)
10288         Bang(kill_x, kill_y);
10289       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
10290         KillHero(player);
10291 #endif
10292     }
10293     else
10294       Bang(good_x, good_y);
10295   }
10296 }
10297
10298 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
10299 {
10300   int i, kill_x = -1, kill_y = -1;
10301   int bad_element = Feld[bad_x][bad_y];
10302   static int test_xy[4][2] =
10303   {
10304     { 0, -1 },
10305     { -1, 0 },
10306     { +1, 0 },
10307     { 0, +1 }
10308   };
10309   static int touch_dir[4] =
10310   {
10311     MV_LEFT | MV_RIGHT,
10312     MV_UP   | MV_DOWN,
10313     MV_UP   | MV_DOWN,
10314     MV_LEFT | MV_RIGHT
10315   };
10316   static int test_dir[4] =
10317   {
10318     MV_UP,
10319     MV_LEFT,
10320     MV_RIGHT,
10321     MV_DOWN
10322   };
10323
10324   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
10325     return;
10326
10327   for (i = 0; i < NUM_DIRECTIONS; i++)
10328   {
10329     int test_x, test_y, test_move_dir, test_element;
10330
10331     test_x = bad_x + test_xy[i][0];
10332     test_y = bad_y + test_xy[i][1];
10333     if (!IN_LEV_FIELD(test_x, test_y))
10334       continue;
10335
10336     test_move_dir =
10337       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
10338
10339     test_element = Feld[test_x][test_y];
10340
10341     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10342        2nd case: DONT_TOUCH style bad thing does not move away from good thing
10343     */
10344     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
10345         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
10346     {
10347       /* good thing is player or penguin that does not move away */
10348       if (IS_PLAYER(test_x, test_y))
10349       {
10350         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
10351
10352         if (bad_element == EL_ROBOT && player->is_moving)
10353           continue;     /* robot does not kill player if he is moving */
10354
10355         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10356         {
10357           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10358             continue;           /* center and border element do not touch */
10359         }
10360
10361         kill_x = test_x;
10362         kill_y = test_y;
10363         break;
10364       }
10365       else if (test_element == EL_PENGUIN)
10366       {
10367         kill_x = test_x;
10368         kill_y = test_y;
10369         break;
10370       }
10371     }
10372   }
10373
10374   if (kill_x != -1 || kill_y != -1)
10375   {
10376     if (IS_PLAYER(kill_x, kill_y))
10377     {
10378       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
10379
10380 #if 1
10381       if (player->shield_deadly_time_left > 0 &&
10382           !IS_INDESTRUCTIBLE(bad_element))
10383         Bang(bad_x, bad_y);
10384       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10385         KillHero(player);
10386 #else
10387       if (player->shield_deadly_time_left > 0)
10388         Bang(bad_x, bad_y);
10389       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
10390         KillHero(player);
10391 #endif
10392     }
10393     else
10394       Bang(kill_x, kill_y);
10395   }
10396 }
10397
10398 void TestIfHeroTouchesBadThing(int x, int y)
10399 {
10400   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10401 }
10402
10403 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
10404 {
10405   TestIfGoodThingHitsBadThing(x, y, move_dir);
10406 }
10407
10408 void TestIfBadThingTouchesHero(int x, int y)
10409 {
10410   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10411 }
10412
10413 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
10414 {
10415   TestIfBadThingHitsGoodThing(x, y, move_dir);
10416 }
10417
10418 void TestIfFriendTouchesBadThing(int x, int y)
10419 {
10420   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
10421 }
10422
10423 void TestIfBadThingTouchesFriend(int x, int y)
10424 {
10425   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
10426 }
10427
10428 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
10429 {
10430   int i, kill_x = bad_x, kill_y = bad_y;
10431   static int xy[4][2] =
10432   {
10433     { 0, -1 },
10434     { -1, 0 },
10435     { +1, 0 },
10436     { 0, +1 }
10437   };
10438
10439   for (i = 0; i < NUM_DIRECTIONS; i++)
10440   {
10441     int x, y, element;
10442
10443     x = bad_x + xy[i][0];
10444     y = bad_y + xy[i][1];
10445     if (!IN_LEV_FIELD(x, y))
10446       continue;
10447
10448     element = Feld[x][y];
10449     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
10450         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
10451     {
10452       kill_x = x;
10453       kill_y = y;
10454       break;
10455     }
10456   }
10457
10458   if (kill_x != bad_x || kill_y != bad_y)
10459     Bang(bad_x, bad_y);
10460 }
10461
10462 void KillHero(struct PlayerInfo *player)
10463 {
10464   int jx = player->jx, jy = player->jy;
10465
10466   if (!player->active)
10467     return;
10468
10469   /* remove accessible field at the player's position */
10470   Feld[jx][jy] = EL_EMPTY;
10471
10472   /* deactivate shield (else Bang()/Explode() would not work right) */
10473   player->shield_normal_time_left = 0;
10474   player->shield_deadly_time_left = 0;
10475
10476   Bang(jx, jy);
10477   BuryHero(player);
10478 }
10479
10480 static void KillHeroUnlessEnemyProtected(int x, int y)
10481 {
10482   if (!PLAYER_ENEMY_PROTECTED(x, y))
10483     KillHero(PLAYERINFO(x, y));
10484 }
10485
10486 static void KillHeroUnlessExplosionProtected(int x, int y)
10487 {
10488   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
10489     KillHero(PLAYERINFO(x, y));
10490 }
10491
10492 void BuryHero(struct PlayerInfo *player)
10493 {
10494   int jx = player->jx, jy = player->jy;
10495
10496   if (!player->active)
10497     return;
10498
10499 #if 1
10500   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
10501 #else
10502   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
10503 #endif
10504   PlayLevelSound(jx, jy, SND_GAME_LOSING);
10505
10506   player->GameOver = TRUE;
10507   RemoveHero(player);
10508 }
10509
10510 void RemoveHero(struct PlayerInfo *player)
10511 {
10512   int jx = player->jx, jy = player->jy;
10513   int i, found = FALSE;
10514
10515   player->present = FALSE;
10516   player->active = FALSE;
10517
10518   if (!ExplodeField[jx][jy])
10519     StorePlayer[jx][jy] = 0;
10520
10521   for (i = 0; i < MAX_PLAYERS; i++)
10522     if (stored_player[i].active)
10523       found = TRUE;
10524
10525   if (!found)
10526     AllPlayersGone = TRUE;
10527
10528   ExitX = ZX = jx;
10529   ExitY = ZY = jy;
10530 }
10531
10532 /*
10533   =============================================================================
10534   checkDiagonalPushing()
10535   -----------------------------------------------------------------------------
10536   check if diagonal input device direction results in pushing of object
10537   (by checking if the alternative direction is walkable, diggable, ...)
10538   =============================================================================
10539 */
10540
10541 static boolean checkDiagonalPushing(struct PlayerInfo *player,
10542                                     int x, int y, int real_dx, int real_dy)
10543 {
10544   int jx, jy, dx, dy, xx, yy;
10545
10546   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
10547     return TRUE;
10548
10549   /* diagonal direction: check alternative direction */
10550   jx = player->jx;
10551   jy = player->jy;
10552   dx = x - jx;
10553   dy = y - jy;
10554   xx = jx + (dx == 0 ? real_dx : 0);
10555   yy = jy + (dy == 0 ? real_dy : 0);
10556
10557   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
10558 }
10559
10560 /*
10561   =============================================================================
10562   DigField()
10563   -----------------------------------------------------------------------------
10564   x, y:                 field next to player (non-diagonal) to try to dig to
10565   real_dx, real_dy:     direction as read from input device (can be diagonal)
10566   =============================================================================
10567 */
10568
10569 int DigField(struct PlayerInfo *player,
10570              int oldx, int oldy, int x, int y,
10571              int real_dx, int real_dy, int mode)
10572 {
10573   static int trigger_sides[4] =
10574   {
10575     CH_SIDE_RIGHT,      /* moving left  */
10576     CH_SIDE_LEFT,       /* moving right */
10577     CH_SIDE_BOTTOM,     /* moving up    */
10578     CH_SIDE_TOP,        /* moving down  */
10579   };
10580 #if 0
10581   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
10582 #endif
10583   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
10584   boolean player_was_pushing = player->is_pushing;
10585   int jx = oldx, jy = oldy;
10586   int dx = x - jx, dy = y - jy;
10587   int nextx = x + dx, nexty = y + dy;
10588   int move_direction = (dx == -1 ? MV_LEFT :
10589                         dx == +1 ? MV_RIGHT :
10590                         dy == -1 ? MV_UP :
10591                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
10592   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
10593   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
10594   int old_element = Feld[jx][jy];
10595   int element;
10596
10597   if (is_player)                /* function can also be called by EL_PENGUIN */
10598   {
10599     if (player->MovPos == 0)
10600     {
10601       player->is_digging = FALSE;
10602       player->is_collecting = FALSE;
10603     }
10604
10605     if (player->MovPos == 0)    /* last pushing move finished */
10606       player->is_pushing = FALSE;
10607
10608     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
10609     {
10610       player->is_switching = FALSE;
10611       player->push_delay = 0;
10612
10613       return MF_NO_ACTION;
10614     }
10615   }
10616
10617   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
10618     return MF_NO_ACTION;
10619
10620 #if 0
10621
10622 #if 0
10623   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
10624 #else
10625   if (IS_TUBE(Feld[jx][jy]) ||
10626       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
10627 #endif
10628   {
10629     int i = 0;
10630     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
10631     int tube_leave_directions[][2] =
10632     {
10633       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10634       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
10635       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
10636       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
10637       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
10638       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
10639       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
10640       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
10641       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
10642       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
10643       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
10644       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
10645     };
10646
10647     while (tube_leave_directions[i][0] != tube_element)
10648     {
10649       i++;
10650       if (tube_leave_directions[i][0] == -1)    /* should not happen */
10651         break;
10652     }
10653
10654     if (!(tube_leave_directions[i][1] & move_direction))
10655       return MF_NO_ACTION;      /* tube has no opening in this direction */
10656   }
10657
10658 #else
10659
10660   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
10661     old_element = Back[jx][jy];
10662
10663 #endif
10664
10665   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
10666     return MF_NO_ACTION;        /* field has no opening in this direction */
10667
10668   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
10669     return MF_NO_ACTION;        /* field has no opening in this direction */
10670
10671   element = Feld[x][y];
10672
10673   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
10674     return MF_NO_ACTION;
10675
10676   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
10677       game.engine_version >= VERSION_IDENT(2,2,0,0))
10678     return MF_NO_ACTION;
10679
10680 #if 1
10681   if (game.gravity && is_player && !player->is_auto_moving &&
10682       canFallDown(player) && move_direction != MV_DOWN &&
10683       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
10684     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
10685 #endif
10686
10687 #if 0
10688   if (element == EL_EMPTY_SPACE &&
10689       game.gravity && !player->is_auto_moving &&
10690       canFallDown(player) && move_direction != MV_DOWN)
10691     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
10692 #endif
10693
10694   switch (element)
10695   {
10696 #if 0
10697     case EL_SP_PORT_LEFT:
10698     case EL_SP_PORT_RIGHT:
10699     case EL_SP_PORT_UP:
10700     case EL_SP_PORT_DOWN:
10701     case EL_SP_PORT_HORIZONTAL:
10702     case EL_SP_PORT_VERTICAL:
10703     case EL_SP_PORT_ANY:
10704     case EL_SP_GRAVITY_PORT_LEFT:
10705     case EL_SP_GRAVITY_PORT_RIGHT:
10706     case EL_SP_GRAVITY_PORT_UP:
10707     case EL_SP_GRAVITY_PORT_DOWN:
10708 #if 1
10709       if (!canEnterSupaplexPort(x, y, dx, dy))
10710         return MF_NO_ACTION;
10711 #else
10712       if ((dx == -1 &&
10713            element != EL_SP_PORT_LEFT &&
10714            element != EL_SP_GRAVITY_PORT_LEFT &&
10715            element != EL_SP_PORT_HORIZONTAL &&
10716            element != EL_SP_PORT_ANY) ||
10717           (dx == +1 &&
10718            element != EL_SP_PORT_RIGHT &&
10719            element != EL_SP_GRAVITY_PORT_RIGHT &&
10720            element != EL_SP_PORT_HORIZONTAL &&
10721            element != EL_SP_PORT_ANY) ||
10722           (dy == -1 &&
10723            element != EL_SP_PORT_UP &&
10724            element != EL_SP_GRAVITY_PORT_UP &&
10725            element != EL_SP_PORT_VERTICAL &&
10726            element != EL_SP_PORT_ANY) ||
10727           (dy == +1 &&
10728            element != EL_SP_PORT_DOWN &&
10729            element != EL_SP_GRAVITY_PORT_DOWN &&
10730            element != EL_SP_PORT_VERTICAL &&
10731            element != EL_SP_PORT_ANY) ||
10732           !IN_LEV_FIELD(nextx, nexty) ||
10733           !IS_FREE(nextx, nexty))
10734         return MF_NO_ACTION;
10735 #endif
10736
10737       if (element == EL_SP_GRAVITY_PORT_LEFT ||
10738           element == EL_SP_GRAVITY_PORT_RIGHT ||
10739           element == EL_SP_GRAVITY_PORT_UP ||
10740           element == EL_SP_GRAVITY_PORT_DOWN)
10741         game.gravity = !game.gravity;
10742
10743       /* automatically move to the next field with double speed */
10744       player->programmed_action = move_direction;
10745 #if 1
10746       if (player->move_delay_reset_counter == 0)
10747       {
10748         player->move_delay_reset_counter = 2;   /* two double speed steps */
10749
10750         DOUBLE_PLAYER_SPEED(player);
10751       }
10752 #else
10753       player->move_delay_reset_counter = 2;
10754
10755       DOUBLE_PLAYER_SPEED(player);
10756 #endif
10757
10758 #if 0
10759       printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
10760 #endif
10761
10762       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
10763       break;
10764 #endif
10765
10766 #if 0
10767     case EL_TUBE_ANY:
10768     case EL_TUBE_VERTICAL:
10769     case EL_TUBE_HORIZONTAL:
10770     case EL_TUBE_VERTICAL_LEFT:
10771     case EL_TUBE_VERTICAL_RIGHT:
10772     case EL_TUBE_HORIZONTAL_UP:
10773     case EL_TUBE_HORIZONTAL_DOWN:
10774     case EL_TUBE_LEFT_UP:
10775     case EL_TUBE_LEFT_DOWN:
10776     case EL_TUBE_RIGHT_UP:
10777     case EL_TUBE_RIGHT_DOWN:
10778       {
10779         int i = 0;
10780         int tube_enter_directions[][2] =
10781         {
10782           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
10783           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
10784           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
10785           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
10786           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
10787           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
10788           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
10789           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
10790           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
10791           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
10792           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
10793           { -1,                         MV_NO_MOVING                         }
10794         };
10795
10796         while (tube_enter_directions[i][0] != element)
10797         {
10798           i++;
10799           if (tube_enter_directions[i][0] == -1)        /* should not happen */
10800             break;
10801         }
10802
10803         if (!(tube_enter_directions[i][1] & move_direction))
10804           return MF_NO_ACTION;  /* tube has no opening in this direction */
10805
10806         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
10807       }
10808       break;
10809 #endif
10810
10811     default:
10812
10813 #if 1
10814       if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
10815 #else
10816       if (IS_WALKABLE(element))
10817 #endif
10818       {
10819         int sound_element = SND_ELEMENT(element);
10820         int sound_action = ACTION_WALKING;
10821
10822 #if 0
10823         if (!ACCESS_FROM(element, opposite_direction))
10824           return MF_NO_ACTION;  /* field not accessible from this direction */
10825 #endif
10826
10827 #if 0
10828         if (element == EL_EMPTY_SPACE &&
10829             game.gravity && !player->is_auto_moving &&
10830             canFallDown(player) && move_direction != MV_DOWN)
10831           return MF_NO_ACTION;  /* player cannot walk here due to gravity */
10832 #endif
10833
10834         if (IS_GATE(element))
10835         {
10836           if (!player->key[element - EL_GATE_1])
10837             return MF_NO_ACTION;
10838         }
10839         else if (IS_GATE_GRAY(element))
10840         {
10841           if (!player->key[element - EL_GATE_1_GRAY])
10842             return MF_NO_ACTION;
10843         }
10844         else if (element == EL_EXIT_OPEN ||
10845                  element == EL_SP_EXIT_OPEN ||
10846                  element == EL_SP_EXIT_OPENING)
10847         {
10848           sound_action = ACTION_PASSING;        /* player is passing exit */
10849         }
10850         else if (element == EL_EMPTY)
10851         {
10852           sound_action = ACTION_MOVING;         /* nothing to walk on */
10853         }
10854
10855         /* play sound from background or player, whatever is available */
10856         if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
10857           PlayLevelSoundElementAction(x, y, sound_element, sound_action);
10858         else
10859           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
10860
10861         break;
10862       }
10863 #if 1
10864       else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
10865 #else
10866       else if (IS_PASSABLE(element))
10867 #endif
10868       {
10869 #if 0
10870         if (!canPassField(x, y, move_direction))
10871           return MF_NO_ACTION;
10872 #else
10873
10874 #if 0
10875 #if 1
10876         if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
10877             !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
10878             (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
10879           return MF_NO_ACTION;
10880 #else
10881         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
10882           return MF_NO_ACTION;
10883 #endif
10884 #endif
10885
10886 #if 1
10887         if (!ACCESS_FROM(element, opposite_direction))
10888           return MF_NO_ACTION;  /* field not accessible from this direction */
10889 #else
10890         if (IS_CUSTOM_ELEMENT(element) &&
10891             !ACCESS_FROM(element, opposite_direction))
10892           return MF_NO_ACTION;  /* field not accessible from this direction */
10893 #endif
10894
10895 #if 1
10896         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
10897           return MF_NO_ACTION;
10898 #endif
10899
10900 #endif
10901
10902         if (IS_EM_GATE(element))
10903         {
10904           if (!player->key[element - EL_EM_GATE_1])
10905             return MF_NO_ACTION;
10906         }
10907         else if (IS_EM_GATE_GRAY(element))
10908         {
10909           if (!player->key[element - EL_EM_GATE_1_GRAY])
10910             return MF_NO_ACTION;
10911         }
10912         else if (IS_SP_PORT(element))
10913         {
10914           if (element == EL_SP_GRAVITY_PORT_LEFT ||
10915               element == EL_SP_GRAVITY_PORT_RIGHT ||
10916               element == EL_SP_GRAVITY_PORT_UP ||
10917               element == EL_SP_GRAVITY_PORT_DOWN)
10918             game.gravity = !game.gravity;
10919         }
10920
10921         /* automatically move to the next field with double speed */
10922         player->programmed_action = move_direction;
10923 #if 1
10924         if (player->move_delay_reset_counter == 0)
10925         {
10926           player->move_delay_reset_counter = 2; /* two double speed steps */
10927
10928           DOUBLE_PLAYER_SPEED(player);
10929         }
10930 #else
10931         player->move_delay_reset_counter = 2;
10932
10933         DOUBLE_PLAYER_SPEED(player);
10934 #endif
10935
10936         PlayLevelSoundAction(x, y, ACTION_PASSING);
10937
10938         break;
10939       }
10940       else if (IS_DIGGABLE(element))
10941       {
10942         RemoveField(x, y);
10943
10944         if (mode != DF_SNAP)
10945         {
10946 #if 1
10947           GfxElement[x][y] = GFX_ELEMENT(element);
10948 #else
10949           GfxElement[x][y] =
10950             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
10951 #endif
10952           player->is_digging = TRUE;
10953         }
10954
10955         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
10956
10957         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
10958                                             player->index_bit, dig_side);
10959
10960 #if 1
10961         if (mode == DF_SNAP)
10962           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
10963 #endif
10964
10965         break;
10966       }
10967       else if (IS_COLLECTIBLE(element))
10968       {
10969         RemoveField(x, y);
10970
10971         if (is_player && mode != DF_SNAP)
10972         {
10973           GfxElement[x][y] = element;
10974           player->is_collecting = TRUE;
10975         }
10976
10977         if (element == EL_SPEED_PILL)
10978           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
10979         else if (element == EL_EXTRA_TIME && level.time > 0)
10980         {
10981           TimeLeft += 10;
10982           DrawGameValue_Time(TimeLeft);
10983         }
10984         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
10985         {
10986           player->shield_normal_time_left += 10;
10987           if (element == EL_SHIELD_DEADLY)
10988             player->shield_deadly_time_left += 10;
10989         }
10990         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
10991         {
10992           if (player->inventory_size < MAX_INVENTORY_SIZE)
10993             player->inventory_element[player->inventory_size++] = element;
10994
10995           DrawGameValue_Dynamite(local_player->inventory_size);
10996         }
10997         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
10998         {
10999           player->dynabomb_count++;
11000           player->dynabombs_left++;
11001         }
11002         else if (element == EL_DYNABOMB_INCREASE_SIZE)
11003         {
11004           player->dynabomb_size++;
11005         }
11006         else if (element == EL_DYNABOMB_INCREASE_POWER)
11007         {
11008           player->dynabomb_xl = TRUE;
11009         }
11010         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
11011                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
11012         {
11013           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
11014                         element - EL_KEY_1 : element - EL_EM_KEY_1);
11015
11016           player->key[key_nr] = TRUE;
11017
11018           DrawGameValue_Keys(player);
11019
11020           redraw_mask |= REDRAW_DOOR_1;
11021         }
11022         else if (IS_ENVELOPE(element))
11023         {
11024 #if 1
11025           player->show_envelope = element;
11026 #else
11027           ShowEnvelope(element - EL_ENVELOPE_1);
11028 #endif
11029         }
11030         else if (IS_DROPPABLE(element) ||
11031                  IS_THROWABLE(element)) /* can be collected and dropped */
11032         {
11033           int i;
11034
11035           if (element_info[element].collect_count == 0)
11036             player->inventory_infinite_element = element;
11037           else
11038             for (i = 0; i < element_info[element].collect_count; i++)
11039               if (player->inventory_size < MAX_INVENTORY_SIZE)
11040                 player->inventory_element[player->inventory_size++] = element;
11041
11042           DrawGameValue_Dynamite(local_player->inventory_size);
11043         }
11044         else if (element_info[element].collect_count > 0)
11045         {
11046           local_player->gems_still_needed -=
11047             element_info[element].collect_count;
11048           if (local_player->gems_still_needed < 0)
11049             local_player->gems_still_needed = 0;
11050
11051           DrawGameValue_Emeralds(local_player->gems_still_needed);
11052         }
11053
11054         RaiseScoreElement(element);
11055         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11056
11057         if (is_player)
11058           CheckTriggeredElementChangeByPlayer(x, y, element,
11059                                               CE_OTHER_GETS_COLLECTED,
11060                                               player->index_bit, dig_side);
11061
11062 #if 1
11063         if (mode == DF_SNAP)
11064           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
11065 #endif
11066
11067         break;
11068       }
11069       else if (IS_PUSHABLE(element))
11070       {
11071         if (mode == DF_SNAP && element != EL_BD_ROCK)
11072           return MF_NO_ACTION;
11073
11074         if (CAN_FALL(element) && dy)
11075           return MF_NO_ACTION;
11076
11077         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11078             !(element == EL_SPRING && level.use_spring_bug))
11079           return MF_NO_ACTION;
11080
11081 #if 1
11082         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11083             ((move_direction & MV_VERTICAL &&
11084               ((element_info[element].move_pattern & MV_LEFT &&
11085                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11086                (element_info[element].move_pattern & MV_RIGHT &&
11087                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11088              (move_direction & MV_HORIZONTAL &&
11089               ((element_info[element].move_pattern & MV_UP &&
11090                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11091                (element_info[element].move_pattern & MV_DOWN &&
11092                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11093           return MF_NO_ACTION;
11094 #endif
11095
11096 #if 1
11097         /* do not push elements already moving away faster than player */
11098         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11099             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11100           return MF_NO_ACTION;
11101 #else
11102         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
11103           return MF_NO_ACTION;
11104 #endif
11105
11106 #if 1
11107
11108 #if 1
11109         if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11110         {
11111           if (player->push_delay_value == -1 || !player_was_pushing)
11112             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11113         }
11114         else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11115         {
11116           if (player->push_delay_value == -1)
11117             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11118         }
11119 #else
11120         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11121         {
11122           if (player->push_delay_value == -1 || !player_was_pushing)
11123             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11124         }
11125 #endif
11126         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11127         {
11128           if (!player->is_pushing)
11129             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11130         }
11131
11132         /*
11133         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
11134             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
11135              !player_is_pushing))
11136           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11137         */
11138 #else
11139         if (!player->is_pushing &&
11140             game.engine_version >= VERSION_IDENT(2,2,0,7))
11141           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11142 #endif
11143
11144 #if 0
11145         printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
11146                player->push_delay, player->push_delay_value,
11147                FrameCounter, game.engine_version,
11148                player_was_pushing, player->is_pushing,
11149                element, element_info[element].token_name,
11150                GET_NEW_PUSH_DELAY(element));
11151 #endif
11152
11153         player->is_pushing = TRUE;
11154
11155         if (!(IN_LEV_FIELD(nextx, nexty) &&
11156               (IS_FREE(nextx, nexty) ||
11157                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11158                 IS_SB_ELEMENT(element)))))
11159           return MF_NO_ACTION;
11160
11161         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11162           return MF_NO_ACTION;
11163
11164         if (player->push_delay == 0)    /* new pushing; restart delay */
11165           player->push_delay = FrameCounter;
11166
11167         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
11168             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11169             element != EL_SPRING && element != EL_BALLOON)
11170         {
11171           /* make sure that there is no move delay before next try to push */
11172           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11173             player->move_delay = INITIAL_MOVE_DELAY_OFF;
11174
11175           return MF_NO_ACTION;
11176         }
11177
11178 #if 0
11179         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
11180 #endif
11181
11182         if (IS_SB_ELEMENT(element))
11183         {
11184           if (element == EL_SOKOBAN_FIELD_FULL)
11185           {
11186             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11187             local_player->sokobanfields_still_needed++;
11188           }
11189
11190           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11191           {
11192             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11193             local_player->sokobanfields_still_needed--;
11194           }
11195
11196           Feld[x][y] = EL_SOKOBAN_OBJECT;
11197
11198           if (Back[x][y] == Back[nextx][nexty])
11199             PlayLevelSoundAction(x, y, ACTION_PUSHING);
11200           else if (Back[x][y] != 0)
11201             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11202                                         ACTION_EMPTYING);
11203           else
11204             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11205                                         ACTION_FILLING);
11206
11207           if (local_player->sokobanfields_still_needed == 0 &&
11208               game.emulation == EMU_SOKOBAN)
11209           {
11210             player->LevelSolved = player->GameOver = TRUE;
11211             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11212           }
11213         }
11214         else
11215           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11216
11217         InitMovingField(x, y, move_direction);
11218         GfxAction[x][y] = ACTION_PUSHING;
11219
11220         if (mode == DF_SNAP)
11221           ContinueMoving(x, y);
11222         else
11223           MovPos[x][y] = (dx != 0 ? dx : dy);
11224
11225         Pushed[x][y] = TRUE;
11226         Pushed[nextx][nexty] = TRUE;
11227
11228         if (game.engine_version < VERSION_IDENT(2,2,0,7))
11229           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11230         else
11231           player->push_delay_value = -1;        /* get new value later */
11232
11233 #if 1
11234         /* check for element change _after_ element has been pushed! */
11235 #else
11236
11237 #if 1
11238       /* !!! TEST ONLY !!! */
11239         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11240                                    player->index_bit, dig_side);
11241         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11242                                             player->index_bit, dig_side);
11243 #else
11244         CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
11245                                             player->index_bit, dig_side);
11246         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11247                                    player->index_bit, dig_side);
11248 #endif
11249 #endif
11250
11251         break;
11252       }
11253       else if (IS_SWITCHABLE(element))
11254       {
11255         if (PLAYER_SWITCHING(player, x, y))
11256           return MF_ACTION;
11257
11258         player->is_switching = TRUE;
11259         player->switch_x = x;
11260         player->switch_y = y;
11261
11262         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11263
11264         if (element == EL_ROBOT_WHEEL)
11265         {
11266           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11267           ZX = x;
11268           ZY = y;
11269
11270           DrawLevelField(x, y);
11271         }
11272         else if (element == EL_SP_TERMINAL)
11273         {
11274           int xx, yy;
11275
11276           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
11277           {
11278             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11279               Bang(xx, yy);
11280             else if (Feld[xx][yy] == EL_SP_TERMINAL)
11281               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11282           }
11283         }
11284         else if (IS_BELT_SWITCH(element))
11285         {
11286           ToggleBeltSwitch(x, y);
11287         }
11288         else if (element == EL_SWITCHGATE_SWITCH_UP ||
11289                  element == EL_SWITCHGATE_SWITCH_DOWN)
11290         {
11291           ToggleSwitchgateSwitch(x, y);
11292         }
11293         else if (element == EL_LIGHT_SWITCH ||
11294                  element == EL_LIGHT_SWITCH_ACTIVE)
11295         {
11296           ToggleLightSwitch(x, y);
11297
11298 #if 0
11299           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
11300                          SND_LIGHT_SWITCH_ACTIVATING :
11301                          SND_LIGHT_SWITCH_DEACTIVATING);
11302 #endif
11303         }
11304         else if (element == EL_TIMEGATE_SWITCH)
11305         {
11306           ActivateTimegateSwitch(x, y);
11307         }
11308         else if (element == EL_BALLOON_SWITCH_LEFT ||
11309                  element == EL_BALLOON_SWITCH_RIGHT ||
11310                  element == EL_BALLOON_SWITCH_UP ||
11311                  element == EL_BALLOON_SWITCH_DOWN ||
11312                  element == EL_BALLOON_SWITCH_ANY)
11313         {
11314           if (element == EL_BALLOON_SWITCH_ANY)
11315             game.balloon_dir = move_direction;
11316           else
11317             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
11318                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11319                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
11320                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
11321                                 MV_NO_MOVING);
11322         }
11323         else if (element == EL_LAMP)
11324         {
11325           Feld[x][y] = EL_LAMP_ACTIVE;
11326           local_player->lights_still_needed--;
11327
11328           DrawLevelField(x, y);
11329         }
11330         else if (element == EL_TIME_ORB_FULL)
11331         {
11332           Feld[x][y] = EL_TIME_ORB_EMPTY;
11333           TimeLeft += 10;
11334           DrawGameValue_Time(TimeLeft);
11335
11336           DrawLevelField(x, y);
11337
11338 #if 0
11339           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
11340 #endif
11341         }
11342
11343         return MF_ACTION;
11344       }
11345       else
11346       {
11347         if (!PLAYER_SWITCHING(player, x, y))
11348         {
11349           player->is_switching = TRUE;
11350           player->switch_x = x;
11351           player->switch_y = y;
11352
11353 #if 1
11354           /* !!! TEST ONLY !!! */
11355           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11356                                      player->index_bit, dig_side);
11357           CheckTriggeredElementChangeByPlayer(x, y, element,
11358                                               CE_OTHER_IS_SWITCHING,
11359                                               player->index_bit, dig_side);
11360 #else
11361           CheckTriggeredElementChangeByPlayer(x, y, element,
11362                                               CE_OTHER_IS_SWITCHING,
11363                                               player->index_bit, dig_side);
11364           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11365                                      player->index_bit, dig_side);
11366 #endif
11367         }
11368
11369 #if 1
11370         /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
11371         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11372                                    player->index_bit, dig_side);
11373         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11374                                             player->index_bit, dig_side);
11375 #else
11376         CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
11377                                             player->index_bit, dig_side);
11378         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
11379                                    player->index_bit, dig_side);
11380 #endif
11381       }
11382
11383       return MF_NO_ACTION;
11384   }
11385
11386   player->push_delay = 0;
11387
11388   if (Feld[x][y] != element)            /* really digged/collected something */
11389     player->is_collecting = !player->is_digging;
11390
11391   return MF_MOVING;
11392 }
11393
11394 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
11395 {
11396   int jx = player->jx, jy = player->jy;
11397   int x = jx + dx, y = jy + dy;
11398   int snap_direction = (dx == -1 ? MV_LEFT :
11399                         dx == +1 ? MV_RIGHT :
11400                         dy == -1 ? MV_UP :
11401                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
11402
11403 #if 0
11404   if (player->MovPos != 0)
11405     return FALSE;
11406 #else
11407   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
11408     return FALSE;
11409 #endif
11410
11411   if (!player->active || !IN_LEV_FIELD(x, y))
11412     return FALSE;
11413
11414   if (dx && dy)
11415     return FALSE;
11416
11417   if (!dx && !dy)
11418   {
11419     if (player->MovPos == 0)
11420       player->is_pushing = FALSE;
11421
11422     player->is_snapping = FALSE;
11423
11424     if (player->MovPos == 0)
11425     {
11426       player->is_moving = FALSE;
11427       player->is_digging = FALSE;
11428       player->is_collecting = FALSE;
11429     }
11430
11431     return FALSE;
11432   }
11433
11434   if (player->is_snapping)
11435     return FALSE;
11436
11437   player->MovDir = snap_direction;
11438
11439 #if 1
11440   if (player->MovPos == 0)
11441 #endif
11442   {
11443     player->is_moving = FALSE;
11444     player->is_digging = FALSE;
11445     player->is_collecting = FALSE;
11446   }
11447
11448   player->is_dropping = FALSE;
11449
11450   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
11451     return FALSE;
11452
11453   player->is_snapping = TRUE;
11454
11455 #if 1
11456   if (player->MovPos == 0)
11457 #endif
11458   {
11459     player->is_moving = FALSE;
11460     player->is_digging = FALSE;
11461     player->is_collecting = FALSE;
11462   }
11463
11464 #if 1
11465   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
11466     DrawLevelField(player->last_jx, player->last_jy);
11467 #endif
11468
11469   DrawLevelField(x, y);
11470
11471 #if 0
11472   BackToFront();
11473 #endif
11474
11475   return TRUE;
11476 }
11477
11478 boolean DropElement(struct PlayerInfo *player)
11479 {
11480   static int trigger_sides[4] =
11481   {
11482     CH_SIDE_LEFT,       /* dropping left  */
11483     CH_SIDE_RIGHT,      /* dropping right */
11484     CH_SIDE_TOP,        /* dropping up    */
11485     CH_SIDE_BOTTOM,     /* dropping down  */
11486   };
11487   int old_element, new_element;
11488   int dropx = player->jx, dropy = player->jy;
11489   int drop_direction = player->MovDir;
11490   int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
11491   int drop_element = (player->inventory_size > 0 ?
11492                       player->inventory_element[player->inventory_size - 1] :
11493                       player->inventory_infinite_element != EL_UNDEFINED ?
11494                       player->inventory_infinite_element :
11495                       player->dynabombs_left > 0 ?
11496                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
11497                       EL_UNDEFINED);
11498
11499   if (IS_THROWABLE(drop_element))
11500   {
11501     dropx += GET_DX_FROM_DIR(drop_direction);
11502     dropy += GET_DY_FROM_DIR(drop_direction);
11503
11504     if (!IN_LEV_FIELD(dropx, dropy))
11505       return FALSE;
11506   }
11507
11508   old_element = Feld[dropx][dropy];     /* old element at dropping position */
11509   new_element = drop_element;           /* default: no change when dropping */
11510
11511   /* check if player is active, not moving and ready to drop */
11512   if (!player->active || player->MovPos || player->drop_delay > 0)
11513     return FALSE;
11514
11515   /* check if player has anything that can be dropped */
11516 #if 1
11517   if (new_element == EL_UNDEFINED)
11518     return FALSE;
11519 #else
11520   if (player->inventory_size == 0 &&
11521       player->inventory_infinite_element == EL_UNDEFINED &&
11522       player->dynabombs_left == 0)
11523     return FALSE;
11524 #endif
11525
11526   /* check if anything can be dropped at the current position */
11527   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
11528     return FALSE;
11529
11530   /* collected custom elements can only be dropped on empty fields */
11531 #if 1
11532   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
11533     return FALSE;
11534 #else
11535   if (player->inventory_size > 0 &&
11536       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
11537       && old_element != EL_EMPTY)
11538     return FALSE;
11539 #endif
11540
11541   if (old_element != EL_EMPTY)
11542     Back[dropx][dropy] = old_element;   /* store old element on this field */
11543
11544   ResetGfxAnimation(dropx, dropy);
11545   ResetRandomAnimationValue(dropx, dropy);
11546
11547   if (player->inventory_size > 0 ||
11548       player->inventory_infinite_element != EL_UNDEFINED)
11549   {
11550     if (player->inventory_size > 0)
11551     {
11552       player->inventory_size--;
11553
11554 #if 0
11555       new_element = player->inventory_element[player->inventory_size];
11556 #endif
11557
11558       DrawGameValue_Dynamite(local_player->inventory_size);
11559
11560       if (new_element == EL_DYNAMITE)
11561         new_element = EL_DYNAMITE_ACTIVE;
11562       else if (new_element == EL_SP_DISK_RED)
11563         new_element = EL_SP_DISK_RED_ACTIVE;
11564     }
11565
11566     Feld[dropx][dropy] = new_element;
11567
11568     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11569       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11570                           el2img(Feld[dropx][dropy]), 0);
11571
11572     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11573
11574 #if 1
11575     /* needed if previous element just changed to "empty" in the last frame */
11576     Changed[dropx][dropy] = 0;          /* allow another change */
11577 #endif
11578
11579 #if 1
11580     /* !!! TEST ONLY !!! */
11581     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11582                                player->index_bit, drop_side);
11583     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11584                                         CE_OTHER_GETS_DROPPED,
11585                                         player->index_bit, drop_side);
11586 #else
11587     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
11588                                         CE_OTHER_GETS_DROPPED,
11589                                         player->index_bit, drop_side);
11590     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
11591                                player->index_bit, drop_side);
11592 #endif
11593
11594     TestIfElementTouchesCustomElement(dropx, dropy);
11595   }
11596   else          /* player is dropping a dyna bomb */
11597   {
11598     player->dynabombs_left--;
11599
11600 #if 0
11601     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
11602 #endif
11603
11604     Feld[dropx][dropy] = new_element;
11605
11606     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
11607       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
11608                           el2img(Feld[dropx][dropy]), 0);
11609
11610     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
11611   }
11612
11613
11614
11615 #if 1
11616
11617   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
11618   {
11619 #if 1
11620     InitField_WithBug1(dropx, dropy, FALSE);
11621 #else
11622     InitField(dropx, dropy, FALSE);
11623     if (CAN_MOVE(Feld[dropx][dropy]))
11624       InitMovDir(dropx, dropy);
11625 #endif
11626   }
11627
11628   new_element = Feld[dropx][dropy];     /* element might have changed */
11629
11630   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
11631       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
11632   {
11633 #if 0
11634     int move_stepsize = element_info[new_element].move_stepsize;
11635 #endif
11636     int move_direction, nextx, nexty;
11637
11638     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
11639       MovDir[dropx][dropy] = drop_direction;
11640
11641     move_direction = MovDir[dropx][dropy];
11642     nextx = dropx + GET_DX_FROM_DIR(move_direction);
11643     nexty = dropy + GET_DY_FROM_DIR(move_direction);
11644
11645 #if 1
11646       Changed[dropx][dropy] = 0;        /* allow another change */
11647       CheckCollision[dropx][dropy] = 2;
11648 #else
11649
11650     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
11651     {
11652 #if 0
11653       WasJustMoving[dropx][dropy] = 3;
11654 #else
11655 #if 1
11656       InitMovingField(dropx, dropy, move_direction);
11657       ContinueMoving(dropx, dropy);
11658 #endif
11659 #endif
11660     }
11661 #if 1
11662     else
11663     {
11664       Changed[dropx][dropy] = 0;        /* allow another change */
11665
11666 #if 1
11667       TestIfElementHitsCustomElement(dropx, dropy, move_direction);
11668 #else
11669       CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
11670                                CE_HITTING_SOMETHING, move_direction);
11671 #endif
11672     }
11673 #endif
11674
11675 #endif
11676
11677 #if 0
11678     player->drop_delay = 2 * TILEX / move_stepsize + 1;
11679 #endif
11680   }
11681
11682 #if 0
11683   player->drop_delay = 8 + 8 + 8;
11684 #endif
11685
11686 #if 1
11687   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
11688 #endif
11689
11690 #endif
11691
11692   player->is_dropping = TRUE;
11693
11694
11695   return TRUE;
11696 }
11697
11698 /* ------------------------------------------------------------------------- */
11699 /* game sound playing functions                                              */
11700 /* ------------------------------------------------------------------------- */
11701
11702 static int *loop_sound_frame = NULL;
11703 static int *loop_sound_volume = NULL;
11704
11705 void InitPlayLevelSound()
11706 {
11707   int num_sounds = getSoundListSize();
11708
11709   checked_free(loop_sound_frame);
11710   checked_free(loop_sound_volume);
11711
11712   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
11713   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
11714 }
11715
11716 static void PlayLevelSound(int x, int y, int nr)
11717 {
11718   int sx = SCREENX(x), sy = SCREENY(y);
11719   int volume, stereo_position;
11720   int max_distance = 8;
11721   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
11722
11723   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
11724       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
11725     return;
11726
11727   if (!IN_LEV_FIELD(x, y) ||
11728       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
11729       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
11730     return;
11731
11732   volume = SOUND_MAX_VOLUME;
11733
11734   if (!IN_SCR_FIELD(sx, sy))
11735   {
11736     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
11737     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
11738
11739     volume -= volume * (dx > dy ? dx : dy) / max_distance;
11740   }
11741
11742   stereo_position = (SOUND_MAX_LEFT +
11743                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
11744                      (SCR_FIELDX + 2 * max_distance));
11745
11746   if (IS_LOOP_SOUND(nr))
11747   {
11748     /* This assures that quieter loop sounds do not overwrite louder ones,
11749        while restarting sound volume comparison with each new game frame. */
11750
11751     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
11752       return;
11753
11754     loop_sound_volume[nr] = volume;
11755     loop_sound_frame[nr] = FrameCounter;
11756   }
11757
11758   PlaySoundExt(nr, volume, stereo_position, type);
11759 }
11760
11761 static void PlayLevelSoundNearest(int x, int y, int sound_action)
11762 {
11763   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
11764                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
11765                  y < LEVELY(BY1) ? LEVELY(BY1) :
11766                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
11767                  sound_action);
11768 }
11769
11770 static void PlayLevelSoundAction(int x, int y, int action)
11771 {
11772   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
11773 }
11774
11775 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
11776 {
11777   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11778
11779   if (sound_effect != SND_UNDEFINED)
11780     PlayLevelSound(x, y, sound_effect);
11781 }
11782
11783 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
11784                                               int action)
11785 {
11786   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
11787
11788   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11789     PlayLevelSound(x, y, sound_effect);
11790 }
11791
11792 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
11793 {
11794   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11795
11796   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11797     PlayLevelSound(x, y, sound_effect);
11798 }
11799
11800 static void StopLevelSoundActionIfLoop(int x, int y, int action)
11801 {
11802   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
11803
11804   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
11805     StopSound(sound_effect);
11806 }
11807
11808 static void PlayLevelMusic()
11809 {
11810   if (levelset.music[level_nr] != MUS_UNDEFINED)
11811     PlayMusic(levelset.music[level_nr]);        /* from config file */
11812   else
11813     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
11814 }
11815
11816 void RaiseScore(int value)
11817 {
11818   local_player->score += value;
11819
11820   DrawGameValue_Score(local_player->score);
11821 }
11822
11823 void RaiseScoreElement(int element)
11824 {
11825   switch(element)
11826   {
11827     case EL_EMERALD:
11828     case EL_BD_DIAMOND:
11829     case EL_EMERALD_YELLOW:
11830     case EL_EMERALD_RED:
11831     case EL_EMERALD_PURPLE:
11832     case EL_SP_INFOTRON:
11833       RaiseScore(level.score[SC_EMERALD]);
11834       break;
11835     case EL_DIAMOND:
11836       RaiseScore(level.score[SC_DIAMOND]);
11837       break;
11838     case EL_CRYSTAL:
11839       RaiseScore(level.score[SC_CRYSTAL]);
11840       break;
11841     case EL_PEARL:
11842       RaiseScore(level.score[SC_PEARL]);
11843       break;
11844     case EL_BUG:
11845     case EL_BD_BUTTERFLY:
11846     case EL_SP_ELECTRON:
11847       RaiseScore(level.score[SC_BUG]);
11848       break;
11849     case EL_SPACESHIP:
11850     case EL_BD_FIREFLY:
11851     case EL_SP_SNIKSNAK:
11852       RaiseScore(level.score[SC_SPACESHIP]);
11853       break;
11854     case EL_YAMYAM:
11855     case EL_DARK_YAMYAM:
11856       RaiseScore(level.score[SC_YAMYAM]);
11857       break;
11858     case EL_ROBOT:
11859       RaiseScore(level.score[SC_ROBOT]);
11860       break;
11861     case EL_PACMAN:
11862       RaiseScore(level.score[SC_PACMAN]);
11863       break;
11864     case EL_NUT:
11865       RaiseScore(level.score[SC_NUT]);
11866       break;
11867     case EL_DYNAMITE:
11868     case EL_SP_DISK_RED:
11869     case EL_DYNABOMB_INCREASE_NUMBER:
11870     case EL_DYNABOMB_INCREASE_SIZE:
11871     case EL_DYNABOMB_INCREASE_POWER:
11872       RaiseScore(level.score[SC_DYNAMITE]);
11873       break;
11874     case EL_SHIELD_NORMAL:
11875     case EL_SHIELD_DEADLY:
11876       RaiseScore(level.score[SC_SHIELD]);
11877       break;
11878     case EL_EXTRA_TIME:
11879       RaiseScore(level.score[SC_TIME_BONUS]);
11880       break;
11881     case EL_KEY_1:
11882     case EL_KEY_2:
11883     case EL_KEY_3:
11884     case EL_KEY_4:
11885       RaiseScore(level.score[SC_KEY]);
11886       break;
11887     default:
11888       RaiseScore(element_info[element].collect_score);
11889       break;
11890   }
11891 }
11892
11893 void RequestQuitGame(boolean ask_if_really_quit)
11894 {
11895   if (AllPlayersGone ||
11896       !ask_if_really_quit ||
11897       level_editor_test_game ||
11898       Request("Do you really want to quit the game ?",
11899               REQ_ASK | REQ_STAY_CLOSED))
11900   {
11901 #if defined(NETWORK_AVALIABLE)
11902     if (options.network)
11903       SendToServer_StopPlaying();
11904     else
11905 #endif
11906     {
11907       game_status = GAME_MODE_MAIN;
11908       DrawMainMenu();
11909     }
11910   }
11911   else
11912   {
11913
11914 #if 1
11915     if (tape.playing && tape.deactivate_display)
11916       TapeDeactivateDisplayOff(TRUE);
11917 #endif
11918
11919     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
11920
11921 #if 1
11922     if (tape.playing && tape.deactivate_display)
11923       TapeDeactivateDisplayOn();
11924 #endif
11925
11926   }
11927 }
11928
11929
11930 /* ---------- new game button stuff ---------------------------------------- */
11931
11932 /* graphic position values for game buttons */
11933 #define GAME_BUTTON_XSIZE       30
11934 #define GAME_BUTTON_YSIZE       30
11935 #define GAME_BUTTON_XPOS        5
11936 #define GAME_BUTTON_YPOS        215
11937 #define SOUND_BUTTON_XPOS       5
11938 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
11939
11940 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11941 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11942 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11943 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
11944 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
11945 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
11946
11947 static struct
11948 {
11949   int x, y;
11950   int gadget_id;
11951   char *infotext;
11952 } gamebutton_info[NUM_GAME_BUTTONS] =
11953 {
11954   {
11955     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
11956     GAME_CTRL_ID_STOP,
11957     "stop game"
11958   },
11959   {
11960     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
11961     GAME_CTRL_ID_PAUSE,
11962     "pause game"
11963   },
11964   {
11965     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
11966     GAME_CTRL_ID_PLAY,
11967     "play game"
11968   },
11969   {
11970     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
11971     SOUND_CTRL_ID_MUSIC,
11972     "background music on/off"
11973   },
11974   {
11975     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
11976     SOUND_CTRL_ID_LOOPS,
11977     "sound loops on/off"
11978   },
11979   {
11980     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
11981     SOUND_CTRL_ID_SIMPLE,
11982     "normal sounds on/off"
11983   }
11984 };
11985
11986 void CreateGameButtons()
11987 {
11988   int i;
11989
11990   for (i = 0; i < NUM_GAME_BUTTONS; i++)
11991   {
11992     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
11993     struct GadgetInfo *gi;
11994     int button_type;
11995     boolean checked;
11996     unsigned long event_mask;
11997     int gd_xoffset, gd_yoffset;
11998     int gd_x1, gd_x2, gd_y1, gd_y2;
11999     int id = i;
12000
12001     gd_xoffset = gamebutton_info[i].x;
12002     gd_yoffset = gamebutton_info[i].y;
12003     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12004     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12005
12006     if (id == GAME_CTRL_ID_STOP ||
12007         id == GAME_CTRL_ID_PAUSE ||
12008         id == GAME_CTRL_ID_PLAY)
12009     {
12010       button_type = GD_TYPE_NORMAL_BUTTON;
12011       checked = FALSE;
12012       event_mask = GD_EVENT_RELEASED;
12013       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12014       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12015     }
12016     else
12017     {
12018       button_type = GD_TYPE_CHECK_BUTTON;
12019       checked =
12020         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12021          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12022          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12023       event_mask = GD_EVENT_PRESSED;
12024       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
12025       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12026     }
12027
12028     gi = CreateGadget(GDI_CUSTOM_ID, id,
12029                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
12030                       GDI_X, DX + gd_xoffset,
12031                       GDI_Y, DY + gd_yoffset,
12032                       GDI_WIDTH, GAME_BUTTON_XSIZE,
12033                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
12034                       GDI_TYPE, button_type,
12035                       GDI_STATE, GD_BUTTON_UNPRESSED,
12036                       GDI_CHECKED, checked,
12037                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12038                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12039                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12040                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12041                       GDI_EVENT_MASK, event_mask,
12042                       GDI_CALLBACK_ACTION, HandleGameButtons,
12043                       GDI_END);
12044
12045     if (gi == NULL)
12046       Error(ERR_EXIT, "cannot create gadget");
12047
12048     game_gadget[id] = gi;
12049   }
12050 }
12051
12052 void FreeGameButtons()
12053 {
12054   int i;
12055
12056   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12057     FreeGadget(game_gadget[i]);
12058 }
12059
12060 static void MapGameButtons()
12061 {
12062   int i;
12063
12064   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12065     MapGadget(game_gadget[i]);
12066 }
12067
12068 void UnmapGameButtons()
12069 {
12070   int i;
12071
12072   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12073     UnmapGadget(game_gadget[i]);
12074 }
12075
12076 static void HandleGameButtons(struct GadgetInfo *gi)
12077 {
12078   int id = gi->custom_id;
12079
12080   if (game_status != GAME_MODE_PLAYING)
12081     return;
12082
12083   switch (id)
12084   {
12085     case GAME_CTRL_ID_STOP:
12086       RequestQuitGame(TRUE);
12087       break;
12088
12089     case GAME_CTRL_ID_PAUSE:
12090       if (options.network)
12091       {
12092 #if defined(NETWORK_AVALIABLE)
12093         if (tape.pausing)
12094           SendToServer_ContinuePlaying();
12095         else
12096           SendToServer_PausePlaying();
12097 #endif
12098       }
12099       else
12100         TapeTogglePause(TAPE_TOGGLE_MANUAL);
12101       break;
12102
12103     case GAME_CTRL_ID_PLAY:
12104       if (tape.pausing)
12105       {
12106 #if defined(NETWORK_AVALIABLE)
12107         if (options.network)
12108           SendToServer_ContinuePlaying();
12109         else
12110 #endif
12111         {
12112           tape.pausing = FALSE;
12113           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12114         }
12115       }
12116       break;
12117
12118     case SOUND_CTRL_ID_MUSIC:
12119       if (setup.sound_music)
12120       { 
12121         setup.sound_music = FALSE;
12122         FadeMusic();
12123       }
12124       else if (audio.music_available)
12125       { 
12126         setup.sound = setup.sound_music = TRUE;
12127
12128         SetAudioMode(setup.sound);
12129
12130         PlayLevelMusic();
12131       }
12132       break;
12133
12134     case SOUND_CTRL_ID_LOOPS:
12135       if (setup.sound_loops)
12136         setup.sound_loops = FALSE;
12137       else if (audio.loops_available)
12138       {
12139         setup.sound = setup.sound_loops = TRUE;
12140         SetAudioMode(setup.sound);
12141       }
12142       break;
12143
12144     case SOUND_CTRL_ID_SIMPLE:
12145       if (setup.sound_simple)
12146         setup.sound_simple = FALSE;
12147       else if (audio.sound_available)
12148       {
12149         setup.sound = setup.sound_simple = TRUE;
12150         SetAudioMode(setup.sound);
12151       }
12152       break;
12153
12154     default:
12155       break;
12156   }
12157 }