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