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