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