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