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