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