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