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