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