rnd-20051125-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* EXPERIMENTAL STUFF */
31 #define USE_NEW_STUFF                   (TRUE                           * 1)
32
33 #define USE_NEW_MOVE_STYLE              (TRUE   * USE_NEW_STUFF         * 1)
34 #define USE_NEW_MOVE_DELAY              (TRUE   * USE_NEW_STUFF         * 1)
35 #define USE_NEW_PUSH_DELAY              (TRUE   * USE_NEW_STUFF         * 1)
36 #define USE_NEW_BLOCK_STYLE             (TRUE   * USE_NEW_STUFF         * 1)
37 #define USE_NEW_SP_SLIPPERY             (TRUE   * USE_NEW_STUFF         * 1)
38 #define USE_NEW_RANDOMIZE               (TRUE   * USE_NEW_STUFF         * 1)
39
40 #define USE_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_BUGFIX      (TRUE   * USE_NEW_STUFF         * 1)
51
52 #define USE_IMPACT_BUGFIX               (TRUE   * USE_NEW_STUFF         * 1)
53
54 #define USE_HITTING_SOMETHING_BUGFIX    (TRUE   * USE_NEW_STUFF         * 1)
55 #define USE_HIT_BY_SOMETHING_BUGFIX     (TRUE   * USE_NEW_STUFF         * 1)
56
57 #define USE_DROP_BUGFIX                 (TRUE   * USE_NEW_STUFF         * 1)
58
59 #define USE_CHANGE_TO_TRIGGERED         (TRUE   * USE_NEW_STUFF         * 1)
60
61 #define USE_BACK_WALKABLE_BUGFIX        (TRUE   * USE_NEW_STUFF         * 1)
62
63 #define TEST_NEW_STUFF                  (TRUE)
64
65
66 /* for DigField() */
67 #define DF_NO_PUSH              0
68 #define DF_DIG                  1
69 #define DF_SNAP                 2
70
71 /* for MovePlayer() */
72 #define MF_NO_ACTION            0
73 #define MF_MOVING               1
74 #define MF_ACTION               2
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
88
89 /* special positions in the game control window (relative to control window) */
90 #define XX_LEVEL                37
91 #define YY_LEVEL                20
92 #define XX_EMERALDS             29
93 #define YY_EMERALDS             54
94 #define XX_DYNAMITE             29
95 #define YY_DYNAMITE             89
96 #define XX_KEYS                 18
97 #define YY_KEYS                 123
98 #define XX_SCORE                15
99 #define YY_SCORE                159
100 #define XX_TIME1                29
101 #define XX_TIME2                30
102 #define YY_TIME                 194
103
104 /* special positions in the game control window (relative to main window) */
105 #define DX_LEVEL                (DX + XX_LEVEL)
106 #define DY_LEVEL                (DY + YY_LEVEL)
107 #define DX_EMERALDS             (DX + XX_EMERALDS)
108 #define DY_EMERALDS             (DY + YY_EMERALDS)
109 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
110 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
111 #define DX_KEYS                 (DX + XX_KEYS)
112 #define DY_KEYS                 (DY + YY_KEYS)
113 #define DX_SCORE                (DX + XX_SCORE)
114 #define DY_SCORE                (DY + YY_SCORE)
115 #define DX_TIME1                (DX + XX_TIME1)
116 #define DX_TIME2                (DX + XX_TIME2)
117 #define DY_TIME                 (DY + YY_TIME)
118
119 /* values for initial player move delay (initial delay counter value) */
120 #define INITIAL_MOVE_DELAY_OFF  -1
121 #define INITIAL_MOVE_DELAY_ON   0
122
123 /* values for player movement speed (which is in fact a delay value) */
124 #define MOVE_DELAY_NORMAL_SPEED 8
125 #define MOVE_DELAY_HIGH_SPEED   4
126
127 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
128 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
129 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
130 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
131
132 /* values for other actions */
133 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
134
135 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
136 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
137
138 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
139
140 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
141                                  RND(element_info[e].push_delay_random))
142 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
143                                  RND(element_info[e].drop_delay_random))
144 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
145                                  RND(element_info[e].move_delay_random))
146 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
147                                     (element_info[e].move_delay_random))
148 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
149                                  RND((c)->delay_random * (c)->delay_frames))
150
151 #define GET_TARGET_ELEMENT(e, ch)                                       \
152         ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element :     \
153          (e) == EL_TRIGGER_PLAYER  ? (ch)->actual_trigger_player : (e))
154
155 #define GET_VALID_PLAYER_ELEMENT(e)                                     \
156         ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
157
158 #define CAN_GROW_INTO(e)                                                \
159         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
160
161 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
162                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
163                                         (condition)))
164
165 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
166                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
167                                         (CAN_MOVE_INTO_ACID(e) &&       \
168                                          Feld[x][y] == EL_ACID) ||      \
169                                         (condition)))
170
171 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
172                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
173                                         (CAN_MOVE_INTO_ACID(e) &&       \
174                                          Feld[x][y] == EL_ACID) ||      \
175                                         (condition)))
176
177 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
178                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
179                                         (condition) ||                  \
180                                         (CAN_MOVE_INTO_ACID(e) &&       \
181                                          Feld[x][y] == EL_ACID) ||      \
182                                         (DONT_COLLIDE_WITH(e) &&        \
183                                          IS_PLAYER(x, y) &&             \
184                                          !PLAYER_ENEMY_PROTECTED(x, y))))
185
186 #if 0
187 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
188                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
189                                         (condition) ||                  \
190                                         (DONT_COLLIDE_WITH(e) &&        \
191                                          IS_PLAYER(x, y) &&             \
192                                          !PLAYER_ENEMY_PROTECTED(x, y))))
193 #endif
194
195 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
196         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
197
198 #if 1
199 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
200         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
201 #else
202 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
203         ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, Feld[x][y] == EL_ACID)
204 #endif
205
206 #if 0
207 #define ENEMY_CAN_ENTER_FIELD(e, x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
208 #endif
209
210 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
211         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
212
213 #if 1
214
215 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
216         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
217
218 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
219         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
220
221 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
222         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
223
224 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
225         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
226
227 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
228         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN ||\
229                                                  IS_FOOD_PENGUIN(Feld[x][y])))
230 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
231         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
232
233 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
234         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
235
236 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
237         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
238
239 #else
240
241 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
242                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
243                                         (CAN_MOVE_INTO_ACID(e) &&       \
244                                          Feld[x][y] == EL_ACID) ||      \
245                                         Feld[x][y] == EL_DIAMOND))
246
247 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
248                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
249                                         (CAN_MOVE_INTO_ACID(e) &&       \
250                                          Feld[x][y] == EL_ACID) ||      \
251                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
252
253 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
254                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
255                                         (CAN_MOVE_INTO_ACID(e) &&       \
256                                          Feld[x][y] == EL_ACID) ||      \
257                                         IS_AMOEBOID(Feld[x][y])))
258
259 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
260                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
261                                         (CAN_MOVE_INTO_ACID(e) &&       \
262                                          Feld[x][y] == EL_ACID) ||      \
263                                         IS_FOOD_PIG(Feld[x][y])))
264
265 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
266                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
267                                         (CAN_MOVE_INTO_ACID(e) &&       \
268                                          Feld[x][y] == EL_ACID) ||      \
269                                         IS_FOOD_PENGUIN(Feld[x][y]) ||  \
270                                         Feld[x][y] == EL_EXIT_OPEN))
271
272 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
273                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
274                                         (CAN_MOVE_INTO_ACID(e) &&       \
275                                          Feld[x][y] == EL_ACID)))
276
277 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
278                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
279                                         (CAN_MOVE_INTO_ACID(e) &&       \
280                                          Feld[x][y] == EL_ACID) ||      \
281                                         (condition)))
282
283 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
284                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
285                                         (CAN_MOVE_INTO_ACID(e) &&       \
286                                          Feld[x][y] == EL_ACID)))
287
288 #endif
289
290 #define GROUP_NR(e)             ((e) - EL_GROUP_START)
291 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
292 #define IS_IN_GROUP(e, nr)      (element_info[e].in_group[nr] == TRUE)
293 #define IS_IN_GROUP_EL(e, ge)   (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
294
295 #define IS_EQUAL_OR_IN_GROUP(e, ge)                                     \
296         (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
297
298 #if 0
299 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
300                 (!IS_PLAYER(x, y) &&                                    \
301                  (Feld[x][y] == EL_ACID ||                              \
302                   IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
303 #else
304 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
305                 (!IS_PLAYER(x, y) &&                                    \
306                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
307 #endif
308
309 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
310         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
311
312 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
313 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
314
315 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
316 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
317 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
318 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
319
320 /* game button identifiers */
321 #define GAME_CTRL_ID_STOP               0
322 #define GAME_CTRL_ID_PAUSE              1
323 #define GAME_CTRL_ID_PLAY               2
324 #define SOUND_CTRL_ID_MUSIC             3
325 #define SOUND_CTRL_ID_LOOPS             4
326 #define SOUND_CTRL_ID_SIMPLE            5
327
328 #define NUM_GAME_BUTTONS                6
329
330
331 /* forward declaration for internal use */
332
333 static void AdvanceFrameAndPlayerCounters(int);
334
335 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
336 static boolean MovePlayer(struct PlayerInfo *, int, int);
337 static void ScrollPlayer(struct PlayerInfo *, int);
338 static void ScrollScreen(struct PlayerInfo *, int);
339
340 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
341
342 static void InitBeltMovement(void);
343 static void CloseAllOpenTimegates(void);
344 static void CheckGravityMovement(struct PlayerInfo *);
345 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
346 static void KillHeroUnlessEnemyProtected(int, int);
347 static void KillHeroUnlessExplosionProtected(int, int);
348
349 static void TestIfPlayerTouchesCustomElement(int, int);
350 static void TestIfElementTouchesCustomElement(int, int);
351 static void TestIfElementHitsCustomElement(int, int, int);
352 #if 0
353 static void TestIfElementSmashesCustomElement(int, int, int);
354 #endif
355
356 static void ChangeElement(int, int, int);
357
358 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
359 #define CheckTriggeredElementChange(x, y, e, ev)                        \
360         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY,      \
361                                        CH_SIDE_ANY, -1)
362 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
363         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
364 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
365         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
366 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
367         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY,      \
368                                        CH_SIDE_ANY, p)
369
370 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
371 #define CheckElementChange(x, y, e, te, ev)                             \
372         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
373 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
374         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, -1)
375 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
376         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, -1)
377 #define CheckElementChangeByPage(x, y, e, te, ev, p)                    \
378         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
379
380 static void PlayLevelSound(int, int, int);
381 static void PlayLevelSoundNearest(int, int, int);
382 static void PlayLevelSoundAction(int, int, int);
383 static void PlayLevelSoundElementAction(int, int, int, int);
384 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
385 static void PlayLevelSoundActionIfLoop(int, int, int);
386 static void StopLevelSoundActionIfLoop(int, int, int);
387 static void PlayLevelMusic();
388
389 static void MapGameButtons();
390 static void HandleGameButtons(struct GadgetInfo *);
391
392 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
393
394
395 /* ------------------------------------------------------------------------- */
396 /* definition of elements that automatically change to other elements after  */
397 /* a specified time, eventually calling a function when changing             */
398 /* ------------------------------------------------------------------------- */
399
400 /* forward declaration for changer functions */
401 static void InitBuggyBase(int x, int y);
402 static void WarnBuggyBase(int x, int y);
403
404 static void InitTrap(int x, int y);
405 static void ActivateTrap(int x, int y);
406 static void ChangeActiveTrap(int x, int y);
407
408 static void InitRobotWheel(int x, int y);
409 static void RunRobotWheel(int x, int y);
410 static void StopRobotWheel(int x, int y);
411
412 static void InitTimegateWheel(int x, int y);
413 static void RunTimegateWheel(int x, int y);
414
415 struct ChangingElementInfo
416 {
417   int element;
418   int target_element;
419   int change_delay;
420   void (*pre_change_function)(int x, int y);
421   void (*change_function)(int x, int y);
422   void (*post_change_function)(int x, int y);
423 };
424
425 static struct ChangingElementInfo change_delay_list[] =
426 {
427   {
428     EL_NUT_BREAKING,
429     EL_EMERALD,
430     6,
431     NULL,
432     NULL,
433     NULL
434   },
435   {
436     EL_PEARL_BREAKING,
437     EL_EMPTY,
438     8,
439     NULL,
440     NULL,
441     NULL
442   },
443   {
444     EL_EXIT_OPENING,
445     EL_EXIT_OPEN,
446     29,
447     NULL,
448     NULL,
449     NULL
450   },
451   {
452     EL_EXIT_CLOSING,
453     EL_EXIT_CLOSED,
454     29,
455     NULL,
456     NULL,
457     NULL
458   },
459   {
460     EL_SP_EXIT_OPENING,
461     EL_SP_EXIT_OPEN,
462     29,
463     NULL,
464     NULL,
465     NULL
466   },
467   {
468     EL_SP_EXIT_CLOSING,
469     EL_SP_EXIT_CLOSED,
470     29,
471     NULL,
472     NULL,
473     NULL
474   },
475   {
476     EL_SWITCHGATE_OPENING,
477     EL_SWITCHGATE_OPEN,
478     29,
479     NULL,
480     NULL,
481     NULL
482   },
483   {
484     EL_SWITCHGATE_CLOSING,
485     EL_SWITCHGATE_CLOSED,
486     29,
487     NULL,
488     NULL,
489     NULL
490   },
491   {
492     EL_TIMEGATE_OPENING,
493     EL_TIMEGATE_OPEN,
494     29,
495     NULL,
496     NULL,
497     NULL
498   },
499   {
500     EL_TIMEGATE_CLOSING,
501     EL_TIMEGATE_CLOSED,
502     29,
503     NULL,
504     NULL,
505     NULL
506   },
507
508   {
509     EL_ACID_SPLASH_LEFT,
510     EL_EMPTY,
511     8,
512     NULL,
513     NULL,
514     NULL
515   },
516   {
517     EL_ACID_SPLASH_RIGHT,
518     EL_EMPTY,
519     8,
520     NULL,
521     NULL,
522     NULL
523   },
524   {
525     EL_SP_BUGGY_BASE,
526     EL_SP_BUGGY_BASE_ACTIVATING,
527     0,
528     InitBuggyBase,
529     NULL,
530     NULL
531   },
532   {
533     EL_SP_BUGGY_BASE_ACTIVATING,
534     EL_SP_BUGGY_BASE_ACTIVE,
535     0,
536     InitBuggyBase,
537     NULL,
538     NULL
539   },
540   {
541     EL_SP_BUGGY_BASE_ACTIVE,
542     EL_SP_BUGGY_BASE,
543     0,
544     InitBuggyBase,
545     WarnBuggyBase,
546     NULL
547   },
548   {
549     EL_TRAP,
550     EL_TRAP_ACTIVE,
551     0,
552     InitTrap,
553     NULL,
554     ActivateTrap
555   },
556   {
557     EL_TRAP_ACTIVE,
558     EL_TRAP,
559     31,
560     NULL,
561     ChangeActiveTrap,
562     NULL
563   },
564   {
565     EL_ROBOT_WHEEL_ACTIVE,
566     EL_ROBOT_WHEEL,
567     0,
568     InitRobotWheel,
569     RunRobotWheel,
570     StopRobotWheel
571   },
572   {
573     EL_TIMEGATE_SWITCH_ACTIVE,
574     EL_TIMEGATE_SWITCH,
575     0,
576     InitTimegateWheel,
577     RunTimegateWheel,
578     NULL
579   },
580
581   {
582     EL_UNDEFINED,
583     EL_UNDEFINED,
584     -1,
585     NULL,
586     NULL,
587     NULL
588   }
589 };
590
591 struct
592 {
593   int element;
594   int push_delay_fixed, push_delay_random;
595 }
596 push_delay_list[] =
597 {
598   { EL_SPRING,                  0, 0 },
599   { EL_BALLOON,                 0, 0 },
600
601   { EL_SOKOBAN_OBJECT,          2, 0 },
602   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
603   { EL_SATELLITE,               2, 0 },
604   { EL_SP_DISK_YELLOW,          2, 0 },
605
606   { EL_UNDEFINED,               0, 0 },
607 };
608
609 struct
610 {
611   int element;
612   int move_stepsize;
613 }
614 move_stepsize_list[] =
615 {
616   { EL_AMOEBA_DROP,             2 },
617   { EL_AMOEBA_DROPPING,         2 },
618   { EL_QUICKSAND_FILLING,       1 },
619   { EL_QUICKSAND_EMPTYING,      1 },
620   { EL_MAGIC_WALL_FILLING,      2 },
621   { EL_BD_MAGIC_WALL_FILLING,   2 },
622   { EL_MAGIC_WALL_EMPTYING,     2 },
623   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
624
625   { EL_UNDEFINED,               0 },
626 };
627
628 struct
629 {
630   int element;
631   int count;
632 }
633 collect_count_list[] =
634 {
635   { EL_EMERALD,                 1 },
636   { EL_BD_DIAMOND,              1 },
637   { EL_EMERALD_YELLOW,          1 },
638   { EL_EMERALD_RED,             1 },
639   { EL_EMERALD_PURPLE,          1 },
640   { EL_DIAMOND,                 3 },
641   { EL_SP_INFOTRON,             1 },
642   { EL_PEARL,                   5 },
643   { EL_CRYSTAL,                 8 },
644
645   { EL_UNDEFINED,               0 },
646 };
647
648 struct
649 {
650   int element;
651   int direction;
652 }
653 access_direction_list[] =
654 {
655   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
656   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
657   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
658   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
659   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
660   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
661   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
662   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
663   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
664   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
665   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
666
667   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
668   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
669   { EL_SP_PORT_UP,                                                   MV_DOWN },
670   { EL_SP_PORT_DOWN,                                         MV_UP           },
671   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
672   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
673   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
674   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
675   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
676   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
677   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
678   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
679   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
680   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
681   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
682   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
683   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
684   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
685   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
686
687   { EL_UNDEFINED,                       MV_NO_MOVING                         }
688 };
689
690 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
691
692 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
693 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
694 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
695                                  IS_JUST_CHANGING(x, y))
696
697 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
698
699
700 void GetPlayerConfig()
701 {
702   if (!audio.sound_available)
703     setup.sound_simple = FALSE;
704
705   if (!audio.loops_available)
706     setup.sound_loops = FALSE;
707
708   if (!audio.music_available)
709     setup.sound_music = FALSE;
710
711   if (!video.fullscreen_available)
712     setup.fullscreen = FALSE;
713
714   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
715
716   SetAudioMode(setup.sound);
717   InitJoysticks();
718 }
719
720 static int getBeltNrFromBeltElement(int element)
721 {
722   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
723           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
724           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
725 }
726
727 static int getBeltNrFromBeltActiveElement(int element)
728 {
729   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
730           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
731           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
732 }
733
734 static int getBeltNrFromBeltSwitchElement(int element)
735 {
736   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
737           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
738           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
739 }
740
741 static int getBeltDirNrFromBeltSwitchElement(int element)
742 {
743   static int belt_base_element[4] =
744   {
745     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
746     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
747     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
748     EL_CONVEYOR_BELT_4_SWITCH_LEFT
749   };
750
751   int belt_nr = getBeltNrFromBeltSwitchElement(element);
752   int belt_dir_nr = element - belt_base_element[belt_nr];
753
754   return (belt_dir_nr % 3);
755 }
756
757 static int getBeltDirFromBeltSwitchElement(int element)
758 {
759   static int belt_move_dir[3] =
760   {
761     MV_LEFT,
762     MV_NO_MOVING,
763     MV_RIGHT
764   };
765
766   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
767
768   return belt_move_dir[belt_dir_nr];
769 }
770
771 static void InitPlayerField(int x, int y, int element, boolean init_game)
772 {
773   if (element == EL_SP_MURPHY)
774   {
775     if (init_game)
776     {
777       if (stored_player[0].present)
778       {
779         Feld[x][y] = EL_SP_MURPHY_CLONE;
780
781         return;
782       }
783       else
784       {
785         stored_player[0].use_murphy_graphic = TRUE;
786       }
787
788       Feld[x][y] = EL_PLAYER_1;
789     }
790   }
791
792   if (init_game)
793   {
794     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
795     int jx = player->jx, jy = player->jy;
796
797     player->present = TRUE;
798
799     player->block_last_field = (element == EL_SP_MURPHY ?
800                                 level.sp_block_last_field :
801                                 level.block_last_field);
802
803 #if USE_NEW_BLOCK_STYLE
804 #if 1
805
806     /* ---------- initialize player's last field block delay --------------- */
807
808     /* always start with reliable default value (no adjustment needed) */
809     player->block_delay_adjustment = 0;
810
811     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
812     if (player->block_last_field && element == EL_SP_MURPHY)
813       player->block_delay_adjustment = 1;
814
815     /* special case 2: in game engines before 3.1.1, blocking was different */
816     if (game.use_block_last_field_bug)
817       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
818
819 #if 0
820     /* blocking the last field when moving was corrected in version 3.1.1 */
821     if (game.use_block_last_field_bug)
822     {
823       /* even "not blocking" was blocking the last field for one frame */
824       level.block_delay    = (level.block_last_field    ? 7 : 1);
825       level.sp_block_delay = (level.sp_block_last_field ? 7 : 1);
826
827       level.block_last_field = TRUE;
828       level.sp_block_last_field = TRUE;
829     }
830 #endif
831
832 #if 0   /* !!! THIS IS NOT A LEVEL SETTING => REMOVED !!! */
833     level.block_delay = 8;              /* when blocking, block 8 frames */
834     level.sp_block_delay = 9;           /* SP indeed blocks 9 frames, not 8 */
835 #endif
836
837 #if 0
838     printf("::: %d, %d\n", level.block_delay, level.sp_block_delay);
839 #endif
840
841 #else
842
843 #if 1
844     player->block_delay = (player->block_last_field ?
845                            (element == EL_SP_MURPHY ?
846                             level.sp_block_delay :
847                             level.block_delay) : 0);
848 #else
849     player->block_delay = (element == EL_SP_MURPHY ?
850                            (player->block_last_field ? 7 : 1) :
851                            (player->block_last_field ? 7 : 1));
852 #endif
853
854 #endif
855
856 #if 0
857     printf("::: block_last_field == %d, block_delay = %d\n",
858            player->block_last_field, player->block_delay);
859 #endif
860 #endif
861
862     if (!options.network || player->connected)
863     {
864       player->active = TRUE;
865
866       /* remove potentially duplicate players */
867       if (StorePlayer[jx][jy] == Feld[x][y])
868         StorePlayer[jx][jy] = 0;
869
870       StorePlayer[x][y] = Feld[x][y];
871
872       if (options.debug)
873       {
874         printf("Player %d activated.\n", player->element_nr);
875         printf("[Local player is %d and currently %s.]\n",
876                local_player->element_nr,
877                local_player->active ? "active" : "not active");
878       }
879     }
880
881     Feld[x][y] = EL_EMPTY;
882
883     player->jx = player->last_jx = x;
884     player->jy = player->last_jy = y;
885   }
886 }
887
888 static void InitField(int x, int y, boolean init_game)
889 {
890   int element = Feld[x][y];
891
892   switch (element)
893   {
894     case EL_SP_MURPHY:
895     case EL_PLAYER_1:
896     case EL_PLAYER_2:
897     case EL_PLAYER_3:
898     case EL_PLAYER_4:
899       InitPlayerField(x, y, element, init_game);
900       break;
901
902     case EL_SOKOBAN_FIELD_PLAYER:
903       element = Feld[x][y] = EL_PLAYER_1;
904       InitField(x, y, init_game);
905
906       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
907       InitField(x, y, init_game);
908       break;
909
910     case EL_SOKOBAN_FIELD_EMPTY:
911       local_player->sokobanfields_still_needed++;
912       break;
913
914     case EL_STONEBLOCK:
915       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
916         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
917       else if (x > 0 && Feld[x-1][y] == EL_ACID)
918         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
919       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
920         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
921       else if (y > 0 && Feld[x][y-1] == EL_ACID)
922         Feld[x][y] = EL_ACID_POOL_BOTTOM;
923       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
924         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
925       break;
926
927     case EL_BUG_RIGHT:
928     case EL_BUG_UP:
929     case EL_BUG_LEFT:
930     case EL_BUG_DOWN:
931     case EL_BUG:
932     case EL_SPACESHIP_RIGHT:
933     case EL_SPACESHIP_UP:
934     case EL_SPACESHIP_LEFT:
935     case EL_SPACESHIP_DOWN:
936     case EL_SPACESHIP:
937     case EL_BD_BUTTERFLY_RIGHT:
938     case EL_BD_BUTTERFLY_UP:
939     case EL_BD_BUTTERFLY_LEFT:
940     case EL_BD_BUTTERFLY_DOWN:
941     case EL_BD_BUTTERFLY:
942     case EL_BD_FIREFLY_RIGHT:
943     case EL_BD_FIREFLY_UP:
944     case EL_BD_FIREFLY_LEFT:
945     case EL_BD_FIREFLY_DOWN:
946     case EL_BD_FIREFLY:
947     case EL_PACMAN_RIGHT:
948     case EL_PACMAN_UP:
949     case EL_PACMAN_LEFT:
950     case EL_PACMAN_DOWN:
951     case EL_YAMYAM:
952     case EL_DARK_YAMYAM:
953     case EL_ROBOT:
954     case EL_PACMAN:
955     case EL_SP_SNIKSNAK:
956     case EL_SP_ELECTRON:
957     case EL_MOLE_LEFT:
958     case EL_MOLE_RIGHT:
959     case EL_MOLE_UP:
960     case EL_MOLE_DOWN:
961     case EL_MOLE:
962       InitMovDir(x, y);
963       break;
964
965     case EL_AMOEBA_FULL:
966     case EL_BD_AMOEBA:
967       InitAmoebaNr(x, y);
968       break;
969
970     case EL_AMOEBA_DROP:
971       if (y == lev_fieldy - 1)
972       {
973         Feld[x][y] = EL_AMOEBA_GROWING;
974         Store[x][y] = EL_AMOEBA_WET;
975       }
976       break;
977
978     case EL_DYNAMITE_ACTIVE:
979     case EL_SP_DISK_RED_ACTIVE:
980     case EL_DYNABOMB_PLAYER_1_ACTIVE:
981     case EL_DYNABOMB_PLAYER_2_ACTIVE:
982     case EL_DYNABOMB_PLAYER_3_ACTIVE:
983     case EL_DYNABOMB_PLAYER_4_ACTIVE:
984       MovDelay[x][y] = 96;
985       break;
986
987     case EL_LAMP:
988       local_player->lights_still_needed++;
989       break;
990
991     case EL_PENGUIN:
992       local_player->friends_still_needed++;
993       break;
994
995     case EL_PIG:
996     case EL_DRAGON:
997       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
998       break;
999
1000 #if 0
1001     case EL_SP_EMPTY:
1002       Feld[x][y] = EL_EMPTY;
1003       break;
1004 #endif
1005
1006 #if 0
1007     case EL_EM_KEY_1_FILE:
1008       Feld[x][y] = EL_EM_KEY_1;
1009       break;
1010     case EL_EM_KEY_2_FILE:
1011       Feld[x][y] = EL_EM_KEY_2;
1012       break;
1013     case EL_EM_KEY_3_FILE:
1014       Feld[x][y] = EL_EM_KEY_3;
1015       break;
1016     case EL_EM_KEY_4_FILE:
1017       Feld[x][y] = EL_EM_KEY_4;
1018       break;
1019 #endif
1020
1021     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1022     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1023     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1024     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1025     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1026     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1027     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1028     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1029     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1030     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1031     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1032     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1033       if (init_game)
1034       {
1035         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1036         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1037         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1038
1039         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1040         {
1041           game.belt_dir[belt_nr] = belt_dir;
1042           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1043         }
1044         else    /* more than one switch -- set it like the first switch */
1045         {
1046           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1047         }
1048       }
1049       break;
1050
1051     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1052       if (init_game)
1053         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1054       break;
1055
1056     case EL_LIGHT_SWITCH_ACTIVE:
1057       if (init_game)
1058         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1059       break;
1060
1061     default:
1062       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
1063         InitMovDir(x, y);
1064       else if (IS_GROUP_ELEMENT(element))
1065       {
1066         struct ElementGroupInfo *group = element_info[element].group;
1067         int last_anim_random_frame = gfx.anim_random_frame;
1068         int element_pos;
1069
1070         if (group->choice_mode == ANIM_RANDOM)
1071           gfx.anim_random_frame = RND(group->num_elements_resolved);
1072
1073         element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1074                                         group->choice_mode, 0,
1075                                         group->choice_pos);
1076
1077         if (group->choice_mode == ANIM_RANDOM)
1078           gfx.anim_random_frame = last_anim_random_frame;
1079
1080         group->choice_pos++;
1081
1082         Feld[x][y] = group->element_resolved[element_pos];
1083
1084         InitField(x, y, init_game);
1085       }
1086       break;
1087   }
1088 }
1089
1090 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1091 {
1092   InitField(x, y, init_game);
1093
1094   /* not needed to call InitMovDir() -- already done by InitField()! */
1095   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1096       CAN_MOVE(Feld[x][y]))
1097     InitMovDir(x, y);
1098 }
1099
1100 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1101 {
1102   int old_element = Feld[x][y];
1103
1104   InitField(x, y, init_game);
1105
1106   /* not needed to call InitMovDir() -- already done by InitField()! */
1107   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1108       CAN_MOVE(old_element) &&
1109       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1110     InitMovDir(x, y);
1111
1112   /* this case is in fact a combination of not less than three bugs:
1113      first, it calls InitMovDir() for elements that can move, although this is
1114      already done by InitField(); then, it checks the element that was at this
1115      field _before_ the call to InitField() (which can change it); lastly, it
1116      was not called for "mole with direction" elements, which were treated as
1117      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1118   */
1119 }
1120
1121 inline void DrawGameValue_Emeralds(int value)
1122 {
1123   DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
1124 }
1125
1126 inline void DrawGameValue_Dynamite(int value)
1127 {
1128   DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
1129 }
1130
1131 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
1132 {
1133   int i;
1134
1135   /* currently only 4 of 8 possible keys are displayed */
1136   for (i = 0; i < STD_NUM_KEYS; i++)
1137   {
1138     if (key[i])
1139       DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
1140                          el2edimg(EL_KEY_1 + i));
1141     else
1142       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1143                  DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
1144                  MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
1145   }
1146 }
1147
1148 inline void DrawGameValue_Score(int value)
1149 {
1150   DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
1151 }
1152
1153 inline void DrawGameValue_Time(int value)
1154 {
1155   if (value < 1000)
1156     DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
1157   else
1158     DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
1159 }
1160
1161 inline void DrawGameValue_Level(int value)
1162 {
1163   if (level_nr < 100)
1164     DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
1165   else
1166   {
1167     /* misuse area for displaying emeralds to draw bigger level number */
1168     DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
1169                 int2str(value, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1170
1171     /* now copy it to the area for displaying level number */
1172     BlitBitmap(drawto, drawto,
1173                DX_EMERALDS, DY_EMERALDS + 1,
1174                getFontWidth(FONT_LEVEL_NUMBER) * 3,
1175                getFontHeight(FONT_LEVEL_NUMBER) - 1,
1176                DX_LEVEL - 1, DY_LEVEL + 1);
1177
1178     /* restore the area for displaying emeralds */
1179     DrawGameValue_Emeralds(local_player->gems_still_needed);
1180
1181     /* yes, this is all really ugly :-) */
1182   }
1183 }
1184
1185 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
1186                        int key_bits)
1187 {
1188   int key[MAX_NUM_KEYS];
1189   int i;
1190
1191   for (i = 0; i < MAX_NUM_KEYS; i++)
1192     key[i] = key_bits & (1 << i);
1193
1194   DrawGameValue_Level(level_nr);
1195
1196   DrawGameValue_Emeralds(emeralds);
1197   DrawGameValue_Dynamite(dynamite);
1198   DrawGameValue_Score(score);
1199   DrawGameValue_Time(time);
1200
1201   DrawGameValue_Keys(key);
1202 }
1203
1204 void DrawGameDoorValues()
1205 {
1206   int i;
1207
1208   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
1209   {
1210     DrawGameDoorValues_EM();
1211
1212     return;
1213   }
1214
1215   DrawGameValue_Level(level_nr);
1216
1217   DrawGameValue_Emeralds(local_player->gems_still_needed);
1218   DrawGameValue_Dynamite(local_player->inventory_size);
1219   DrawGameValue_Score(local_player->score);
1220   DrawGameValue_Time(TimeLeft);
1221
1222   for (i = 0; i < MAX_PLAYERS; i++)
1223     DrawGameValue_Keys(stored_player[i].key);
1224 }
1225
1226 static void resolve_group_element(int group_element, int recursion_depth)
1227 {
1228   static int group_nr;
1229   static struct ElementGroupInfo *group;
1230   struct ElementGroupInfo *actual_group = element_info[group_element].group;
1231   int i;
1232
1233   if (recursion_depth > NUM_GROUP_ELEMENTS)     /* recursion too deep */
1234   {
1235     Error(ERR_WARN, "recursion too deep when resolving group element %d",
1236           group_element - EL_GROUP_START + 1);
1237
1238     /* replace element which caused too deep recursion by question mark */
1239     group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
1240
1241     return;
1242   }
1243
1244   if (recursion_depth == 0)                     /* initialization */
1245   {
1246     group = element_info[group_element].group;
1247     group_nr = group_element - EL_GROUP_START;
1248
1249     group->num_elements_resolved = 0;
1250     group->choice_pos = 0;
1251   }
1252
1253   for (i = 0; i < actual_group->num_elements; i++)
1254   {
1255     int element = actual_group->element[i];
1256
1257     if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
1258       break;
1259
1260     if (IS_GROUP_ELEMENT(element))
1261       resolve_group_element(element, recursion_depth + 1);
1262     else
1263     {
1264       group->element_resolved[group->num_elements_resolved++] = element;
1265       element_info[element].in_group[group_nr] = TRUE;
1266     }
1267   }
1268
1269 #if 0
1270   if (recursion_depth == 0 && group_element <= EL_GROUP_4)
1271   {
1272     printf("::: group %d: %d resolved elements\n",
1273            group_element - EL_GROUP_START, group->num_elements_resolved);
1274     for (i = 0; i < group->num_elements_resolved; i++)
1275       printf("::: - %d ['%s']\n", group->element_resolved[i],
1276              element_info[group->element_resolved[i]].token_name);
1277   }
1278 #endif
1279 }
1280
1281
1282 /*
1283   =============================================================================
1284   InitGameEngine()
1285   -----------------------------------------------------------------------------
1286   initialize game engine due to level / tape version number
1287   =============================================================================
1288 */
1289
1290 static void InitGameEngine()
1291 {
1292   int i, j, k, l;
1293
1294   /* set game engine from tape file when re-playing, else from level file */
1295   game.engine_version = (tape.playing ? tape.engine_version :
1296                          level.game_version);
1297
1298   /* ---------------------------------------------------------------------- */
1299   /* set flags for bugs and changes according to active game engine version */
1300   /* ---------------------------------------------------------------------- */
1301
1302   /*
1303     Summary of bugfix/change:
1304     Fixed handling for custom elements that change when pushed by the player.
1305
1306     Fixed/changed in version:
1307     3.1.0
1308
1309     Description:
1310     Before 3.1.0, custom elements that "change when pushing" changed directly
1311     after the player started pushing them (until then handled in "DigField()").
1312     Since 3.1.0, these custom elements are not changed until the "pushing"
1313     move of the element is finished (now handled in "ContinueMoving()").
1314
1315     Affected levels/tapes:
1316     The first condition is generally needed for all levels/tapes before version
1317     3.1.0, which might use the old behaviour before it was changed; known tapes
1318     that are affected are some tapes from the level set "Walpurgis Gardens" by
1319     Jamie Cullen.
1320     The second condition is an exception from the above case and is needed for
1321     the special case of tapes recorded with game (not engine!) version 3.1.0 or
1322     above (including some development versions of 3.1.0), but before it was
1323     known that this change would break tapes like the above and was fixed in
1324     3.1.1, so that the changed behaviour was active although the engine version
1325     while recording maybe was before 3.1.0. There is at least one tape that is
1326     affected by this exception, which is the tape for the one-level set "Bug
1327     Machine" by Juergen Bonhagen.
1328   */
1329
1330   game.use_change_when_pushing_bug =
1331     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1332      !(tape.playing &&
1333        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
1334        tape.game_version <  VERSION_IDENT(3,1,1,0)));
1335
1336   /*
1337     Summary of bugfix/change:
1338     Fixed handling for blocking the field the player leaves when moving.
1339
1340     Fixed/changed in version:
1341     3.1.1
1342
1343     Description:
1344     Before 3.1.1, when "block last field when moving" was enabled, the field
1345     the player is leaving when moving was blocked for the time of the move,
1346     and was directly unblocked afterwards. This resulted in the last field
1347     being blocked for exactly one less than the number of frames of one player
1348     move. Additionally, even when blocking was disabled, the last field was
1349     blocked for exactly one frame.
1350     Since 3.1.1, due to changes in player movement handling, the last field
1351     is not blocked at all when blocking is disabled. When blocking is enabled,
1352     the last field is blocked for exactly the number of frames of one player
1353     move. Additionally, if the player is Murphy, the hero of Supaplex, the
1354     last field is blocked for exactly one more than the number of frames of
1355     one player move.
1356
1357     Affected levels/tapes:
1358     (!!! yet to be determined -- probably many !!!)
1359   */
1360
1361   game.use_block_last_field_bug =
1362     (game.engine_version < VERSION_IDENT(3,1,1,0));
1363
1364   /* ---------------------------------------------------------------------- */
1365
1366   /* dynamically adjust element properties according to game engine version */
1367   InitElementPropertiesEngine(game.engine_version);
1368
1369 #if 0
1370   printf("level %d: level version == %06d\n", level_nr, level.game_version);
1371   printf("          tape version == %06d [%s] [file: %06d]\n",
1372          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
1373          tape.file_version);
1374   printf("       => game.engine_version == %06d\n", game.engine_version);
1375 #endif
1376
1377   /* ---------- recursively resolve group elements ------------------------- */
1378
1379   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1380     for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
1381       element_info[i].in_group[j] = FALSE;
1382
1383   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
1384     resolve_group_element(EL_GROUP_START + i, 0);
1385
1386   /* ---------- initialize player's initial move delay --------------------- */
1387
1388 #if USE_NEW_MOVE_DELAY
1389   /* dynamically adjust player properties according to level information */
1390   game.initial_move_delay_value =
1391     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1392
1393   /* dynamically adjust player properties according to game engine version */
1394   game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
1395                              game.initial_move_delay_value : 0);
1396 #else
1397   /* dynamically adjust player properties according to game engine version */
1398   game.initial_move_delay =
1399     (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
1400      INITIAL_MOVE_DELAY_OFF);
1401
1402   /* dynamically adjust player properties according to level information */
1403   game.initial_move_delay_value =
1404     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
1405 #endif
1406
1407   /* ---------- initialize player's initial push delay --------------------- */
1408
1409   /* dynamically adjust player properties according to game engine version */
1410   game.initial_push_delay_value =
1411     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
1412
1413   /* ---------- initialize changing elements ------------------------------- */
1414
1415   /* initialize changing elements information */
1416   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1417   {
1418     struct ElementInfo *ei = &element_info[i];
1419
1420     /* this pointer might have been changed in the level editor */
1421     ei->change = &ei->change_page[0];
1422
1423     if (!IS_CUSTOM_ELEMENT(i))
1424     {
1425       ei->change->target_element = EL_EMPTY_SPACE;
1426       ei->change->delay_fixed = 0;
1427       ei->change->delay_random = 0;
1428       ei->change->delay_frames = 1;
1429     }
1430
1431     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1432     {
1433       ei->has_change_event[j] = FALSE;
1434
1435       ei->event_page_nr[j] = 0;
1436       ei->event_page[j] = &ei->change_page[0];
1437     }
1438   }
1439
1440   /* add changing elements from pre-defined list */
1441   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
1442   {
1443     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1444     struct ElementInfo *ei = &element_info[ch_delay->element];
1445
1446     ei->change->target_element       = ch_delay->target_element;
1447     ei->change->delay_fixed          = ch_delay->change_delay;
1448
1449     ei->change->pre_change_function  = ch_delay->pre_change_function;
1450     ei->change->change_function      = ch_delay->change_function;
1451     ei->change->post_change_function = ch_delay->post_change_function;
1452
1453     ei->has_change_event[CE_DELAY] = TRUE;
1454
1455 #if 1
1456     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
1457 #endif
1458   }
1459
1460 #if 1
1461   /* add change events from custom element configuration */
1462   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1463   {
1464     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1465
1466     for (j = 0; j < ei->num_change_pages; j++)
1467     {
1468       if (!ei->change_page[j].can_change)
1469         continue;
1470
1471       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1472       {
1473         /* only add event page for the first page found with this event */
1474         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
1475         {
1476           ei->has_change_event[k] = TRUE;
1477
1478           ei->event_page_nr[k] = j;
1479           ei->event_page[k] = &ei->change_page[j];
1480         }
1481       }
1482     }
1483   }
1484
1485 #else
1486
1487   /* add change events from custom element configuration */
1488   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1489   {
1490     int element = EL_CUSTOM_START + i;
1491
1492     /* only add custom elements that change after fixed/random frame delay */
1493     if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1494       element_info[element].has_change_event[CE_DELAY] = TRUE;
1495   }
1496 #endif
1497
1498   /* ---------- initialize internal run-time variables ------------- */
1499
1500   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1501   {
1502     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1503
1504     for (j = 0; j < ei->num_change_pages; j++)
1505     {
1506       ei->change_page[j].can_change_or_has_action =
1507         (ei->change_page[j].can_change |
1508          ei->change_page[j].has_action);
1509     }
1510   }
1511
1512   /* ---------- initialize run-time trigger player and element ------------- */
1513
1514   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1515   {
1516     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1517
1518     for (j = 0; j < ei->num_change_pages; j++)
1519     {
1520       ei->change_page[j].actual_trigger_element = EL_EMPTY;
1521       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
1522     }
1523   }
1524
1525   /* ---------- initialize trigger events ---------------------------------- */
1526
1527   /* initialize trigger events information */
1528   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1529     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1530       trigger_events[i][j] = FALSE;
1531
1532 #if 1
1533   /* add trigger events from element change event properties */
1534   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1535   {
1536     struct ElementInfo *ei = &element_info[i];
1537
1538     for (j = 0; j < ei->num_change_pages; j++)
1539     {
1540       if (!ei->change_page[j].can_change)
1541         continue;
1542
1543       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
1544       {
1545         int trigger_element = ei->change_page[j].trigger_element;
1546
1547         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1548         {
1549           if (ei->change_page[j].has_event[k])
1550           {
1551             if (IS_GROUP_ELEMENT(trigger_element))
1552             {
1553               struct ElementGroupInfo *group =
1554                 element_info[trigger_element].group;
1555
1556               for (l = 0; l < group->num_elements_resolved; l++)
1557                 trigger_events[group->element_resolved[l]][k] = TRUE;
1558             }
1559             else
1560               trigger_events[trigger_element][k] = TRUE;
1561           }
1562         }
1563       }
1564     }
1565   }
1566 #else
1567   /* add trigger events from element change event properties */
1568   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1569     if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1570       for (j = 0; j < NUM_CHANGE_EVENTS; j++)
1571         if (element_info[i].change->has_event[j])
1572           trigger_events[element_info[i].change->trigger_element][j] = TRUE;
1573 #endif
1574
1575   /* ---------- initialize push delay -------------------------------------- */
1576
1577   /* initialize push delay values to default */
1578   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1579   {
1580     if (!IS_CUSTOM_ELEMENT(i))
1581     {
1582       element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
1583       element_info[i].push_delay_random = game.default_push_delay_random;
1584     }
1585   }
1586
1587   /* set push delay value for certain elements from pre-defined list */
1588   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1589   {
1590     int e = push_delay_list[i].element;
1591
1592     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1593     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1594   }
1595
1596   /* set push delay value for Supaplex elements for newer engine versions */
1597   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
1598   {
1599     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1600     {
1601       if (IS_SP_ELEMENT(i))
1602       {
1603 #if USE_NEW_MOVE_STYLE
1604         /* set SP push delay to just enough to push under a falling zonk */
1605         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
1606
1607         element_info[i].push_delay_fixed  = delay;
1608         element_info[i].push_delay_random = 0;
1609 #else
1610         element_info[i].push_delay_fixed  = 6;  /* just enough to escape ... */
1611         element_info[i].push_delay_random = 0;  /* ... from falling zonk     */
1612 #endif
1613       }
1614     }
1615   }
1616
1617   /* ---------- initialize move stepsize ----------------------------------- */
1618
1619   /* initialize move stepsize values to default */
1620   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1621     if (!IS_CUSTOM_ELEMENT(i))
1622       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1623
1624   /* set move stepsize value for certain elements from pre-defined list */
1625   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1626   {
1627     int e = move_stepsize_list[i].element;
1628
1629     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1630   }
1631
1632 #if 0
1633   /* ---------- initialize move dig/leave ---------------------------------- */
1634
1635   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1636   {
1637     element_info[i].can_leave_element = FALSE;
1638     element_info[i].can_leave_element_last = FALSE;
1639   }
1640 #endif
1641
1642   /* ---------- initialize gem count --------------------------------------- */
1643
1644   /* initialize gem count values for each element */
1645   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1646     if (!IS_CUSTOM_ELEMENT(i))
1647       element_info[i].collect_count = 0;
1648
1649   /* add gem count values for all elements from pre-defined list */
1650   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1651     element_info[collect_count_list[i].element].collect_count =
1652       collect_count_list[i].count;
1653
1654   /* ---------- initialize access direction -------------------------------- */
1655
1656   /* initialize access direction values to default (access from every side) */
1657   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1658     if (!IS_CUSTOM_ELEMENT(i))
1659       element_info[i].access_direction = MV_ALL_DIRECTIONS;
1660
1661   /* set access direction value for certain elements from pre-defined list */
1662   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
1663     element_info[access_direction_list[i].element].access_direction =
1664       access_direction_list[i].direction;
1665 }
1666
1667
1668 /*
1669   =============================================================================
1670   InitGame()
1671   -----------------------------------------------------------------------------
1672   initialize and start new game
1673   =============================================================================
1674 */
1675
1676 void InitGame()
1677 {
1678   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
1679   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
1680   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
1681   int i, j, k, x, y;
1682
1683   InitGameEngine();
1684
1685 #if 0
1686 #if DEBUG
1687 #if USE_NEW_AMOEBA_CODE
1688   printf("Using new amoeba code.\n");
1689 #else
1690   printf("Using old amoeba code.\n");
1691 #endif
1692 #endif
1693 #endif
1694
1695   /* don't play tapes over network */
1696   network_playing = (options.network && !tape.playing);
1697
1698   for (i = 0; i < MAX_PLAYERS; i++)
1699   {
1700     struct PlayerInfo *player = &stored_player[i];
1701
1702     player->index_nr = i;
1703     player->index_bit = (1 << i);
1704     player->element_nr = EL_PLAYER_1 + i;
1705
1706     player->present = FALSE;
1707     player->active = FALSE;
1708
1709     player->action = 0;
1710     player->effective_action = 0;
1711     player->programmed_action = 0;
1712
1713     player->score = 0;
1714     player->gems_still_needed = level.gems_needed;
1715     player->sokobanfields_still_needed = 0;
1716     player->lights_still_needed = 0;
1717     player->friends_still_needed = 0;
1718
1719     for (j = 0; j < MAX_NUM_KEYS; j++)
1720       player->key[j] = FALSE;
1721
1722     player->dynabomb_count = 0;
1723     player->dynabomb_size = 1;
1724     player->dynabombs_left = 0;
1725     player->dynabomb_xl = FALSE;
1726
1727     player->MovDir = MV_NO_MOVING;
1728     player->MovPos = 0;
1729     player->GfxPos = 0;
1730     player->GfxDir = MV_NO_MOVING;
1731     player->GfxAction = ACTION_DEFAULT;
1732     player->Frame = 0;
1733     player->StepFrame = 0;
1734
1735     player->use_murphy_graphic = FALSE;
1736
1737     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
1738     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
1739
1740     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
1741
1742     player->actual_frame_counter = 0;
1743
1744     player->step_counter = 0;
1745
1746     player->last_move_dir = MV_NO_MOVING;
1747
1748     player->is_waiting = FALSE;
1749     player->is_moving = FALSE;
1750     player->is_auto_moving = FALSE;
1751     player->is_digging = FALSE;
1752     player->is_snapping = FALSE;
1753     player->is_collecting = FALSE;
1754     player->is_pushing = FALSE;
1755     player->is_switching = FALSE;
1756     player->is_dropping = FALSE;
1757
1758     player->is_bored = FALSE;
1759     player->is_sleeping = FALSE;
1760
1761     player->frame_counter_bored = -1;
1762     player->frame_counter_sleeping = -1;
1763
1764     player->anim_delay_counter = 0;
1765     player->post_delay_counter = 0;
1766
1767     player->action_waiting = ACTION_DEFAULT;
1768     player->last_action_waiting = ACTION_DEFAULT;
1769     player->special_action_bored = ACTION_DEFAULT;
1770     player->special_action_sleeping = ACTION_DEFAULT;
1771
1772     player->num_special_action_bored = 0;
1773     player->num_special_action_sleeping = 0;
1774
1775     /* determine number of special actions for bored and sleeping animation */
1776     for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1777     {
1778       boolean found = FALSE;
1779
1780       for (k = 0; k < NUM_DIRECTIONS; k++)
1781         if (el_act_dir2img(player->element_nr, j, k) !=
1782             el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1783           found = TRUE;
1784
1785       if (found)
1786         player->num_special_action_bored++;
1787       else
1788         break;
1789     }
1790     for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1791     {
1792       boolean found = FALSE;
1793
1794       for (k = 0; k < NUM_DIRECTIONS; k++)
1795         if (el_act_dir2img(player->element_nr, j, k) !=
1796             el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1797           found = TRUE;
1798
1799       if (found)
1800         player->num_special_action_sleeping++;
1801       else
1802         break;
1803     }
1804
1805     player->switch_x = -1;
1806     player->switch_y = -1;
1807
1808 #if USE_DROP_BUGFIX
1809     player->drop_x = -1;
1810     player->drop_y = -1;
1811 #endif
1812
1813     player->show_envelope = 0;
1814
1815     player->move_delay       = game.initial_move_delay;
1816     player->move_delay_value = game.initial_move_delay_value;
1817
1818     player->move_delay_reset_counter = 0;
1819
1820 #if USE_NEW_PUSH_DELAY
1821     player->push_delay       = -1;      /* initialized when pushing starts */
1822     player->push_delay_value = game.initial_push_delay_value;
1823 #else
1824     player->push_delay       = 0;
1825     player->push_delay_value = game.initial_push_delay_value;
1826 #endif
1827
1828     player->drop_delay = 0;
1829
1830     player->last_jx = player->last_jy = 0;
1831     player->jx = player->jy = 0;
1832
1833     player->shield_normal_time_left = 0;
1834     player->shield_deadly_time_left = 0;
1835
1836     player->inventory_infinite_element = EL_UNDEFINED;
1837     player->inventory_size = 0;
1838
1839     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
1840     SnapField(player, 0, 0);
1841
1842     player->LevelSolved = FALSE;
1843     player->GameOver = FALSE;
1844   }
1845
1846   network_player_action_received = FALSE;
1847
1848 #if defined(NETWORK_AVALIABLE)
1849   /* initial null action */
1850   if (network_playing)
1851     SendToServer_MovePlayer(MV_NO_MOVING);
1852 #endif
1853
1854   ZX = ZY = -1;
1855   ExitX = ExitY = -1;
1856
1857   FrameCounter = 0;
1858   TimeFrames = 0;
1859   TimePlayed = 0;
1860   TimeLeft = level.time;
1861   TapeTime = 0;
1862
1863   ScreenMovDir = MV_NO_MOVING;
1864   ScreenMovPos = 0;
1865   ScreenGfxPos = 0;
1866
1867   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
1868
1869   AllPlayersGone = FALSE;
1870
1871   game.yamyam_content_nr = 0;
1872   game.magic_wall_active = FALSE;
1873   game.magic_wall_time_left = 0;
1874   game.light_time_left = 0;
1875   game.timegate_time_left = 0;
1876   game.switchgate_pos = 0;
1877   game.balloon_dir = MV_NO_MOVING;
1878   game.gravity = level.initial_gravity;
1879   game.explosions_delayed = TRUE;
1880
1881   game.envelope_active = FALSE;
1882
1883   for (i = 0; i < NUM_BELTS; i++)
1884   {
1885     game.belt_dir[i] = MV_NO_MOVING;
1886     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
1887   }
1888
1889   for (i = 0; i < MAX_NUM_AMOEBA; i++)
1890     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1891
1892   for (x = 0; x < lev_fieldx; x++)
1893   {
1894     for (y = 0; y < lev_fieldy; y++)
1895     {
1896       Feld[x][y] = level.field[x][y];
1897       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1898       ChangeDelay[x][y] = 0;
1899       ChangePage[x][y] = -1;
1900       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1901       AmoebaNr[x][y] = 0;
1902       WasJustMoving[x][y] = 0;
1903       WasJustFalling[x][y] = 0;
1904       CheckCollision[x][y] = 0;
1905       Stop[x][y] = FALSE;
1906       Pushed[x][y] = FALSE;
1907
1908       Changed[x][y] = FALSE;
1909       ChangeEvent[x][y] = -1;
1910
1911       ExplodePhase[x][y] = 0;
1912       ExplodeDelay[x][y] = 0;
1913       ExplodeField[x][y] = EX_TYPE_NONE;
1914
1915       RunnerVisit[x][y] = 0;
1916       PlayerVisit[x][y] = 0;
1917
1918       GfxFrame[x][y] = 0;
1919       GfxRandom[x][y] = INIT_GFX_RANDOM();
1920       GfxElement[x][y] = EL_UNDEFINED;
1921       GfxAction[x][y] = ACTION_DEFAULT;
1922       GfxDir[x][y] = MV_NO_MOVING;
1923     }
1924   }
1925
1926   for (y = 0; y < lev_fieldy; y++)
1927   {
1928     for (x = 0; x < lev_fieldx; x++)
1929     {
1930       if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1931         emulate_bd = FALSE;
1932       if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1933         emulate_sb = FALSE;
1934       if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1935         emulate_sp = FALSE;
1936
1937       InitField(x, y, TRUE);
1938     }
1939   }
1940
1941   InitBeltMovement();
1942
1943   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1944                     emulate_sb ? EMU_SOKOBAN :
1945                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1946
1947   /* initialize explosion and ignition delay */
1948   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1949   {
1950     if (!IS_CUSTOM_ELEMENT(i))
1951     {
1952       int num_phase = 8;
1953       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
1954                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
1955                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
1956       int last_phase = (num_phase + 1) * delay;
1957       int half_phase = (num_phase / 2) * delay;
1958
1959       element_info[i].explosion_delay = last_phase - 1;
1960       element_info[i].ignition_delay = half_phase;
1961
1962 #if 0
1963       if (i == EL_BLACK_ORB)
1964         element_info[i].ignition_delay = 0;
1965 #else
1966       if (i == EL_BLACK_ORB)
1967         element_info[i].ignition_delay = 1;
1968 #endif
1969     }
1970
1971 #if 0
1972     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
1973       element_info[i].explosion_delay = 1;
1974
1975     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
1976       element_info[i].ignition_delay = 1;
1977 #endif
1978   }
1979
1980   /* correct non-moving belts to start moving left */
1981   for (i = 0; i < NUM_BELTS; i++)
1982     if (game.belt_dir[i] == MV_NO_MOVING)
1983       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
1984
1985   /* check if any connected player was not found in playfield */
1986   for (i = 0; i < MAX_PLAYERS; i++)
1987   {
1988     struct PlayerInfo *player = &stored_player[i];
1989
1990     if (player->connected && !player->present)
1991     {
1992       for (j = 0; j < MAX_PLAYERS; j++)
1993       {
1994         struct PlayerInfo *some_player = &stored_player[j];
1995         int jx = some_player->jx, jy = some_player->jy;
1996
1997         /* assign first free player found that is present in the playfield */
1998         if (some_player->present && !some_player->connected)
1999         {
2000           player->present = TRUE;
2001           player->active = TRUE;
2002
2003           some_player->present = FALSE;
2004           some_player->active = FALSE;
2005
2006 #if 0
2007           player->element_nr = some_player->element_nr;
2008 #endif
2009
2010 #if USE_NEW_BLOCK_STYLE
2011           player->block_last_field       = some_player->block_last_field;
2012           player->block_delay_adjustment = some_player->block_delay_adjustment;
2013 #endif
2014
2015           StorePlayer[jx][jy] = player->element_nr;
2016           player->jx = player->last_jx = jx;
2017           player->jy = player->last_jy = jy;
2018
2019           break;
2020         }
2021       }
2022     }
2023   }
2024
2025   if (tape.playing)
2026   {
2027     /* when playing a tape, eliminate all players which do not participate */
2028
2029     for (i = 0; i < MAX_PLAYERS; i++)
2030     {
2031       if (stored_player[i].active && !tape.player_participates[i])
2032       {
2033         struct PlayerInfo *player = &stored_player[i];
2034         int jx = player->jx, jy = player->jy;
2035
2036         player->active = FALSE;
2037         StorePlayer[jx][jy] = 0;
2038         Feld[jx][jy] = EL_EMPTY;
2039       }
2040     }
2041   }
2042   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
2043   {
2044     /* when in single player mode, eliminate all but the first active player */
2045
2046     for (i = 0; i < MAX_PLAYERS; i++)
2047     {
2048       if (stored_player[i].active)
2049       {
2050         for (j = i + 1; j < MAX_PLAYERS; j++)
2051         {
2052           if (stored_player[j].active)
2053           {
2054             struct PlayerInfo *player = &stored_player[j];
2055             int jx = player->jx, jy = player->jy;
2056
2057             player->active = FALSE;
2058             player->present = FALSE;
2059
2060             StorePlayer[jx][jy] = 0;
2061             Feld[jx][jy] = EL_EMPTY;
2062           }
2063         }
2064       }
2065     }
2066   }
2067
2068   /* when recording the game, store which players take part in the game */
2069   if (tape.recording)
2070   {
2071     for (i = 0; i < MAX_PLAYERS; i++)
2072       if (stored_player[i].active)
2073         tape.player_participates[i] = TRUE;
2074   }
2075
2076   if (options.debug)
2077   {
2078     for (i = 0; i < MAX_PLAYERS; i++)
2079     {
2080       struct PlayerInfo *player = &stored_player[i];
2081
2082       printf("Player %d: present == %d, connected == %d, active == %d.\n",
2083              i+1,
2084              player->present,
2085              player->connected,
2086              player->active);
2087       if (local_player == player)
2088         printf("Player  %d is local player.\n", i+1);
2089     }
2090   }
2091
2092   if (BorderElement == EL_EMPTY)
2093   {
2094     SBX_Left = 0;
2095     SBX_Right = lev_fieldx - SCR_FIELDX;
2096     SBY_Upper = 0;
2097     SBY_Lower = lev_fieldy - SCR_FIELDY;
2098   }
2099   else
2100   {
2101     SBX_Left = -1;
2102     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
2103     SBY_Upper = -1;
2104     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
2105   }
2106
2107   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
2108     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
2109
2110   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
2111     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
2112
2113   /* if local player not found, look for custom element that might create
2114      the player (make some assumptions about the right custom element) */
2115   if (!local_player->present)
2116   {
2117     int start_x = 0, start_y = 0;
2118     int found_rating = 0;
2119     int found_element = EL_UNDEFINED;
2120
2121     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
2122     {
2123       int element = Feld[x][y];
2124       int content;
2125       int xx, yy;
2126       boolean is_player;
2127
2128       if (!IS_CUSTOM_ELEMENT(element))
2129         continue;
2130
2131       if (CAN_CHANGE(element))
2132       {
2133         for (i = 0; i < element_info[element].num_change_pages; i++)
2134         {
2135           content = element_info[element].change_page[i].target_element;
2136           is_player = ELEM_IS_PLAYER(content);
2137
2138           if (is_player && (found_rating < 3 || element < found_element))
2139           {
2140             start_x = x;
2141             start_y = y;
2142
2143             found_rating = 3;
2144             found_element = element;
2145           }
2146         }
2147       }
2148
2149       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
2150       {
2151         content = element_info[element].content[xx][yy];
2152         is_player = ELEM_IS_PLAYER(content);
2153
2154         if (is_player && (found_rating < 2 || element < found_element))
2155         {
2156           start_x = x + xx - 1;
2157           start_y = y + yy - 1;
2158
2159           found_rating = 2;
2160           found_element = element;
2161         }
2162
2163         if (!CAN_CHANGE(element))
2164           continue;
2165
2166         for (i = 0; i < element_info[element].num_change_pages; i++)
2167         {
2168           content= element_info[element].change_page[i].target_content[xx][yy];
2169           is_player = ELEM_IS_PLAYER(content);
2170
2171           if (is_player && (found_rating < 1 || element < found_element))
2172           {
2173             start_x = x + xx - 1;
2174             start_y = y + yy - 1;
2175
2176             found_rating = 1;
2177             found_element = element;
2178           }
2179         }
2180       }
2181     }
2182
2183     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
2184                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
2185                 start_x - MIDPOSX);
2186
2187     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
2188                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
2189                 start_y - MIDPOSY);
2190   }
2191   else
2192   {
2193 #if 1
2194     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2195                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2196                 local_player->jx - MIDPOSX);
2197
2198     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2199                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2200                 local_player->jy - MIDPOSY);
2201 #else
2202     scroll_x = SBX_Left;
2203     scroll_y = SBY_Upper;
2204     if (local_player->jx >= SBX_Left + MIDPOSX)
2205       scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
2206                   local_player->jx - MIDPOSX :
2207                   SBX_Right);
2208     if (local_player->jy >= SBY_Upper + MIDPOSY)
2209       scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
2210                   local_player->jy - MIDPOSY :
2211                   SBY_Lower);
2212 #endif
2213   }
2214
2215   if (!game.restart_level)
2216     CloseDoor(DOOR_CLOSE_1);
2217
2218   /* !!! FIX THIS (START) !!! */
2219   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2220   {
2221     InitGameEngine_EM();
2222   }
2223   else
2224   {
2225     DrawLevel();
2226     DrawAllPlayers();
2227
2228     /* after drawing the level, correct some elements */
2229     if (game.timegate_time_left == 0)
2230       CloseAllOpenTimegates();
2231
2232     if (setup.soft_scrolling)
2233       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
2234
2235     redraw_mask |= REDRAW_FROM_BACKBUFFER;
2236     FadeToFront();
2237   }
2238   /* !!! FIX THIS (END) !!! */
2239
2240   if (!game.restart_level)
2241   {
2242     /* copy default game door content to main double buffer */
2243     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2244                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2245   }
2246
2247   DrawGameDoorValues();
2248
2249   if (!game.restart_level)
2250   {
2251     UnmapGameButtons();
2252     UnmapTapeButtons();
2253     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
2254     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
2255     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
2256     MapGameButtons();
2257     MapTapeButtons();
2258
2259     /* copy actual game door content to door double buffer for OpenDoor() */
2260     BlitBitmap(drawto, bitmap_db_door,
2261                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
2262
2263     OpenDoor(DOOR_OPEN_ALL);
2264
2265     PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
2266
2267     if (setup.sound_music)
2268       PlayLevelMusic();
2269
2270     KeyboardAutoRepeatOffUnlessAutoplay();
2271
2272     if (options.debug)
2273     {
2274       for (i = 0; i < MAX_PLAYERS; i++)
2275         printf("Player %d %sactive.\n",
2276                i + 1, (stored_player[i].active ? "" : "not "));
2277     }
2278   }
2279
2280   game.restart_level = FALSE;
2281
2282 #if 0
2283   printf("::: starting game [%d]\n", FrameCounter);
2284 #endif
2285 }
2286
2287 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
2288 {
2289   /* this is used for non-R'n'D game engines to update certain engine values */
2290
2291   /* needed to determine if sounds are played within the visible screen area */
2292   scroll_x = actual_scroll_x;
2293   scroll_y = actual_scroll_y;
2294 }
2295
2296 void InitMovDir(int x, int y)
2297 {
2298   int i, element = Feld[x][y];
2299   static int xy[4][2] =
2300   {
2301     {  0, +1 },
2302     { +1,  0 },
2303     {  0, -1 },
2304     { -1,  0 }
2305   };
2306   static int direction[3][4] =
2307   {
2308     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
2309     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
2310     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
2311   };
2312
2313   switch(element)
2314   {
2315     case EL_BUG_RIGHT:
2316     case EL_BUG_UP:
2317     case EL_BUG_LEFT:
2318     case EL_BUG_DOWN:
2319       Feld[x][y] = EL_BUG;
2320       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
2321       break;
2322
2323     case EL_SPACESHIP_RIGHT:
2324     case EL_SPACESHIP_UP:
2325     case EL_SPACESHIP_LEFT:
2326     case EL_SPACESHIP_DOWN:
2327       Feld[x][y] = EL_SPACESHIP;
2328       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
2329       break;
2330
2331     case EL_BD_BUTTERFLY_RIGHT:
2332     case EL_BD_BUTTERFLY_UP:
2333     case EL_BD_BUTTERFLY_LEFT:
2334     case EL_BD_BUTTERFLY_DOWN:
2335       Feld[x][y] = EL_BD_BUTTERFLY;
2336       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
2337       break;
2338
2339     case EL_BD_FIREFLY_RIGHT:
2340     case EL_BD_FIREFLY_UP:
2341     case EL_BD_FIREFLY_LEFT:
2342     case EL_BD_FIREFLY_DOWN:
2343       Feld[x][y] = EL_BD_FIREFLY;
2344       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
2345       break;
2346
2347     case EL_PACMAN_RIGHT:
2348     case EL_PACMAN_UP:
2349     case EL_PACMAN_LEFT:
2350     case EL_PACMAN_DOWN:
2351       Feld[x][y] = EL_PACMAN;
2352       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
2353       break;
2354
2355     case EL_SP_SNIKSNAK:
2356       MovDir[x][y] = MV_UP;
2357       break;
2358
2359     case EL_SP_ELECTRON:
2360       MovDir[x][y] = MV_LEFT;
2361       break;
2362
2363     case EL_MOLE_LEFT:
2364     case EL_MOLE_RIGHT:
2365     case EL_MOLE_UP:
2366     case EL_MOLE_DOWN:
2367       Feld[x][y] = EL_MOLE;
2368       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
2369       break;
2370
2371     default:
2372       if (IS_CUSTOM_ELEMENT(element))
2373       {
2374         struct ElementInfo *ei = &element_info[element];
2375         int move_direction_initial = ei->move_direction_initial;
2376         int move_pattern = ei->move_pattern;
2377
2378         if (move_direction_initial == MV_START_PREVIOUS)
2379         {
2380           if (MovDir[x][y] != MV_NO_MOVING)
2381             return;
2382
2383           move_direction_initial = MV_START_AUTOMATIC;
2384         }
2385
2386         if (move_direction_initial == MV_START_RANDOM)
2387           MovDir[x][y] = 1 << RND(4);
2388         else if (move_direction_initial & MV_ANY_DIRECTION)
2389           MovDir[x][y] = move_direction_initial;
2390         else if (move_pattern == MV_ALL_DIRECTIONS ||
2391                  move_pattern == MV_TURNING_LEFT ||
2392                  move_pattern == MV_TURNING_RIGHT ||
2393                  move_pattern == MV_TURNING_LEFT_RIGHT ||
2394                  move_pattern == MV_TURNING_RIGHT_LEFT ||
2395                  move_pattern == MV_TURNING_RANDOM)
2396           MovDir[x][y] = 1 << RND(4);
2397         else if (move_pattern == MV_HORIZONTAL)
2398           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
2399         else if (move_pattern == MV_VERTICAL)
2400           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
2401         else if (move_pattern & MV_ANY_DIRECTION)
2402           MovDir[x][y] = element_info[element].move_pattern;
2403         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
2404                  move_pattern == MV_ALONG_RIGHT_SIDE)
2405         {
2406 #if 1
2407           /* use random direction as default start direction */
2408           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2409             MovDir[x][y] = 1 << RND(4);
2410 #endif
2411
2412           for (i = 0; i < NUM_DIRECTIONS; i++)
2413           {
2414             int x1 = x + xy[i][0];
2415             int y1 = y + xy[i][1];
2416
2417             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2418             {
2419               if (move_pattern == MV_ALONG_RIGHT_SIDE)
2420                 MovDir[x][y] = direction[0][i];
2421               else
2422                 MovDir[x][y] = direction[1][i];
2423
2424               break;
2425             }
2426           }
2427         }                
2428       }
2429       else
2430       {
2431         MovDir[x][y] = 1 << RND(4);
2432
2433         if (element != EL_BUG &&
2434             element != EL_SPACESHIP &&
2435             element != EL_BD_BUTTERFLY &&
2436             element != EL_BD_FIREFLY)
2437           break;
2438
2439         for (i = 0; i < NUM_DIRECTIONS; i++)
2440         {
2441           int x1 = x + xy[i][0];
2442           int y1 = y + xy[i][1];
2443
2444           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
2445           {
2446             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
2447             {
2448               MovDir[x][y] = direction[0][i];
2449               break;
2450             }
2451             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
2452                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
2453             {
2454               MovDir[x][y] = direction[1][i];
2455               break;
2456             }
2457           }
2458         }
2459       }
2460       break;
2461   }
2462
2463   GfxDir[x][y] = MovDir[x][y];
2464 }
2465
2466 void InitAmoebaNr(int x, int y)
2467 {
2468   int i;
2469   int group_nr = AmoebeNachbarNr(x, y);
2470
2471   if (group_nr == 0)
2472   {
2473     for (i = 1; i < MAX_NUM_AMOEBA; i++)
2474     {
2475       if (AmoebaCnt[i] == 0)
2476       {
2477         group_nr = i;
2478         break;
2479       }
2480     }
2481   }
2482
2483   AmoebaNr[x][y] = group_nr;
2484   AmoebaCnt[group_nr]++;
2485   AmoebaCnt2[group_nr]++;
2486 }
2487
2488 void GameWon()
2489 {
2490   int hi_pos;
2491   boolean raise_level = FALSE;
2492
2493   if (local_player->MovPos)
2494     return;
2495
2496 #if 1
2497   if (tape.auto_play)           /* tape might already be stopped here */
2498     tape.auto_play_level_solved = TRUE;
2499 #else
2500   if (tape.playing && tape.auto_play)
2501     tape.auto_play_level_solved = TRUE;
2502 #endif
2503
2504   local_player->LevelSolved = FALSE;
2505
2506   PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
2507
2508   if (TimeLeft)
2509   {
2510     if (!tape.playing && setup.sound_loops)
2511       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2512                    SND_CTRL_PLAY_LOOP);
2513
2514     while (TimeLeft > 0)
2515     {
2516       if (!tape.playing && !setup.sound_loops)
2517         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2518       if (TimeLeft > 0 && !(TimeLeft % 10))
2519         RaiseScore(level.score[SC_TIME_BONUS]);
2520       if (TimeLeft > 100 && !(TimeLeft % 10))
2521         TimeLeft -= 10;
2522       else
2523         TimeLeft--;
2524
2525       DrawGameValue_Time(TimeLeft);
2526
2527       BackToFront();
2528
2529       if (!tape.playing)
2530         Delay(10);
2531     }
2532
2533     if (!tape.playing && setup.sound_loops)
2534       StopSound(SND_GAME_LEVELTIME_BONUS);
2535   }
2536   else if (level.time == 0)             /* level without time limit */
2537   {
2538     if (!tape.playing && setup.sound_loops)
2539       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
2540                    SND_CTRL_PLAY_LOOP);
2541
2542     while (TimePlayed < 999)
2543     {
2544       if (!tape.playing && !setup.sound_loops)
2545         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
2546       if (TimePlayed < 999 && !(TimePlayed % 10))
2547         RaiseScore(level.score[SC_TIME_BONUS]);
2548       if (TimePlayed < 900 && !(TimePlayed % 10))
2549         TimePlayed += 10;
2550       else
2551         TimePlayed++;
2552
2553       DrawGameValue_Time(TimePlayed);
2554
2555       BackToFront();
2556
2557       if (!tape.playing)
2558         Delay(10);
2559     }
2560
2561     if (!tape.playing && setup.sound_loops)
2562       StopSound(SND_GAME_LEVELTIME_BONUS);
2563   }
2564
2565   /* close exit door after last player */
2566   if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
2567       (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2568        Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
2569   {
2570     int element = Feld[ExitX][ExitY];
2571
2572     Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2573                           EL_SP_EXIT_CLOSING);
2574
2575     PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2576   }
2577
2578   /* Hero disappears */
2579   if (ExitX >= 0 && ExitY >= 0)
2580     DrawLevelField(ExitX, ExitY);
2581
2582   BackToFront();
2583
2584   if (tape.playing)
2585     return;
2586
2587   CloseDoor(DOOR_CLOSE_1);
2588
2589   if (tape.recording)
2590   {
2591     TapeStop();
2592     SaveTape(tape.level_nr);            /* Ask to save tape */
2593   }
2594
2595   if (level_nr == leveldir_current->handicap_level)
2596   {
2597     leveldir_current->handicap_level++;
2598     SaveLevelSetup_SeriesInfo();
2599   }
2600
2601   if (level_editor_test_game)
2602     local_player->score = -1;   /* no highscore when playing from editor */
2603   else if (level_nr < leveldir_current->last_level)
2604     raise_level = TRUE;         /* advance to next level */
2605
2606   if ((hi_pos = NewHiScore()) >= 0) 
2607   {
2608     game_status = GAME_MODE_SCORES;
2609     DrawHallOfFame(hi_pos);
2610     if (raise_level)
2611     {
2612       level_nr++;
2613       TapeErase();
2614     }
2615   }
2616   else
2617   {
2618     game_status = GAME_MODE_MAIN;
2619     if (raise_level)
2620     {
2621       level_nr++;
2622       TapeErase();
2623     }
2624     DrawMainMenu();
2625   }
2626
2627   BackToFront();
2628 }
2629
2630 int NewHiScore()
2631 {
2632   int k, l;
2633   int position = -1;
2634
2635   LoadScore(level_nr);
2636
2637   if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2638       local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
2639     return -1;
2640
2641   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
2642   {
2643     if (local_player->score > highscore[k].Score)
2644     {
2645       /* player has made it to the hall of fame */
2646
2647       if (k < MAX_SCORE_ENTRIES - 1)
2648       {
2649         int m = MAX_SCORE_ENTRIES - 1;
2650
2651 #ifdef ONE_PER_NAME
2652         for (l = k; l < MAX_SCORE_ENTRIES; l++)
2653           if (!strcmp(setup.player_name, highscore[l].Name))
2654             m = l;
2655         if (m == k)     /* player's new highscore overwrites his old one */
2656           goto put_into_list;
2657 #endif
2658
2659         for (l = m; l > k; l--)
2660         {
2661           strcpy(highscore[l].Name, highscore[l - 1].Name);
2662           highscore[l].Score = highscore[l - 1].Score;
2663         }
2664       }
2665
2666 #ifdef ONE_PER_NAME
2667       put_into_list:
2668 #endif
2669       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2670       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2671       highscore[k].Score = local_player->score; 
2672       position = k;
2673       break;
2674     }
2675
2676 #ifdef ONE_PER_NAME
2677     else if (!strncmp(setup.player_name, highscore[k].Name,
2678                       MAX_PLAYER_NAME_LEN))
2679       break;    /* player already there with a higher score */
2680 #endif
2681
2682   }
2683
2684   if (position >= 0) 
2685     SaveScore(level_nr);
2686
2687   return position;
2688 }
2689
2690 inline static int getElementMoveStepsize(int x, int y)
2691 {
2692   int element = Feld[x][y];
2693   int direction = MovDir[x][y];
2694   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2695   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2696   int horiz_move = (dx != 0);
2697   int sign = (horiz_move ? dx : dy);
2698   int step = sign * element_info[element].move_stepsize;
2699
2700   /* special values for move stepsize for spring and things on conveyor belt */
2701   if (horiz_move)
2702   {
2703 #if 0
2704     if (element == EL_SPRING)
2705       step = sign * MOVE_STEPSIZE_NORMAL * 2;
2706     else if (CAN_FALL(element) && !CAN_MOVE(element) &&
2707              y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2708       step = sign * MOVE_STEPSIZE_NORMAL / 2;
2709 #else
2710     if (CAN_FALL(element) &&
2711         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
2712       step = sign * MOVE_STEPSIZE_NORMAL / 2;
2713     else if (element == EL_SPRING)
2714       step = sign * MOVE_STEPSIZE_NORMAL * 2;
2715 #endif
2716   }
2717
2718   return step;
2719 }
2720
2721 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2722 {
2723   if (player->GfxAction != action || player->GfxDir != dir)
2724   {
2725 #if 0
2726     printf("Player frame reset! (%d => %d, %d => %d)\n",
2727            player->GfxAction, action, player->GfxDir, dir);
2728 #endif
2729
2730     player->GfxAction = action;
2731     player->GfxDir = dir;
2732     player->Frame = 0;
2733     player->StepFrame = 0;
2734   }
2735 }
2736
2737 static void ResetRandomAnimationValue(int x, int y)
2738 {
2739   GfxRandom[x][y] = INIT_GFX_RANDOM();
2740 }
2741
2742 static void ResetGfxAnimation(int x, int y)
2743 {
2744   GfxFrame[x][y] = 0;
2745   GfxAction[x][y] = ACTION_DEFAULT;
2746   GfxDir[x][y] = MovDir[x][y];
2747 }
2748
2749 void InitMovingField(int x, int y, int direction)
2750 {
2751   int element = Feld[x][y];
2752   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2753   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2754   int newx = x + dx;
2755   int newy = y + dy;
2756
2757   if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2758     ResetGfxAnimation(x, y);
2759
2760 #if USE_CAN_MOVE_NOT_MOVING
2761
2762   MovDir[x][y] = direction;
2763   GfxDir[x][y] = direction;
2764   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
2765                      ACTION_FALLING : ACTION_MOVING);
2766
2767   if (getElementMoveStepsize(x, y) != 0)        /* moving or being moved */
2768   {
2769     if (Feld[newx][newy] == EL_EMPTY)
2770       Feld[newx][newy] = EL_BLOCKED;
2771
2772     MovDir[newx][newy] = MovDir[x][y];
2773     GfxFrame[newx][newy] = GfxFrame[x][y];
2774     GfxRandom[newx][newy] = GfxRandom[x][y];
2775     GfxAction[newx][newy] = GfxAction[x][y];
2776     GfxDir[newx][newy] = GfxDir[x][y];
2777   }
2778
2779 #else
2780
2781   MovDir[newx][newy] = MovDir[x][y] = direction;
2782   GfxDir[x][y] = direction;
2783
2784   if (Feld[newx][newy] == EL_EMPTY)
2785     Feld[newx][newy] = EL_BLOCKED;
2786
2787   if (direction == MV_DOWN && CAN_FALL(element))
2788     GfxAction[x][y] = ACTION_FALLING;
2789   else
2790     GfxAction[x][y] = ACTION_MOVING;
2791
2792   GfxFrame[newx][newy] = GfxFrame[x][y];
2793   GfxRandom[newx][newy] = GfxRandom[x][y];
2794   GfxAction[newx][newy] = GfxAction[x][y];
2795   GfxDir[newx][newy] = GfxDir[x][y];
2796 #endif
2797 }
2798
2799 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2800 {
2801   int direction = MovDir[x][y];
2802   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2803   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2804
2805   *goes_to_x = newx;
2806   *goes_to_y = newy;
2807 }
2808
2809 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2810 {
2811   int oldx = x, oldy = y;
2812   int direction = MovDir[x][y];
2813
2814   if (direction == MV_LEFT)
2815     oldx++;
2816   else if (direction == MV_RIGHT)
2817     oldx--;
2818   else if (direction == MV_UP)
2819     oldy++;
2820   else if (direction == MV_DOWN)
2821     oldy--;
2822
2823   *comes_from_x = oldx;
2824   *comes_from_y = oldy;
2825 }
2826
2827 int MovingOrBlocked2Element(int x, int y)
2828 {
2829   int element = Feld[x][y];
2830
2831   if (element == EL_BLOCKED)
2832   {
2833     int oldx, oldy;
2834
2835     Blocked2Moving(x, y, &oldx, &oldy);
2836     return Feld[oldx][oldy];
2837   }
2838   else
2839     return element;
2840 }
2841
2842 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2843 {
2844   /* like MovingOrBlocked2Element(), but if element is moving
2845      and (x,y) is the field the moving element is just leaving,
2846      return EL_BLOCKED instead of the element value */
2847   int element = Feld[x][y];
2848
2849   if (IS_MOVING(x, y))
2850   {
2851     if (element == EL_BLOCKED)
2852     {
2853       int oldx, oldy;
2854
2855       Blocked2Moving(x, y, &oldx, &oldy);
2856       return Feld[oldx][oldy];
2857     }
2858     else
2859       return EL_BLOCKED;
2860   }
2861   else
2862     return element;
2863 }
2864
2865 static void RemoveField(int x, int y)
2866 {
2867   Feld[x][y] = EL_EMPTY;
2868
2869   MovPos[x][y] = 0;
2870   MovDir[x][y] = 0;
2871   MovDelay[x][y] = 0;
2872
2873   AmoebaNr[x][y] = 0;
2874   ChangeDelay[x][y] = 0;
2875   ChangePage[x][y] = -1;
2876   Pushed[x][y] = FALSE;
2877
2878 #if 0
2879   ExplodeField[x][y] = EX_TYPE_NONE;
2880 #endif
2881
2882   GfxElement[x][y] = EL_UNDEFINED;
2883   GfxAction[x][y] = ACTION_DEFAULT;
2884   GfxDir[x][y] = MV_NO_MOVING;
2885 }
2886
2887 void RemoveMovingField(int x, int y)
2888 {
2889   int oldx = x, oldy = y, newx = x, newy = y;
2890   int element = Feld[x][y];
2891   int next_element = EL_UNDEFINED;
2892
2893   if (element != EL_BLOCKED && !IS_MOVING(x, y))
2894     return;
2895
2896   if (IS_MOVING(x, y))
2897   {
2898     Moving2Blocked(x, y, &newx, &newy);
2899 #if 0
2900     if (Feld[newx][newy] != EL_BLOCKED)
2901       return;
2902 #else
2903     if (Feld[newx][newy] != EL_BLOCKED)
2904     {
2905       /* element is moving, but target field is not free (blocked), but
2906          already occupied by something different (example: acid pool);
2907          in this case, only remove the moving field, but not the target */
2908
2909       RemoveField(oldx, oldy);
2910
2911       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2912
2913       DrawLevelField(oldx, oldy);
2914
2915       return;
2916     }
2917 #endif
2918   }
2919   else if (element == EL_BLOCKED)
2920   {
2921     Blocked2Moving(x, y, &oldx, &oldy);
2922     if (!IS_MOVING(oldx, oldy))
2923       return;
2924   }
2925
2926   if (element == EL_BLOCKED &&
2927       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2928        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2929        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2930        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2931     next_element = get_next_element(Feld[oldx][oldy]);
2932
2933   RemoveField(oldx, oldy);
2934   RemoveField(newx, newy);
2935
2936   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2937
2938   if (next_element != EL_UNDEFINED)
2939     Feld[oldx][oldy] = next_element;
2940
2941   DrawLevelField(oldx, oldy);
2942   DrawLevelField(newx, newy);
2943 }
2944
2945 void DrawDynamite(int x, int y)
2946 {
2947   int sx = SCREENX(x), sy = SCREENY(y);
2948   int graphic = el2img(Feld[x][y]);
2949   int frame;
2950
2951   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2952     return;
2953
2954   if (IS_WALKABLE_INSIDE(Back[x][y]))
2955     return;
2956
2957   if (Back[x][y])
2958     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2959   else if (Store[x][y])
2960     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2961
2962   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2963
2964 #if 1
2965   if (Back[x][y] || Store[x][y])
2966     DrawGraphicThruMask(sx, sy, graphic, frame);
2967   else
2968     DrawGraphic(sx, sy, graphic, frame);
2969 #else
2970   if (game.emulation == EMU_SUPAPLEX)
2971     DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2972   else if (Store[x][y])
2973     DrawGraphicThruMask(sx, sy, graphic, frame);
2974   else
2975     DrawGraphic(sx, sy, graphic, frame);
2976 #endif
2977 }
2978
2979 void CheckDynamite(int x, int y)
2980 {
2981   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
2982   {
2983     MovDelay[x][y]--;
2984
2985     if (MovDelay[x][y] != 0)
2986     {
2987       DrawDynamite(x, y);
2988       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2989
2990       return;
2991     }
2992   }
2993
2994 #if 1
2995   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2996 #else
2997   if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2998       Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2999     StopSound(SND_DYNAMITE_ACTIVE);
3000   else
3001     StopSound(SND_DYNABOMB_ACTIVE);
3002 #endif
3003
3004   Bang(x, y);
3005 }
3006
3007 void DrawRelocatePlayer(struct PlayerInfo *player)
3008 {
3009   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3010   boolean no_delay = (tape.warp_forward);
3011   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3012   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3013   int jx = player->jx;
3014   int jy = player->jy;
3015
3016   if (level.instant_relocation)
3017   {
3018 #if 1
3019     int offset = (setup.scroll_delay ? 3 : 0);
3020
3021     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3022     {
3023       scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3024                   local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3025                   local_player->jx - MIDPOSX);
3026
3027       scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3028                   local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3029                   local_player->jy - MIDPOSY);
3030     }
3031     else
3032     {
3033       if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
3034           (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3035         scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3036
3037       if ((player->MovDir == MV_UP  && scroll_y > jy - MIDPOSY + offset) ||
3038           (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3039         scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3040
3041       /* don't scroll over playfield boundaries */
3042       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3043         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3044
3045       /* don't scroll over playfield boundaries */
3046       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3047         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3048     }
3049 #else
3050     scroll_x += (local_player->jx - old_jx);
3051     scroll_y += (local_player->jy - old_jy);
3052
3053     /* don't scroll over playfield boundaries */
3054     if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3055       scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3056
3057     /* don't scroll over playfield boundaries */
3058     if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3059       scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3060 #endif
3061
3062     RedrawPlayfield(TRUE, 0,0,0,0);
3063   }
3064   else
3065   {
3066 #if 1
3067 #if 0
3068     int offset = (setup.scroll_delay ? 3 : 0);
3069 #endif
3070     int scroll_xx = -999, scroll_yy = -999;
3071
3072     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3073
3074     while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3075     {
3076       int dx = 0, dy = 0;
3077       int fx = FX, fy = FY;
3078
3079       scroll_xx = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3080                    local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3081                    local_player->jx - MIDPOSX);
3082
3083       scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3084                    local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3085                    local_player->jy - MIDPOSY);
3086
3087       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3088       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3089
3090 #if 1
3091       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3092         break;
3093 #else
3094       if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3095         break;
3096 #endif
3097
3098       scroll_x -= dx;
3099       scroll_y -= dy;
3100
3101       fx += dx * TILEX / 2;
3102       fy += dy * TILEY / 2;
3103
3104       ScrollLevel(dx, dy);
3105       DrawAllPlayers();
3106
3107       /* scroll in two steps of half tile size to make things smoother */
3108       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3109       FlushDisplay();
3110       Delay(wait_delay_value);
3111
3112       /* scroll second step to align at full tile size */
3113       BackToFront();
3114       Delay(wait_delay_value);
3115     }
3116 #else
3117     int scroll_xx = -999, scroll_yy = -999;
3118
3119     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3120
3121     while (scroll_xx != scroll_x || scroll_yy != scroll_y)
3122     {
3123       int dx = 0, dy = 0;
3124       int fx = FX, fy = FY;
3125
3126       scroll_xx = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3127                    local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3128                    local_player->jx - MIDPOSX);
3129
3130       scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3131                    local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3132                    local_player->jy - MIDPOSY);
3133
3134       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3135       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3136
3137 #if 1
3138       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3139         break;
3140 #else
3141       if (scroll_xx == scroll_x && scroll_yy == scroll_y)
3142         break;
3143 #endif
3144
3145       scroll_x -= dx;
3146       scroll_y -= dy;
3147
3148       fx += dx * TILEX / 2;
3149       fy += dy * TILEY / 2;
3150
3151       ScrollLevel(dx, dy);
3152       DrawAllPlayers();
3153
3154       /* scroll in two steps of half tile size to make things smoother */
3155       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3156       FlushDisplay();
3157       Delay(wait_delay_value);
3158
3159       /* scroll second step to align at full tile size */
3160       BackToFront();
3161       Delay(wait_delay_value);
3162     }
3163 #endif
3164
3165     DrawPlayer(player);
3166     BackToFront();
3167     Delay(wait_delay_value);
3168   }
3169 }
3170
3171 void RelocatePlayer(int jx, int jy, int el_player_raw)
3172 {
3173 #if 1
3174   int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
3175 #else
3176   int el_player = (el_player_raw == EL_SP_MURPHY ? EL_PLAYER_1 :el_player_raw);
3177 #endif
3178   struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
3179   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3180   boolean no_delay = (tape.warp_forward);
3181   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3182   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3183   int old_jx = player->jx;
3184   int old_jy = player->jy;
3185   int old_element = Feld[old_jx][old_jy];
3186   int element = Feld[jx][jy];
3187   boolean player_relocated = (old_jx != jx || old_jy != jy);
3188
3189   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3190   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
3191 #if 1
3192   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3193   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
3194   int leave_side_horiz = move_dir_horiz;
3195   int leave_side_vert  = move_dir_vert;
3196 #else
3197   static int trigger_sides[4][2] =
3198   {
3199     /* enter side               leave side */
3200     { CH_SIDE_RIGHT,            CH_SIDE_LEFT    },      /* moving left  */
3201     { CH_SIDE_LEFT,             CH_SIDE_RIGHT   },      /* moving right */
3202     { CH_SIDE_BOTTOM,           CH_SIDE_TOP     },      /* moving up    */
3203     { CH_SIDE_TOP,              CH_SIDE_BOTTOM  }       /* moving down  */
3204   };
3205   int enter_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][0];
3206   int enter_side_vert  = trigger_sides[MV_DIR_BIT(move_dir_vert)][0];
3207   int leave_side_horiz = trigger_sides[MV_DIR_BIT(move_dir_horiz)][1];
3208   int leave_side_vert  = trigger_sides[MV_DIR_BIT(move_dir_vert)][1];
3209 #endif
3210   int enter_side = enter_side_horiz | enter_side_vert;
3211   int leave_side = leave_side_horiz | leave_side_vert;
3212
3213   if (player->GameOver)         /* do not reanimate dead player */
3214     return;
3215
3216   if (!player_relocated)        /* no need to relocate the player */
3217     return;
3218
3219   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
3220   {
3221     RemoveField(jx, jy);        /* temporarily remove newly placed player */
3222     DrawLevelField(jx, jy);
3223   }
3224
3225   if (player->present)
3226   {
3227     while (player->MovPos)
3228     {
3229       ScrollPlayer(player, SCROLL_GO_ON);
3230       ScrollScreen(NULL, SCROLL_GO_ON);
3231
3232 #if USE_NEW_MOVE_DELAY
3233       AdvanceFrameAndPlayerCounters(player->index_nr);
3234 #else
3235       FrameCounter++;
3236 #endif
3237
3238       DrawPlayer(player);
3239
3240       BackToFront();
3241       Delay(wait_delay_value);
3242     }
3243
3244     DrawPlayer(player);         /* needed here only to cleanup last field */
3245     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
3246
3247     player->is_moving = FALSE;
3248   }
3249
3250 #if 1
3251   if (IS_CUSTOM_ELEMENT(old_element))
3252     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3253                                CE_LEFT_BY_PLAYER,
3254                                player->index_bit, leave_side);
3255
3256   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3257                                       CE_PLAYER_LEAVES_X,
3258                                       player->index_bit, leave_side);
3259 #endif
3260
3261   Feld[jx][jy] = el_player;
3262   InitPlayerField(jx, jy, el_player, TRUE);
3263
3264   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3265   {
3266     Feld[jx][jy] = element;
3267     InitField(jx, jy, FALSE);
3268   }
3269
3270 #if 1
3271   if (player == local_player)   /* only visually relocate local player */
3272     DrawRelocatePlayer(player);
3273 #endif
3274
3275 #if 1
3276   TestIfHeroTouchesBadThing(jx, jy);
3277   TestIfPlayerTouchesCustomElement(jx, jy);
3278 #endif
3279
3280 #if 0
3281   printf("::: %d,%d: %d\n", jx, jy-1, Changed[jx][jy-1]);
3282 #endif
3283
3284 #if 0
3285 #if 0
3286   /* needed to allow change of walkable custom element by entering player */
3287   if (!(Changed[jx][jy] & CH_EVENT_BIT(CE_ENTERED_BY_PLAYER)))
3288     Changed[jx][jy] = 0;        /* allow another change (but prevent loop) */
3289 #else
3290   /* needed to allow change of walkable custom element by entering player */
3291   Changed[jx][jy] = 0;          /* allow another change */
3292 #endif
3293 #endif
3294
3295 #if 0
3296   printf("::: player entering %d, %d from %s ...\n", jx, jy,
3297          enter_side == MV_LEFT  ? "left" :
3298          enter_side == MV_RIGHT ? "right" :
3299          enter_side == MV_UP    ? "top" :
3300          enter_side == MV_DOWN  ? "bottom" : "oops! no idea!");
3301 #endif
3302
3303 #if 1
3304   if (IS_CUSTOM_ELEMENT(element))
3305     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3306                                player->index_bit, enter_side);
3307
3308   CheckTriggeredElementChangeByPlayer(jx, jy, element,
3309                                       CE_PLAYER_ENTERS_X,
3310                                       player->index_bit, enter_side);
3311 #endif
3312 }
3313
3314 void Explode(int ex, int ey, int phase, int mode)
3315 {
3316   int x, y;
3317 #if 0
3318   int num_phase = 9;
3319 #endif
3320
3321   /* !!! eliminate this variable !!! */
3322   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3323
3324 #if 1
3325   int last_phase;
3326 #else
3327   int last_phase = num_phase * delay;
3328   int half_phase = (num_phase / 2) * delay;
3329   int first_phase_after_start = EX_PHASE_START + 1;
3330 #endif
3331   int border_element;
3332
3333   if (game.explosions_delayed)
3334   {
3335     ExplodeField[ex][ey] = mode;
3336     return;
3337   }
3338
3339   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
3340   {
3341     int center_element = Feld[ex][ey];
3342
3343 #if 0
3344     printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
3345 #endif
3346
3347 #if 0
3348     /* --- This is only really needed (and now handled) in "Impact()". --- */
3349     /* do not explode moving elements that left the explode field in time */
3350     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3351         center_element == EL_EMPTY &&
3352         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3353       return;
3354 #endif
3355
3356 #if 1
3357     if (mode == EX_TYPE_NORMAL ||
3358         mode == EX_TYPE_CENTER ||
3359         mode == EX_TYPE_CROSS)
3360       PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3361 #else
3362     if (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER)
3363       PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
3364 #endif
3365
3366     /* remove things displayed in background while burning dynamite */
3367     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3368       Back[ex][ey] = 0;
3369
3370     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3371     {
3372       /* put moving element to center field (and let it explode there) */
3373       center_element = MovingOrBlocked2Element(ex, ey);
3374       RemoveMovingField(ex, ey);
3375       Feld[ex][ey] = center_element;
3376     }
3377
3378 #if 1
3379
3380 #if 1
3381     last_phase = element_info[center_element].explosion_delay + 1;
3382 #else
3383     last_phase = element_info[center_element].explosion_delay;
3384 #endif
3385
3386 #if 0
3387     printf("::: %d -> %d\n", center_element, last_phase);
3388 #endif
3389 #endif
3390
3391     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3392     {
3393       int xx = x - ex + 1;
3394       int yy = y - ey + 1;
3395       int element;
3396
3397 #if 1
3398 #if 1
3399       if (!IN_LEV_FIELD(x, y) ||
3400           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3401           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
3402         continue;
3403 #else
3404       if (!IN_LEV_FIELD(x, y) ||
3405           (mode != EX_TYPE_NORMAL && (x != ex || y != ey)))
3406         continue;
3407 #endif
3408 #else
3409       if (!IN_LEV_FIELD(x, y) ||
3410           ((mode != EX_TYPE_NORMAL ||
3411             center_element == EL_AMOEBA_TO_DIAMOND) &&
3412            (x != ex || y != ey)))
3413         continue;
3414 #endif
3415
3416       element = Feld[x][y];
3417
3418       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3419       {
3420         element = MovingOrBlocked2Element(x, y);
3421
3422         if (!IS_EXPLOSION_PROOF(element))
3423           RemoveMovingField(x, y);
3424       }
3425
3426 #if 1
3427
3428 #if 0
3429       if (IS_EXPLOSION_PROOF(element))
3430         continue;
3431 #else
3432       /* indestructible elements can only explode in center (but not flames) */
3433 #if 1
3434       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3435                                            mode == EX_TYPE_BORDER)) ||
3436           element == EL_FLAMES)
3437         continue;
3438 #else
3439       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
3440           element == EL_FLAMES)
3441         continue;
3442 #endif
3443 #endif
3444
3445 #else
3446       if ((IS_INDESTRUCTIBLE(element) &&
3447            (game.engine_version < VERSION_IDENT(2,2,0,0) ||
3448             (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
3449           element == EL_FLAMES)
3450         continue;
3451 #endif
3452
3453       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3454          behaviour, for example when touching a yamyam that explodes to rocks
3455          with active deadly shield, a rock is created under the player !!! */
3456       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3457 #if 0
3458       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3459           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3460            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3461 #else
3462       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3463 #endif
3464       {
3465         if (IS_ACTIVE_BOMB(element))
3466         {
3467           /* re-activate things under the bomb like gate or penguin */
3468 #if 1
3469           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3470           Back[x][y] = 0;
3471 #else
3472           Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
3473           Store[x][y] = 0;
3474 #endif
3475
3476 #if 0
3477         printf("::: %d,%d: %d %s [%d, %d]\n", x, y, Feld[x][y],
3478                element_info[Feld[x][y]].token_name,
3479                Store[x][y], Store2[x][y]);
3480 #endif
3481         }
3482
3483         continue;
3484       }
3485
3486       /* save walkable background elements while explosion on same tile */
3487 #if 0
3488       if (IS_INDESTRUCTIBLE(element))
3489         Back[x][y] = element;
3490 #else
3491 #if 1
3492 #if 1
3493       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3494           (x != ex || y != ey || mode == EX_TYPE_BORDER))
3495         Back[x][y] = element;
3496 #else
3497       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3498           (x != ex || y != ey))
3499         Back[x][y] = element;
3500 #endif
3501 #else
3502       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
3503         Back[x][y] = element;
3504 #endif
3505 #endif
3506
3507       /* ignite explodable elements reached by other explosion */
3508       if (element == EL_EXPLOSION)
3509         element = Store2[x][y];
3510
3511 #if 1
3512       if (AmoebaNr[x][y] &&
3513           (element == EL_AMOEBA_FULL ||
3514            element == EL_BD_AMOEBA ||
3515            element == EL_AMOEBA_GROWING))
3516       {
3517         AmoebaCnt[AmoebaNr[x][y]]--;
3518         AmoebaCnt2[AmoebaNr[x][y]]--;
3519       }
3520
3521       RemoveField(x, y);
3522 #endif
3523
3524       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3525       {
3526         switch(StorePlayer[ex][ey])
3527         {
3528           case EL_PLAYER_2:
3529             Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3530             break;
3531           case EL_PLAYER_3:
3532             Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3533             break;
3534           case EL_PLAYER_4:
3535             Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3536             break;
3537           case EL_PLAYER_1:
3538           default:
3539             Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3540             break;
3541         }
3542
3543 #if 1
3544         if (PLAYERINFO(ex, ey)->use_murphy_graphic)
3545           Store[x][y] = EL_EMPTY;
3546 #else
3547         if (game.emulation == EMU_SUPAPLEX)
3548           Store[x][y] = EL_EMPTY;
3549 #endif
3550       }
3551       else if (center_element == EL_MOLE)
3552         Store[x][y] = EL_EMERALD_RED;
3553       else if (center_element == EL_PENGUIN)
3554         Store[x][y] = EL_EMERALD_PURPLE;
3555       else if (center_element == EL_BUG)
3556         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3557       else if (center_element == EL_BD_BUTTERFLY)
3558         Store[x][y] = EL_BD_DIAMOND;
3559       else if (center_element == EL_SP_ELECTRON)
3560         Store[x][y] = EL_SP_INFOTRON;
3561       else if (center_element == EL_AMOEBA_TO_DIAMOND)
3562         Store[x][y] = level.amoeba_content;
3563       else if (center_element == EL_YAMYAM)
3564         Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
3565       else if (IS_CUSTOM_ELEMENT(center_element) &&
3566                element_info[center_element].content[xx][yy] != EL_EMPTY)
3567         Store[x][y] = element_info[center_element].content[xx][yy];
3568       else if (element == EL_WALL_EMERALD)
3569         Store[x][y] = EL_EMERALD;
3570       else if (element == EL_WALL_DIAMOND)
3571         Store[x][y] = EL_DIAMOND;
3572       else if (element == EL_WALL_BD_DIAMOND)
3573         Store[x][y] = EL_BD_DIAMOND;
3574       else if (element == EL_WALL_EMERALD_YELLOW)
3575         Store[x][y] = EL_EMERALD_YELLOW;
3576       else if (element == EL_WALL_EMERALD_RED)
3577         Store[x][y] = EL_EMERALD_RED;
3578       else if (element == EL_WALL_EMERALD_PURPLE)
3579         Store[x][y] = EL_EMERALD_PURPLE;
3580       else if (element == EL_WALL_PEARL)
3581         Store[x][y] = EL_PEARL;
3582       else if (element == EL_WALL_CRYSTAL)
3583         Store[x][y] = EL_CRYSTAL;
3584       else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3585         Store[x][y] = element_info[element].content[1][1];
3586       else
3587         Store[x][y] = EL_EMPTY;
3588
3589       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3590           center_element == EL_AMOEBA_TO_DIAMOND)
3591         Store2[x][y] = element;
3592
3593 #if 0
3594       printf("::: %d,%d: %d %s\n", x, y, Store2[x][y],
3595              element_info[Store2[x][y]].token_name);
3596 #endif
3597
3598 #if 0
3599       if (AmoebaNr[x][y] &&
3600           (element == EL_AMOEBA_FULL ||
3601            element == EL_BD_AMOEBA ||
3602            element == EL_AMOEBA_GROWING))
3603       {
3604         AmoebaCnt[AmoebaNr[x][y]]--;
3605         AmoebaCnt2[AmoebaNr[x][y]]--;
3606       }
3607
3608 #if 1
3609       RemoveField(x, y);
3610 #else
3611       MovDir[x][y] = MovPos[x][y] = 0;
3612       GfxDir[x][y] = MovDir[x][y];
3613       AmoebaNr[x][y] = 0;
3614 #endif
3615 #endif
3616
3617       Feld[x][y] = EL_EXPLOSION;
3618 #if 1
3619       GfxElement[x][y] = center_element;
3620 #else
3621       GfxElement[x][y] = EL_UNDEFINED;
3622 #endif
3623
3624       ExplodePhase[x][y] = 1;
3625 #if 1
3626       ExplodeDelay[x][y] = last_phase;
3627 #endif
3628
3629 #if 0
3630 #if 1
3631       GfxFrame[x][y] = 0;       /* animation does not start until next frame */
3632 #else
3633       GfxFrame[x][y] = -1;      /* animation does not start until next frame */
3634 #endif
3635 #endif
3636
3637       Stop[x][y] = TRUE;
3638     }
3639
3640     if (center_element == EL_YAMYAM)
3641       game.yamyam_content_nr =
3642         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3643
3644 #if 0
3645   printf("::: %d,%d: %d %s [%d]\n", ex + 1, ey, Feld[ex + 1][ey],
3646          element_info[Feld[ex + 1][ey]].token_name, Store2[ex + 1][ey]);
3647 #endif
3648
3649     return;
3650   }
3651
3652   if (Stop[ex][ey])
3653     return;
3654
3655   x = ex;
3656   y = ey;
3657
3658 #if 1
3659   if (phase == 1)
3660     GfxFrame[x][y] = 0;         /* restart explosion animation */
3661 #endif
3662
3663 #if 0
3664   printf(":X: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3665 #endif
3666
3667 #if 1
3668   last_phase = ExplodeDelay[x][y];
3669 #endif
3670
3671   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3672
3673 #ifdef DEBUG
3674
3675   /* activate this even in non-DEBUG version until cause for crash in
3676      getGraphicAnimationFrame() (see below) is found and eliminated */
3677 #endif
3678 #if 1
3679
3680   if (GfxElement[x][y] == EL_UNDEFINED)
3681   {
3682     printf("\n\n");
3683     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3684     printf("Explode(): This should never happen!\n");
3685     printf("\n\n");
3686
3687     GfxElement[x][y] = EL_EMPTY;
3688   }
3689 #endif
3690
3691 #if 1
3692
3693   border_element = Store2[x][y];
3694 #if 1
3695   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3696     border_element = StorePlayer[x][y];
3697 #else
3698   if (IS_PLAYER(x, y))
3699     border_element = StorePlayer[x][y];
3700 #endif
3701
3702 #if 0
3703   printf("::: %d,%d: %d %s [%d]\n", x, y, border_element,
3704          element_info[border_element].token_name, Store2[x][y]);
3705 #endif
3706
3707 #if 0
3708   printf("::: phase == %d\n", phase);
3709 #endif
3710
3711   if (phase == element_info[border_element].ignition_delay ||
3712       phase == last_phase)
3713   {
3714     boolean border_explosion = FALSE;
3715
3716 #if 1
3717 #if 1
3718     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3719         !PLAYER_EXPLOSION_PROTECTED(x, y))
3720 #else
3721     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present)
3722 #endif
3723 #else
3724     if (IS_PLAYER(x, y))
3725 #endif
3726     {
3727       KillHeroUnlessExplosionProtected(x, y);
3728       border_explosion = TRUE;
3729
3730 #if 0
3731       if (phase == last_phase)
3732         printf("::: IS_PLAYER\n");
3733 #endif
3734     }
3735     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3736     {
3737 #if 0
3738       printf("::: %d,%d: %d %s\n", x, y, border_element,
3739              element_info[border_element].token_name);
3740 #endif
3741
3742       Feld[x][y] = Store2[x][y];
3743       Store2[x][y] = 0;
3744       Bang(x, y);
3745       border_explosion = TRUE;
3746
3747 #if 0
3748       if (phase == last_phase)
3749         printf("::: CAN_EXPLODE_BY_EXPLOSION\n");
3750 #endif
3751     }
3752     else if (border_element == EL_AMOEBA_TO_DIAMOND)
3753     {
3754       AmoebeUmwandeln(x, y);
3755       Store2[x][y] = 0;
3756       border_explosion = TRUE;
3757
3758 #if 0
3759       if (phase == last_phase)
3760         printf("::: EL_AMOEBA_TO_DIAMOND [%d, %d] [%d]\n",
3761                element_info[border_element].explosion_delay,
3762                element_info[border_element].ignition_delay,
3763                phase);
3764 #endif
3765     }
3766
3767 #if 1
3768     /* if an element just explodes due to another explosion (chain-reaction),
3769        do not immediately end the new explosion when it was the last frame of
3770        the explosion (as it would be done in the following "if"-statement!) */
3771     if (border_explosion && phase == last_phase)
3772       return;
3773 #endif
3774   }
3775
3776 #else
3777
3778   if (phase == first_phase_after_start)
3779   {
3780     int element = Store2[x][y];
3781
3782     if (element == EL_BLACK_ORB)
3783     {
3784       Feld[x][y] = Store2[x][y];
3785       Store2[x][y] = 0;
3786       Bang(x, y);
3787     }
3788   }
3789   else if (phase == half_phase)
3790   {
3791     int element = Store2[x][y];
3792
3793     if (IS_PLAYER(x, y))
3794       KillHeroUnlessExplosionProtected(x, y);
3795     else if (CAN_EXPLODE_BY_EXPLOSION(element))
3796     {
3797       Feld[x][y] = Store2[x][y];
3798       Store2[x][y] = 0;
3799       Bang(x, y);
3800     }
3801     else if (element == EL_AMOEBA_TO_DIAMOND)
3802       AmoebeUmwandeln(x, y);
3803   }
3804 #endif
3805
3806   if (phase == last_phase)
3807   {
3808     int element;
3809
3810 #if 0
3811   printf("::: done: phase == %d\n", phase);
3812 #endif
3813
3814 #if 0
3815     printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
3816 #endif
3817
3818     element = Feld[x][y] = Store[x][y];
3819     Store[x][y] = Store2[x][y] = 0;
3820     GfxElement[x][y] = EL_UNDEFINED;
3821
3822     /* player can escape from explosions and might therefore be still alive */
3823     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
3824         element <= EL_PLAYER_IS_EXPLODING_4)
3825       Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
3826                     EL_EMPTY :
3827                     element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
3828                     element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
3829                     element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
3830                     EL_EMERALD_PURPLE);
3831
3832     /* restore probably existing indestructible background element */
3833     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
3834       element = Feld[x][y] = Back[x][y];
3835     Back[x][y] = 0;
3836
3837     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
3838     GfxDir[x][y] = MV_NO_MOVING;
3839     ChangeDelay[x][y] = 0;
3840     ChangePage[x][y] = -1;
3841
3842 #if 1
3843     InitField_WithBug2(x, y, FALSE);
3844 #else
3845     InitField(x, y, FALSE);
3846 #if 1
3847     /* !!! not needed !!! */
3848 #if 1
3849     if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3850         CAN_MOVE(Feld[x][y]) && Feld[x][y] != EL_MOLE)
3851       InitMovDir(x, y);
3852 #else
3853     if (CAN_MOVE(element))
3854       InitMovDir(x, y);
3855 #endif
3856 #endif
3857 #endif
3858     DrawLevelField(x, y);
3859
3860     TestIfElementTouchesCustomElement(x, y);
3861
3862     if (GFX_CRUMBLED(element))
3863       DrawLevelFieldCrumbledSandNeighbours(x, y);
3864
3865     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
3866       StorePlayer[x][y] = 0;
3867
3868     if (ELEM_IS_PLAYER(element))
3869       RelocatePlayer(x, y, element);
3870   }
3871 #if 1
3872   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3873 #else
3874   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3875 #endif
3876   {
3877 #if 1
3878     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
3879 #else
3880     int stored = Store[x][y];
3881     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
3882                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
3883                    IMG_SP_EXPLOSION);
3884 #endif
3885 #if 1
3886     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3887 #else
3888     int frame = getGraphicAnimationFrame(graphic, phase - delay);
3889 #endif
3890
3891 #if 0
3892   printf("::: phase == %d [%d]\n", phase, GfxFrame[x][y]);
3893 #endif
3894
3895 #if 0
3896     printf("::: %d / %d [%d - %d]\n",
3897            GfxFrame[x][y], phase - delay, phase, delay);
3898 #endif
3899
3900 #if 0
3901     printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
3902            element_info[GfxElement[x][y]].token_name,
3903            graphic);
3904 #endif
3905
3906     if (phase == delay)
3907       DrawLevelFieldCrumbledSand(x, y);
3908
3909     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
3910     {
3911       DrawLevelElement(x, y, Back[x][y]);
3912       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
3913     }
3914     else if (IS_WALKABLE_UNDER(Back[x][y]))
3915     {
3916       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3917       DrawLevelElementThruMask(x, y, Back[x][y]);
3918     }
3919     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
3920       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
3921   }
3922 }
3923
3924 void DynaExplode(int ex, int ey)
3925 {
3926   int i, j;
3927   int dynabomb_element = Feld[ex][ey];
3928   int dynabomb_size = 1;
3929   boolean dynabomb_xl = FALSE;
3930   struct PlayerInfo *player;
3931   static int xy[4][2] =
3932   {
3933     { 0, -1 },
3934     { -1, 0 },
3935     { +1, 0 },
3936     { 0, +1 }
3937   };
3938
3939   if (IS_ACTIVE_BOMB(dynabomb_element))
3940   {
3941     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
3942     dynabomb_size = player->dynabomb_size;
3943     dynabomb_xl = player->dynabomb_xl;
3944     player->dynabombs_left++;
3945   }
3946
3947   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
3948
3949   for (i = 0; i < NUM_DIRECTIONS; i++)
3950   {
3951     for (j = 1; j <= dynabomb_size; j++)
3952     {
3953       int x = ex + j * xy[i][0];
3954       int y = ey + j * xy[i][1];
3955       int element;
3956
3957       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
3958         break;
3959
3960       element = Feld[x][y];
3961
3962       /* do not restart explosions of fields with active bombs */
3963       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
3964         continue;
3965
3966       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
3967
3968 #if 1
3969 #if 1
3970       if (element != EL_EMPTY && element != EL_EXPLOSION &&
3971           !IS_DIGGABLE(element) && !dynabomb_xl)
3972         break;
3973 #else
3974       if (element != EL_EMPTY && element != EL_EXPLOSION &&
3975           !CAN_GROW_INTO(element) && !dynabomb_xl)
3976         break;
3977 #endif
3978 #else
3979       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
3980       if (element != EL_EMPTY && element != EL_EXPLOSION &&
3981           element != EL_SAND && !dynabomb_xl)
3982         break;
3983 #endif
3984     }
3985   }
3986 }
3987
3988 void Bang(int x, int y)
3989 {
3990 #if 1
3991   int element = MovingOrBlocked2Element(x, y);
3992 #else
3993   int element = Feld[x][y];
3994 #endif
3995
3996 #if 1
3997   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3998 #else
3999   if (IS_PLAYER(x, y))
4000 #endif
4001   {
4002     struct PlayerInfo *player = PLAYERINFO(x, y);
4003
4004     element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
4005                             player->element_nr);
4006   }
4007
4008 #if 0
4009 #if 1
4010   PlayLevelSoundAction(x, y, ACTION_EXPLODING);
4011 #else
4012   if (game.emulation == EMU_SUPAPLEX)
4013     PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
4014   else
4015     PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
4016 #endif
4017 #endif
4018
4019 #if 0
4020   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
4021     element = EL_EMPTY;
4022 #endif
4023
4024   switch(element)
4025   {
4026     case EL_BUG:
4027     case EL_SPACESHIP:
4028     case EL_BD_BUTTERFLY:
4029     case EL_BD_FIREFLY:
4030     case EL_YAMYAM:
4031     case EL_DARK_YAMYAM:
4032     case EL_ROBOT:
4033     case EL_PACMAN:
4034     case EL_MOLE:
4035       RaiseScoreElement(element);
4036       Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4037       break;
4038     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4039     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4040     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4041     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4042     case EL_DYNABOMB_INCREASE_NUMBER:
4043     case EL_DYNABOMB_INCREASE_SIZE:
4044     case EL_DYNABOMB_INCREASE_POWER:
4045       DynaExplode(x, y);
4046       break;
4047     case EL_PENGUIN:
4048     case EL_LAMP:
4049     case EL_LAMP_ACTIVE:
4050 #if 1
4051     case EL_AMOEBA_TO_DIAMOND:
4052 #endif
4053       if (IS_PLAYER(x, y))
4054         Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4055       else
4056         Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4057       break;
4058     default:
4059 #if 1
4060       if (element_info[element].explosion_type == EXPLODES_CROSS)
4061 #else
4062       if (CAN_EXPLODE_CROSS(element))
4063 #endif
4064 #if 1
4065         Explode(x, y, EX_PHASE_START, EX_TYPE_CROSS);
4066 #else
4067         DynaExplode(x, y);
4068 #endif
4069 #if 1
4070       else if (element_info[element].explosion_type == EXPLODES_1X1)
4071 #else
4072       else if (CAN_EXPLODE_1X1(element))
4073 #endif
4074         Explode(x, y, EX_PHASE_START, EX_TYPE_CENTER);
4075       else
4076         Explode(x, y, EX_PHASE_START, EX_TYPE_NORMAL);
4077       break;
4078   }
4079
4080   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4081 }
4082
4083 void SplashAcid(int x, int y)
4084 {
4085 #if 1
4086   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4087       (!IN_LEV_FIELD(x - 1, y - 2) ||
4088        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4089     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4090
4091   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4092       (!IN_LEV_FIELD(x + 1, y - 2) ||
4093        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4094     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4095
4096   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4097 #else
4098   /* input: position of element entering acid (obsolete) */
4099
4100   int element = Feld[x][y];
4101
4102   if (!IN_LEV_FIELD(x, y + 1) || Feld[x][y + 1] != EL_ACID)
4103     return;
4104
4105   if (element != EL_ACID_SPLASH_LEFT &&
4106       element != EL_ACID_SPLASH_RIGHT)
4107   {
4108     PlayLevelSound(x, y, SND_ACID_SPLASHING);
4109
4110     if (IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y) &&
4111         (!IN_LEV_FIELD(x - 1, y - 1) ||
4112          !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 1))))
4113       Feld[x - 1][y] = EL_ACID_SPLASH_LEFT;
4114
4115     if (IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y) &&
4116         (!IN_LEV_FIELD(x + 1, y - 1) ||
4117          !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 1))))
4118       Feld[x + 1][y] = EL_ACID_SPLASH_RIGHT;
4119   }
4120 #endif
4121 }
4122
4123 static void InitBeltMovement()
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
4140   int x, y, i, j;
4141
4142   /* set frame order for belt animation graphic according to belt direction */
4143   for (i = 0; i < NUM_BELTS; i++)
4144   {
4145     int belt_nr = i;
4146
4147     for (j = 0; j < NUM_BELT_PARTS; j++)
4148     {
4149       int element = belt_base_active_element[belt_nr] + j;
4150       int graphic = el2img(element);
4151
4152       if (game.belt_dir[i] == MV_LEFT)
4153         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4154       else
4155         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4156     }
4157   }
4158
4159   for (y = 0; y < lev_fieldy; y++)
4160   {
4161     for (x = 0; x < lev_fieldx; x++)
4162     {
4163       int element = Feld[x][y];
4164
4165       for (i = 0; i < NUM_BELTS; i++)
4166       {
4167         if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
4168         {
4169           int e_belt_nr = getBeltNrFromBeltElement(element);
4170           int belt_nr = i;
4171
4172           if (e_belt_nr == belt_nr)
4173           {
4174             int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4175
4176             Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4177           }
4178         }
4179       }
4180     }
4181   }
4182 }
4183
4184 static void ToggleBeltSwitch(int x, int y)
4185 {
4186   static int belt_base_element[4] =
4187   {
4188     EL_CONVEYOR_BELT_1_LEFT,
4189     EL_CONVEYOR_BELT_2_LEFT,
4190     EL_CONVEYOR_BELT_3_LEFT,
4191     EL_CONVEYOR_BELT_4_LEFT
4192   };
4193   static int belt_base_active_element[4] =
4194   {
4195     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4196     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4197     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4198     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4199   };
4200   static int belt_base_switch_element[4] =
4201   {
4202     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4203     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4204     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4205     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4206   };
4207   static int belt_move_dir[4] =
4208   {
4209     MV_LEFT,
4210     MV_NO_MOVING,
4211     MV_RIGHT,
4212     MV_NO_MOVING,
4213   };
4214
4215   int element = Feld[x][y];
4216   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4217   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4218   int belt_dir = belt_move_dir[belt_dir_nr];
4219   int xx, yy, i;
4220
4221   if (!IS_BELT_SWITCH(element))
4222     return;
4223
4224   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4225   game.belt_dir[belt_nr] = belt_dir;
4226
4227   if (belt_dir_nr == 3)
4228     belt_dir_nr = 1;
4229
4230   /* set frame order for belt animation graphic according to belt direction */
4231   for (i = 0; i < NUM_BELT_PARTS; i++)
4232   {
4233     int element = belt_base_active_element[belt_nr] + i;
4234     int graphic = el2img(element);
4235
4236     if (belt_dir == MV_LEFT)
4237       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4238     else
4239       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4240   }
4241
4242   for (yy = 0; yy < lev_fieldy; yy++)
4243   {
4244     for (xx = 0; xx < lev_fieldx; xx++)
4245     {
4246       int element = Feld[xx][yy];
4247
4248       if (IS_BELT_SWITCH(element))
4249       {
4250         int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4251
4252         if (e_belt_nr == belt_nr)
4253         {
4254           Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4255           DrawLevelField(xx, yy);
4256         }
4257       }
4258       else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
4259       {
4260         int e_belt_nr = getBeltNrFromBeltElement(element);
4261
4262         if (e_belt_nr == belt_nr)
4263         {
4264           int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4265
4266           Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4267           DrawLevelField(xx, yy);
4268         }
4269       }
4270       else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
4271       {
4272         int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4273
4274         if (e_belt_nr == belt_nr)
4275         {
4276           int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4277
4278           Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4279           DrawLevelField(xx, yy);
4280         }
4281       }
4282     }
4283   }
4284 }
4285
4286 static void ToggleSwitchgateSwitch(int x, int y)
4287 {
4288   int xx, yy;
4289
4290   game.switchgate_pos = !game.switchgate_pos;
4291
4292   for (yy = 0; yy < lev_fieldy; yy++)
4293   {
4294     for (xx = 0; xx < lev_fieldx; xx++)
4295     {
4296       int element = Feld[xx][yy];
4297
4298       if (element == EL_SWITCHGATE_SWITCH_UP ||
4299           element == EL_SWITCHGATE_SWITCH_DOWN)
4300       {
4301         Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4302         DrawLevelField(xx, yy);
4303       }
4304       else if (element == EL_SWITCHGATE_OPEN ||
4305                element == EL_SWITCHGATE_OPENING)
4306       {
4307         Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4308 #if 1
4309         PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4310 #else
4311         PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
4312 #endif
4313       }
4314       else if (element == EL_SWITCHGATE_CLOSED ||
4315                element == EL_SWITCHGATE_CLOSING)
4316       {
4317         Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4318 #if 1
4319         PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4320 #else
4321         PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
4322 #endif
4323       }
4324     }
4325   }
4326 }
4327
4328 static int getInvisibleActiveFromInvisibleElement(int element)
4329 {
4330   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4331           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
4332           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
4333           element);
4334 }
4335
4336 static int getInvisibleFromInvisibleActiveElement(int element)
4337 {
4338   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4339           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
4340           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
4341           element);
4342 }
4343
4344 static void RedrawAllLightSwitchesAndInvisibleElements()
4345 {
4346   int x, y;
4347
4348   for (y = 0; y < lev_fieldy; y++)
4349   {
4350     for (x = 0; x < lev_fieldx; x++)
4351     {
4352       int element = Feld[x][y];
4353
4354       if (element == EL_LIGHT_SWITCH &&
4355           game.light_time_left > 0)
4356       {
4357         Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4358         DrawLevelField(x, y);
4359       }
4360       else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4361                game.light_time_left == 0)
4362       {
4363         Feld[x][y] = EL_LIGHT_SWITCH;
4364         DrawLevelField(x, y);
4365       }
4366       else if (element == EL_INVISIBLE_STEELWALL ||
4367                element == EL_INVISIBLE_WALL ||
4368                element == EL_INVISIBLE_SAND)
4369       {
4370         if (game.light_time_left > 0)
4371           Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4372
4373         DrawLevelField(x, y);
4374
4375         /* uncrumble neighbour fields, if needed */
4376         if (element == EL_INVISIBLE_SAND)
4377           DrawLevelFieldCrumbledSandNeighbours(x, y);
4378       }
4379       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4380                element == EL_INVISIBLE_WALL_ACTIVE ||
4381                element == EL_INVISIBLE_SAND_ACTIVE)
4382       {
4383         if (game.light_time_left == 0)
4384           Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4385
4386         DrawLevelField(x, y);
4387
4388         /* re-crumble neighbour fields, if needed */
4389         if (element == EL_INVISIBLE_SAND)
4390           DrawLevelFieldCrumbledSandNeighbours(x, y);
4391       }
4392     }
4393   }
4394 }
4395
4396 static void ToggleLightSwitch(int x, int y)
4397 {
4398   int element = Feld[x][y];
4399
4400   game.light_time_left =
4401     (element == EL_LIGHT_SWITCH ?
4402      level.time_light * FRAMES_PER_SECOND : 0);
4403
4404   RedrawAllLightSwitchesAndInvisibleElements();
4405 }
4406
4407 static void ActivateTimegateSwitch(int x, int y)
4408 {
4409   int xx, yy;
4410
4411   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4412
4413   for (yy = 0; yy < lev_fieldy; yy++)
4414   {
4415     for (xx = 0; xx < lev_fieldx; xx++)
4416     {
4417       int element = Feld[xx][yy];
4418
4419       if (element == EL_TIMEGATE_CLOSED ||
4420           element == EL_TIMEGATE_CLOSING)
4421       {
4422         Feld[xx][yy] = EL_TIMEGATE_OPENING;
4423         PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4424       }
4425
4426       /*
4427       else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4428       {
4429         Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4430         DrawLevelField(xx, yy);
4431       }
4432       */
4433
4434     }
4435   }
4436
4437   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4438 }
4439
4440 void Impact(int x, int y)
4441 {
4442   boolean last_line = (y == lev_fieldy - 1);
4443   boolean object_hit = FALSE;
4444   boolean impact = (last_line || object_hit);
4445   int element = Feld[x][y];
4446   int smashed = EL_STEELWALL;
4447
4448 #if 0
4449   printf("IMPACT!\n");
4450 #endif
4451
4452   if (!last_line)       /* check if element below was hit */
4453   {
4454     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4455       return;
4456
4457     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4458                                          MovDir[x][y + 1] != MV_DOWN ||
4459                                          MovPos[x][y + 1] <= TILEY / 2));
4460
4461 #if 0
4462     object_hit = !IS_FREE(x, y + 1);
4463 #endif
4464
4465     /* do not smash moving elements that left the smashed field in time */
4466     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4467         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4468       object_hit = FALSE;
4469
4470     if (object_hit)
4471       smashed = MovingOrBlocked2Element(x, y + 1);
4472
4473     impact = (last_line || object_hit);
4474   }
4475
4476   if (!last_line && smashed == EL_ACID) /* element falls into acid */
4477   {
4478     SplashAcid(x, y + 1);
4479     return;
4480   }
4481
4482   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4483   /* only reset graphic animation if graphic really changes after impact */
4484   if (impact &&
4485       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4486   {
4487     ResetGfxAnimation(x, y);
4488     DrawLevelField(x, y);
4489   }
4490
4491   if (impact && CAN_EXPLODE_IMPACT(element))
4492   {
4493     Bang(x, y);
4494     return;
4495   }
4496   else if (impact && element == EL_PEARL)
4497   {
4498     ResetGfxAnimation(x, y);
4499
4500     Feld[x][y] = EL_PEARL_BREAKING;
4501     PlayLevelSound(x, y, SND_PEARL_BREAKING);
4502     return;
4503   }
4504   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4505   {
4506     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4507
4508     return;
4509   }
4510
4511   if (impact && element == EL_AMOEBA_DROP)
4512   {
4513     if (object_hit && IS_PLAYER(x, y + 1))
4514       KillHeroUnlessEnemyProtected(x, y + 1);
4515     else if (object_hit && smashed == EL_PENGUIN)
4516       Bang(x, y + 1);
4517     else
4518     {
4519       Feld[x][y] = EL_AMOEBA_GROWING;
4520       Store[x][y] = EL_AMOEBA_WET;
4521
4522       ResetRandomAnimationValue(x, y);
4523     }
4524     return;
4525   }
4526
4527   if (object_hit)               /* check which object was hit */
4528   {
4529     if (CAN_PASS_MAGIC_WALL(element) && 
4530         (smashed == EL_MAGIC_WALL ||
4531          smashed == EL_BD_MAGIC_WALL))
4532     {
4533       int xx, yy;
4534       int activated_magic_wall =
4535         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4536          EL_BD_MAGIC_WALL_ACTIVE);
4537
4538       /* activate magic wall / mill */
4539       for (yy = 0; yy < lev_fieldy; yy++)
4540         for (xx = 0; xx < lev_fieldx; xx++)
4541           if (Feld[xx][yy] == smashed)
4542             Feld[xx][yy] = activated_magic_wall;
4543
4544       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4545       game.magic_wall_active = TRUE;
4546
4547       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4548                             SND_MAGIC_WALL_ACTIVATING :
4549                             SND_BD_MAGIC_WALL_ACTIVATING));
4550     }
4551
4552     if (IS_PLAYER(x, y + 1))
4553     {
4554       if (CAN_SMASH_PLAYER(element))
4555       {
4556         KillHeroUnlessEnemyProtected(x, y + 1);
4557         return;
4558       }
4559     }
4560     else if (smashed == EL_PENGUIN)
4561     {
4562       if (CAN_SMASH_PLAYER(element))
4563       {
4564         Bang(x, y + 1);
4565         return;
4566       }
4567     }
4568     else if (element == EL_BD_DIAMOND)
4569     {
4570       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4571       {
4572         Bang(x, y + 1);
4573         return;
4574       }
4575     }
4576     else if (((element == EL_SP_INFOTRON ||
4577                element == EL_SP_ZONK) &&
4578               (smashed == EL_SP_SNIKSNAK ||
4579                smashed == EL_SP_ELECTRON ||
4580                smashed == EL_SP_DISK_ORANGE)) ||
4581              (element == EL_SP_INFOTRON &&
4582               smashed == EL_SP_DISK_YELLOW))
4583     {
4584       Bang(x, y + 1);
4585       return;
4586     }
4587 #if 0
4588     else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
4589     {
4590       Bang(x, y + 1);
4591       return;
4592     }
4593 #endif
4594     else if (CAN_SMASH_EVERYTHING(element))
4595     {
4596       if (IS_CLASSIC_ENEMY(smashed) ||
4597           CAN_EXPLODE_SMASHED(smashed))
4598       {
4599         Bang(x, y + 1);
4600         return;
4601       }
4602       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4603       {
4604         if (smashed == EL_LAMP ||
4605             smashed == EL_LAMP_ACTIVE)
4606         {
4607           Bang(x, y + 1);
4608           return;
4609         }
4610         else if (smashed == EL_NUT)
4611         {
4612           Feld[x][y + 1] = EL_NUT_BREAKING;
4613           PlayLevelSound(x, y, SND_NUT_BREAKING);
4614           RaiseScoreElement(EL_NUT);
4615           return;
4616         }
4617         else if (smashed == EL_PEARL)
4618         {
4619           ResetGfxAnimation(x, y);
4620
4621           Feld[x][y + 1] = EL_PEARL_BREAKING;
4622           PlayLevelSound(x, y, SND_PEARL_BREAKING);
4623           return;
4624         }
4625         else if (smashed == EL_DIAMOND)
4626         {
4627           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4628           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4629           return;
4630         }
4631         else if (IS_BELT_SWITCH(smashed))
4632         {
4633           ToggleBeltSwitch(x, y + 1);
4634         }
4635         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4636                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
4637         {
4638           ToggleSwitchgateSwitch(x, y + 1);
4639         }
4640         else if (smashed == EL_LIGHT_SWITCH ||
4641                  smashed == EL_LIGHT_SWITCH_ACTIVE)
4642         {
4643           ToggleLightSwitch(x, y + 1);
4644         }
4645         else
4646         {
4647 #if 0
4648           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4649 #endif
4650
4651           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4652
4653 #if 1
4654           /* !!! TEST ONLY !!! */
4655           CheckElementChangeBySide(x, y + 1, smashed, element,
4656                                    CE_SWITCHED, CH_SIDE_TOP);
4657           CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4658                                             CE_SWITCH_OF_X, CH_SIDE_TOP);
4659 #else
4660           CheckTriggeredElementChangeBySide(x, y + 1, smashed,
4661                                             CE_SWITCH_OF_X, CH_SIDE_TOP);
4662           CheckElementChangeBySide(x, y + 1, smashed, element,
4663                                    CE_SWITCHED, CH_SIDE_TOP);
4664 #endif
4665         }
4666       }
4667       else
4668       {
4669         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4670       }
4671     }
4672   }
4673
4674   /* play sound of magic wall / mill */
4675   if (!last_line &&
4676       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4677        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4678   {
4679     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4680       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4681     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4682       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4683
4684     return;
4685   }
4686
4687   /* play sound of object that hits the ground */
4688   if (last_line || object_hit)
4689     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4690 }
4691
4692 inline static void TurnRoundExt(int x, int y)
4693 {
4694   static struct
4695   {
4696     int x, y;
4697   } move_xy[] =
4698   {
4699     {  0,  0 },
4700     { -1,  0 },
4701     { +1,  0 },
4702     {  0,  0 },
4703     {  0, -1 },
4704     {  0,  0 }, { 0, 0 }, { 0, 0 },
4705     {  0, +1 }
4706   };
4707   static struct
4708   {
4709     int left, right, back;
4710   } turn[] =
4711   {
4712     { 0,        0,              0        },
4713     { MV_DOWN,  MV_UP,          MV_RIGHT },
4714     { MV_UP,    MV_DOWN,        MV_LEFT  },
4715     { 0,        0,              0        },
4716     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
4717     { 0,        0,              0        },
4718     { 0,        0,              0        },
4719     { 0,        0,              0        },
4720     { MV_RIGHT, MV_LEFT,        MV_UP    }
4721   };
4722
4723   int element = Feld[x][y];
4724   int move_pattern = element_info[element].move_pattern;
4725
4726   int old_move_dir = MovDir[x][y];
4727   int left_dir  = turn[old_move_dir].left;
4728   int right_dir = turn[old_move_dir].right;
4729   int back_dir  = turn[old_move_dir].back;
4730
4731   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
4732   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
4733   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
4734   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
4735
4736   int left_x  = x + left_dx,  left_y  = y + left_dy;
4737   int right_x = x + right_dx, right_y = y + right_dy;
4738   int move_x  = x + move_dx,  move_y  = y + move_dy;
4739
4740   int xx, yy;
4741
4742   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4743   {
4744     TestIfBadThingTouchesOtherBadThing(x, y);
4745
4746     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4747       MovDir[x][y] = right_dir;
4748     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4749       MovDir[x][y] = left_dir;
4750
4751     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4752       MovDelay[x][y] = 9;
4753     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
4754       MovDelay[x][y] = 1;
4755   }
4756 #if 0
4757   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4758            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4759   {
4760     TestIfBadThingTouchesOtherBadThing(x, y);
4761
4762     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4763       MovDir[x][y] = left_dir;
4764     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4765       MovDir[x][y] = right_dir;
4766
4767     if ((element == EL_SPACESHIP ||
4768          element == EL_SP_SNIKSNAK ||
4769          element == EL_SP_ELECTRON)
4770         && MovDir[x][y] != old_move_dir)
4771       MovDelay[x][y] = 9;
4772     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
4773       MovDelay[x][y] = 1;
4774   }
4775 #else
4776   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4777   {
4778     TestIfBadThingTouchesOtherBadThing(x, y);
4779
4780     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4781       MovDir[x][y] = left_dir;
4782     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4783       MovDir[x][y] = right_dir;
4784
4785     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4786       MovDelay[x][y] = 9;
4787     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
4788       MovDelay[x][y] = 1;
4789   }
4790   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4791   {
4792     TestIfBadThingTouchesOtherBadThing(x, y);
4793
4794     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4795       MovDir[x][y] = left_dir;
4796     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4797       MovDir[x][y] = right_dir;
4798
4799     if (MovDir[x][y] != old_move_dir)
4800       MovDelay[x][y] = 9;
4801   }
4802 #endif
4803   else if (element == EL_YAMYAM)
4804   {
4805     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4806     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4807
4808     if (can_turn_left && can_turn_right)
4809       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4810     else if (can_turn_left)
4811       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4812     else if (can_turn_right)
4813       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4814     else
4815       MovDir[x][y] = back_dir;
4816
4817     MovDelay[x][y] = 16 + 16 * RND(3);
4818   }
4819   else if (element == EL_DARK_YAMYAM)
4820   {
4821     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4822                                                          left_x, left_y);
4823     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4824                                                          right_x, right_y);
4825
4826     if (can_turn_left && can_turn_right)
4827       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4828     else if (can_turn_left)
4829       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4830     else if (can_turn_right)
4831       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4832     else
4833       MovDir[x][y] = back_dir;
4834
4835     MovDelay[x][y] = 16 + 16 * RND(3);
4836   }
4837   else if (element == EL_PACMAN)
4838   {
4839     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
4840     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
4841
4842     if (can_turn_left && can_turn_right)
4843       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4844     else if (can_turn_left)
4845       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4846     else if (can_turn_right)
4847       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4848     else
4849       MovDir[x][y] = back_dir;
4850
4851     MovDelay[x][y] = 6 + RND(40);
4852   }
4853   else if (element == EL_PIG)
4854   {
4855     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
4856     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
4857     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
4858     boolean should_turn_left, should_turn_right, should_move_on;
4859     int rnd_value = 24;
4860     int rnd = RND(rnd_value);
4861
4862     should_turn_left = (can_turn_left &&
4863                         (!can_move_on ||
4864                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
4865                                                    y + back_dy + left_dy)));
4866     should_turn_right = (can_turn_right &&
4867                          (!can_move_on ||
4868                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
4869                                                     y + back_dy + right_dy)));
4870     should_move_on = (can_move_on &&
4871                       (!can_turn_left ||
4872                        !can_turn_right ||
4873                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
4874                                                  y + move_dy + left_dy) ||
4875                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
4876                                                  y + move_dy + right_dy)));
4877
4878     if (should_turn_left || should_turn_right || should_move_on)
4879     {
4880       if (should_turn_left && should_turn_right && should_move_on)
4881         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
4882                         rnd < 2 * rnd_value / 3 ? right_dir :
4883                         old_move_dir);
4884       else if (should_turn_left && should_turn_right)
4885         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4886       else if (should_turn_left && should_move_on)
4887         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
4888       else if (should_turn_right && should_move_on)
4889         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
4890       else if (should_turn_left)
4891         MovDir[x][y] = left_dir;
4892       else if (should_turn_right)
4893         MovDir[x][y] = right_dir;
4894       else if (should_move_on)
4895         MovDir[x][y] = old_move_dir;
4896     }
4897     else if (can_move_on && rnd > rnd_value / 8)
4898       MovDir[x][y] = old_move_dir;
4899     else if (can_turn_left && can_turn_right)
4900       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4901     else if (can_turn_left && rnd > rnd_value / 8)
4902       MovDir[x][y] = left_dir;
4903     else if (can_turn_right && rnd > rnd_value/8)
4904       MovDir[x][y] = right_dir;
4905     else
4906       MovDir[x][y] = back_dir;
4907
4908     xx = x + move_xy[MovDir[x][y]].x;
4909     yy = y + move_xy[MovDir[x][y]].y;
4910
4911 #if 1
4912     /* !!! this bugfix breaks at least BD2K3, level 010 !!! [re-recorded] */
4913     if (!IN_LEV_FIELD(xx, yy) ||
4914         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
4915       MovDir[x][y] = old_move_dir;
4916 #else
4917     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
4918       MovDir[x][y] = old_move_dir;
4919 #endif
4920
4921     MovDelay[x][y] = 0;
4922   }
4923   else if (element == EL_DRAGON)
4924   {
4925     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
4926     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
4927     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
4928     int rnd_value = 24;
4929     int rnd = RND(rnd_value);
4930
4931 #if 0
4932     if (FrameCounter < 1 && x == 0 && y == 29)
4933       printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4934 #endif
4935
4936     if (can_move_on && rnd > rnd_value / 8)
4937       MovDir[x][y] = old_move_dir;
4938     else if (can_turn_left && can_turn_right)
4939       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
4940     else if (can_turn_left && rnd > rnd_value / 8)
4941       MovDir[x][y] = left_dir;
4942     else if (can_turn_right && rnd > rnd_value / 8)
4943       MovDir[x][y] = right_dir;
4944     else
4945       MovDir[x][y] = back_dir;
4946
4947     xx = x + move_xy[MovDir[x][y]].x;
4948     yy = y + move_xy[MovDir[x][y]].y;
4949
4950 #if 0
4951     if (FrameCounter < 1 && x == 0 && y == 29)
4952       printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
4953              xx, yy, Feld[xx][yy],
4954              FrameCounter);
4955 #endif
4956
4957 #if 1
4958     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
4959       MovDir[x][y] = old_move_dir;
4960 #else
4961     if (!IS_FREE(xx, yy))
4962       MovDir[x][y] = old_move_dir;
4963 #endif
4964
4965 #if 0
4966     if (FrameCounter < 1 && x == 0 && y == 29)
4967       printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4968 #endif
4969
4970     MovDelay[x][y] = 0;
4971   }
4972   else if (element == EL_MOLE)
4973   {
4974     boolean can_move_on =
4975       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
4976                             IS_AMOEBOID(Feld[move_x][move_y]) ||
4977                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
4978     if (!can_move_on)
4979     {
4980       boolean can_turn_left =
4981         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
4982                               IS_AMOEBOID(Feld[left_x][left_y])));
4983
4984       boolean can_turn_right =
4985         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
4986                               IS_AMOEBOID(Feld[right_x][right_y])));
4987
4988       if (can_turn_left && can_turn_right)
4989         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
4990       else if (can_turn_left)
4991         MovDir[x][y] = left_dir;
4992       else
4993         MovDir[x][y] = right_dir;
4994     }
4995
4996     if (MovDir[x][y] != old_move_dir)
4997       MovDelay[x][y] = 9;
4998   }
4999   else if (element == EL_BALLOON)
5000   {
5001     MovDir[x][y] = game.balloon_dir;
5002     MovDelay[x][y] = 0;
5003   }
5004   else if (element == EL_SPRING)
5005   {
5006 #if 0
5007     if (MovDir[x][y] & MV_HORIZONTAL &&
5008         !SPRING_CAN_ENTER_FIELD(element, move_x, move_y))
5009       MovDir[x][y] = MV_NO_MOVING;
5010 #else
5011     if (MovDir[x][y] & MV_HORIZONTAL &&
5012         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5013          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5014       MovDir[x][y] = MV_NO_MOVING;
5015 #endif
5016
5017     MovDelay[x][y] = 0;
5018   }
5019   else if (element == EL_ROBOT ||
5020            element == EL_SATELLITE ||
5021            element == EL_PENGUIN)
5022   {
5023     int attr_x = -1, attr_y = -1;
5024
5025     if (AllPlayersGone)
5026     {
5027       attr_x = ExitX;
5028       attr_y = ExitY;
5029     }
5030     else
5031     {
5032       int i;
5033
5034       for (i = 0; i < MAX_PLAYERS; i++)
5035       {
5036         struct PlayerInfo *player = &stored_player[i];
5037         int jx = player->jx, jy = player->jy;
5038
5039         if (!player->active)
5040           continue;
5041
5042         if (attr_x == -1 ||
5043             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5044         {
5045           attr_x = jx;
5046           attr_y = jy;
5047         }
5048       }
5049     }
5050
5051 #if 1
5052     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5053         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5054          game.engine_version < VERSION_IDENT(3,1,0,0)))
5055 #else
5056     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
5057 #endif
5058     {
5059       attr_x = ZX;
5060       attr_y = ZY;
5061     }
5062
5063     if (element == EL_PENGUIN)
5064     {
5065       int i;
5066       static int xy[4][2] =
5067       {
5068         { 0, -1 },
5069         { -1, 0 },
5070         { +1, 0 },
5071         { 0, +1 }
5072       };
5073
5074       for (i = 0; i < NUM_DIRECTIONS; i++)
5075       {
5076         int ex = x + xy[i][0];
5077         int ey = y + xy[i][1];
5078
5079         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5080         {
5081           attr_x = ex;
5082           attr_y = ey;
5083           break;
5084         }
5085       }
5086     }
5087
5088     MovDir[x][y] = MV_NO_MOVING;
5089     if (attr_x < x)
5090       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5091     else if (attr_x > x)
5092       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5093     if (attr_y < y)
5094       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5095     else if (attr_y > y)
5096       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5097
5098     if (element == EL_ROBOT)
5099     {
5100       int newx, newy;
5101
5102       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5103         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5104       Moving2Blocked(x, y, &newx, &newy);
5105
5106       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5107         MovDelay[x][y] = 8 + 8 * !RND(3);
5108       else
5109         MovDelay[x][y] = 16;
5110     }
5111     else if (element == EL_PENGUIN)
5112     {
5113       int newx, newy;
5114
5115       MovDelay[x][y] = 1;
5116
5117       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5118       {
5119         boolean first_horiz = RND(2);
5120         int new_move_dir = MovDir[x][y];
5121
5122         MovDir[x][y] =
5123           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5124         Moving2Blocked(x, y, &newx, &newy);
5125
5126         if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5127           return;
5128
5129         MovDir[x][y] =
5130           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5131         Moving2Blocked(x, y, &newx, &newy);
5132
5133         if (PENGUIN_CAN_ENTER_FIELD(EL_PENGUIN, newx, newy))
5134           return;
5135
5136         MovDir[x][y] = old_move_dir;
5137         return;
5138       }
5139     }
5140     else        /* (element == EL_SATELLITE) */
5141     {
5142       int newx, newy;
5143
5144       MovDelay[x][y] = 1;
5145
5146       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5147       {
5148         boolean first_horiz = RND(2);
5149         int new_move_dir = MovDir[x][y];
5150
5151         MovDir[x][y] =
5152           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5153         Moving2Blocked(x, y, &newx, &newy);
5154
5155         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5156           return;
5157
5158         MovDir[x][y] =
5159           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5160         Moving2Blocked(x, y, &newx, &newy);
5161
5162         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5163           return;
5164
5165         MovDir[x][y] = old_move_dir;
5166         return;
5167       }
5168     }
5169   }
5170   else if (move_pattern == MV_TURNING_LEFT ||
5171            move_pattern == MV_TURNING_RIGHT ||
5172            move_pattern == MV_TURNING_LEFT_RIGHT ||
5173            move_pattern == MV_TURNING_RIGHT_LEFT ||
5174            move_pattern == MV_TURNING_RANDOM ||
5175            move_pattern == MV_ALL_DIRECTIONS)
5176   {
5177     boolean can_turn_left =
5178       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5179     boolean can_turn_right =
5180       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5181
5182 #if USE_CAN_MOVE_NOT_MOVING
5183     if (element_info[element].move_stepsize == 0)       /* not moving */
5184       return;
5185 #endif
5186
5187     if (move_pattern == MV_TURNING_LEFT)
5188       MovDir[x][y] = left_dir;
5189     else if (move_pattern == MV_TURNING_RIGHT)
5190       MovDir[x][y] = right_dir;
5191     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5192       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5193     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5194       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5195     else if (move_pattern == MV_TURNING_RANDOM)
5196       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5197                       can_turn_right && !can_turn_left ? right_dir :
5198                       RND(2) ? left_dir : right_dir);
5199     else if (can_turn_left && can_turn_right)
5200       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5201     else if (can_turn_left)
5202       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5203     else if (can_turn_right)
5204       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5205     else
5206       MovDir[x][y] = back_dir;
5207
5208     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5209   }
5210   else if (move_pattern == MV_HORIZONTAL ||
5211            move_pattern == MV_VERTICAL)
5212   {
5213     if (move_pattern & old_move_dir)
5214       MovDir[x][y] = back_dir;
5215     else if (move_pattern == MV_HORIZONTAL)
5216       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5217     else if (move_pattern == MV_VERTICAL)
5218       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5219
5220     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5221   }
5222   else if (move_pattern & MV_ANY_DIRECTION)
5223   {
5224     MovDir[x][y] = move_pattern;
5225     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5226   }
5227   else if (move_pattern == MV_ALONG_LEFT_SIDE)
5228   {
5229     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5230       MovDir[x][y] = left_dir;
5231     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5232       MovDir[x][y] = right_dir;
5233
5234     if (MovDir[x][y] != old_move_dir)
5235       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5236   }
5237   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5238   {
5239     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5240       MovDir[x][y] = right_dir;
5241     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5242       MovDir[x][y] = left_dir;
5243
5244     if (MovDir[x][y] != old_move_dir)
5245       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5246   }
5247   else if (move_pattern == MV_TOWARDS_PLAYER ||
5248            move_pattern == MV_AWAY_FROM_PLAYER)
5249   {
5250     int attr_x = -1, attr_y = -1;
5251     int newx, newy;
5252     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5253
5254     if (AllPlayersGone)
5255     {
5256       attr_x = ExitX;
5257       attr_y = ExitY;
5258     }
5259     else
5260     {
5261       int i;
5262
5263       for (i = 0; i < MAX_PLAYERS; i++)
5264       {
5265         struct PlayerInfo *player = &stored_player[i];
5266         int jx = player->jx, jy = player->jy;
5267
5268         if (!player->active)
5269           continue;
5270
5271         if (attr_x == -1 ||
5272             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5273         {
5274           attr_x = jx;
5275           attr_y = jy;
5276         }
5277       }
5278     }
5279
5280     MovDir[x][y] = MV_NO_MOVING;
5281     if (attr_x < x)
5282       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5283     else if (attr_x > x)
5284       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5285     if (attr_y < y)
5286       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5287     else if (attr_y > y)
5288       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5289
5290     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5291
5292     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5293     {
5294       boolean first_horiz = RND(2);
5295       int new_move_dir = MovDir[x][y];
5296
5297 #if USE_CAN_MOVE_NOT_MOVING
5298       if (element_info[element].move_stepsize == 0)     /* not moving */
5299       {
5300         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5301         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5302
5303         return;
5304       }
5305 #endif
5306
5307       MovDir[x][y] =
5308         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5309       Moving2Blocked(x, y, &newx, &newy);
5310
5311       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5312         return;
5313
5314       MovDir[x][y] =
5315         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5316       Moving2Blocked(x, y, &newx, &newy);
5317
5318       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5319         return;
5320
5321       MovDir[x][y] = old_move_dir;
5322     }
5323   }
5324   else if (move_pattern == MV_WHEN_PUSHED ||
5325            move_pattern == MV_WHEN_DROPPED)
5326   {
5327     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5328       MovDir[x][y] = MV_NO_MOVING;
5329
5330     MovDelay[x][y] = 0;
5331   }
5332   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5333   {
5334     static int test_xy[7][2] =
5335     {
5336       { 0, -1 },
5337       { -1, 0 },
5338       { +1, 0 },
5339       { 0, +1 },
5340       { 0, -1 },
5341       { -1, 0 },
5342       { +1, 0 },
5343     };
5344     static int test_dir[7] =
5345     {
5346       MV_UP,
5347       MV_LEFT,
5348       MV_RIGHT,
5349       MV_DOWN,
5350       MV_UP,
5351       MV_LEFT,
5352       MV_RIGHT,
5353     };
5354     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5355     int move_preference = -1000000;     /* start with very low preference */
5356     int new_move_dir = MV_NO_MOVING;
5357     int start_test = RND(4);
5358     int i;
5359
5360     for (i = 0; i < NUM_DIRECTIONS; i++)
5361     {
5362       int move_dir = test_dir[start_test + i];
5363       int move_dir_preference;
5364
5365       xx = x + test_xy[start_test + i][0];
5366       yy = y + test_xy[start_test + i][1];
5367
5368       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5369           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5370       {
5371         new_move_dir = move_dir;
5372
5373         break;
5374       }
5375
5376       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5377         continue;
5378
5379       move_dir_preference = -1 * RunnerVisit[xx][yy];
5380       if (hunter_mode && PlayerVisit[xx][yy] > 0)
5381         move_dir_preference = PlayerVisit[xx][yy];
5382
5383       if (move_dir_preference > move_preference)
5384       {
5385         /* prefer field that has not been visited for the longest time */
5386         move_preference = move_dir_preference;
5387         new_move_dir = move_dir;
5388       }
5389       else if (move_dir_preference == move_preference &&
5390                move_dir == old_move_dir)
5391       {
5392         /* prefer last direction when all directions are preferred equally */
5393         move_preference = move_dir_preference;
5394         new_move_dir = move_dir;
5395       }
5396     }
5397
5398     MovDir[x][y] = new_move_dir;
5399     if (old_move_dir != new_move_dir)
5400     {
5401 #if 1
5402       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5403 #else
5404       MovDelay[x][y] = 9;
5405 #endif
5406     }
5407   }
5408 }
5409
5410 static void TurnRound(int x, int y)
5411 {
5412   int direction = MovDir[x][y];
5413
5414 #if 0
5415   GfxDir[x][y] = MovDir[x][y];
5416 #endif
5417
5418   TurnRoundExt(x, y);
5419
5420 #if 1
5421   GfxDir[x][y] = MovDir[x][y];
5422 #endif
5423
5424   if (direction != MovDir[x][y])
5425     GfxFrame[x][y] = 0;
5426
5427 #if 1
5428   if (MovDelay[x][y])
5429     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
5430 #else
5431   if (MovDelay[x][y])
5432     GfxAction[x][y] = ACTION_WAITING;
5433 #endif
5434 }
5435
5436 static boolean JustBeingPushed(int x, int y)
5437 {
5438   int i;
5439
5440   for (i = 0; i < MAX_PLAYERS; i++)
5441   {
5442     struct PlayerInfo *player = &stored_player[i];
5443
5444     if (player->active && player->is_pushing && player->MovPos)
5445     {
5446       int next_jx = player->jx + (player->jx - player->last_jx);
5447       int next_jy = player->jy + (player->jy - player->last_jy);
5448
5449       if (x == next_jx && y == next_jy)
5450         return TRUE;
5451     }
5452   }
5453
5454   return FALSE;
5455 }
5456
5457 void StartMoving(int x, int y)
5458 {
5459 #if 0
5460   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
5461 #endif
5462   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
5463   int element = Feld[x][y];
5464
5465   if (Stop[x][y])
5466     return;
5467
5468 #if 1
5469   if (MovDelay[x][y] == 0)
5470     GfxAction[x][y] = ACTION_DEFAULT;
5471 #else
5472   /* !!! this should be handled more generic (not only for mole) !!! */
5473   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
5474     GfxAction[x][y] = ACTION_DEFAULT;
5475 #endif
5476
5477   if (CAN_FALL(element) && y < lev_fieldy - 1)
5478   {
5479     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5480         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5481       if (JustBeingPushed(x, y))
5482         return;
5483
5484     if (element == EL_QUICKSAND_FULL)
5485     {
5486       if (IS_FREE(x, y + 1))
5487       {
5488         InitMovingField(x, y, MV_DOWN);
5489         started_moving = TRUE;
5490
5491         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5492         Store[x][y] = EL_ROCK;
5493 #if 1
5494         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5495 #else
5496         PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
5497 #endif
5498       }
5499       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5500       {
5501         if (!MovDelay[x][y])
5502           MovDelay[x][y] = TILEY + 1;
5503
5504         if (MovDelay[x][y])
5505         {
5506           MovDelay[x][y]--;
5507           if (MovDelay[x][y])
5508             return;
5509         }
5510
5511         Feld[x][y] = EL_QUICKSAND_EMPTY;
5512         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5513         Store[x][y + 1] = Store[x][y];
5514         Store[x][y] = 0;
5515 #if 1
5516         PlayLevelSoundAction(x, y, ACTION_FILLING);
5517 #else
5518         PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5519 #endif
5520       }
5521     }
5522     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5523              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5524     {
5525       InitMovingField(x, y, MV_DOWN);
5526       started_moving = TRUE;
5527
5528       Feld[x][y] = EL_QUICKSAND_FILLING;
5529       Store[x][y] = element;
5530 #if 1
5531       PlayLevelSoundAction(x, y, ACTION_FILLING);
5532 #else
5533       PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
5534 #endif
5535     }
5536     else if (element == EL_MAGIC_WALL_FULL)
5537     {
5538       if (IS_FREE(x, y + 1))
5539       {
5540         InitMovingField(x, y, MV_DOWN);
5541         started_moving = TRUE;
5542
5543         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5544         Store[x][y] = EL_CHANGED(Store[x][y]);
5545       }
5546       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5547       {
5548         if (!MovDelay[x][y])
5549           MovDelay[x][y] = TILEY/4 + 1;
5550
5551         if (MovDelay[x][y])
5552         {
5553           MovDelay[x][y]--;
5554           if (MovDelay[x][y])
5555             return;
5556         }
5557
5558         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5559         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5560         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5561         Store[x][y] = 0;
5562       }
5563     }
5564     else if (element == EL_BD_MAGIC_WALL_FULL)
5565     {
5566       if (IS_FREE(x, y + 1))
5567       {
5568         InitMovingField(x, y, MV_DOWN);
5569         started_moving = TRUE;
5570
5571         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5572         Store[x][y] = EL_CHANGED2(Store[x][y]);
5573       }
5574       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5575       {
5576         if (!MovDelay[x][y])
5577           MovDelay[x][y] = TILEY/4 + 1;
5578
5579         if (MovDelay[x][y])
5580         {
5581           MovDelay[x][y]--;
5582           if (MovDelay[x][y])
5583             return;
5584         }
5585
5586         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5587         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5588         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5589         Store[x][y] = 0;
5590       }
5591     }
5592     else if (CAN_PASS_MAGIC_WALL(element) &&
5593              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5594               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5595     {
5596       InitMovingField(x, y, MV_DOWN);
5597       started_moving = TRUE;
5598
5599       Feld[x][y] =
5600         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5601          EL_BD_MAGIC_WALL_FILLING);
5602       Store[x][y] = element;
5603     }
5604 #if 0
5605     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
5606 #else
5607     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5608 #endif
5609     {
5610       SplashAcid(x, y + 1);
5611
5612       InitMovingField(x, y, MV_DOWN);
5613       started_moving = TRUE;
5614
5615       Store[x][y] = EL_ACID;
5616 #if 0
5617       /* !!! TEST !!! better use "_FALLING" etc. !!! */
5618       GfxAction[x][y + 1] = ACTION_ACTIVE;
5619 #endif
5620     }
5621 #if 1
5622     else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5623               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5624
5625 #if USE_IMPACT_BUGFIX
5626              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5627               CAN_FALL(element) && WasJustFalling[x][y] &&
5628               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5629
5630              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5631               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5632               (Feld[x][y + 1] == EL_BLOCKED)))
5633 #else
5634              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5635               CAN_SMASH(element) && WasJustFalling[x][y] &&
5636               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5637
5638              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5639               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5640               (Feld[x][y + 1] == EL_BLOCKED)))
5641 #endif
5642
5643 #else
5644 #if 1
5645     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5646              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5647              WasJustMoving[x][y] && !Pushed[x][y + 1])
5648 #else
5649     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
5650              WasJustMoving[x][y])
5651 #endif
5652 #endif
5653
5654     {
5655       /* this is needed for a special case not covered by calling "Impact()"
5656          from "ContinueMoving()": if an element moves to a tile directly below
5657          another element which was just falling on that tile (which was empty
5658          in the previous frame), the falling element above would just stop
5659          instead of smashing the element below (in previous version, the above
5660          element was just checked for "moving" instead of "falling", resulting
5661          in incorrect smashes caused by horizontal movement of the above
5662          element; also, the case of the player being the element to smash was
5663          simply not covered here... :-/ ) */
5664
5665 #if 0
5666       WasJustMoving[x][y] = 0;
5667       WasJustFalling[x][y] = 0;
5668 #endif
5669
5670       CheckCollision[x][y] = 0;
5671
5672 #if 0
5673       if (IS_PLAYER(x, y + 1))
5674         printf("::: we ARE now killing the player [%d]\n", FrameCounter);
5675 #endif
5676
5677       Impact(x, y);
5678     }
5679     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5680     {
5681       if (MovDir[x][y] == MV_NO_MOVING)
5682       {
5683         InitMovingField(x, y, MV_DOWN);
5684         started_moving = TRUE;
5685       }
5686     }
5687     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5688     {
5689       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5690         MovDir[x][y] = MV_DOWN;
5691
5692       InitMovingField(x, y, MV_DOWN);
5693       started_moving = TRUE;
5694     }
5695     else if (element == EL_AMOEBA_DROP)
5696     {
5697       Feld[x][y] = EL_AMOEBA_GROWING;
5698       Store[x][y] = EL_AMOEBA_WET;
5699     }
5700     /* Store[x][y + 1] must be zero, because:
5701        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
5702     */
5703 #if 0
5704 #if OLD_GAME_BEHAVIOUR
5705     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
5706 #else
5707     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
5708              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5709              element != EL_DX_SUPABOMB)
5710 #endif
5711 #else
5712     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5713               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5714              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5715              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5716 #endif
5717     {
5718       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
5719                                 (IS_FREE(x - 1, y + 1) ||
5720                                  Feld[x - 1][y + 1] == EL_ACID));
5721       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5722                                 (IS_FREE(x + 1, y + 1) ||
5723                                  Feld[x + 1][y + 1] == EL_ACID));
5724       boolean can_fall_any  = (can_fall_left || can_fall_right);
5725       boolean can_fall_both = (can_fall_left && can_fall_right);
5726
5727       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5728       {
5729         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5730
5731         if (slippery_type == SLIPPERY_ONLY_LEFT)
5732           can_fall_right = FALSE;
5733         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5734           can_fall_left = FALSE;
5735         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5736           can_fall_right = FALSE;
5737         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5738           can_fall_left = FALSE;
5739
5740         can_fall_any  = (can_fall_left || can_fall_right);
5741         can_fall_both = (can_fall_left && can_fall_right);
5742       }
5743
5744 #if USE_NEW_SP_SLIPPERY
5745       /* !!! better use the same properties as for custom elements here !!! */
5746       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5747                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5748       {
5749         can_fall_right = FALSE;         /* slip down on left side */
5750         can_fall_both = FALSE;
5751       }
5752 #endif
5753
5754 #if 1
5755       if (can_fall_both)
5756       {
5757         if (game.emulation == EMU_BOULDERDASH ||
5758             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5759           can_fall_right = FALSE;       /* slip down on left side */
5760         else
5761           can_fall_left = !(can_fall_right = RND(2));
5762
5763         can_fall_both = FALSE;
5764       }
5765 #endif
5766
5767       if (can_fall_any)
5768       {
5769 #if 0
5770         if (can_fall_both &&
5771             (game.emulation != EMU_BOULDERDASH &&
5772              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
5773           can_fall_left = !(can_fall_right = RND(2));
5774 #endif
5775
5776         /* if not determined otherwise, prefer left side for slipping down */
5777         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
5778         started_moving = TRUE;
5779       }
5780     }
5781 #if 0
5782     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
5783 #else
5784     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
5785 #endif
5786     {
5787       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
5788       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
5789       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
5790       int belt_dir = game.belt_dir[belt_nr];
5791
5792       if ((belt_dir == MV_LEFT  && left_is_free) ||
5793           (belt_dir == MV_RIGHT && right_is_free))
5794       {
5795 #if 1
5796         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
5797 #endif
5798
5799         InitMovingField(x, y, belt_dir);
5800         started_moving = TRUE;
5801
5802 #if 1
5803         Pushed[x][y] = TRUE;
5804         Pushed[nextx][y] = TRUE;
5805 #endif
5806
5807         GfxAction[x][y] = ACTION_DEFAULT;
5808       }
5809       else
5810       {
5811         MovDir[x][y] = 0;       /* if element was moving, stop it */
5812       }
5813     }
5814   }
5815
5816   /* not "else if" because of elements that can fall and move (EL_SPRING) */
5817 #if 0
5818   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
5819 #else
5820   if (CAN_MOVE(element) && !started_moving)
5821 #endif
5822   {
5823     int move_pattern = element_info[element].move_pattern;
5824     int newx, newy;
5825
5826 #if 0
5827 #if DEBUG
5828     if (MovDir[x][y] == MV_NO_MOVING)
5829     {
5830       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
5831              x, y, element, element_info[element].token_name);
5832       printf("StartMoving(): This should never happen!\n");
5833     }
5834 #endif
5835 #endif
5836
5837     Moving2Blocked(x, y, &newx, &newy);
5838
5839 #if 1
5840     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
5841       return;
5842 #else
5843     if ((element == EL_SATELLITE ||
5844          element == EL_BALLOON ||
5845          element == EL_SPRING)
5846         && JustBeingPushed(x, y))
5847       return;
5848 #endif
5849
5850 #if 1
5851
5852 #if 1
5853     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5854         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5855 #else
5856     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5857         WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
5858         (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
5859 #endif
5860     {
5861 #if 0
5862       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
5863              element, element_info[element].token_name,
5864              WasJustMoving[x][y],
5865              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
5866              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
5867              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_X),
5868              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_X));
5869 #endif
5870
5871 #if 1
5872       WasJustMoving[x][y] = 0;
5873 #endif
5874
5875       CheckCollision[x][y] = 0;
5876
5877       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
5878
5879 #if 0
5880       if (Feld[x][y] != element)        /* element has changed */
5881       {
5882         element = Feld[x][y];
5883         move_pattern = element_info[element].move_pattern;
5884
5885         if (!CAN_MOVE(element))
5886           return;
5887       }
5888 #else
5889       if (Feld[x][y] != element)        /* element has changed */
5890         return;
5891 #endif
5892     }
5893 #endif
5894
5895 #if 0
5896 #if 0
5897     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
5898       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
5899 #else
5900     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
5901     {
5902       Moving2Blocked(x, y, &newx, &newy);
5903       if (Feld[newx][newy] == EL_BLOCKED)
5904         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
5905     }
5906 #endif
5907 #endif
5908
5909 #if 0
5910     if (FrameCounter < 1 && x == 0 && y == 29)
5911       printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
5912 #endif
5913
5914     if (!MovDelay[x][y])        /* start new movement phase */
5915     {
5916       /* all objects that can change their move direction after each step
5917          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
5918
5919       if (element != EL_YAMYAM &&
5920           element != EL_DARK_YAMYAM &&
5921           element != EL_PACMAN &&
5922           !(move_pattern & MV_ANY_DIRECTION) &&
5923           move_pattern != MV_TURNING_LEFT &&
5924           move_pattern != MV_TURNING_RIGHT &&
5925           move_pattern != MV_TURNING_LEFT_RIGHT &&
5926           move_pattern != MV_TURNING_RIGHT_LEFT &&
5927           move_pattern != MV_TURNING_RANDOM)
5928       {
5929         TurnRound(x, y);
5930
5931 #if 0
5932         if (FrameCounter < 1 && x == 0 && y == 29)
5933           printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
5934 #endif
5935
5936         if (MovDelay[x][y] && (element == EL_BUG ||
5937                                element == EL_SPACESHIP ||
5938                                element == EL_SP_SNIKSNAK ||
5939                                element == EL_SP_ELECTRON ||
5940                                element == EL_MOLE))
5941           DrawLevelField(x, y);
5942       }
5943     }
5944
5945     if (MovDelay[x][y])         /* wait some time before next movement */
5946     {
5947       MovDelay[x][y]--;
5948
5949 #if 0
5950       if (element == EL_YAMYAM)
5951       {
5952         printf("::: %d\n",
5953                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
5954         DrawLevelElementAnimation(x, y, element);
5955       }
5956 #endif
5957
5958       if (MovDelay[x][y])       /* element still has to wait some time */
5959       {
5960 #if 0
5961         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
5962         ResetGfxAnimation(x, y);
5963 #endif
5964
5965 #if 0
5966         if (GfxAction[x][y] != ACTION_WAITING)
5967           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
5968
5969         GfxAction[x][y] = ACTION_WAITING;
5970 #endif
5971       }
5972
5973       if (element == EL_ROBOT ||
5974 #if 0
5975           element == EL_PACMAN ||
5976 #endif
5977           element == EL_YAMYAM ||
5978           element == EL_DARK_YAMYAM)
5979       {
5980 #if 0
5981         DrawLevelElementAnimation(x, y, element);
5982 #else
5983         DrawLevelElementAnimationIfNeeded(x, y, element);
5984 #endif
5985         PlayLevelSoundAction(x, y, ACTION_WAITING);
5986       }
5987       else if (element == EL_SP_ELECTRON)
5988         DrawLevelElementAnimationIfNeeded(x, y, element);
5989       else if (element == EL_DRAGON)
5990       {
5991         int i;
5992         int dir = MovDir[x][y];
5993         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
5994         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
5995         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
5996                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
5997                        dir == MV_UP     ? IMG_FLAMES_1_UP :
5998                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
5999         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6000
6001 #if 0
6002         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
6003 #endif
6004
6005         GfxAction[x][y] = ACTION_ATTACKING;
6006
6007         if (IS_PLAYER(x, y))
6008           DrawPlayerField(x, y);
6009         else
6010           DrawLevelField(x, y);
6011
6012         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6013
6014         for (i = 1; i <= 3; i++)
6015         {
6016           int xx = x + i * dx;
6017           int yy = y + i * dy;
6018           int sx = SCREENX(xx);
6019           int sy = SCREENY(yy);
6020           int flame_graphic = graphic + (i - 1);
6021
6022           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6023             break;
6024
6025           if (MovDelay[x][y])
6026           {
6027             int flamed = MovingOrBlocked2Element(xx, yy);
6028
6029             /* !!! */
6030 #if 0
6031             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6032               Bang(xx, yy);
6033             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6034               RemoveMovingField(xx, yy);
6035             else
6036               RemoveField(xx, yy);
6037 #else
6038             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6039               Bang(xx, yy);
6040             else
6041               RemoveMovingField(xx, yy);
6042 #endif
6043
6044 #if 0
6045             if (ChangeDelay[xx][yy])
6046               printf("::: !!! [%d]\n", (IS_MOVING(xx, yy) ||
6047                                         Feld[xx][yy] == EL_BLOCKED));
6048 #endif
6049
6050 #if 1
6051             ChangeDelay[xx][yy] = 0;
6052 #endif
6053             Feld[xx][yy] = EL_FLAMES;
6054             if (IN_SCR_FIELD(sx, sy))
6055             {
6056               DrawLevelFieldCrumbledSand(xx, yy);
6057               DrawGraphic(sx, sy, flame_graphic, frame);
6058             }
6059           }
6060           else
6061           {
6062             if (Feld[xx][yy] == EL_FLAMES)
6063               Feld[xx][yy] = EL_EMPTY;
6064             DrawLevelField(xx, yy);
6065           }
6066         }
6067       }
6068
6069       if (MovDelay[x][y])       /* element still has to wait some time */
6070       {
6071         PlayLevelSoundAction(x, y, ACTION_WAITING);
6072
6073         return;
6074       }
6075
6076 #if 0
6077       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
6078          for all other elements GfxAction will be set by InitMovingField() */
6079       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
6080         GfxAction[x][y] = ACTION_MOVING;
6081 #endif
6082     }
6083
6084     /* now make next step */
6085
6086     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6087
6088     if (DONT_COLLIDE_WITH(element) &&
6089         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6090         !PLAYER_ENEMY_PROTECTED(newx, newy))
6091     {
6092 #if 1
6093       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
6094
6095       return;
6096 #else
6097       /* player killed by element which is deadly when colliding with */
6098       MovDir[x][y] = 0;
6099       KillHero(PLAYERINFO(newx, newy));
6100       return;
6101 #endif
6102
6103     }
6104 #if 1
6105 #if 1
6106     else if (CAN_MOVE_INTO_ACID(element) &&
6107              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6108              (MovDir[x][y] == MV_DOWN ||
6109               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6110 #else
6111     else if (CAN_MOVE_INTO_ACID(element) && MovDir[x][y] == MV_DOWN &&
6112              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID)
6113 #endif
6114 #else
6115
6116     else if ((element == EL_PENGUIN ||
6117               element == EL_ROBOT ||
6118               element == EL_SATELLITE ||
6119               element == EL_BALLOON ||
6120               IS_CUSTOM_ELEMENT(element)) &&
6121              IN_LEV_FIELD(newx, newy) &&
6122              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
6123 #endif
6124     {
6125       SplashAcid(newx, newy);
6126       Store[x][y] = EL_ACID;
6127     }
6128     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6129     {
6130       if (Feld[newx][newy] == EL_EXIT_OPEN)
6131       {
6132 #if 1
6133         RemoveField(x, y);
6134         DrawLevelField(x, y);
6135 #else
6136         Feld[x][y] = EL_EMPTY;
6137         DrawLevelField(x, y);
6138 #endif
6139
6140         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6141         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6142           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6143
6144         local_player->friends_still_needed--;
6145         if (!local_player->friends_still_needed &&
6146             !local_player->GameOver && AllPlayersGone)
6147           local_player->LevelSolved = local_player->GameOver = TRUE;
6148
6149         return;
6150       }
6151       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6152       {
6153         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
6154           DrawLevelField(newx, newy);
6155         else
6156           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6157       }
6158       else if (!IS_FREE(newx, newy))
6159       {
6160         GfxAction[x][y] = ACTION_WAITING;
6161
6162         if (IS_PLAYER(x, y))
6163           DrawPlayerField(x, y);
6164         else
6165           DrawLevelField(x, y);
6166
6167         return;
6168       }
6169     }
6170     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6171     {
6172       if (IS_FOOD_PIG(Feld[newx][newy]))
6173       {
6174         if (IS_MOVING(newx, newy))
6175           RemoveMovingField(newx, newy);
6176         else
6177         {
6178           Feld[newx][newy] = EL_EMPTY;
6179           DrawLevelField(newx, newy);
6180         }
6181
6182         PlayLevelSound(x, y, SND_PIG_DIGGING);
6183       }
6184       else if (!IS_FREE(newx, newy))
6185       {
6186         if (IS_PLAYER(x, y))
6187           DrawPlayerField(x, y);
6188         else
6189           DrawLevelField(x, y);
6190
6191         return;
6192       }
6193     }
6194
6195 #if 1
6196
6197     /*
6198     else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
6199     */
6200
6201     else if (IS_CUSTOM_ELEMENT(element) &&
6202              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
6203
6204 #if 0
6205  &&
6206              !IS_FREE(newx, newy)
6207 #endif
6208
6209 )
6210     {
6211       int new_element = Feld[newx][newy];
6212
6213 #if 0
6214       printf("::: '%s' digs '%s' [%d]\n",
6215              element_info[element].token_name,
6216              element_info[Feld[newx][newy]].token_name,
6217              StorePlayer[newx][newy]);
6218 #endif
6219
6220       if (!IS_FREE(newx, newy))
6221       {
6222         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6223                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6224                       ACTION_BREAKING);
6225
6226         /* no element can dig solid indestructible elements */
6227         if (IS_INDESTRUCTIBLE(new_element) &&
6228             !IS_DIGGABLE(new_element) &&
6229             !IS_COLLECTIBLE(new_element))
6230           return;
6231
6232         if (AmoebaNr[newx][newy] &&
6233             (new_element == EL_AMOEBA_FULL ||
6234              new_element == EL_BD_AMOEBA ||
6235              new_element == EL_AMOEBA_GROWING))
6236         {
6237           AmoebaCnt[AmoebaNr[newx][newy]]--;
6238           AmoebaCnt2[AmoebaNr[newx][newy]]--;
6239         }
6240
6241         if (IS_MOVING(newx, newy))
6242           RemoveMovingField(newx, newy);
6243         else
6244         {
6245           RemoveField(newx, newy);
6246           DrawLevelField(newx, newy);
6247         }
6248
6249         /* if digged element was about to explode, prevent the explosion */
6250         ExplodeField[newx][newy] = EX_TYPE_NONE;
6251
6252         PlayLevelSoundAction(x, y, action);
6253       }
6254
6255 #if 1
6256 #if 1
6257       Store[newx][newy] = EL_EMPTY;
6258       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6259       {
6260 #if USE_CHANGE_TO_TRIGGERED
6261         int move_leave_element = element_info[element].move_leave_element;
6262
6263         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6264                              new_element : move_leave_element);
6265 #else
6266         Store[newx][newy] = element_info[element].move_leave_element;
6267 #endif
6268       }
6269 #else
6270       Store[newx][newy] = EL_EMPTY;
6271       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)) ||
6272           element_info[element].move_leave_type == LEAVE_TYPE_UNLIMITED)
6273         Store[newx][newy] = element_info[element].move_leave_element;
6274 #endif
6275 #else
6276       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6277         element_info[element].can_leave_element = TRUE;
6278 #endif
6279
6280       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6281       {
6282         RunnerVisit[x][y] = FrameCounter;
6283         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6284       }
6285     }
6286
6287 #endif
6288
6289     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6290     {
6291       if (!IS_FREE(newx, newy))
6292       {
6293         if (IS_PLAYER(x, y))
6294           DrawPlayerField(x, y);
6295         else
6296           DrawLevelField(x, y);
6297
6298         return;
6299       }
6300       else
6301       {
6302         boolean wanna_flame = !RND(10);
6303         int dx = newx - x, dy = newy - y;
6304         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6305         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6306         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6307                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6308         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6309                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6310
6311         if ((wanna_flame ||
6312              IS_CLASSIC_ENEMY(element1) ||
6313              IS_CLASSIC_ENEMY(element2)) &&
6314             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6315             element1 != EL_FLAMES && element2 != EL_FLAMES)
6316         {
6317 #if 1
6318           ResetGfxAnimation(x, y);
6319           GfxAction[x][y] = ACTION_ATTACKING;
6320 #endif
6321
6322           if (IS_PLAYER(x, y))
6323             DrawPlayerField(x, y);
6324           else
6325             DrawLevelField(x, y);
6326
6327           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6328
6329           MovDelay[x][y] = 50;
6330
6331           /* !!! */
6332 #if 0
6333           RemoveField(newx, newy);
6334 #endif
6335           Feld[newx][newy] = EL_FLAMES;
6336           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6337           {
6338 #if 0
6339             RemoveField(newx1, newy1);
6340 #endif
6341             Feld[newx1][newy1] = EL_FLAMES;
6342           }
6343           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6344           {
6345 #if 0
6346             RemoveField(newx2, newy2);
6347 #endif
6348             Feld[newx2][newy2] = EL_FLAMES;
6349           }
6350
6351           return;
6352         }
6353       }
6354     }
6355     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6356              Feld[newx][newy] == EL_DIAMOND)
6357     {
6358       if (IS_MOVING(newx, newy))
6359         RemoveMovingField(newx, newy);
6360       else
6361       {
6362         Feld[newx][newy] = EL_EMPTY;
6363         DrawLevelField(newx, newy);
6364       }
6365
6366       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6367     }
6368     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6369              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6370     {
6371       if (AmoebaNr[newx][newy])
6372       {
6373         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6374         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6375             Feld[newx][newy] == EL_BD_AMOEBA)
6376           AmoebaCnt[AmoebaNr[newx][newy]]--;
6377       }
6378
6379 #if 0
6380       /* !!! test !!! */
6381       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6382 #else
6383       if (IS_MOVING(newx, newy))
6384 #endif
6385       {
6386         RemoveMovingField(newx, newy);
6387       }
6388       else
6389       {
6390         Feld[newx][newy] = EL_EMPTY;
6391         DrawLevelField(newx, newy);
6392       }
6393
6394       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6395     }
6396     else if ((element == EL_PACMAN || element == EL_MOLE)
6397              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6398     {
6399       if (AmoebaNr[newx][newy])
6400       {
6401         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6402         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6403             Feld[newx][newy] == EL_BD_AMOEBA)
6404           AmoebaCnt[AmoebaNr[newx][newy]]--;
6405       }
6406
6407       if (element == EL_MOLE)
6408       {
6409         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6410         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6411
6412         ResetGfxAnimation(x, y);
6413         GfxAction[x][y] = ACTION_DIGGING;
6414         DrawLevelField(x, y);
6415
6416         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6417
6418         return;                         /* wait for shrinking amoeba */
6419       }
6420       else      /* element == EL_PACMAN */
6421       {
6422         Feld[newx][newy] = EL_EMPTY;
6423         DrawLevelField(newx, newy);
6424         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6425       }
6426     }
6427     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6428              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6429               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6430     {
6431       /* wait for shrinking amoeba to completely disappear */
6432       return;
6433     }
6434     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6435     {
6436       /* object was running against a wall */
6437
6438       TurnRound(x, y);
6439
6440 #if 0
6441       if (move_pattern & MV_ANY_DIRECTION &&
6442           move_pattern == MovDir[x][y])
6443       {
6444         int blocking_element =
6445           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6446
6447 #if 0
6448         printf("::: '%s' is blocked by '%s'! [%d,%d -> %d,%d]\n",
6449                element_info[element].token_name,
6450                element_info[blocking_element].token_name,
6451                x, y, newx, newy);
6452 #endif
6453
6454         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6455                                  MovDir[x][y]);
6456
6457         element = Feld[x][y];   /* element might have changed */
6458       }
6459 #endif
6460
6461 #if 1
6462       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
6463         DrawLevelElementAnimation(x, y, element);
6464 #else
6465       if (element == EL_BUG ||
6466           element == EL_SPACESHIP ||
6467           element == EL_SP_SNIKSNAK)
6468         DrawLevelField(x, y);
6469       else if (element == EL_MOLE)
6470         DrawLevelField(x, y);
6471       else if (element == EL_BD_BUTTERFLY ||
6472                element == EL_BD_FIREFLY)
6473         DrawLevelElementAnimationIfNeeded(x, y, element);
6474       else if (element == EL_SATELLITE)
6475         DrawLevelElementAnimationIfNeeded(x, y, element);
6476       else if (element == EL_SP_ELECTRON)
6477         DrawLevelElementAnimationIfNeeded(x, y, element);
6478 #endif
6479
6480       if (DONT_TOUCH(element))
6481         TestIfBadThingTouchesHero(x, y);
6482
6483 #if 0
6484       PlayLevelSoundAction(x, y, ACTION_WAITING);
6485 #endif
6486
6487       return;
6488     }
6489
6490     InitMovingField(x, y, MovDir[x][y]);
6491
6492     PlayLevelSoundAction(x, y, ACTION_MOVING);
6493   }
6494
6495   if (MovDir[x][y])
6496     ContinueMoving(x, y);
6497 }
6498
6499 void dummy()
6500 {
6501 }
6502
6503 void ContinueMoving(int x, int y)
6504 {
6505   int element = Feld[x][y];
6506   int stored = Store[x][y];
6507   struct ElementInfo *ei = &element_info[element];
6508   int direction = MovDir[x][y];
6509   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6510   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
6511   int newx = x + dx, newy = y + dy;
6512 #if 0
6513   int nextx = newx + dx, nexty = newy + dy;
6514 #endif
6515 #if 1
6516   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
6517   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6518 #else
6519   boolean pushed_by_player = Pushed[x][y];
6520 #endif
6521   boolean last_line = (newy == lev_fieldy - 1);
6522
6523   MovPos[x][y] += getElementMoveStepsize(x, y);
6524
6525 #if 0
6526   if (pushed_by_player && IS_PLAYER(x, y))
6527   {
6528     /* special case: moving object pushed by player */
6529     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6530   }
6531 #else
6532   if (pushed_by_player) /* special case: moving object pushed by player */
6533     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6534 #endif
6535
6536   if (ABS(MovPos[x][y]) < TILEX)
6537   {
6538     DrawLevelField(x, y);
6539
6540     return;     /* element is still moving */
6541   }
6542
6543   /* element reached destination field */
6544
6545   Feld[x][y] = EL_EMPTY;
6546   Feld[newx][newy] = element;
6547   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6548
6549 #if 1
6550   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6551   {
6552     element = Feld[newx][newy] = EL_ACID;
6553   }
6554 #endif
6555   else if (element == EL_MOLE)
6556   {
6557     Feld[x][y] = EL_SAND;
6558
6559     DrawLevelFieldCrumbledSandNeighbours(x, y);
6560   }
6561   else if (element == EL_QUICKSAND_FILLING)
6562   {
6563     element = Feld[newx][newy] = get_next_element(element);
6564     Store[newx][newy] = Store[x][y];
6565   }
6566   else if (element == EL_QUICKSAND_EMPTYING)
6567   {
6568     Feld[x][y] = get_next_element(element);
6569     element = Feld[newx][newy] = Store[x][y];
6570   }
6571   else if (element == EL_MAGIC_WALL_FILLING)
6572   {
6573     element = Feld[newx][newy] = get_next_element(element);
6574     if (!game.magic_wall_active)
6575       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6576     Store[newx][newy] = Store[x][y];
6577   }
6578   else if (element == EL_MAGIC_WALL_EMPTYING)
6579   {
6580     Feld[x][y] = get_next_element(element);
6581     if (!game.magic_wall_active)
6582       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6583     element = Feld[newx][newy] = Store[x][y];
6584   }
6585   else if (element == EL_BD_MAGIC_WALL_FILLING)
6586   {
6587     element = Feld[newx][newy] = get_next_element(element);
6588     if (!game.magic_wall_active)
6589       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6590     Store[newx][newy] = Store[x][y];
6591   }
6592   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6593   {
6594     Feld[x][y] = get_next_element(element);
6595     if (!game.magic_wall_active)
6596       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6597     element = Feld[newx][newy] = Store[x][y];
6598   }
6599   else if (element == EL_AMOEBA_DROPPING)
6600   {
6601     Feld[x][y] = get_next_element(element);
6602     element = Feld[newx][newy] = Store[x][y];
6603   }
6604   else if (element == EL_SOKOBAN_OBJECT)
6605   {
6606     if (Back[x][y])
6607       Feld[x][y] = Back[x][y];
6608
6609     if (Back[newx][newy])
6610       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6611
6612     Back[x][y] = Back[newx][newy] = 0;
6613   }
6614 #if 0
6615   else if (Store[x][y] == EL_ACID)
6616   {
6617     element = Feld[newx][newy] = EL_ACID;
6618   }
6619 #endif
6620 #if 0
6621   else if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6622            ei->move_leave_element != EL_EMPTY &&
6623            (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6624             Store[x][y] != EL_EMPTY))
6625   {
6626     /* some elements can leave other elements behind after moving */
6627
6628     Feld[x][y] = ei->move_leave_element;
6629     InitField(x, y, FALSE);
6630
6631     if (GFX_CRUMBLED(Feld[x][y]))
6632       DrawLevelFieldCrumbledSandNeighbours(x, y);
6633   }
6634 #endif
6635
6636   Store[x][y] = EL_EMPTY;
6637   MovPos[x][y] = 0;
6638   MovDir[x][y] = 0;
6639   MovDelay[x][y] = 0;
6640   MovDelay[newx][newy] = 0;
6641
6642   if (CAN_CHANGE(element))
6643   {
6644     /* copy element change control values to new field */
6645     ChangeDelay[newx][newy] = ChangeDelay[x][y];
6646     ChangePage[newx][newy]  = ChangePage[x][y];
6647     Changed[newx][newy]     = Changed[x][y];
6648     ChangeEvent[newx][newy] = ChangeEvent[x][y];
6649   }
6650
6651   ChangeDelay[x][y] = 0;
6652   ChangePage[x][y] = -1;
6653   Changed[x][y] = FALSE;
6654   ChangeEvent[x][y] = -1;
6655
6656   /* copy animation control values to new field */
6657   GfxFrame[newx][newy]  = GfxFrame[x][y];
6658   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
6659   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
6660   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
6661
6662   Pushed[x][y] = Pushed[newx][newy] = FALSE;
6663
6664 #if 0
6665   /* do this after checking for left-behind element */
6666   ResetGfxAnimation(x, y);      /* reset animation values for old field */
6667 #endif
6668
6669 #if 1
6670   /* some elements can leave other elements behind after moving */
6671 #if 1
6672   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6673       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6674       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6675 #else
6676   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6677       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6678       !IS_PLAYER(x, y))
6679 #endif
6680   {
6681     int move_leave_element = ei->move_leave_element;
6682
6683 #if USE_CHANGE_TO_TRIGGERED
6684     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6685         ei->move_leave_element == EL_TRIGGER_ELEMENT)
6686       move_leave_element = stored;
6687 #endif
6688
6689     Feld[x][y] = move_leave_element;
6690
6691 #if USE_PREVIOUS_MOVE_DIR
6692     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6693       MovDir[x][y] = direction;
6694 #endif
6695
6696     InitField(x, y, FALSE);
6697
6698     if (GFX_CRUMBLED(Feld[x][y]))
6699       DrawLevelFieldCrumbledSandNeighbours(x, y);
6700
6701     if (ELEM_IS_PLAYER(move_leave_element))
6702       RelocatePlayer(x, y, move_leave_element);
6703   }
6704 #endif
6705
6706 #if 0
6707   /* some elements can leave other elements behind after moving */
6708   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
6709       ei->move_leave_element != EL_EMPTY &&
6710       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
6711        ei->can_leave_element_last))
6712   {
6713     Feld[x][y] = ei->move_leave_element;
6714     InitField(x, y, FALSE);
6715
6716     if (GFX_CRUMBLED(Feld[x][y]))
6717       DrawLevelFieldCrumbledSandNeighbours(x, y);
6718   }
6719
6720   ei->can_leave_element_last = ei->can_leave_element;
6721   ei->can_leave_element = FALSE;
6722 #endif
6723
6724 #if 1
6725   /* do this after checking for left-behind element */
6726   ResetGfxAnimation(x, y);      /* reset animation values for old field */
6727 #endif
6728
6729 #if 0
6730   /* 2.1.1 (does not work correctly for spring) */
6731   if (!CAN_MOVE(element))
6732     MovDir[newx][newy] = 0;
6733 #else
6734
6735 #if 0
6736   /* (does not work for falling objects that slide horizontally) */
6737   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
6738     MovDir[newx][newy] = 0;
6739 #else
6740   /*
6741   if (!CAN_MOVE(element) ||
6742       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
6743     MovDir[newx][newy] = 0;
6744   */
6745
6746 #if 0
6747   if (!CAN_MOVE(element) ||
6748       (CAN_FALL(element) && direction == MV_DOWN))
6749     GfxDir[x][y] = MovDir[newx][newy] = 0;
6750 #else
6751   if (!CAN_MOVE(element) ||
6752       (CAN_FALL(element) && direction == MV_DOWN &&
6753        (element == EL_SPRING ||
6754         element_info[element].move_pattern == MV_WHEN_PUSHED ||
6755         element_info[element].move_pattern == MV_WHEN_DROPPED)))
6756     GfxDir[x][y] = MovDir[newx][newy] = 0;
6757 #endif
6758
6759 #endif
6760 #endif
6761
6762   DrawLevelField(x, y);
6763   DrawLevelField(newx, newy);
6764
6765   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
6766
6767   /* prevent pushed element from moving on in pushed direction */
6768   if (pushed_by_player && CAN_MOVE(element) &&
6769       element_info[element].move_pattern & MV_ANY_DIRECTION &&
6770       !(element_info[element].move_pattern & direction))
6771     TurnRound(newx, newy);
6772
6773 #if 1
6774   /* prevent elements on conveyor belt from moving on in last direction */
6775   if (pushed_by_conveyor && CAN_FALL(element) &&
6776       direction & MV_HORIZONTAL)
6777   {
6778 #if 0
6779     if (CAN_MOVE(element))
6780       InitMovDir(newx, newy);
6781     else
6782       MovDir[newx][newy] = 0;
6783 #else
6784     MovDir[newx][newy] = 0;
6785 #endif
6786   }
6787 #endif
6788
6789   if (!pushed_by_player)
6790   {
6791     int nextx = newx + dx, nexty = newy + dy;
6792     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6793
6794     WasJustMoving[newx][newy] = 3;
6795
6796     if (CAN_FALL(element) && direction == MV_DOWN)
6797       WasJustFalling[newx][newy] = 3;
6798
6799     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6800       CheckCollision[newx][newy] = 2;
6801   }
6802
6803   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
6804   {
6805     TestIfBadThingTouchesHero(newx, newy);
6806     TestIfBadThingTouchesFriend(newx, newy);
6807
6808     if (!IS_CUSTOM_ELEMENT(element))
6809       TestIfBadThingTouchesOtherBadThing(newx, newy);
6810   }
6811   else if (element == EL_PENGUIN)
6812     TestIfFriendTouchesBadThing(newx, newy);
6813
6814 #if USE_NEW_MOVE_STYLE
6815 #if 0
6816   if (CAN_FALL(element) && direction == MV_DOWN &&
6817       !last_line && IS_PLAYER(x, newy + 1))
6818     printf("::: we would now kill the player [%d]\n", FrameCounter);
6819 #endif
6820
6821   /* give the player one last chance (one more frame) to move away */
6822   if (CAN_FALL(element) && direction == MV_DOWN &&
6823       (last_line || (!IS_FREE(x, newy + 1) &&
6824                      (!IS_PLAYER(x, newy + 1) ||
6825                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
6826     Impact(x, newy);
6827 #else
6828   if (CAN_FALL(element) && direction == MV_DOWN &&
6829       (last_line || !IS_FREE(x, newy + 1)))
6830     Impact(x, newy);
6831 #endif
6832
6833 #if 1
6834
6835 #if USE_PUSH_BUGFIX
6836 #if 1
6837   if (pushed_by_player && !game.use_change_when_pushing_bug)
6838 #else
6839   if (pushed_by_player && game.engine_version >= VERSION_IDENT(3,1,0,0))
6840 #endif
6841 #else
6842   if (pushed_by_player)
6843 #endif
6844
6845   {
6846 #if 1
6847     int dig_side = MV_DIR_OPPOSITE(direction);
6848 #else
6849     static int trigger_sides[4] =
6850     {
6851       CH_SIDE_RIGHT,    /* moving left  */
6852       CH_SIDE_LEFT,     /* moving right */
6853       CH_SIDE_BOTTOM,   /* moving up    */
6854       CH_SIDE_TOP,      /* moving down  */
6855     };
6856     int dig_side = trigger_sides[MV_DIR_BIT(direction)];
6857 #endif
6858     struct PlayerInfo *player = PLAYERINFO(x, y);
6859
6860     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6861                                player->index_bit, dig_side);
6862     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6863                                         player->index_bit, dig_side);
6864   }
6865 #endif
6866
6867 #if 1
6868   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
6869 #endif
6870
6871 #if 0
6872   if (ChangePage[newx][newy] != -1)                     /* delayed change */
6873     ChangeElement(newx, newy, ChangePage[newx][newy]);
6874 #endif
6875
6876 #if 1
6877
6878   TestIfElementHitsCustomElement(newx, newy, direction);
6879
6880 #else
6881
6882   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
6883   {
6884     int hitting_element = Feld[newx][newy];
6885
6886     /* !!! fix side (direction) orientation here and elsewhere !!! */
6887     CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
6888                              direction);
6889
6890 #if 0
6891     if (IN_LEV_FIELD(nextx, nexty))
6892     {
6893       int opposite_direction = MV_DIR_OPPOSITE(direction);
6894       int hitting_side = direction;
6895       int touched_side = opposite_direction;
6896       int touched_element = MovingOrBlocked2Element(nextx, nexty);
6897       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
6898                             MovDir[nextx][nexty] != direction ||
6899                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
6900
6901       if (object_hit)
6902       {
6903         int i;
6904
6905         CheckElementChangeBySide(nextx, nexty, touched_element,
6906                                  CE_HIT_BY_SOMETHING, opposite_direction);
6907
6908         if (IS_CUSTOM_ELEMENT(hitting_element) &&
6909             HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
6910         {
6911           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
6912           {
6913             struct ElementChangeInfo *change =
6914               &element_info[hitting_element].change_page[i];
6915
6916             if (change->can_change &&
6917                 change->has_event[CE_HITTING_X] &&
6918                 change->trigger_side & touched_side &&
6919                 change->trigger_element == touched_element)
6920             {
6921               CheckElementChangeByPage(newx, newy, hitting_element,
6922                                        touched_element, CE_HITTING_X, i);
6923               break;
6924             }
6925           }
6926         }
6927
6928         if (IS_CUSTOM_ELEMENT(touched_element) &&
6929             HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
6930         {
6931           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
6932           {
6933             struct ElementChangeInfo *change =
6934               &element_info[touched_element].change_page[i];
6935
6936             if (change->can_change &&
6937                 change->has_event[CE_HIT_BY_X] &&
6938                 change->trigger_side & hitting_side &&
6939                 change->trigger_element == hitting_element)
6940             {
6941               CheckElementChangeByPage(nextx, nexty, touched_element,
6942                                        hitting_element, CE_HIT_BY_X,i);
6943               break;
6944             }
6945           }
6946         }
6947       }
6948     }
6949 #endif
6950   }
6951 #endif
6952
6953   TestIfPlayerTouchesCustomElement(newx, newy);
6954   TestIfElementTouchesCustomElement(newx, newy);
6955 }
6956
6957 int AmoebeNachbarNr(int ax, int ay)
6958 {
6959   int i;
6960   int element = Feld[ax][ay];
6961   int group_nr = 0;
6962   static int xy[4][2] =
6963   {
6964     { 0, -1 },
6965     { -1, 0 },
6966     { +1, 0 },
6967     { 0, +1 }
6968   };
6969
6970   for (i = 0; i < NUM_DIRECTIONS; i++)
6971   {
6972     int x = ax + xy[i][0];
6973     int y = ay + xy[i][1];
6974
6975     if (!IN_LEV_FIELD(x, y))
6976       continue;
6977
6978     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6979       group_nr = AmoebaNr[x][y];
6980   }
6981
6982   return group_nr;
6983 }
6984
6985 void AmoebenVereinigen(int ax, int ay)
6986 {
6987   int i, x, y, xx, yy;
6988   int new_group_nr = AmoebaNr[ax][ay];
6989   static int xy[4][2] =
6990   {
6991     { 0, -1 },
6992     { -1, 0 },
6993     { +1, 0 },
6994     { 0, +1 }
6995   };
6996
6997   if (new_group_nr == 0)
6998     return;
6999
7000   for (i = 0; i < NUM_DIRECTIONS; i++)
7001   {
7002     x = ax + xy[i][0];
7003     y = ay + xy[i][1];
7004
7005     if (!IN_LEV_FIELD(x, y))
7006       continue;
7007
7008     if ((Feld[x][y] == EL_AMOEBA_FULL ||
7009          Feld[x][y] == EL_BD_AMOEBA ||
7010          Feld[x][y] == EL_AMOEBA_DEAD) &&
7011         AmoebaNr[x][y] != new_group_nr)
7012     {
7013       int old_group_nr = AmoebaNr[x][y];
7014
7015       if (old_group_nr == 0)
7016         return;
7017
7018       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
7019       AmoebaCnt[old_group_nr] = 0;
7020       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7021       AmoebaCnt2[old_group_nr] = 0;
7022
7023       for (yy = 0; yy < lev_fieldy; yy++)
7024       {
7025         for (xx = 0; xx < lev_fieldx; xx++)
7026         {
7027           if (AmoebaNr[xx][yy] == old_group_nr)
7028             AmoebaNr[xx][yy] = new_group_nr;
7029         }
7030       }
7031     }
7032   }
7033 }
7034
7035 void AmoebeUmwandeln(int ax, int ay)
7036 {
7037   int i, x, y;
7038
7039   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7040   {
7041     int group_nr = AmoebaNr[ax][ay];
7042
7043 #ifdef DEBUG
7044     if (group_nr == 0)
7045     {
7046       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7047       printf("AmoebeUmwandeln(): This should never happen!\n");
7048       return;
7049     }
7050 #endif
7051
7052     for (y = 0; y < lev_fieldy; y++)
7053     {
7054       for (x = 0; x < lev_fieldx; x++)
7055       {
7056         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7057         {
7058           AmoebaNr[x][y] = 0;
7059           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7060         }
7061       }
7062     }
7063     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7064                             SND_AMOEBA_TURNING_TO_GEM :
7065                             SND_AMOEBA_TURNING_TO_ROCK));
7066     Bang(ax, ay);
7067   }
7068   else
7069   {
7070     static int xy[4][2] =
7071     {
7072       { 0, -1 },
7073       { -1, 0 },
7074       { +1, 0 },
7075       { 0, +1 }
7076     };
7077
7078     for (i = 0; i < NUM_DIRECTIONS; i++)
7079     {
7080       x = ax + xy[i][0];
7081       y = ay + xy[i][1];
7082
7083       if (!IN_LEV_FIELD(x, y))
7084         continue;
7085
7086       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7087       {
7088         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7089                               SND_AMOEBA_TURNING_TO_GEM :
7090                               SND_AMOEBA_TURNING_TO_ROCK));
7091         Bang(x, y);
7092       }
7093     }
7094   }
7095 }
7096
7097 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7098 {
7099   int x, y;
7100   int group_nr = AmoebaNr[ax][ay];
7101   boolean done = FALSE;
7102
7103 #ifdef DEBUG
7104   if (group_nr == 0)
7105   {
7106     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7107     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7108     return;
7109   }
7110 #endif
7111
7112   for (y = 0; y < lev_fieldy; y++)
7113   {
7114     for (x = 0; x < lev_fieldx; x++)
7115     {
7116       if (AmoebaNr[x][y] == group_nr &&
7117           (Feld[x][y] == EL_AMOEBA_DEAD ||
7118            Feld[x][y] == EL_BD_AMOEBA ||
7119            Feld[x][y] == EL_AMOEBA_GROWING))
7120       {
7121         AmoebaNr[x][y] = 0;
7122         Feld[x][y] = new_element;
7123         InitField(x, y, FALSE);
7124         DrawLevelField(x, y);
7125         done = TRUE;
7126       }
7127     }
7128   }
7129
7130   if (done)
7131     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7132                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7133                             SND_BD_AMOEBA_TURNING_TO_GEM));
7134 }
7135
7136 void AmoebeWaechst(int x, int y)
7137 {
7138   static unsigned long sound_delay = 0;
7139   static unsigned long sound_delay_value = 0;
7140
7141   if (!MovDelay[x][y])          /* start new growing cycle */
7142   {
7143     MovDelay[x][y] = 7;
7144
7145     if (DelayReached(&sound_delay, sound_delay_value))
7146     {
7147 #if 1
7148       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7149 #else
7150       if (Store[x][y] == EL_BD_AMOEBA)
7151         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
7152       else
7153         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
7154 #endif
7155       sound_delay_value = 30;
7156     }
7157   }
7158
7159   if (MovDelay[x][y])           /* wait some time before growing bigger */
7160   {
7161     MovDelay[x][y]--;
7162     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7163     {
7164       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7165                                            6 - MovDelay[x][y]);
7166
7167       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7168     }
7169
7170     if (!MovDelay[x][y])
7171     {
7172       Feld[x][y] = Store[x][y];
7173       Store[x][y] = 0;
7174       DrawLevelField(x, y);
7175     }
7176   }
7177 }
7178
7179 void AmoebaDisappearing(int x, int y)
7180 {
7181   static unsigned long sound_delay = 0;
7182   static unsigned long sound_delay_value = 0;
7183
7184   if (!MovDelay[x][y])          /* start new shrinking cycle */
7185   {
7186     MovDelay[x][y] = 7;
7187
7188     if (DelayReached(&sound_delay, sound_delay_value))
7189       sound_delay_value = 30;
7190   }
7191
7192   if (MovDelay[x][y])           /* wait some time before shrinking */
7193   {
7194     MovDelay[x][y]--;
7195     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7196     {
7197       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7198                                            6 - MovDelay[x][y]);
7199
7200       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7201     }
7202
7203     if (!MovDelay[x][y])
7204     {
7205       Feld[x][y] = EL_EMPTY;
7206       DrawLevelField(x, y);
7207
7208       /* don't let mole enter this field in this cycle;
7209          (give priority to objects falling to this field from above) */
7210       Stop[x][y] = TRUE;
7211     }
7212   }
7213 }
7214
7215 void AmoebeAbleger(int ax, int ay)
7216 {
7217   int i;
7218   int element = Feld[ax][ay];
7219   int graphic = el2img(element);
7220   int newax = ax, neway = ay;
7221   static int xy[4][2] =
7222   {
7223     { 0, -1 },
7224     { -1, 0 },
7225     { +1, 0 },
7226     { 0, +1 }
7227   };
7228
7229   if (!level.amoeba_speed)
7230   {
7231     Feld[ax][ay] = EL_AMOEBA_DEAD;
7232     DrawLevelField(ax, ay);
7233     return;
7234   }
7235
7236   if (IS_ANIMATED(graphic))
7237     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7238
7239   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7240     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7241
7242   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7243   {
7244     MovDelay[ax][ay]--;
7245     if (MovDelay[ax][ay])
7246       return;
7247   }
7248
7249   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
7250   {
7251     int start = RND(4);
7252     int x = ax + xy[start][0];
7253     int y = ay + xy[start][1];
7254
7255     if (!IN_LEV_FIELD(x, y))
7256       return;
7257
7258 #if 1
7259     if (IS_FREE(x, y) ||
7260         CAN_GROW_INTO(Feld[x][y]) ||
7261         Feld[x][y] == EL_QUICKSAND_EMPTY)
7262     {
7263       newax = x;
7264       neway = y;
7265     }
7266 #else
7267     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7268     if (IS_FREE(x, y) ||
7269         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7270     {
7271       newax = x;
7272       neway = y;
7273     }
7274 #endif
7275
7276     if (newax == ax && neway == ay)
7277       return;
7278   }
7279   else                          /* normal or "filled" (BD style) amoeba */
7280   {
7281     int start = RND(4);
7282     boolean waiting_for_player = FALSE;
7283
7284     for (i = 0; i < NUM_DIRECTIONS; i++)
7285     {
7286       int j = (start + i) % 4;
7287       int x = ax + xy[j][0];
7288       int y = ay + xy[j][1];
7289
7290       if (!IN_LEV_FIELD(x, y))
7291         continue;
7292
7293 #if 1
7294       if (IS_FREE(x, y) ||
7295           CAN_GROW_INTO(Feld[x][y]) ||
7296           Feld[x][y] == EL_QUICKSAND_EMPTY)
7297       {
7298         newax = x;
7299         neway = y;
7300         break;
7301       }
7302 #else
7303       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7304       if (IS_FREE(x, y) ||
7305           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
7306       {
7307         newax = x;
7308         neway = y;
7309         break;
7310       }
7311 #endif
7312       else if (IS_PLAYER(x, y))
7313         waiting_for_player = TRUE;
7314     }
7315
7316     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7317     {
7318 #if 1
7319       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7320 #else
7321       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
7322 #endif
7323       {
7324         Feld[ax][ay] = EL_AMOEBA_DEAD;
7325         DrawLevelField(ax, ay);
7326         AmoebaCnt[AmoebaNr[ax][ay]]--;
7327
7328         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7329         {
7330           if (element == EL_AMOEBA_FULL)
7331             AmoebeUmwandeln(ax, ay);
7332           else if (element == EL_BD_AMOEBA)
7333             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7334         }
7335       }
7336       return;
7337     }
7338     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7339     {
7340       /* amoeba gets larger by growing in some direction */
7341
7342       int new_group_nr = AmoebaNr[ax][ay];
7343
7344 #ifdef DEBUG
7345   if (new_group_nr == 0)
7346   {
7347     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7348     printf("AmoebeAbleger(): This should never happen!\n");
7349     return;
7350   }
7351 #endif
7352
7353       AmoebaNr[newax][neway] = new_group_nr;
7354       AmoebaCnt[new_group_nr]++;
7355       AmoebaCnt2[new_group_nr]++;
7356
7357       /* if amoeba touches other amoeba(s) after growing, unify them */
7358       AmoebenVereinigen(newax, neway);
7359
7360       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7361       {
7362         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7363         return;
7364       }
7365     }
7366   }
7367
7368   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
7369       (neway == lev_fieldy - 1 && newax != ax))
7370   {
7371     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7372     Store[newax][neway] = element;
7373   }
7374   else if (neway == ay)
7375   {
7376     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7377 #if 1
7378     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7379 #else
7380     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
7381 #endif
7382   }
7383   else
7384   {
7385     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7386     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7387     Store[ax][ay] = EL_AMOEBA_DROP;
7388     ContinueMoving(ax, ay);
7389     return;
7390   }
7391
7392   DrawLevelField(newax, neway);
7393 }
7394
7395 void Life(int ax, int ay)
7396 {
7397   int x1, y1, x2, y2;
7398   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
7399   int life_time = 40;
7400   int element = Feld[ax][ay];
7401   int graphic = el2img(element);
7402   boolean changed = FALSE;
7403
7404   if (IS_ANIMATED(graphic))
7405     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7406
7407   if (Stop[ax][ay])
7408     return;
7409
7410   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7411     MovDelay[ax][ay] = life_time;
7412
7413   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7414   {
7415     MovDelay[ax][ay]--;
7416     if (MovDelay[ax][ay])
7417       return;
7418   }
7419
7420   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7421   {
7422     int xx = ax+x1, yy = ay+y1;
7423     int nachbarn = 0;
7424
7425     if (!IN_LEV_FIELD(xx, yy))
7426       continue;
7427
7428     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7429     {
7430       int x = xx+x2, y = yy+y2;
7431
7432       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7433         continue;
7434
7435       if (((Feld[x][y] == element ||
7436             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7437            !Stop[x][y]) ||
7438           (IS_FREE(x, y) && Stop[x][y]))
7439         nachbarn++;
7440     }
7441
7442     if (xx == ax && yy == ay)           /* field in the middle */
7443     {
7444       if (nachbarn < life[0] || nachbarn > life[1])
7445       {
7446         Feld[xx][yy] = EL_EMPTY;
7447         if (!Stop[xx][yy])
7448           DrawLevelField(xx, yy);
7449         Stop[xx][yy] = TRUE;
7450         changed = TRUE;
7451       }
7452     }
7453 #if 1
7454     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7455     {                                   /* free border field */
7456       if (nachbarn >= life[2] && nachbarn <= life[3])
7457       {
7458         Feld[xx][yy] = element;
7459         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7460         if (!Stop[xx][yy])
7461           DrawLevelField(xx, yy);
7462         Stop[xx][yy] = TRUE;
7463         changed = TRUE;
7464       }
7465     }
7466 #else
7467     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7468     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
7469     {                                   /* free border field */
7470       if (nachbarn >= life[2] && nachbarn <= life[3])
7471       {
7472         Feld[xx][yy] = element;
7473         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7474         if (!Stop[xx][yy])
7475           DrawLevelField(xx, yy);
7476         Stop[xx][yy] = TRUE;
7477         changed = TRUE;
7478       }
7479     }
7480 #endif
7481   }
7482
7483   if (changed)
7484     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7485                    SND_GAME_OF_LIFE_GROWING);
7486 }
7487
7488 static void InitRobotWheel(int x, int y)
7489 {
7490   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7491 }
7492
7493 static void RunRobotWheel(int x, int y)
7494 {
7495   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7496 }
7497
7498 static void StopRobotWheel(int x, int y)
7499 {
7500   if (ZX == x && ZY == y)
7501     ZX = ZY = -1;
7502 }
7503
7504 static void InitTimegateWheel(int x, int y)
7505 {
7506 #if 1
7507   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7508 #else
7509   /* another brainless, "type style" bug ... :-( */
7510   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7511 #endif
7512 }
7513
7514 static void RunTimegateWheel(int x, int y)
7515 {
7516   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7517 }
7518
7519 void CheckExit(int x, int y)
7520 {
7521   if (local_player->gems_still_needed > 0 ||
7522       local_player->sokobanfields_still_needed > 0 ||
7523       local_player->lights_still_needed > 0)
7524   {
7525     int element = Feld[x][y];
7526     int graphic = el2img(element);
7527
7528     if (IS_ANIMATED(graphic))
7529       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7530
7531     return;
7532   }
7533
7534   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7535     return;
7536
7537   Feld[x][y] = EL_EXIT_OPENING;
7538
7539   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7540 }
7541
7542 void CheckExitSP(int x, int y)
7543 {
7544   if (local_player->gems_still_needed > 0)
7545   {
7546     int element = Feld[x][y];
7547     int graphic = el2img(element);
7548
7549     if (IS_ANIMATED(graphic))
7550       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7551
7552     return;
7553   }
7554
7555   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7556     return;
7557
7558   Feld[x][y] = EL_SP_EXIT_OPENING;
7559
7560   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7561 }
7562
7563 static void CloseAllOpenTimegates()
7564 {
7565   int x, y;
7566
7567   for (y = 0; y < lev_fieldy; y++)
7568   {
7569     for (x = 0; x < lev_fieldx; x++)
7570     {
7571       int element = Feld[x][y];
7572
7573       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7574       {
7575         Feld[x][y] = EL_TIMEGATE_CLOSING;
7576 #if 1
7577         PlayLevelSoundAction(x, y, ACTION_CLOSING);
7578 #else
7579         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
7580 #endif
7581       }
7582     }
7583   }
7584 }
7585
7586 void EdelsteinFunkeln(int x, int y)
7587 {
7588   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7589     return;
7590
7591   if (Feld[x][y] == EL_BD_DIAMOND)
7592     return;
7593
7594   if (MovDelay[x][y] == 0)      /* next animation frame */
7595     MovDelay[x][y] = 11 * !SimpleRND(500);
7596
7597   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7598   {
7599     MovDelay[x][y]--;
7600
7601     if (setup.direct_draw && MovDelay[x][y])
7602       SetDrawtoField(DRAW_BUFFERED);
7603
7604     DrawLevelElementAnimation(x, y, Feld[x][y]);
7605
7606     if (MovDelay[x][y] != 0)
7607     {
7608       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7609                                            10 - MovDelay[x][y]);
7610
7611       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7612
7613       if (setup.direct_draw)
7614       {
7615         int dest_x, dest_y;
7616
7617         dest_x = FX + SCREENX(x) * TILEX;
7618         dest_y = FY + SCREENY(y) * TILEY;
7619
7620         BlitBitmap(drawto_field, window,
7621                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7622         SetDrawtoField(DRAW_DIRECT);
7623       }
7624     }
7625   }
7626 }
7627
7628 void MauerWaechst(int x, int y)
7629 {
7630   int delay = 6;
7631
7632   if (!MovDelay[x][y])          /* next animation frame */
7633     MovDelay[x][y] = 3 * delay;
7634
7635   if (MovDelay[x][y])           /* wait some time before next frame */
7636   {
7637     MovDelay[x][y]--;
7638
7639     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7640     {
7641       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7642       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7643
7644       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7645     }
7646
7647     if (!MovDelay[x][y])
7648     {
7649       if (MovDir[x][y] == MV_LEFT)
7650       {
7651         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7652           DrawLevelField(x - 1, y);
7653       }
7654       else if (MovDir[x][y] == MV_RIGHT)
7655       {
7656         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7657           DrawLevelField(x + 1, y);
7658       }
7659       else if (MovDir[x][y] == MV_UP)
7660       {
7661         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7662           DrawLevelField(x, y - 1);
7663       }
7664       else
7665       {
7666         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7667           DrawLevelField(x, y + 1);
7668       }
7669
7670       Feld[x][y] = Store[x][y];
7671       Store[x][y] = 0;
7672       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
7673       DrawLevelField(x, y);
7674     }
7675   }
7676 }
7677
7678 void MauerAbleger(int ax, int ay)
7679 {
7680   int element = Feld[ax][ay];
7681   int graphic = el2img(element);
7682   boolean oben_frei = FALSE, unten_frei = FALSE;
7683   boolean links_frei = FALSE, rechts_frei = FALSE;
7684   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7685   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7686   boolean new_wall = FALSE;
7687
7688   if (IS_ANIMATED(graphic))
7689     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7690
7691   if (!MovDelay[ax][ay])        /* start building new wall */
7692     MovDelay[ax][ay] = 6;
7693
7694   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7695   {
7696     MovDelay[ax][ay]--;
7697     if (MovDelay[ax][ay])
7698       return;
7699   }
7700
7701   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7702     oben_frei = TRUE;
7703   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7704     unten_frei = TRUE;
7705   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7706     links_frei = TRUE;
7707   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7708     rechts_frei = TRUE;
7709
7710   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7711       element == EL_EXPANDABLE_WALL_ANY)
7712   {
7713     if (oben_frei)
7714     {
7715       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7716       Store[ax][ay-1] = element;
7717       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7718       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7719         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7720                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7721       new_wall = TRUE;
7722     }
7723     if (unten_frei)
7724     {
7725       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7726       Store[ax][ay+1] = element;
7727       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7728       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7729         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7730                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7731       new_wall = TRUE;
7732     }
7733   }
7734
7735   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7736       element == EL_EXPANDABLE_WALL_ANY ||
7737       element == EL_EXPANDABLE_WALL)
7738   {
7739     if (links_frei)
7740     {
7741       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7742       Store[ax-1][ay] = element;
7743       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7744       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7745         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7746                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7747       new_wall = TRUE;
7748     }
7749
7750     if (rechts_frei)
7751     {
7752       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7753       Store[ax+1][ay] = element;
7754       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7755       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7756         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7757                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7758       new_wall = TRUE;
7759     }
7760   }
7761
7762   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7763     DrawLevelField(ax, ay);
7764
7765   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7766     oben_massiv = TRUE;
7767   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7768     unten_massiv = TRUE;
7769   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7770     links_massiv = TRUE;
7771   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7772     rechts_massiv = TRUE;
7773
7774   if (((oben_massiv && unten_massiv) ||
7775        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7776        element == EL_EXPANDABLE_WALL) &&
7777       ((links_massiv && rechts_massiv) ||
7778        element == EL_EXPANDABLE_WALL_VERTICAL))
7779     Feld[ax][ay] = EL_WALL;
7780
7781   if (new_wall)
7782 #if 1
7783     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7784 #else
7785     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
7786 #endif
7787 }
7788
7789 void CheckForDragon(int x, int y)
7790 {
7791   int i, j;
7792   boolean dragon_found = FALSE;
7793   static int xy[4][2] =
7794   {
7795     { 0, -1 },
7796     { -1, 0 },
7797     { +1, 0 },
7798     { 0, +1 }
7799   };
7800
7801   for (i = 0; i < NUM_DIRECTIONS; i++)
7802   {
7803     for (j = 0; j < 4; j++)
7804     {
7805       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7806
7807       if (IN_LEV_FIELD(xx, yy) &&
7808           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7809       {
7810         if (Feld[xx][yy] == EL_DRAGON)
7811           dragon_found = TRUE;
7812       }
7813       else
7814         break;
7815     }
7816   }
7817
7818   if (!dragon_found)
7819   {
7820     for (i = 0; i < NUM_DIRECTIONS; i++)
7821     {
7822       for (j = 0; j < 3; j++)
7823       {
7824         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7825   
7826         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7827         {
7828           Feld[xx][yy] = EL_EMPTY;
7829           DrawLevelField(xx, yy);
7830         }
7831         else
7832           break;
7833       }
7834     }
7835   }
7836 }
7837
7838 static void InitBuggyBase(int x, int y)
7839 {
7840   int element = Feld[x][y];
7841   int activating_delay = FRAMES_PER_SECOND / 4;
7842
7843   ChangeDelay[x][y] =
7844     (element == EL_SP_BUGGY_BASE ?
7845      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7846      element == EL_SP_BUGGY_BASE_ACTIVATING ?
7847      activating_delay :
7848      element == EL_SP_BUGGY_BASE_ACTIVE ?
7849      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7850 }
7851
7852 static void WarnBuggyBase(int x, int y)
7853 {
7854   int i;
7855   static int xy[4][2] =
7856   {
7857     { 0, -1 },
7858     { -1, 0 },
7859     { +1, 0 },
7860     { 0, +1 }
7861   };
7862
7863   for (i = 0; i < NUM_DIRECTIONS; i++)
7864   {
7865     int xx = x + xy[i][0], yy = y + xy[i][1];
7866
7867     if (IS_PLAYER(xx, yy))
7868     {
7869       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7870
7871       break;
7872     }
7873   }
7874 }
7875
7876 static void InitTrap(int x, int y)
7877 {
7878   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7879 }
7880
7881 static void ActivateTrap(int x, int y)
7882 {
7883   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7884 }
7885
7886 static void ChangeActiveTrap(int x, int y)
7887 {
7888   int graphic = IMG_TRAP_ACTIVE;
7889
7890   /* if new animation frame was drawn, correct crumbled sand border */
7891   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7892     DrawLevelFieldCrumbledSand(x, y);
7893 }
7894
7895 static int getSpecialActionElement(int element, int number, int base_element)
7896 {
7897   return (element != EL_EMPTY ? element :
7898           number != -1 ? base_element + number - 1 :
7899           EL_EMPTY);
7900 }
7901
7902 static int getModifiedActionNumber(int value_old, int value_min, int value_max,
7903                                    int operator, int operand)
7904 {
7905   int value_new = (operator == CA_MODE_ADD      ? value_old + operand :
7906                    operator == CA_MODE_SUBTRACT ? value_old - operand :
7907                    operator == CA_MODE_MULTIPLY ? value_old * operand :
7908                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
7909                    operator == CA_MODE_SET      ? operand :
7910                    value_old);
7911
7912   return (value_new < value_min ? value_min :
7913           value_new > value_max ? value_max :
7914           value_new);
7915 }
7916
7917 static void ExecuteCustomElementAction(int element, int page)
7918 {
7919   struct ElementInfo *ei = &element_info[element];
7920   struct ElementChangeInfo *change = &ei->change_page[page];
7921   int action_type = change->action_type;
7922   int action_mode = change->action_mode;
7923   int action_arg = change->action_arg;
7924   int i;
7925
7926   if (!change->has_action)
7927     return;
7928
7929   /* ---------- determine action paramater values ---------- */
7930
7931   int action_arg_element =
7932     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
7933      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7934      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
7935      EL_EMPTY);
7936
7937   int action_arg_number =
7938     (action_arg <= CA_ARG_MAX ? action_arg :
7939      action_arg == CA_ARG_NUMBER_MIN ? CA_ARG_MIN :
7940      action_arg == CA_ARG_NUMBER_MAX ? CA_ARG_MAX :
7941      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7942      action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count :
7943      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
7944      -1);
7945
7946   /* (for explicit player choice, set invalid value to "no player") */
7947   int action_arg_player_bits =
7948     (action_arg == CA_ARG_PLAYER_ANY ? action_arg - CA_ARG_PLAYER :
7949      action_arg >= CA_ARG_PLAYER_1 &&
7950      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
7951      action_arg >= CA_ARG_1 &&
7952      action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - 1)) :
7953      action_arg_element >= EL_PLAYER_1 &&
7954      action_arg_element <= EL_PLAYER_4 ?
7955      (1 << (action_arg_element - EL_PLAYER_1)) :
7956      0);
7957
7958   /* (for implicit player choice, set invalid value to "all players") */
7959   int trigger_player_bits =
7960     (change->actual_trigger_player >= EL_PLAYER_1 &&
7961      change->actual_trigger_player <= EL_PLAYER_4 ?
7962      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7963      PLAYER_BITS_ANY);
7964
7965   /* ---------- execute action  ---------- */
7966
7967   switch(action_type)
7968   {
7969     case CA_NO_ACTION:
7970     {
7971       return;
7972     }
7973
7974     case CA_EXIT_PLAYER:
7975     {
7976       for (i = 0; i < MAX_PLAYERS; i++)
7977         if (action_arg_player_bits & (1 << i))
7978           stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
7979
7980       break;
7981     }
7982
7983     case CA_KILL_PLAYER:
7984     {
7985       for (i = 0; i < MAX_PLAYERS; i++)
7986         if (action_arg_player_bits & (1 << i))
7987           KillHero(&stored_player[i]);
7988
7989       break;
7990     }
7991
7992     case CA_RESTART_LEVEL:
7993     {
7994       game.restart_level = TRUE;
7995
7996       break;
7997     }
7998
7999     case CA_SHOW_ENVELOPE:
8000     {
8001       int element = getSpecialActionElement(action_arg_element,
8002                                             action_arg_number, EL_ENVELOPE_1);
8003
8004       if (IS_ENVELOPE(element))
8005         local_player->show_envelope = element;
8006
8007       break;
8008     }
8009
8010     case CA_ADD_KEY:
8011     {
8012       int element = getSpecialActionElement(action_arg_element,
8013                                             action_arg_number, EL_KEY_1);
8014
8015       if (IS_KEY(element))
8016       {
8017         for (i = 0; i < MAX_PLAYERS; i++)
8018         {
8019           if (trigger_player_bits & (1 << i))
8020           {
8021             stored_player[i].key[KEY_NR(element)] = TRUE;
8022
8023             DrawGameValue_Keys(stored_player[i].key);
8024
8025             redraw_mask |= REDRAW_DOOR_1;
8026           }
8027         }
8028       }
8029
8030       break;
8031     }
8032
8033     case CA_DEL_KEY:
8034     {
8035       int element = getSpecialActionElement(action_arg_element,
8036                                             action_arg_number, EL_KEY_1);
8037
8038       if (IS_KEY(element))
8039       {
8040         for (i = 0; i < MAX_PLAYERS; i++)
8041         {
8042           if (trigger_player_bits & (1 << i))
8043           {
8044             stored_player[i].key[KEY_NR(element)] = FALSE;
8045
8046             DrawGameValue_Keys(stored_player[i].key);
8047
8048             redraw_mask |= REDRAW_DOOR_1;
8049           }
8050         }
8051       }
8052
8053       break;
8054     }
8055
8056     case CA_SET_PLAYER_SPEED:
8057     {
8058       for (i = 0; i < MAX_PLAYERS; i++)
8059       {
8060         if (trigger_player_bits & (1 << i))
8061         {
8062           if (action_arg == CA_ARG_NUMBER_RESET)
8063             stored_player[i].move_delay_value = game.initial_move_delay_value;
8064           else if (action_arg == CA_ARG_NUMBER_NORMAL)
8065             stored_player[i].move_delay_value = MOVE_DELAY_NORMAL_SPEED;
8066           else if (action_arg == CA_ARG_NUMBER_MIN)
8067             stored_player[i].move_delay_value = 16;
8068           else if (action_arg == CA_ARG_NUMBER_MAX)
8069             stored_player[i].move_delay_value = MOVE_DELAY_HIGH_SPEED;
8070           else
8071           {
8072 #if 0
8073             if (action_mode == CA_MODE_ADD)
8074             {
8075               action_mode = CA_MODE_DIVIDE;
8076               action_arg_number = (1 << action_arg_number);
8077             }
8078             else if (action_mode == CA_MODE_SUBTRACT)
8079             {
8080               action_mode = CA_MODE_MULTIPLY;
8081               action_arg_number = (1 << action_arg_number);
8082             }
8083
8084             int mode = (action_mode == CA_MODE_MULTIPLY ? CA_MODE_DIVIDE :
8085                         action_mode == CA_MODE_DIVIDE   ? CA_MODE_MULTIPLY :
8086                         action_mode);
8087
8088             stored_player[i].move_delay_value =
8089               getModifiedActionNumber(stored_player[i].move_delay_value,
8090                                       1, 16,
8091                                       action_mode, action_arg_number);
8092 #endif
8093           }
8094         }
8095       }
8096
8097       break;
8098     }
8099
8100     case CA_SET_GEMS:
8101     {
8102       local_player->gems_still_needed =
8103         getModifiedActionNumber(local_player->gems_still_needed, 0, 999,
8104                                 action_mode, action_arg_number);
8105
8106       DrawGameValue_Emeralds(local_player->gems_still_needed);
8107
8108       break;
8109     }
8110
8111     case CA_SET_TIME:
8112     {
8113       if (level.time > 0)       /* only modify limited time value */
8114       {
8115         TimeLeft = getModifiedActionNumber(TimeLeft, 0, 9999,
8116                                            action_mode, action_arg_number);
8117
8118         DrawGameValue_Time(TimeLeft);
8119       }
8120
8121       break;
8122     }
8123
8124     case CA_SET_SCORE:
8125     {
8126       local_player->score =
8127         getModifiedActionNumber(local_player->score, 0, 99999,
8128                                 action_mode, action_arg_number);
8129
8130       DrawGameValue_Score(local_player->score);
8131
8132       break;
8133     }
8134
8135     case CA_SET_CE_SCORE:
8136     {
8137       printf("::: CA_SET_CE_SCORE -- not yet implemented\n");
8138
8139       break;
8140     }
8141
8142     case CA_SET_CE_COUNT:
8143     {
8144       printf("::: CA_SET_CE_COUNT -- not yet implemented\n");
8145
8146       break;
8147     }
8148
8149     case CA_SET_DYNABOMB_NUMBER:
8150     {
8151       printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
8152
8153       break;
8154     }
8155
8156     case CA_SET_DYNABOMB_SIZE:
8157     {
8158       printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
8159
8160       break;
8161     }
8162
8163     case CA_SET_DYNABOMB_POWER:
8164     {
8165       printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
8166
8167       break;
8168     }
8169
8170     case CA_TOGGLE_PLAYER_GRAVITY:
8171     {
8172       game.gravity = !game.gravity;
8173
8174       break;
8175     }
8176
8177     case CA_ENABLE_PLAYER_GRAVITY:
8178     {
8179       game.gravity = TRUE;
8180
8181       break;
8182     }
8183
8184     case CA_DISABLE_PLAYER_GRAVITY:
8185     {
8186       game.gravity = FALSE;
8187
8188       break;
8189     }
8190
8191     default:
8192       break;
8193   }
8194 }
8195
8196 static void ChangeElementNowExt(struct ElementChangeInfo *change,
8197                                 int x, int y, int target_element)
8198 {
8199   int previous_move_direction = MovDir[x][y];
8200 #if 1
8201   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
8202                         IS_WALKABLE(Feld[x][y]));
8203 #else
8204   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
8205                         IS_WALKABLE(Feld[x][y]) &&
8206                         !IS_MOVING(x, y));
8207 #endif
8208
8209   /* check if element under player changes from accessible to unaccessible
8210      (needed for special case of dropping element which then changes) */
8211   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8212       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
8213   {
8214 #if 0
8215     printf("::: BOOOM! [%d, '%s']\n", target_element,
8216            element_info[target_element].token_name);
8217 #endif
8218
8219     Bang(x, y);
8220     return;
8221   }
8222
8223 #if 1
8224   if (!add_player)
8225 #endif
8226   {
8227 #if 1
8228     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8229       RemoveMovingField(x, y);
8230     else
8231       RemoveField(x, y);
8232
8233     Feld[x][y] = target_element;
8234 #else
8235     RemoveField(x, y);
8236     Feld[x][y] = target_element;
8237 #endif
8238
8239     ResetGfxAnimation(x, y);
8240     ResetRandomAnimationValue(x, y);
8241
8242     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8243       MovDir[x][y] = previous_move_direction;
8244
8245 #if 1
8246     InitField_WithBug1(x, y, FALSE);
8247 #else
8248     InitField(x, y, FALSE);
8249     if (CAN_MOVE(Feld[x][y]))
8250       InitMovDir(x, y);
8251 #endif
8252
8253     DrawLevelField(x, y);
8254
8255     if (GFX_CRUMBLED(Feld[x][y]))
8256       DrawLevelFieldCrumbledSandNeighbours(x, y);
8257   }
8258
8259 #if 0
8260   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
8261 #endif
8262
8263 #if 0
8264   TestIfBadThingTouchesHero(x, y);
8265   TestIfPlayerTouchesCustomElement(x, y);
8266   TestIfElementTouchesCustomElement(x, y);
8267 #endif
8268
8269   /* "Changed[][]" not set yet to allow "entered by player" change one time */
8270   if (ELEM_IS_PLAYER(target_element))
8271     RelocatePlayer(x, y, target_element);
8272
8273 #if 1
8274   Changed[x][y] = TRUE;         /* ignore all further changes in this frame */
8275 #else
8276   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
8277 #endif
8278
8279 #if 1
8280   TestIfBadThingTouchesHero(x, y);
8281   TestIfPlayerTouchesCustomElement(x, y);
8282   TestIfElementTouchesCustomElement(x, y);
8283 #endif
8284
8285 #if 0
8286   if (change->has_action)
8287     ExecuteCustomElementAction(...);
8288 #endif
8289 }
8290
8291 static boolean ChangeElementNow(int x, int y, int element, int page)
8292 {
8293   struct ElementChangeInfo *change = &element_info[element].change_page[page];
8294   int target_element;
8295   int old_element = Feld[x][y];
8296
8297   /* always use default change event to prevent running into a loop */
8298   if (ChangeEvent[x][y] == -1)
8299     ChangeEvent[x][y] = CE_DELAY;
8300
8301   if (ChangeEvent[x][y] == CE_DELAY)
8302   {
8303     /* reset actual trigger element, trigger player and action element */
8304     change->actual_trigger_element = EL_EMPTY;
8305     change->actual_trigger_player = EL_PLAYER_1;
8306   }
8307
8308 #if 1
8309   /* do not change any elements that have already changed in this frame */
8310   if (Changed[x][y])
8311     return FALSE;
8312 #else
8313   /* do not change already changed elements with same change event */
8314   if (Changed[x][y] & ChangeEvent[x][y])
8315     return FALSE;
8316 #endif
8317
8318 #if 1
8319   Changed[x][y] = TRUE;         /* ignore all further changes in this frame */
8320 #else
8321   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
8322 #endif
8323
8324 #if 0
8325   /* !!! indirect change before direct change !!! */
8326   CheckTriggeredElementChangeByPage(x, y, Feld[x][y], CE_CHANGE_OF_X, page);
8327 #endif
8328
8329   if (change->explode)
8330   {
8331     Bang(x, y);
8332
8333     return TRUE;
8334   }
8335
8336   if (change->use_target_content)
8337   {
8338     boolean complete_replace = TRUE;
8339     boolean can_replace[3][3];
8340     int xx, yy;
8341
8342     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8343     {
8344       boolean is_empty;
8345       boolean is_walkable;
8346       boolean is_diggable;
8347       boolean is_collectible;
8348       boolean is_removable;
8349       boolean is_destructible;
8350       int ex = x + xx - 1;
8351       int ey = y + yy - 1;
8352       int content_element = change->target_content[xx][yy];
8353       int e;
8354
8355       can_replace[xx][yy] = TRUE;
8356
8357       if (ex == x && ey == y)   /* do not check changing element itself */
8358         continue;
8359
8360       if (content_element == EL_EMPTY_SPACE)
8361       {
8362         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8363
8364         continue;
8365       }
8366
8367       if (!IN_LEV_FIELD(ex, ey))
8368       {
8369         can_replace[xx][yy] = FALSE;
8370         complete_replace = FALSE;
8371
8372         continue;
8373       }
8374
8375 #if 0
8376       if (Changed[ex][ey])      /* do not change already changed elements */
8377       {
8378         can_replace[xx][yy] = FALSE;
8379         complete_replace = FALSE;
8380
8381         continue;
8382       }
8383 #endif
8384
8385       e = Feld[ex][ey];
8386
8387       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8388         e = MovingOrBlocked2Element(ex, ey);
8389
8390 #if 1
8391
8392 #if 0
8393       is_empty = (IS_FREE(ex, ey) ||
8394                   (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)) ||
8395                   (IS_WALKABLE(e) && ELEM_IS_PLAYER(content_element) &&
8396                    !IS_MOVING(ex, ey) && !IS_BLOCKED(ex, ey)));
8397 #else
8398
8399 #if 0
8400       is_empty = (IS_FREE(ex, ey) ||
8401                   (IS_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8402 #else
8403       is_empty = (IS_FREE(ex, ey) ||
8404                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8405 #endif
8406
8407 #endif
8408
8409       is_walkable     = (is_empty || IS_WALKABLE(e));
8410       is_diggable     = (is_empty || IS_DIGGABLE(e));
8411       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8412       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8413       is_removable    = (is_diggable || is_collectible);
8414
8415       can_replace[xx][yy] =
8416         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
8417           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
8418           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
8419           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
8420           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
8421           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8422          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8423
8424       if (!can_replace[xx][yy])
8425         complete_replace = FALSE;
8426 #else
8427       empty_for_element = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
8428                                                IS_WALKABLE(content_element)));
8429 #if 1
8430       half_destructible = (empty_for_element || IS_DIGGABLE(e));
8431 #else
8432       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
8433 #endif
8434
8435       if ((change->replace_when <= CP_WHEN_EMPTY  && !empty_for_element) ||
8436           (change->replace_when <= CP_WHEN_DIGGABLE && !half_destructible) ||
8437           (change->replace_when <= CP_WHEN_DESTRUCTIBLE && IS_INDESTRUCTIBLE(e)))
8438       {
8439         can_replace[xx][yy] = FALSE;
8440         complete_replace = FALSE;
8441       }
8442 #endif
8443     }
8444
8445     if (!change->only_if_complete || complete_replace)
8446     {
8447       boolean something_has_changed = FALSE;
8448
8449       if (change->only_if_complete && change->use_random_replace &&
8450           RND(100) < change->random_percentage)
8451         return FALSE;
8452
8453       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8454       {
8455         int ex = x + xx - 1;
8456         int ey = y + yy - 1;
8457         int content_element;
8458
8459         if (can_replace[xx][yy] && (!change->use_random_replace ||
8460                                     RND(100) < change->random_percentage))
8461         {
8462           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8463             RemoveMovingField(ex, ey);
8464
8465           ChangeEvent[ex][ey] = ChangeEvent[x][y];
8466
8467           content_element = change->target_content[xx][yy];
8468           target_element = GET_TARGET_ELEMENT(content_element, change);
8469
8470           ChangeElementNowExt(change, ex, ey, target_element);
8471
8472           something_has_changed = TRUE;
8473
8474           /* for symmetry reasons, freeze newly created border elements */
8475           if (ex != x || ey != y)
8476             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
8477         }
8478       }
8479
8480       if (something_has_changed)
8481         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8482     }
8483   }
8484   else
8485   {
8486     target_element = GET_TARGET_ELEMENT(change->target_element, change);
8487
8488     ChangeElementNowExt(change, x, y, target_element);
8489
8490     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8491   }
8492
8493 #if 1
8494   /* this uses direct change before indirect change */
8495   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8496 #endif
8497
8498   return TRUE;
8499 }
8500
8501 static void ChangeElement(int x, int y, int page)
8502 {
8503   int element = MovingOrBlocked2Element(x, y);
8504   struct ElementInfo *ei = &element_info[element];
8505   struct ElementChangeInfo *change = &ei->change_page[page];
8506
8507 #ifdef DEBUG
8508   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8509   {
8510     printf("\n\n");
8511     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
8512            x, y, element, element_info[element].token_name);
8513     printf("ChangeElement(): This should never happen!\n");
8514     printf("\n\n");
8515   }
8516 #endif
8517
8518   /* this can happen with classic bombs on walkable, changing elements */
8519   if (!CAN_CHANGE(element))
8520   {
8521 #if 0
8522     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
8523       ChangeDelay[x][y] = 0;
8524 #endif
8525
8526     return;
8527   }
8528
8529   if (ChangeDelay[x][y] == 0)           /* initialize element change */
8530   {
8531 #if 1
8532     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8533 #else
8534     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
8535                          RND(change->delay_random * change->delay_frames)) + 1;
8536 #endif
8537
8538     ResetGfxAnimation(x, y);
8539     ResetRandomAnimationValue(x, y);
8540
8541     if (change->pre_change_function)
8542       change->pre_change_function(x, y);
8543   }
8544
8545   ChangeDelay[x][y]--;
8546
8547   if (ChangeDelay[x][y] != 0)           /* continue element change */
8548   {
8549     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8550
8551     if (IS_ANIMATED(graphic))
8552       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8553
8554     if (change->change_function)
8555       change->change_function(x, y);
8556   }
8557   else                                  /* finish element change */
8558   {
8559     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
8560     {
8561       page = ChangePage[x][y];
8562       ChangePage[x][y] = -1;
8563
8564       change = &ei->change_page[page];
8565     }
8566
8567 #if 0
8568     if (IS_MOVING(x, y) && !change->explode)
8569 #else
8570     if (IS_MOVING(x, y))                /* never change a running system ;-) */
8571 #endif
8572     {
8573       ChangeDelay[x][y] = 1;            /* try change after next move step */
8574       ChangePage[x][y] = page;          /* remember page to use for change */
8575
8576       return;
8577     }
8578
8579 #if 0
8580     if (change->has_action)
8581       ExecuteCustomElementAction(element, page);
8582 #endif
8583
8584     if (ChangeElementNow(x, y, element, page))
8585     {
8586       if (change->post_change_function)
8587         change->post_change_function(x, y);
8588     }
8589   }
8590 }
8591
8592 static boolean CheckTriggeredElementChangeExt(int lx, int ly,
8593                                               int trigger_element,
8594                                               int trigger_event,
8595                                               int trigger_player,
8596                                               int trigger_side,
8597                                               int trigger_page)
8598 {
8599   int i, j, x, y;
8600   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8601
8602   if (!(trigger_events[trigger_element][trigger_event]))
8603     return FALSE;
8604
8605   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8606   {
8607     int element = EL_CUSTOM_START + i;
8608
8609     boolean change_found = FALSE;
8610     boolean change_element = FALSE;
8611     int page = 0;
8612
8613     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8614         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8615       continue;
8616
8617     for (j = 0; j < element_info[element].num_change_pages; j++)
8618     {
8619       struct ElementChangeInfo *change = &element_info[element].change_page[j];
8620
8621       if (change->can_change_or_has_action &&
8622           change->has_event[trigger_event] &&
8623           change->trigger_side & trigger_side &&
8624           change->trigger_player & trigger_player &&
8625           change->trigger_page & trigger_page_bits &&
8626           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8627       {
8628 #if 0
8629         if (!(change->has_event[trigger_event]))
8630           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
8631                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
8632 #endif
8633
8634 #if 1
8635         change->actual_trigger_element = trigger_element;
8636         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8637
8638         if (change->can_change && !change_found)
8639         {
8640           change_found = TRUE;
8641
8642           for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8643           {
8644             if (Feld[x][y] == element)
8645             {
8646               ChangeDelay[x][y] = 1;
8647               ChangeEvent[x][y] = trigger_event;
8648               ChangeElement(x, y, j);
8649             }
8650           }
8651         }
8652
8653         if (change->has_action)
8654           ExecuteCustomElementAction(element, j);
8655 #else
8656         change_element = TRUE;
8657         page = j;
8658
8659         change->actual_trigger_element = trigger_element;
8660         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8661
8662         break;
8663 #endif
8664       }
8665     }
8666
8667 #if 0
8668     if (!change_element)
8669       continue;
8670
8671     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8672     {
8673 #if 0
8674       if (x == lx && y == ly)   /* do not change trigger element itself */
8675         continue;
8676 #endif
8677
8678       if (Feld[x][y] == element)
8679       {
8680         ChangeDelay[x][y] = 1;
8681         ChangeEvent[x][y] = trigger_event;
8682         ChangeElement(x, y, page);
8683       }
8684     }
8685 #endif
8686   }
8687
8688   return TRUE;
8689 }
8690
8691 static boolean CheckElementChangeExt(int x, int y,
8692                                      int element,
8693                                      int trigger_element,
8694                                      int trigger_event,
8695                                      int trigger_player,
8696                                      int trigger_side,
8697                                      int trigger_page)
8698 {
8699   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8700     return FALSE;
8701
8702   if (Feld[x][y] == EL_BLOCKED)
8703   {
8704     Blocked2Moving(x, y, &x, &y);
8705     element = Feld[x][y];
8706   }
8707
8708 #if 1
8709   if (Feld[x][y] != element)    /* check if element has already changed */
8710   {
8711 #if 0
8712     printf("::: %d ('%s') != %d ('%s') [%d]\n",
8713            Feld[x][y], element_info[Feld[x][y]].token_name,
8714            element, element_info[element].token_name,
8715            trigger_event);
8716 #endif
8717
8718     return FALSE;
8719   }
8720 #endif
8721
8722 #if 1
8723   if (trigger_page < 0)
8724   {
8725     boolean change_element = FALSE;
8726     int i;
8727
8728     for (i = 0; i < element_info[element].num_change_pages; i++)
8729     {
8730       struct ElementChangeInfo *change = &element_info[element].change_page[i];
8731
8732       boolean check_trigger_element =
8733         (trigger_event == CE_TOUCHING_X ||
8734          trigger_event == CE_HITTING_X ||
8735          trigger_event == CE_HIT_BY_X);
8736
8737       if (change->can_change &&
8738           change->has_event[trigger_event] &&
8739           change->trigger_side & trigger_side &&
8740           change->trigger_player & trigger_player
8741 #if 1
8742           &&
8743           (!check_trigger_element ||
8744            IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8745 #endif
8746           )
8747       {
8748         change_element = TRUE;
8749         trigger_page = i;
8750
8751         change->actual_trigger_element = trigger_element;
8752         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8753
8754         break;
8755       }
8756     }
8757
8758     if (!change_element)
8759       return FALSE;
8760   }
8761   else
8762   {
8763     struct ElementInfo *ei = &element_info[element];
8764     struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8765
8766     change->actual_trigger_element = trigger_element;
8767     change->actual_trigger_player = EL_PLAYER_1;        /* unused */
8768   }
8769
8770 #else
8771
8772   /* !!! this check misses pages with same event, but different side !!! */
8773
8774   if (trigger_page < 0)
8775     trigger_page = element_info[element].event_page_nr[trigger_event];
8776
8777   if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8778     return FALSE;
8779 #endif
8780
8781   ChangeDelay[x][y] = 1;
8782   ChangeEvent[x][y] = trigger_event;
8783   ChangeElement(x, y, trigger_page);
8784
8785   return TRUE;
8786 }
8787
8788 static boolean CheckElementChangeExtTEST(int x, int y,
8789                                      int element,
8790                                      int trigger_element,
8791                                      int trigger_event,
8792                                      int trigger_player,
8793                                      int trigger_side,
8794                                      int trigger_page)
8795 {
8796   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8797     return FALSE;
8798
8799   if (Feld[x][y] == EL_BLOCKED)
8800   {
8801     Blocked2Moving(x, y, &x, &y);
8802     element = Feld[x][y];
8803   }
8804
8805 #if 1
8806   if (Feld[x][y] != element)    /* check if element has already changed */
8807   {
8808 #if 0
8809     printf("::: %d ('%s') != %d ('%s') [%d]\n",
8810            Feld[x][y], element_info[Feld[x][y]].token_name,
8811            element, element_info[element].token_name,
8812            trigger_event);
8813 #endif
8814
8815     return FALSE;
8816   }
8817 #endif
8818
8819 #if 1
8820   if (trigger_page < 0)
8821   {
8822     boolean change_element = FALSE;
8823     int i;
8824
8825     for (i = 0; i < element_info[element].num_change_pages; i++)
8826     {
8827       struct ElementChangeInfo *change = &element_info[element].change_page[i];
8828
8829       if (change->can_change &&
8830           change->has_event[trigger_event] &&
8831           change->trigger_side & trigger_side &&
8832           change->trigger_player & trigger_player &&
8833           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8834       {
8835         change_element = TRUE;
8836         trigger_page = i;
8837
8838         change->actual_trigger_element = trigger_element;
8839         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8840
8841         break;
8842       }
8843     }
8844
8845     if (!change_element)
8846       return FALSE;
8847   }
8848   else
8849   {
8850     struct ElementInfo *ei = &element_info[element];
8851     struct ElementChangeInfo *change = &ei->change_page[trigger_page];
8852
8853     change->actual_trigger_element = trigger_element;
8854     change->actual_trigger_player = EL_PLAYER_1;        /* unused */
8855   }
8856
8857 #else
8858
8859   /* !!! this check misses pages with same event, but different side !!! */
8860
8861   if (trigger_page < 0)
8862     trigger_page = element_info[element].event_page_nr[trigger_event];
8863
8864   if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
8865     return FALSE;
8866 #endif
8867
8868   ChangeDelay[x][y] = 1;
8869   ChangeEvent[x][y] = trigger_event;
8870   ChangeElement(x, y, trigger_page);
8871
8872   return TRUE;
8873 }
8874
8875 static void PlayPlayerSound(struct PlayerInfo *player)
8876 {
8877   int jx = player->jx, jy = player->jy;
8878   int element = player->element_nr;
8879   int last_action = player->last_action_waiting;
8880   int action = player->action_waiting;
8881
8882   if (player->is_waiting)
8883   {
8884     if (action != last_action)
8885       PlayLevelSoundElementAction(jx, jy, element, action);
8886     else
8887       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
8888   }
8889   else
8890   {
8891     if (action != last_action)
8892       StopSound(element_info[element].sound[last_action]);
8893
8894     if (last_action == ACTION_SLEEPING)
8895       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
8896   }
8897 }
8898
8899 static void PlayAllPlayersSound()
8900 {
8901   int i;
8902
8903   for (i = 0; i < MAX_PLAYERS; i++)
8904     if (stored_player[i].active)
8905       PlayPlayerSound(&stored_player[i]);
8906 }
8907
8908 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8909 {
8910   boolean last_waiting = player->is_waiting;
8911   int move_dir = player->MovDir;
8912
8913   player->last_action_waiting = player->action_waiting;
8914
8915   if (is_waiting)
8916   {
8917     if (!last_waiting)          /* not waiting -> waiting */
8918     {
8919       player->is_waiting = TRUE;
8920
8921       player->frame_counter_bored =
8922         FrameCounter +
8923         game.player_boring_delay_fixed +
8924         SimpleRND(game.player_boring_delay_random);
8925       player->frame_counter_sleeping =
8926         FrameCounter +
8927         game.player_sleeping_delay_fixed +
8928         SimpleRND(game.player_sleeping_delay_random);
8929
8930       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
8931     }
8932
8933     if (game.player_sleeping_delay_fixed +
8934         game.player_sleeping_delay_random > 0 &&
8935         player->anim_delay_counter == 0 &&
8936         player->post_delay_counter == 0 &&
8937         FrameCounter >= player->frame_counter_sleeping)
8938       player->is_sleeping = TRUE;
8939     else if (game.player_boring_delay_fixed +
8940              game.player_boring_delay_random > 0 &&
8941              FrameCounter >= player->frame_counter_bored)
8942       player->is_bored = TRUE;
8943
8944     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
8945                               player->is_bored ? ACTION_BORING :
8946                               ACTION_WAITING);
8947
8948     if (player->is_sleeping)
8949     {
8950       if (player->num_special_action_sleeping > 0)
8951       {
8952         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8953         {
8954           int last_special_action = player->special_action_sleeping;
8955           int num_special_action = player->num_special_action_sleeping;
8956           int special_action =
8957             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
8958              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
8959              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
8960              last_special_action + 1 : ACTION_SLEEPING);
8961           int special_graphic =
8962             el_act_dir2img(player->element_nr, special_action, move_dir);
8963
8964           player->anim_delay_counter =
8965             graphic_info[special_graphic].anim_delay_fixed +
8966             SimpleRND(graphic_info[special_graphic].anim_delay_random);
8967           player->post_delay_counter =
8968             graphic_info[special_graphic].post_delay_fixed +
8969             SimpleRND(graphic_info[special_graphic].post_delay_random);
8970
8971           player->special_action_sleeping = special_action;
8972         }
8973
8974         if (player->anim_delay_counter > 0)
8975         {
8976           player->action_waiting = player->special_action_sleeping;
8977           player->anim_delay_counter--;
8978         }
8979         else if (player->post_delay_counter > 0)
8980         {
8981           player->post_delay_counter--;
8982         }
8983       }
8984     }
8985     else if (player->is_bored)
8986     {
8987       if (player->num_special_action_bored > 0)
8988       {
8989         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
8990         {
8991           int special_action =
8992             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
8993           int special_graphic =
8994             el_act_dir2img(player->element_nr, special_action, move_dir);
8995
8996           player->anim_delay_counter =
8997             graphic_info[special_graphic].anim_delay_fixed +
8998             SimpleRND(graphic_info[special_graphic].anim_delay_random);
8999           player->post_delay_counter =
9000             graphic_info[special_graphic].post_delay_fixed +
9001             SimpleRND(graphic_info[special_graphic].post_delay_random);
9002
9003           player->special_action_bored = special_action;
9004         }
9005
9006         if (player->anim_delay_counter > 0)
9007         {
9008           player->action_waiting = player->special_action_bored;
9009           player->anim_delay_counter--;
9010         }
9011         else if (player->post_delay_counter > 0)
9012         {
9013           player->post_delay_counter--;
9014         }
9015       }
9016     }
9017   }
9018   else if (last_waiting)        /* waiting -> not waiting */
9019   {
9020     player->is_waiting = FALSE;
9021     player->is_bored = FALSE;
9022     player->is_sleeping = FALSE;
9023
9024     player->frame_counter_bored = -1;
9025     player->frame_counter_sleeping = -1;
9026
9027     player->anim_delay_counter = 0;
9028     player->post_delay_counter = 0;
9029
9030     player->action_waiting = ACTION_DEFAULT;
9031
9032     player->special_action_bored = ACTION_DEFAULT;
9033     player->special_action_sleeping = ACTION_DEFAULT;
9034   }
9035 }
9036
9037 #if 1
9038 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9039 {
9040 #if 0
9041   static byte stored_player_action[MAX_PLAYERS];
9042   static int num_stored_actions = 0;
9043 #endif
9044   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9045   int left      = player_action & JOY_LEFT;
9046   int right     = player_action & JOY_RIGHT;
9047   int up        = player_action & JOY_UP;
9048   int down      = player_action & JOY_DOWN;
9049   int button1   = player_action & JOY_BUTTON_1;
9050   int button2   = player_action & JOY_BUTTON_2;
9051   int dx        = (left ? -1    : right ? 1     : 0);
9052   int dy        = (up   ? -1    : down  ? 1     : 0);
9053
9054 #if 0
9055   stored_player_action[player->index_nr] = 0;
9056   num_stored_actions++;
9057 #endif
9058
9059 #if 0
9060   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
9061 #endif
9062
9063   if (!player->active || tape.pausing)
9064     return 0;
9065
9066 #if 0
9067   printf("::: [%d %d %d %d] [%d %d]\n",
9068          left, right, up, down, button1, button2);
9069 #endif
9070
9071   if (player_action)
9072   {
9073 #if 0
9074     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
9075 #endif
9076
9077 #if 0
9078     /* !!! TEST !!! */
9079     if (player->MovPos == 0)
9080       CheckGravityMovement(player);
9081 #endif
9082     if (button1)
9083       snapped = SnapField(player, dx, dy);
9084     else
9085     {
9086       if (button2)
9087         dropped = DropElement(player);
9088
9089       moved = MovePlayer(player, dx, dy);
9090     }
9091
9092     if (tape.single_step && tape.recording && !tape.pausing)
9093     {
9094       if (button1 || (dropped && !moved))
9095       {
9096         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9097         SnapField(player, 0, 0);                /* stop snapping */
9098       }
9099     }
9100
9101     SetPlayerWaiting(player, FALSE);
9102
9103 #if 1
9104     return player_action;
9105 #else
9106     stored_player_action[player->index_nr] = player_action;
9107 #endif
9108   }
9109   else
9110   {
9111 #if 0
9112     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
9113 #endif
9114
9115     /* no actions for this player (no input at player's configured device) */
9116
9117     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9118     SnapField(player, 0, 0);
9119     CheckGravityMovementWhenNotMoving(player);
9120
9121     if (player->MovPos == 0)
9122       SetPlayerWaiting(player, TRUE);
9123
9124     if (player->MovPos == 0)    /* needed for tape.playing */
9125       player->is_moving = FALSE;
9126
9127     player->is_dropping = FALSE;
9128
9129     return 0;
9130   }
9131
9132 #if 0
9133   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
9134   {
9135     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
9136
9137     TapeRecordAction(stored_player_action);
9138     num_stored_actions = 0;
9139   }
9140 #endif
9141 }
9142
9143 #else
9144
9145 static void PlayerActions(struct PlayerInfo *player, byte player_action)
9146 {
9147   static byte stored_player_action[MAX_PLAYERS];
9148   static int num_stored_actions = 0;
9149   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9150   int left      = player_action & JOY_LEFT;
9151   int right     = player_action & JOY_RIGHT;
9152   int up        = player_action & JOY_UP;
9153   int down      = player_action & JOY_DOWN;
9154   int button1   = player_action & JOY_BUTTON_1;
9155   int button2   = player_action & JOY_BUTTON_2;
9156   int dx        = (left ? -1    : right ? 1     : 0);
9157   int dy        = (up   ? -1    : down  ? 1     : 0);
9158
9159   stored_player_action[player->index_nr] = 0;
9160   num_stored_actions++;
9161
9162   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
9163
9164   if (!player->active || tape.pausing)
9165     return;
9166
9167   if (player_action)
9168   {
9169     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
9170
9171     if (button1)
9172       snapped = SnapField(player, dx, dy);
9173     else
9174     {
9175       if (button2)
9176         dropped = DropElement(player);
9177
9178       moved = MovePlayer(player, dx, dy);
9179     }
9180
9181     if (tape.single_step && tape.recording && !tape.pausing)
9182     {
9183       if (button1 || (dropped && !moved))
9184       {
9185         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9186         SnapField(player, 0, 0);                /* stop snapping */
9187       }
9188     }
9189
9190     stored_player_action[player->index_nr] = player_action;
9191   }
9192   else
9193   {
9194     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
9195
9196     /* no actions for this player (no input at player's configured device) */
9197
9198     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9199     SnapField(player, 0, 0);
9200     CheckGravityMovementWhenNotMoving(player);
9201
9202     if (player->MovPos == 0)
9203       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
9204
9205     if (player->MovPos == 0)    /* needed for tape.playing */
9206       player->is_moving = FALSE;
9207   }
9208
9209   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
9210   {
9211     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
9212
9213     TapeRecordAction(stored_player_action);
9214     num_stored_actions = 0;
9215   }
9216 }
9217 #endif
9218
9219 void AdvanceFrameAndPlayerCounters(int player_nr)
9220 {
9221   int i;
9222
9223   /* advance frame counters (global frame counter and time frame counter) */
9224   FrameCounter++;
9225   TimeFrames++;
9226
9227   /* advance player counters (counters for move delay, move animation etc.) */
9228   for (i = 0; i < MAX_PLAYERS; i++)
9229   {
9230     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9231     int move_frames =
9232       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
9233
9234     if (!advance_player_counters)       /* not all players may be affected */
9235       continue;
9236
9237     stored_player[i].Frame += move_frames;
9238
9239     if (stored_player[i].MovPos != 0)
9240       stored_player[i].StepFrame += move_frames;
9241
9242 #if USE_NEW_MOVE_DELAY
9243     if (stored_player[i].move_delay > 0)
9244       stored_player[i].move_delay--;
9245 #endif
9246
9247 #if USE_NEW_PUSH_DELAY
9248     /* due to bugs in previous versions, counter must count up, not down */
9249     if (stored_player[i].push_delay != -1)
9250       stored_player[i].push_delay++;
9251 #endif
9252
9253     if (stored_player[i].drop_delay > 0)
9254       stored_player[i].drop_delay--;
9255   }
9256 }
9257
9258 void GameActions()
9259 {
9260   static unsigned long game_frame_delay = 0;
9261   unsigned long game_frame_delay_value;
9262   int magic_wall_x = 0, magic_wall_y = 0;
9263   int i, x, y, element, graphic;
9264   byte *recorded_player_action;
9265   byte summarized_player_action = 0;
9266 #if 1
9267   byte tape_action[MAX_PLAYERS];
9268 #endif
9269
9270   if (game_status != GAME_MODE_PLAYING)
9271     return;
9272
9273   game_frame_delay_value =
9274     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9275
9276   if (tape.playing && tape.warp_forward && !tape.pausing)
9277     game_frame_delay_value = 0;
9278
9279   /* ---------- main game synchronization point ---------- */
9280
9281   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9282
9283   if (network_playing && !network_player_action_received)
9284   {
9285     /*
9286 #ifdef DEBUG
9287     printf("DEBUG: try to get network player actions in time\n");
9288 #endif
9289     */
9290
9291 #if defined(NETWORK_AVALIABLE)
9292     /* last chance to get network player actions without main loop delay */
9293     HandleNetworking();
9294 #endif
9295
9296     if (game_status != GAME_MODE_PLAYING)
9297       return;
9298
9299     if (!network_player_action_received)
9300     {
9301       /*
9302 #ifdef DEBUG
9303       printf("DEBUG: failed to get network player actions in time\n");
9304 #endif
9305       */
9306       return;
9307     }
9308   }
9309
9310   if (tape.pausing)
9311     return;
9312
9313 #if 0
9314   printf("::: getting new tape action [%d]\n", FrameCounter);
9315 #endif
9316
9317   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9318
9319 #if 1
9320   /* !!! CHECK THIS (tape.pausing is always FALSE here!) !!! */
9321   if (recorded_player_action == NULL && tape.pausing)
9322     return;
9323 #endif
9324
9325 #if 0
9326   printf("::: %d\n", stored_player[0].action);
9327 #endif
9328
9329 #if 0
9330   if (recorded_player_action != NULL)
9331     for (i = 0; i < MAX_PLAYERS; i++)
9332       stored_player[i].action = recorded_player_action[i];
9333 #endif
9334
9335   for (i = 0; i < MAX_PLAYERS; i++)
9336   {
9337     summarized_player_action |= stored_player[i].action;
9338
9339     if (!network_playing)
9340       stored_player[i].effective_action = stored_player[i].action;
9341   }
9342
9343 #if defined(NETWORK_AVALIABLE)
9344   if (network_playing)
9345     SendToServer_MovePlayer(summarized_player_action);
9346 #endif
9347
9348   if (!options.network && !setup.team_mode)
9349     local_player->effective_action = summarized_player_action;
9350
9351 #if 1
9352   if (recorded_player_action != NULL)
9353     for (i = 0; i < MAX_PLAYERS; i++)
9354       stored_player[i].effective_action = recorded_player_action[i];
9355 #endif
9356
9357 #if 1
9358   for (i = 0; i < MAX_PLAYERS; i++)
9359   {
9360     tape_action[i] = stored_player[i].effective_action;
9361
9362     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9363       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9364   }
9365
9366   /* only save actions from input devices, but not programmed actions */
9367   if (tape.recording)
9368     TapeRecordAction(tape_action);
9369 #endif
9370
9371   for (i = 0; i < MAX_PLAYERS; i++)
9372   {
9373     int actual_player_action = stored_player[i].effective_action;
9374
9375 #if 1
9376     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9377        - rnd_equinox_tetrachloride 048
9378        - rnd_equinox_tetrachloride_ii 096
9379        - rnd_emanuel_schmieg 002
9380        - doctor_sloan_ww 001, 020
9381     */
9382     if (stored_player[i].MovPos == 0)
9383       CheckGravityMovement(&stored_player[i]);
9384 #endif
9385
9386 #if 1
9387     /* overwrite programmed action with tape action */
9388     if (stored_player[i].programmed_action)
9389       actual_player_action = stored_player[i].programmed_action;
9390 #endif
9391
9392 #if 0
9393     if (stored_player[i].programmed_action)
9394       printf("::: %d\n", stored_player[i].programmed_action);
9395 #endif
9396
9397     if (recorded_player_action)
9398     {
9399 #if 0
9400       if (stored_player[i].programmed_action &&
9401           stored_player[i].programmed_action != recorded_player_action[i])
9402         printf("::: %d: %d <-> %d\n", i,
9403                stored_player[i].programmed_action, recorded_player_action[i]);
9404 #endif
9405
9406 #if 0
9407       actual_player_action = recorded_player_action[i];
9408 #endif
9409     }
9410
9411 #if 0
9412     /* overwrite tape action with programmed action */
9413     if (stored_player[i].programmed_action)
9414       actual_player_action = stored_player[i].programmed_action;
9415 #endif
9416
9417 #if 0
9418     if (i == 0)
9419       printf("::: action: %d: %x [%d]\n",
9420              stored_player[i].MovPos, actual_player_action, FrameCounter);
9421 #endif
9422
9423 #if 1
9424     PlayerActions(&stored_player[i], actual_player_action);
9425 #else
9426     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9427
9428     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9429       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9430 #endif
9431
9432     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9433   }
9434
9435 #if 0
9436   if (tape.recording)
9437     TapeRecordAction(tape_action);
9438 #endif
9439
9440   network_player_action_received = FALSE;
9441
9442   ScrollScreen(NULL, SCROLL_GO_ON);
9443
9444 #if 0
9445   FrameCounter++;
9446   TimeFrames++;
9447
9448   for (i = 0; i < MAX_PLAYERS; i++)
9449     stored_player[i].Frame++;
9450 #endif
9451
9452 #if 1
9453   /* for backwards compatibility, the following code emulates a fixed bug that
9454      occured when pushing elements (causing elements that just made their last
9455      pushing step to already (if possible) make their first falling step in the
9456      same game frame, which is bad); this code is also needed to use the famous
9457      "spring push bug" which is used in older levels and might be wanted to be
9458      used also in newer levels, but in this case the buggy pushing code is only
9459      affecting the "spring" element and no other elements */
9460
9461 #if 1
9462   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9463 #else
9464   if (game.engine_version < VERSION_IDENT(2,2,0,7))
9465 #endif
9466   {
9467     for (i = 0; i < MAX_PLAYERS; i++)
9468     {
9469       struct PlayerInfo *player = &stored_player[i];
9470       int x = player->jx;
9471       int y = player->jy;
9472
9473 #if 1
9474       if (player->active && player->is_pushing && player->is_moving &&
9475           IS_MOVING(x, y) &&
9476           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9477            Feld[x][y] == EL_SPRING))
9478 #else
9479       if (player->active && player->is_pushing && player->is_moving &&
9480           IS_MOVING(x, y))
9481 #endif
9482       {
9483         ContinueMoving(x, y);
9484
9485         /* continue moving after pushing (this is actually a bug) */
9486         if (!IS_MOVING(x, y))
9487         {
9488           Stop[x][y] = FALSE;
9489         }
9490       }
9491     }
9492   }
9493 #endif
9494
9495   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9496   {
9497     Changed[x][y] = FALSE;
9498     ChangeEvent[x][y] = -1;
9499
9500 #if USE_NEW_BLOCK_STYLE
9501     /* this must be handled before main playfield loop */
9502     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9503     {
9504       MovDelay[x][y]--;
9505       if (MovDelay[x][y] <= 0)
9506         RemoveField(x, y);
9507     }
9508 #endif
9509
9510 #if DEBUG
9511     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9512     {
9513       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9514       printf("GameActions(): This should never happen!\n");
9515
9516       ChangePage[x][y] = -1;
9517     }
9518 #endif
9519
9520     Stop[x][y] = FALSE;
9521     if (WasJustMoving[x][y] > 0)
9522       WasJustMoving[x][y]--;
9523     if (WasJustFalling[x][y] > 0)
9524       WasJustFalling[x][y]--;
9525     if (CheckCollision[x][y] > 0)
9526       CheckCollision[x][y]--;
9527
9528     GfxFrame[x][y]++;
9529
9530 #if 1
9531     /* reset finished pushing action (not done in ContinueMoving() to allow
9532        continous pushing animation for elements with zero push delay) */
9533     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9534     {
9535       ResetGfxAnimation(x, y);
9536       DrawLevelField(x, y);
9537     }
9538 #endif
9539
9540 #if DEBUG
9541     if (IS_BLOCKED(x, y))
9542     {
9543       int oldx, oldy;
9544
9545       Blocked2Moving(x, y, &oldx, &oldy);
9546       if (!IS_MOVING(oldx, oldy))
9547       {
9548         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9549         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9550         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9551         printf("GameActions(): This should never happen!\n");
9552       }
9553     }
9554 #endif
9555   }
9556
9557   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9558   {
9559     element = Feld[x][y];
9560 #if 1
9561     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9562 #else
9563     graphic = el2img(element);
9564 #endif
9565
9566 #if 0
9567     if (element == -1)
9568     {
9569       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
9570
9571       element = graphic = 0;
9572     }
9573 #endif
9574
9575     if (graphic_info[graphic].anim_global_sync)
9576       GfxFrame[x][y] = FrameCounter;
9577
9578     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9579         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9580       ResetRandomAnimationValue(x, y);
9581
9582     SetRandomAnimationValue(x, y);
9583
9584 #if 1
9585     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9586 #endif
9587
9588     if (IS_INACTIVE(element))
9589     {
9590       if (IS_ANIMATED(graphic))
9591         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9592
9593       continue;
9594     }
9595
9596 #if 1
9597     /* this may take place after moving, so 'element' may have changed */
9598 #if 0
9599     if (IS_CHANGING(x, y))
9600 #else
9601     if (IS_CHANGING(x, y) &&
9602         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9603 #endif
9604     {
9605 #if 0
9606       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
9607                     element_info[element].event_page_nr[CE_DELAY]);
9608 #else
9609       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
9610 #endif
9611
9612       element = Feld[x][y];
9613       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9614     }
9615 #endif
9616
9617     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9618     {
9619       StartMoving(x, y);
9620
9621 #if 1
9622       element = Feld[x][y];
9623       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9624 #if 0
9625       if (element == EL_MOLE)
9626         printf("::: %d, %d, %d [%d]\n",
9627                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
9628                GfxAction[x][y]);
9629 #endif
9630 #if 0
9631       if (element == EL_YAMYAM)
9632         printf("::: %d, %d, %d\n",
9633                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
9634 #endif
9635 #endif
9636
9637       if (IS_ANIMATED(graphic) &&
9638           !IS_MOVING(x, y) &&
9639           !Stop[x][y])
9640       {
9641         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9642
9643 #if 0
9644         if (element == EL_BUG)
9645           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9646 #endif
9647
9648 #if 0
9649         if (element == EL_MOLE)
9650           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
9651 #endif
9652       }
9653
9654       if (IS_GEM(element) || element == EL_SP_INFOTRON)
9655         EdelsteinFunkeln(x, y);
9656     }
9657     else if ((element == EL_ACID ||
9658               element == EL_EXIT_OPEN ||
9659               element == EL_SP_EXIT_OPEN ||
9660               element == EL_SP_TERMINAL ||
9661               element == EL_SP_TERMINAL_ACTIVE ||
9662               element == EL_EXTRA_TIME ||
9663               element == EL_SHIELD_NORMAL ||
9664               element == EL_SHIELD_DEADLY) &&
9665              IS_ANIMATED(graphic))
9666       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9667     else if (IS_MOVING(x, y))
9668       ContinueMoving(x, y);
9669     else if (IS_ACTIVE_BOMB(element))
9670       CheckDynamite(x, y);
9671 #if 0
9672     else if (element == EL_EXPLOSION && !game.explosions_delayed)
9673       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9674 #endif
9675     else if (element == EL_AMOEBA_GROWING)
9676       AmoebeWaechst(x, y);
9677     else if (element == EL_AMOEBA_SHRINKING)
9678       AmoebaDisappearing(x, y);
9679
9680 #if !USE_NEW_AMOEBA_CODE
9681     else if (IS_AMOEBALIVE(element))
9682       AmoebeAbleger(x, y);
9683 #endif
9684
9685     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9686       Life(x, y);
9687     else if (element == EL_EXIT_CLOSED)
9688       CheckExit(x, y);
9689     else if (element == EL_SP_EXIT_CLOSED)
9690       CheckExitSP(x, y);
9691     else if (element == EL_EXPANDABLE_WALL_GROWING)
9692       MauerWaechst(x, y);
9693     else if (element == EL_EXPANDABLE_WALL ||
9694              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9695              element == EL_EXPANDABLE_WALL_VERTICAL ||
9696              element == EL_EXPANDABLE_WALL_ANY)
9697       MauerAbleger(x, y);
9698     else if (element == EL_FLAMES)
9699       CheckForDragon(x, y);
9700 #if 0
9701     else if (IS_AUTO_CHANGING(element))
9702       ChangeElement(x, y);
9703 #endif
9704     else if (element == EL_EXPLOSION)
9705       ; /* drawing of correct explosion animation is handled separately */
9706     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9707       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9708
9709 #if 0
9710     /* this may take place after moving, so 'element' may have changed */
9711     if (IS_AUTO_CHANGING(Feld[x][y]))
9712       ChangeElement(x, y);
9713 #endif
9714
9715     if (IS_BELT_ACTIVE(element))
9716       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9717
9718     if (game.magic_wall_active)
9719     {
9720       int jx = local_player->jx, jy = local_player->jy;
9721
9722       /* play the element sound at the position nearest to the player */
9723       if ((element == EL_MAGIC_WALL_FULL ||
9724            element == EL_MAGIC_WALL_ACTIVE ||
9725            element == EL_MAGIC_WALL_EMPTYING ||
9726            element == EL_BD_MAGIC_WALL_FULL ||
9727            element == EL_BD_MAGIC_WALL_ACTIVE ||
9728            element == EL_BD_MAGIC_WALL_EMPTYING) &&
9729           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9730       {
9731         magic_wall_x = x;
9732         magic_wall_y = y;
9733       }
9734     }
9735   }
9736
9737 #if USE_NEW_AMOEBA_CODE
9738   /* new experimental amoeba growth stuff */
9739 #if 1
9740   if (!(FrameCounter % 8))
9741 #endif
9742   {
9743     static unsigned long random = 1684108901;
9744
9745     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9746     {
9747 #if 0
9748       x = (random >> 10) % lev_fieldx;
9749       y = (random >> 20) % lev_fieldy;
9750 #else
9751       x = RND(lev_fieldx);
9752       y = RND(lev_fieldy);
9753 #endif
9754       element = Feld[x][y];
9755
9756 #if 1
9757       if (!IS_PLAYER(x,y) &&
9758           (element == EL_EMPTY ||
9759            CAN_GROW_INTO(element) ||
9760            element == EL_QUICKSAND_EMPTY ||
9761            element == EL_ACID_SPLASH_LEFT ||
9762            element == EL_ACID_SPLASH_RIGHT))
9763       {
9764         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9765             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9766             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9767             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9768           Feld[x][y] = EL_AMOEBA_DROP;
9769       }
9770 #else
9771       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
9772       if (!IS_PLAYER(x,y) &&
9773           (element == EL_EMPTY ||
9774            element == EL_SAND ||
9775            element == EL_QUICKSAND_EMPTY ||
9776            element == EL_ACID_SPLASH_LEFT ||
9777            element == EL_ACID_SPLASH_RIGHT))
9778       {
9779         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9780             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9781             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9782             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9783           Feld[x][y] = EL_AMOEBA_DROP;
9784       }
9785 #endif
9786
9787       random = random * 129 + 1;
9788     }
9789   }
9790 #endif
9791
9792 #if 0
9793   if (game.explosions_delayed)
9794 #endif
9795   {
9796     game.explosions_delayed = FALSE;
9797
9798     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9799     {
9800       element = Feld[x][y];
9801
9802       if (ExplodeField[x][y])
9803         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9804       else if (element == EL_EXPLOSION)
9805         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9806
9807       ExplodeField[x][y] = EX_TYPE_NONE;
9808     }
9809
9810     game.explosions_delayed = TRUE;
9811   }
9812
9813   if (game.magic_wall_active)
9814   {
9815     if (!(game.magic_wall_time_left % 4))
9816     {
9817       int element = Feld[magic_wall_x][magic_wall_y];
9818
9819       if (element == EL_BD_MAGIC_WALL_FULL ||
9820           element == EL_BD_MAGIC_WALL_ACTIVE ||
9821           element == EL_BD_MAGIC_WALL_EMPTYING)
9822         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9823       else
9824         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9825     }
9826
9827     if (game.magic_wall_time_left > 0)
9828     {
9829       game.magic_wall_time_left--;
9830       if (!game.magic_wall_time_left)
9831       {
9832         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9833         {
9834           element = Feld[x][y];
9835
9836           if (element == EL_MAGIC_WALL_ACTIVE ||
9837               element == EL_MAGIC_WALL_FULL)
9838           {
9839             Feld[x][y] = EL_MAGIC_WALL_DEAD;
9840             DrawLevelField(x, y);
9841           }
9842           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9843                    element == EL_BD_MAGIC_WALL_FULL)
9844           {
9845             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9846             DrawLevelField(x, y);
9847           }
9848         }
9849
9850         game.magic_wall_active = FALSE;
9851       }
9852     }
9853   }
9854
9855   if (game.light_time_left > 0)
9856   {
9857     game.light_time_left--;
9858
9859     if (game.light_time_left == 0)
9860       RedrawAllLightSwitchesAndInvisibleElements();
9861   }
9862
9863   if (game.timegate_time_left > 0)
9864   {
9865     game.timegate_time_left--;
9866
9867     if (game.timegate_time_left == 0)
9868       CloseAllOpenTimegates();
9869   }
9870
9871   for (i = 0; i < MAX_PLAYERS; i++)
9872   {
9873     struct PlayerInfo *player = &stored_player[i];
9874
9875     if (SHIELD_ON(player))
9876     {
9877       if (player->shield_deadly_time_left)
9878         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9879       else if (player->shield_normal_time_left)
9880         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9881     }
9882   }
9883
9884   if (TimeFrames >= FRAMES_PER_SECOND)
9885   {
9886     TimeFrames = 0;
9887     TapeTime++;
9888
9889     for (i = 0; i < MAX_PLAYERS; i++)
9890     {
9891       struct PlayerInfo *player = &stored_player[i];
9892
9893       if (SHIELD_ON(player))
9894       {
9895         player->shield_normal_time_left--;
9896
9897         if (player->shield_deadly_time_left > 0)
9898           player->shield_deadly_time_left--;
9899       }
9900     }
9901
9902     if (!level.use_step_counter)
9903     {
9904       TimePlayed++;
9905
9906       if (TimeLeft > 0)
9907       {
9908         TimeLeft--;
9909
9910         if (TimeLeft <= 10 && setup.time_limit)
9911           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9912
9913         DrawGameValue_Time(TimeLeft);
9914
9915         if (!TimeLeft && setup.time_limit)
9916           for (i = 0; i < MAX_PLAYERS; i++)
9917             KillHero(&stored_player[i]);
9918       }
9919       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9920         DrawGameValue_Time(TimePlayed);
9921     }
9922
9923     if (tape.recording || tape.playing)
9924       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9925   }
9926
9927   DrawAllPlayers();
9928   PlayAllPlayersSound();
9929
9930   if (options.debug)                    /* calculate frames per second */
9931   {
9932     static unsigned long fps_counter = 0;
9933     static int fps_frames = 0;
9934     unsigned long fps_delay_ms = Counter() - fps_counter;
9935
9936     fps_frames++;
9937
9938     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
9939     {
9940       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9941
9942       fps_frames = 0;
9943       fps_counter = Counter();
9944     }
9945
9946     redraw_mask |= REDRAW_FPS;
9947   }
9948
9949 #if 0
9950   if (stored_player[0].jx != stored_player[0].last_jx ||
9951       stored_player[0].jy != stored_player[0].last_jy)
9952     printf("::: %d, %d, %d, %d, %d\n",
9953            stored_player[0].MovDir,
9954            stored_player[0].MovPos,
9955            stored_player[0].GfxPos,
9956            stored_player[0].Frame,
9957            stored_player[0].StepFrame);
9958 #endif
9959
9960 #if USE_NEW_MOVE_DELAY
9961   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9962 #else
9963   FrameCounter++;
9964   TimeFrames++;
9965
9966   for (i = 0; i < MAX_PLAYERS; i++)
9967   {
9968     int move_frames =
9969       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
9970
9971     stored_player[i].Frame += move_frames;
9972
9973     if (stored_player[i].MovPos != 0)
9974       stored_player[i].StepFrame += move_frames;
9975
9976 #if USE_NEW_MOVE_DELAY
9977     if (stored_player[i].move_delay > 0)
9978       stored_player[i].move_delay--;
9979 #endif
9980
9981     if (stored_player[i].drop_delay > 0)
9982       stored_player[i].drop_delay--;
9983   }
9984 #endif
9985
9986 #if 1
9987   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9988   {
9989     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9990
9991     local_player->show_envelope = 0;
9992   }
9993 #endif
9994
9995 #if USE_NEW_RANDOMIZE
9996   /* use random number generator in every frame to make it less predictable */
9997   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9998     RND(1);
9999 #endif
10000 }
10001
10002 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10003 {
10004   int min_x = x, min_y = y, max_x = x, max_y = y;
10005   int i;
10006
10007   for (i = 0; i < MAX_PLAYERS; i++)
10008   {
10009     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10010
10011     if (!stored_player[i].active || &stored_player[i] == player)
10012       continue;
10013
10014     min_x = MIN(min_x, jx);
10015     min_y = MIN(min_y, jy);
10016     max_x = MAX(max_x, jx);
10017     max_y = MAX(max_y, jy);
10018   }
10019
10020   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10021 }
10022
10023 static boolean AllPlayersInVisibleScreen()
10024 {
10025   int i;
10026
10027   for (i = 0; i < MAX_PLAYERS; i++)
10028   {
10029     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10030
10031     if (!stored_player[i].active)
10032       continue;
10033
10034     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10035       return FALSE;
10036   }
10037
10038   return TRUE;
10039 }
10040
10041 void ScrollLevel(int dx, int dy)
10042 {
10043   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10044   int x, y;
10045
10046   BlitBitmap(drawto_field, drawto_field,
10047              FX + TILEX * (dx == -1) - softscroll_offset,
10048              FY + TILEY * (dy == -1) - softscroll_offset,
10049              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10050              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10051              FX + TILEX * (dx == 1) - softscroll_offset,
10052              FY + TILEY * (dy == 1) - softscroll_offset);
10053
10054   if (dx)
10055   {
10056     x = (dx == 1 ? BX1 : BX2);
10057     for (y = BY1; y <= BY2; y++)
10058       DrawScreenField(x, y);
10059   }
10060
10061   if (dy)
10062   {
10063     y = (dy == 1 ? BY1 : BY2);
10064     for (x = BX1; x <= BX2; x++)
10065       DrawScreenField(x, y);
10066   }
10067
10068   redraw_mask |= REDRAW_FIELD;
10069 }
10070
10071 #if 0
10072 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
10073 {
10074   int nextx = x + dx, nexty = y + dy;
10075   int element = Feld[x][y];
10076
10077   if ((dx == -1 &&
10078        element != EL_SP_PORT_LEFT &&
10079        element != EL_SP_GRAVITY_PORT_LEFT &&
10080        element != EL_SP_PORT_HORIZONTAL &&
10081        element != EL_SP_PORT_ANY) ||
10082       (dx == +1 &&
10083        element != EL_SP_PORT_RIGHT &&
10084        element != EL_SP_GRAVITY_PORT_RIGHT &&
10085        element != EL_SP_PORT_HORIZONTAL &&
10086        element != EL_SP_PORT_ANY) ||
10087       (dy == -1 &&
10088        element != EL_SP_PORT_UP &&
10089        element != EL_SP_GRAVITY_PORT_UP &&
10090        element != EL_SP_PORT_VERTICAL &&
10091        element != EL_SP_PORT_ANY) ||
10092       (dy == +1 &&
10093        element != EL_SP_PORT_DOWN &&
10094        element != EL_SP_GRAVITY_PORT_DOWN &&
10095        element != EL_SP_PORT_VERTICAL &&
10096        element != EL_SP_PORT_ANY) ||
10097       !IN_LEV_FIELD(nextx, nexty) ||
10098       !IS_FREE(nextx, nexty))
10099     return FALSE;
10100
10101   return TRUE;
10102 }
10103 #endif
10104
10105 static boolean canFallDown(struct PlayerInfo *player)
10106 {
10107   int jx = player->jx, jy = player->jy;
10108
10109   return (IN_LEV_FIELD(jx, jy + 1) &&
10110           (IS_FREE(jx, jy + 1) ||
10111 #if USE_NEW_BLOCK_STYLE
10112 #if USE_GRAVITY_BUGFIX_OLD
10113            Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
10114 #endif
10115 #endif
10116            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10117           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10118           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10119 }
10120
10121 static boolean canPassField(int x, int y, int move_dir)
10122 {
10123   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10124   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10125   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10126   int nextx = x + dx;
10127   int nexty = y + dy;
10128   int element = Feld[x][y];
10129
10130   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10131           !CAN_MOVE(element) &&
10132           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10133           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10134           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10135 }
10136
10137 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10138 {
10139   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10140   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10141   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10142   int newx = x + dx;
10143   int newy = y + dy;
10144 #if 0
10145   int nextx = newx + dx;
10146   int nexty = newy + dy;
10147 #endif
10148
10149 #if 1
10150   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10151           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10152 #if 0
10153           (!IS_SP_PORT(Feld[newx][newy]) || move_dir == MV_UP) &&
10154 #endif
10155           (IS_DIGGABLE(Feld[newx][newy]) ||
10156            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10157            canPassField(newx, newy, move_dir)));
10158 #else
10159 #if 1
10160   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10161           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10162           (IS_DIGGABLE(Feld[newx][newy]) ||
10163            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10164            canPassField(newx, newy, move_dir)));
10165 #else
10166 #if 1
10167   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10168           (IS_DIGGABLE_WITH_GRAVITY(Feld[newx][newy]) ||
10169            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10170            canPassField(newx, newy, move_dir)));
10171 #else
10172   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10173           (IS_DIGGABLE(Feld[newx][newy]) ||
10174            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10175            (IS_PASSABLE_FROM(Feld[newx][newy], opposite_dir) &&
10176             !CAN_MOVE(Feld[newx][newy]) &&
10177             IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10178             IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10179             (level.can_pass_to_walkable || IS_FREE(nextx, nexty)))));
10180 #endif
10181 #endif
10182 #endif
10183 }
10184
10185 static void CheckGravityMovement(struct PlayerInfo *player)
10186 {
10187   if (game.gravity && !player->programmed_action)
10188   {
10189 #if 1
10190     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10191     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10192 #else
10193     int move_dir_horizontal = player->action & MV_HORIZONTAL;
10194     int move_dir_vertical   = player->action & MV_VERTICAL;
10195 #endif
10196
10197 #if 1
10198     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10199 #else
10200     boolean player_is_snapping = player->action & JOY_BUTTON_1;
10201 #endif
10202
10203     int jx = player->jx, jy = player->jy;
10204
10205     boolean player_is_moving_to_valid_field =
10206       (!player_is_snapping &&
10207        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10208         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10209
10210 #if 0
10211     int move_dir =
10212       (player->last_move_dir & MV_HORIZONTAL ?
10213        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
10214        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
10215 #endif
10216
10217 #if 0
10218     int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10219     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10220     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
10221     int new_jx = jx + dx, new_jy = jy + dy;
10222     int nextx = new_jx + dx, nexty = new_jy + dy;
10223 #endif
10224
10225 #if 1
10226
10227 #if 1
10228     boolean player_can_fall_down = canFallDown(player);
10229 #else
10230     boolean player_can_fall_down =
10231       (IN_LEV_FIELD(jx, jy + 1) &&
10232        (IS_FREE(jx, jy + 1) ||
10233         (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)));
10234 #endif
10235
10236 #else
10237     boolean player_can_fall_down =
10238       (IN_LEV_FIELD(jx, jy + 1) &&
10239        (IS_FREE(jx, jy + 1)));
10240 #endif
10241
10242 #if 0
10243     boolean player_is_moving_to_valid_field =
10244       (
10245 #if 1
10246        !player_is_snapping &&
10247 #endif
10248
10249 #if 1
10250        IN_LEV_FIELD(new_jx, new_jy) &&
10251        (IS_DIGGABLE(Feld[new_jx][new_jy]) ||
10252         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
10253          element_info[Feld[new_jx][new_jy]].access_direction & opposite_dir &&
10254          IN_LEV_FIELD(nextx, nexty) &&
10255          element_info[Feld[nextx][nexty]].access_direction & move_dir))
10256 #else
10257        IN_LEV_FIELD(new_jx, new_jy) &&
10258        (Feld[new_jx][new_jy] == EL_SP_BASE ||
10259         Feld[new_jx][new_jy] == EL_SAND ||
10260         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
10261          canEnterSupaplexPort(new_jx, new_jy, dx, dy)))
10262     /* !!! extend EL_SAND to anything diggable !!! */
10263 #endif
10264        );
10265 #endif
10266
10267 #if 0
10268     boolean player_is_standing_on_valid_field =
10269       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10270        (IS_WALKABLE(Feld[jx][jy]) && !ACCESS_FROM(Feld[jx][jy], MV_DOWN)));
10271 #endif
10272
10273 #if 0
10274     printf("::: checking gravity NOW [%d, %d, %d] [%d] [%d / %d] ...\n",
10275            player_can_fall_down,
10276            player_is_standing_on_valid_field,
10277            player_is_moving_to_valid_field,
10278            (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1),
10279            player->effective_action,
10280            player->can_fall_into_acid);
10281 #endif
10282
10283     if (player_can_fall_down &&
10284 #if 0
10285         !player_is_standing_on_valid_field &&
10286 #endif
10287         !player_is_moving_to_valid_field)
10288     {
10289 #if 0
10290       printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
10291              jx, jy, FrameCounter);
10292 #endif
10293
10294       player->programmed_action = MV_DOWN;
10295     }
10296   }
10297 }
10298
10299 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10300 {
10301 #if 1
10302   return CheckGravityMovement(player);
10303 #endif
10304
10305   if (game.gravity && !player->programmed_action)
10306   {
10307     int jx = player->jx, jy = player->jy;
10308     boolean field_under_player_is_free =
10309       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10310     boolean player_is_standing_on_valid_field =
10311       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10312        (IS_WALKABLE(Feld[jx][jy]) &&
10313         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10314
10315     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10316       player->programmed_action = MV_DOWN;
10317   }
10318 }
10319
10320 /*
10321   MovePlayerOneStep()
10322   -----------------------------------------------------------------------------
10323   dx, dy:               direction (non-diagonal) to try to move the player to
10324   real_dx, real_dy:     direction as read from input device (can be diagonal)
10325 */
10326
10327 boolean MovePlayerOneStep(struct PlayerInfo *player,
10328                           int dx, int dy, int real_dx, int real_dy)
10329 {
10330 #if 0
10331   static int trigger_sides[4][2] =
10332   {
10333     /* enter side        leave side */
10334     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
10335     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
10336     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
10337     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
10338   };
10339   int move_direction = (dx == -1 ? MV_LEFT :
10340                         dx == +1 ? MV_RIGHT :
10341                         dy == -1 ? MV_UP :
10342                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
10343   int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10344   int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10345 #endif
10346   int jx = player->jx, jy = player->jy;
10347   int new_jx = jx + dx, new_jy = jy + dy;
10348   int element;
10349   int can_move;
10350
10351   if (!player->active || (!dx && !dy))
10352     return MF_NO_ACTION;
10353
10354   player->MovDir = (dx < 0 ? MV_LEFT :
10355                     dx > 0 ? MV_RIGHT :
10356                     dy < 0 ? MV_UP :
10357                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
10358
10359   if (!IN_LEV_FIELD(new_jx, new_jy))
10360     return MF_NO_ACTION;
10361
10362   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10363     return MF_NO_ACTION;
10364
10365 #if 0
10366   element = MovingOrBlocked2Element(new_jx, new_jy);
10367 #else
10368   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10369 #endif
10370
10371   if (DONT_RUN_INTO(element))
10372   {
10373     if (element == EL_ACID && dx == 0 && dy == 1)
10374     {
10375       SplashAcid(new_jx, new_jy);
10376       Feld[jx][jy] = EL_PLAYER_1;
10377       InitMovingField(jx, jy, MV_DOWN);
10378       Store[jx][jy] = EL_ACID;
10379       ContinueMoving(jx, jy);
10380       BuryHero(player);
10381     }
10382     else
10383       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
10384
10385     return MF_MOVING;
10386   }
10387
10388   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10389   if (can_move != MF_MOVING)
10390     return can_move;
10391
10392   /* check if DigField() has caused relocation of the player */
10393   if (player->jx != jx || player->jy != jy)
10394     return MF_NO_ACTION;        /* <-- !!! CHECK THIS [-> MF_ACTION ?] !!! */
10395
10396   StorePlayer[jx][jy] = 0;
10397   player->last_jx = jx;
10398   player->last_jy = jy;
10399   player->jx = new_jx;
10400   player->jy = new_jy;
10401   StorePlayer[new_jx][new_jy] = player->element_nr;
10402
10403   player->MovPos =
10404     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10405
10406   player->step_counter++;
10407
10408 #if 0
10409   player->drop_delay = 0;
10410 #endif
10411
10412   PlayerVisit[jx][jy] = FrameCounter;
10413
10414   ScrollPlayer(player, SCROLL_INIT);
10415
10416 #if 0
10417   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
10418   {
10419     CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_PLAYER_LEAVES_X,
10420                                       leave_side);
10421     CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
10422   }
10423
10424   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
10425   {
10426     CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
10427                                       CE_PLAYER_ENTERS_X, enter_side);
10428     CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
10429                              CE_ENTERED_BY_PLAYER, enter_side);
10430   }
10431 #endif
10432
10433   return MF_MOVING;
10434 }
10435
10436 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10437 {
10438   int jx = player->jx, jy = player->jy;
10439   int old_jx = jx, old_jy = jy;
10440   int moved = MF_NO_ACTION;
10441
10442 #if 1
10443   if (!player->active)
10444     return FALSE;
10445
10446   if (!dx && !dy)
10447   {
10448     if (player->MovPos == 0)
10449     {
10450       player->is_moving = FALSE;
10451       player->is_digging = FALSE;
10452       player->is_collecting = FALSE;
10453       player->is_snapping = FALSE;
10454       player->is_pushing = FALSE;
10455     }
10456
10457     return FALSE;
10458   }
10459 #else
10460   if (!player->active || (!dx && !dy))
10461     return FALSE;
10462 #endif
10463
10464 #if 0
10465   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10466       !tape.playing)
10467     return FALSE;
10468 #else
10469
10470 #if 1
10471
10472 #if 0
10473   printf("::: %d <= %d < %d ?\n", player->move_delay, FrameCounter,
10474          player->move_delay + player->move_delay_value);
10475 #endif
10476
10477 #if USE_NEW_MOVE_DELAY
10478   if (player->move_delay > 0)
10479 #else
10480   if (!FrameReached(&player->move_delay, player->move_delay_value))
10481 #endif
10482   {
10483 #if 0
10484     printf("::: can NOT move\n");
10485 #endif
10486
10487     return FALSE;
10488   }
10489 #else
10490   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
10491       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
10492     return FALSE;
10493 #endif
10494
10495 #endif
10496
10497 #if 0
10498   printf("::: COULD move now\n");
10499 #endif
10500
10501 #if USE_NEW_MOVE_DELAY
10502   player->move_delay = -1;              /* set to "uninitialized" value */
10503 #endif
10504
10505   /* store if player is automatically moved to next field */
10506   player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
10507
10508   /* remove the last programmed player action */
10509   player->programmed_action = 0;
10510
10511   if (player->MovPos)
10512   {
10513     /* should only happen if pre-1.2 tape recordings are played */
10514     /* this is only for backward compatibility */
10515
10516     int original_move_delay_value = player->move_delay_value;
10517
10518 #if DEBUG
10519     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10520            tape.counter);
10521 #endif
10522
10523     /* scroll remaining steps with finest movement resolution */
10524     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10525
10526     while (player->MovPos)
10527     {
10528       ScrollPlayer(player, SCROLL_GO_ON);
10529       ScrollScreen(NULL, SCROLL_GO_ON);
10530
10531 #if USE_NEW_MOVE_DELAY
10532       AdvanceFrameAndPlayerCounters(player->index_nr);
10533 #else
10534       FrameCounter++;
10535 #endif
10536
10537       DrawAllPlayers();
10538       BackToFront();
10539     }
10540
10541     player->move_delay_value = original_move_delay_value;
10542   }
10543
10544   if (player->last_move_dir & MV_HORIZONTAL)
10545   {
10546     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10547       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10548   }
10549   else
10550   {
10551     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10552       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10553   }
10554
10555   jx = player->jx;
10556   jy = player->jy;
10557
10558   if (moved & MF_MOVING && !ScreenMovPos &&
10559       (player == local_player || !options.network))
10560   {
10561     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10562     int offset = (setup.scroll_delay ? 3 : 0);
10563
10564     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10565     {
10566       /* actual player has left the screen -- scroll in that direction */
10567       if (jx != old_jx)         /* player has moved horizontally */
10568         scroll_x += (jx - old_jx);
10569       else                      /* player has moved vertically */
10570         scroll_y += (jy - old_jy);
10571     }
10572     else
10573     {
10574       if (jx != old_jx)         /* player has moved horizontally */
10575       {
10576         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10577             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10578           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10579
10580         /* don't scroll over playfield boundaries */
10581         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10582           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10583
10584         /* don't scroll more than one field at a time */
10585         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10586
10587         /* don't scroll against the player's moving direction */
10588         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10589             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10590           scroll_x = old_scroll_x;
10591       }
10592       else                      /* player has moved vertically */
10593       {
10594         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10595             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10596           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10597
10598         /* don't scroll over playfield boundaries */
10599         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10600           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10601
10602         /* don't scroll more than one field at a time */
10603         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10604
10605         /* don't scroll against the player's moving direction */
10606         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10607             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10608           scroll_y = old_scroll_y;
10609       }
10610     }
10611
10612     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10613     {
10614       if (!options.network && !AllPlayersInVisibleScreen())
10615       {
10616         scroll_x = old_scroll_x;
10617         scroll_y = old_scroll_y;
10618       }
10619       else
10620       {
10621         ScrollScreen(player, SCROLL_INIT);
10622         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10623       }
10624     }
10625   }
10626
10627 #if 0
10628 #if 1
10629   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
10630 #else
10631   if (!(moved & MF_MOVING) && !player->is_pushing)
10632     player->Frame = 0;
10633 #endif
10634 #endif
10635
10636   player->StepFrame = 0;
10637
10638   if (moved & MF_MOVING)
10639   {
10640 #if 0
10641     printf("::: REALLY moves now\n");
10642 #endif
10643
10644     if (old_jx != jx && old_jy == jy)
10645       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10646     else if (old_jx == jx && old_jy != jy)
10647       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10648
10649     DrawLevelField(jx, jy);     /* for "crumbled sand" */
10650
10651     player->last_move_dir = player->MovDir;
10652     player->is_moving = TRUE;
10653 #if 1
10654     player->is_snapping = FALSE;
10655 #endif
10656
10657 #if 1
10658     player->is_switching = FALSE;
10659 #endif
10660
10661     player->is_dropping = FALSE;
10662
10663
10664 #if 0
10665     /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
10666
10667 #if 1
10668     if (game.engine_version < VERSION_IDENT(3,1,0,0))
10669 #endif
10670     {
10671       int move_direction = player->MovDir;
10672 #if 1
10673       int enter_side = MV_DIR_OPPOSITE(move_direction);
10674       int leave_side = move_direction;
10675 #else
10676       static int trigger_sides[4][2] =
10677       {
10678         /* enter side           leave side */
10679         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
10680         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
10681         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
10682         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
10683       };
10684       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10685       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10686 #endif
10687       int old_element = Feld[old_jx][old_jy];
10688       int new_element = Feld[jx][jy];
10689
10690 #if 1
10691       /* !!! TEST ONLY !!! */
10692       if (IS_CUSTOM_ELEMENT(old_element))
10693         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10694                                    CE_LEFT_BY_PLAYER,
10695                                    player->index_bit, leave_side);
10696
10697       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10698                                           CE_PLAYER_LEAVES_X,
10699                                           player->index_bit, leave_side);
10700
10701       if (IS_CUSTOM_ELEMENT(new_element))
10702         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10703                                    player->index_bit, enter_side);
10704
10705       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10706                                           CE_PLAYER_ENTERS_X,
10707                                           player->index_bit, enter_side);
10708 #endif
10709
10710     }
10711 #endif
10712
10713
10714   }
10715   else
10716   {
10717     CheckGravityMovementWhenNotMoving(player);
10718
10719     /*
10720     player->last_move_dir = MV_NO_MOVING;
10721     */
10722     player->is_moving = FALSE;
10723
10724 #if USE_NEW_MOVE_STYLE
10725     /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
10726     /* ensure that the player is also allowed to move in the next frame */
10727     /* (currently, the player is forced to wait eight frames before he can try
10728        again!!!) */
10729
10730     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10731       player->move_delay = 0;   /* allow direct movement in the next frame */
10732 #endif
10733   }
10734
10735 #if USE_NEW_MOVE_DELAY
10736   if (player->move_delay == -1)         /* not yet initialized by DigField() */
10737     player->move_delay = player->move_delay_value;
10738 #endif
10739
10740   if (game.engine_version < VERSION_IDENT(3,0,7,0))
10741   {
10742     TestIfHeroTouchesBadThing(jx, jy);
10743     TestIfPlayerTouchesCustomElement(jx, jy);
10744   }
10745
10746   if (!player->active)
10747     RemoveHero(player);
10748
10749   return moved;
10750 }
10751
10752 void ScrollPlayer(struct PlayerInfo *player, int mode)
10753 {
10754   int jx = player->jx, jy = player->jy;
10755   int last_jx = player->last_jx, last_jy = player->last_jy;
10756   int move_stepsize = TILEX / player->move_delay_value;
10757
10758   if (!player->active || !player->MovPos)
10759     return;
10760
10761   if (mode == SCROLL_INIT)
10762   {
10763     player->actual_frame_counter = FrameCounter;
10764     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10765
10766 #if 0
10767     printf("::: %06d: %d,%d: %d (%d) [%d]\n",
10768            FrameCounter,
10769            last_jx, last_jy, Feld[last_jx][last_jy], EL_EXPLOSION,
10770            player->block_delay);
10771 #endif
10772
10773 #if USE_NEW_BLOCK_STYLE
10774
10775 #if 0
10776     if (player->block_delay <= 0)
10777       printf("::: ALERT! block_delay == %d\n", player->block_delay);
10778 #endif
10779
10780     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10781         Feld[last_jx][last_jy] == EL_EMPTY)
10782     {
10783       int last_field_block_delay = 0;   /* start with no blocking at all */
10784       int block_delay_adjustment = player->block_delay_adjustment;
10785
10786       /* if player blocks last field, add delay for exactly one move */
10787       if (player->block_last_field)
10788       {
10789         last_field_block_delay += player->move_delay_value;
10790
10791 #if USE_GRAVITY_BUGFIX_NEW
10792         /* when blocking enabled, prevent moving up despite gravity */
10793         if (game.gravity && player->MovDir == MV_UP)
10794           block_delay_adjustment = -1;
10795 #endif
10796       }
10797
10798       /* add block delay adjustment (also possible when not blocking) */
10799       last_field_block_delay += block_delay_adjustment;
10800
10801 #if 0
10802 #if USE_BLOCK_DELAY_BUGFIX
10803       /* when blocking enabled, correct block delay for fast movement */
10804       if (player->block_last_field &&
10805           player->move_delay_value < MOVE_DELAY_NORMAL_SPEED)
10806         last_field_block_delay =
10807           player->move_delay_value + player->block_delay_adjustment;
10808 #endif
10809 #endif
10810
10811 #if 0
10812 #if USE_GRAVITY_BUGFIX_NEW
10813       /* when blocking enabled, correct block delay for gravity movement */
10814       if (player->block_last_field &&
10815           game.gravity && player->MovDir == MV_UP)
10816         last_field_block_delay = player->move_delay_value - 1;
10817 #endif
10818 #endif
10819
10820       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10821       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10822     }
10823 #else
10824 #if USE_NEW_MOVE_STYLE
10825     if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
10826          player->block_last_field) &&
10827         Feld[last_jx][last_jy] == EL_EMPTY)
10828       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10829 #else
10830     if (Feld[last_jx][last_jy] == EL_EMPTY)
10831       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10832 #endif
10833 #endif
10834
10835 #if 0
10836     DrawPlayer(player);
10837 #endif
10838
10839     return;
10840   }
10841   else if (!FrameReached(&player->actual_frame_counter, 1))
10842     return;
10843
10844   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10845   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10846
10847 #if USE_NEW_BLOCK_STYLE
10848 #else
10849   if (!player->block_last_field &&
10850       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10851 #if 1
10852     RemoveField(last_jx, last_jy);
10853 #else
10854     Feld[last_jx][last_jy] = EL_EMPTY;
10855 #endif
10856 #endif
10857
10858   /* before DrawPlayer() to draw correct player graphic for this case */
10859   if (player->MovPos == 0)
10860     CheckGravityMovement(player);
10861
10862 #if 0
10863   DrawPlayer(player);   /* needed here only to cleanup last field */
10864 #endif
10865
10866   if (player->MovPos == 0)      /* player reached destination field */
10867   {
10868 #if 1
10869     if (player->move_delay_reset_counter > 0)
10870     {
10871       player->move_delay_reset_counter--;
10872
10873       if (player->move_delay_reset_counter == 0)
10874       {
10875         /* continue with normal speed after quickly moving through gate */
10876         HALVE_PLAYER_SPEED(player);
10877
10878         /* be able to make the next move without delay */
10879         player->move_delay = 0;
10880       }
10881     }
10882 #else
10883     if (IS_PASSABLE(Feld[last_jx][last_jy]))
10884     {
10885       /* continue with normal speed after quickly moving through gate */
10886       HALVE_PLAYER_SPEED(player);
10887
10888       /* be able to make the next move without delay */
10889       player->move_delay = 0;
10890     }
10891 #endif
10892
10893 #if USE_NEW_BLOCK_STYLE
10894 #else
10895     if (player->block_last_field &&
10896         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
10897 #if 1
10898       RemoveField(last_jx, last_jy);
10899 #else
10900       Feld[last_jx][last_jy] = EL_EMPTY;
10901 #endif
10902 #endif
10903
10904     player->last_jx = jx;
10905     player->last_jy = jy;
10906
10907     if (Feld[jx][jy] == EL_EXIT_OPEN ||
10908         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10909         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
10910     {
10911       DrawPlayer(player);       /* needed here only to cleanup last field */
10912       RemoveHero(player);
10913
10914       if (local_player->friends_still_needed == 0 ||
10915           IS_SP_ELEMENT(Feld[jx][jy]))
10916         player->LevelSolved = player->GameOver = TRUE;
10917     }
10918
10919 #if 1
10920     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
10921     /* this breaks one level: "machine", level 000 */
10922 #if 0
10923     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
10924 #endif
10925     {
10926       int move_direction = player->MovDir;
10927 #if 1
10928       int enter_side = MV_DIR_OPPOSITE(move_direction);
10929       int leave_side = move_direction;
10930 #else
10931       static int trigger_sides[4][2] =
10932       {
10933         /* enter side           leave side */
10934         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
10935         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
10936         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
10937         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
10938       };
10939       int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
10940       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
10941 #endif
10942       int old_jx = last_jx;
10943       int old_jy = last_jy;
10944       int old_element = Feld[old_jx][old_jy];
10945       int new_element = Feld[jx][jy];
10946
10947 #if 1
10948       /* !!! TEST ONLY !!! */
10949       if (IS_CUSTOM_ELEMENT(old_element))
10950         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10951                                    CE_LEFT_BY_PLAYER,
10952                                    player->index_bit, leave_side);
10953
10954       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10955                                           CE_PLAYER_LEAVES_X,
10956                                           player->index_bit, leave_side);
10957
10958       if (IS_CUSTOM_ELEMENT(new_element))
10959         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10960                                    player->index_bit, enter_side);
10961
10962       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10963                                           CE_PLAYER_ENTERS_X,
10964                                           player->index_bit, enter_side);
10965 #endif
10966
10967     }
10968 #endif
10969
10970     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10971     {
10972       TestIfHeroTouchesBadThing(jx, jy);
10973       TestIfPlayerTouchesCustomElement(jx, jy);
10974 #if 1
10975 #if 1
10976       /* needed because pushed element has not yet reached its destination,
10977          so it would trigger a change event at its previous field location */
10978       if (!player->is_pushing)
10979 #endif
10980         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
10981 #endif
10982
10983       if (!player->active)
10984         RemoveHero(player);
10985     }
10986
10987     if (level.use_step_counter)
10988     {
10989       int i;
10990
10991       TimePlayed++;
10992
10993       if (TimeLeft > 0)
10994       {
10995         TimeLeft--;
10996
10997         if (TimeLeft <= 10 && setup.time_limit)
10998           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10999
11000         DrawGameValue_Time(TimeLeft);
11001
11002         if (!TimeLeft && setup.time_limit)
11003           for (i = 0; i < MAX_PLAYERS; i++)
11004             KillHero(&stored_player[i]);
11005       }
11006       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11007         DrawGameValue_Time(TimePlayed);
11008     }
11009
11010     if (tape.single_step && tape.recording && !tape.pausing &&
11011         !player->programmed_action)
11012       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11013   }
11014 }
11015
11016 void ScrollScreen(struct PlayerInfo *player, int mode)
11017 {
11018   static unsigned long screen_frame_counter = 0;
11019
11020   if (mode == SCROLL_INIT)
11021   {
11022     /* set scrolling step size according to actual player's moving speed */
11023     ScrollStepSize = TILEX / player->move_delay_value;
11024
11025     screen_frame_counter = FrameCounter;
11026     ScreenMovDir = player->MovDir;
11027     ScreenMovPos = player->MovPos;
11028     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11029     return;
11030   }
11031   else if (!FrameReached(&screen_frame_counter, 1))
11032     return;
11033
11034   if (ScreenMovPos)
11035   {
11036     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
11037     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
11038     redraw_mask |= REDRAW_FIELD;
11039   }
11040   else
11041     ScreenMovDir = MV_NO_MOVING;
11042 }
11043
11044 void TestIfPlayerTouchesCustomElement(int x, int y)
11045 {
11046   static int xy[4][2] =
11047   {
11048     { 0, -1 },
11049     { -1, 0 },
11050     { +1, 0 },
11051     { 0, +1 }
11052   };
11053   static int trigger_sides[4][2] =
11054   {
11055     /* center side       border side */
11056     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11057     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11058     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11059     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11060   };
11061   static int touch_dir[4] =
11062   {
11063     MV_LEFT | MV_RIGHT,
11064     MV_UP   | MV_DOWN,
11065     MV_UP   | MV_DOWN,
11066     MV_LEFT | MV_RIGHT
11067   };
11068   int center_element = Feld[x][y];      /* should always be non-moving! */
11069   int i;
11070
11071   for (i = 0; i < NUM_DIRECTIONS; i++)
11072   {
11073     int xx = x + xy[i][0];
11074     int yy = y + xy[i][1];
11075     int center_side = trigger_sides[i][0];
11076     int border_side = trigger_sides[i][1];
11077     int border_element;
11078
11079     if (!IN_LEV_FIELD(xx, yy))
11080       continue;
11081
11082     if (IS_PLAYER(x, y))
11083     {
11084       struct PlayerInfo *player = PLAYERINFO(x, y);
11085
11086       if (game.engine_version < VERSION_IDENT(3,0,7,0))
11087         border_element = Feld[xx][yy];          /* may be moving! */
11088       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11089         border_element = Feld[xx][yy];
11090       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
11091         border_element = MovingOrBlocked2Element(xx, yy);
11092       else
11093         continue;               /* center and border element do not touch */
11094
11095 #if 1
11096       /* !!! TEST ONLY !!! */
11097       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11098                                  player->index_bit, border_side);
11099       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11100                                           CE_PLAYER_TOUCHES_X,
11101                                           player->index_bit, border_side);
11102 #else
11103       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
11104                                           CE_PLAYER_TOUCHES_X,
11105                                           player->index_bit, border_side);
11106       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
11107                                  player->index_bit, border_side);
11108 #endif
11109     }
11110     else if (IS_PLAYER(xx, yy))
11111     {
11112       struct PlayerInfo *player = PLAYERINFO(xx, yy);
11113
11114       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11115       {
11116         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11117           continue;             /* center and border element do not touch */
11118       }
11119
11120 #if 1
11121       /* !!! TEST ONLY !!! */
11122       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11123                                  player->index_bit, center_side);
11124       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11125                                           CE_PLAYER_TOUCHES_X,
11126                                           player->index_bit, center_side);
11127 #else
11128       CheckTriggeredElementChangeByPlayer(x, y, center_element,
11129                                           CE_PLAYER_TOUCHES_X,
11130                                           player->index_bit, center_side);
11131       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
11132                                  player->index_bit, center_side);
11133 #endif
11134
11135       break;
11136     }
11137   }
11138 }
11139
11140 void TestIfElementTouchesCustomElement(int x, int y)
11141 {
11142   static int xy[4][2] =
11143   {
11144     { 0, -1 },
11145     { -1, 0 },
11146     { +1, 0 },
11147     { 0, +1 }
11148   };
11149   static int trigger_sides[4][2] =
11150   {
11151     /* center side      border side */
11152     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11153     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11154     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11155     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11156   };
11157   static int touch_dir[4] =
11158   {
11159     MV_LEFT | MV_RIGHT,
11160     MV_UP   | MV_DOWN,
11161     MV_UP   | MV_DOWN,
11162     MV_LEFT | MV_RIGHT
11163   };
11164   boolean change_center_element = FALSE;
11165   int center_element_change_page = 0;
11166   int center_element = Feld[x][y];      /* should always be non-moving! */
11167   int border_trigger_element = EL_UNDEFINED;
11168   int i, j;
11169
11170   for (i = 0; i < NUM_DIRECTIONS; i++)
11171   {
11172     int xx = x + xy[i][0];
11173     int yy = y + xy[i][1];
11174     int center_side = trigger_sides[i][0];
11175     int border_side = trigger_sides[i][1];
11176     int border_element;
11177
11178     if (!IN_LEV_FIELD(xx, yy))
11179       continue;
11180
11181     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11182       border_element = Feld[xx][yy];    /* may be moving! */
11183     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11184       border_element = Feld[xx][yy];
11185     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11186       border_element = MovingOrBlocked2Element(xx, yy);
11187     else
11188       continue;                 /* center and border element do not touch */
11189
11190 #if TEST_NEW_STUFF
11191
11192     /* check for change of center element (but change it only once) */
11193     if (!change_center_element)
11194       change_center_element =
11195         CheckElementChangeBySide(x, y, center_element, border_element,
11196                                  CE_TOUCHING_X, border_side);
11197
11198     /* -> CheckElementChangeExtTEST */
11199
11200 #else
11201
11202     /* check for change of center element (but change it only once) */
11203     if (IS_CUSTOM_ELEMENT(center_element) &&
11204         HAS_ANY_CHANGE_EVENT(center_element, CE_TOUCHING_X) &&
11205         !change_center_element)
11206     {
11207       for (j = 0; j < element_info[center_element].num_change_pages; j++)
11208       {
11209         struct ElementChangeInfo *change =
11210           &element_info[center_element].change_page[j];
11211
11212         if (change->can_change &&
11213             change->has_event[CE_TOUCHING_X] &&
11214             change->trigger_side & border_side &&
11215 #if 1
11216             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
11217 #else
11218             change->trigger_element == border_element
11219 #endif
11220             )
11221         {
11222           change_center_element = TRUE;
11223           center_element_change_page = j;
11224           border_trigger_element = border_element;
11225
11226           break;
11227         }
11228       }
11229     }
11230
11231 #endif
11232
11233 #if TEST_NEW_STUFF
11234
11235     /* check for change of border element */
11236     CheckElementChangeBySide(xx, yy, border_element, center_element,
11237                              CE_TOUCHING_X, center_side);
11238
11239 #else
11240
11241     /* check for change of border element */
11242     if (IS_CUSTOM_ELEMENT(border_element) &&
11243         HAS_ANY_CHANGE_EVENT(border_element, CE_TOUCHING_X))
11244     {
11245       for (j = 0; j < element_info[border_element].num_change_pages; j++)
11246       {
11247         struct ElementChangeInfo *change =
11248           &element_info[border_element].change_page[j];
11249
11250         if (change->can_change &&
11251             change->has_event[CE_TOUCHING_X] &&
11252             change->trigger_side & center_side &&
11253 #if 1
11254             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
11255 #else
11256             change->trigger_element == center_element
11257 #endif
11258             )
11259         {
11260 #if 0
11261           printf("::: border_element %d, %d\n", x, y);
11262 #endif
11263
11264           CheckElementChangeByPage(xx, yy, border_element, center_element,
11265                                    CE_TOUCHING_X, j);
11266           break;
11267         }
11268       }
11269     }
11270
11271 #endif
11272
11273   }
11274
11275 #if !TEST_NEW_STUFF
11276
11277   if (change_center_element)
11278   {
11279 #if 0
11280     printf("::: center_element %d, %d\n", x, y);
11281 #endif
11282
11283     CheckElementChangeByPage(x, y, center_element, border_trigger_element,
11284                              CE_TOUCHING_X, center_element_change_page);
11285   }
11286
11287 #endif
11288
11289 }
11290
11291 void TestIfElementHitsCustomElement(int x, int y, int direction)
11292 {
11293   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11294   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11295   int hitx = x + dx, hity = y + dy;
11296   int hitting_element = Feld[x][y];
11297   int touched_element;
11298 #if 0
11299   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11300                         !IS_FREE(hitx, hity) &&
11301                         (!IS_MOVING(hitx, hity) ||
11302                          MovDir[hitx][hity] != direction ||
11303                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11304 #endif
11305
11306   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11307     return;
11308
11309 #if 0
11310   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11311     return;
11312 #endif
11313
11314   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11315                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11316
11317 #if !USE_HITTING_SOMETHING_BUGFIX
11318   /* "hitting something" is also true when hitting the playfield border */
11319   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11320                            CE_HITTING_SOMETHING, direction);
11321 #endif
11322
11323   if (IN_LEV_FIELD(hitx, hity))
11324   {
11325     int opposite_direction = MV_DIR_OPPOSITE(direction);
11326     int hitting_side = direction;
11327     int touched_side = opposite_direction;
11328 #if 0
11329     int touched_element = MovingOrBlocked2Element(hitx, hity);
11330 #endif
11331 #if 1
11332     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11333                           MovDir[hitx][hity] != direction ||
11334                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11335
11336     object_hit = TRUE;
11337 #endif
11338
11339     if (object_hit)
11340     {
11341       int i;
11342
11343 #if !USE_HIT_BY_SOMETHING_BUGFIX
11344       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11345                                CE_HIT_BY_SOMETHING, opposite_direction);
11346 #endif
11347
11348 #if TEST_NEW_STUFF
11349
11350       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11351                                CE_HITTING_X, touched_side);
11352
11353 #else
11354
11355       if (IS_CUSTOM_ELEMENT(hitting_element) &&
11356           HAS_ANY_CHANGE_EVENT(hitting_element, CE_HITTING_X))
11357       {
11358         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
11359         {
11360           struct ElementChangeInfo *change =
11361             &element_info[hitting_element].change_page[i];
11362
11363           if (change->can_change &&
11364               change->has_event[CE_HITTING_X] &&
11365               change->trigger_side & touched_side &&
11366           
11367 #if 1
11368               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
11369 #else
11370               change->trigger_element == touched_element
11371 #endif
11372               )
11373           {
11374             CheckElementChangeByPage(x, y, hitting_element, touched_element,
11375                                      CE_HITTING_X, i);
11376             break;
11377           }
11378         }
11379       }
11380
11381 #endif
11382
11383 #if TEST_NEW_STUFF
11384
11385       CheckElementChangeBySide(hitx, hity, touched_element,
11386                                hitting_element, CE_HIT_BY_X, hitting_side);
11387 #else
11388
11389       if (IS_CUSTOM_ELEMENT(touched_element) &&
11390           HAS_ANY_CHANGE_EVENT(touched_element, CE_HIT_BY_X))
11391       {
11392         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
11393         {
11394           struct ElementChangeInfo *change =
11395             &element_info[touched_element].change_page[i];
11396
11397           if (change->can_change &&
11398               change->has_event[CE_HIT_BY_X] &&
11399               change->trigger_side & hitting_side &&
11400 #if 1
11401               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
11402 #else
11403               change->trigger_element == hitting_element
11404 #endif
11405               )
11406           {
11407             CheckElementChangeByPage(hitx, hity, touched_element,
11408                                      hitting_element, CE_HIT_BY_X, i);
11409             break;
11410           }
11411         }
11412       }
11413
11414 #endif
11415
11416 #if USE_HIT_BY_SOMETHING_BUGFIX
11417       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11418                                CE_HIT_BY_SOMETHING, opposite_direction);
11419 #endif
11420     }
11421   }
11422
11423 #if USE_HITTING_SOMETHING_BUGFIX
11424   /* "hitting something" is also true when hitting the playfield border */
11425   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11426                            CE_HITTING_SOMETHING, direction);
11427 #endif
11428 }
11429
11430 #if 0
11431 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11432 {
11433   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11434   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11435   int hitx = x + dx, hity = y + dy;
11436   int hitting_element = Feld[x][y];
11437   int touched_element;
11438 #if 0
11439   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11440                         !IS_FREE(hitx, hity) &&
11441                         (!IS_MOVING(hitx, hity) ||
11442                          MovDir[hitx][hity] != direction ||
11443                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11444 #endif
11445
11446   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11447     return;
11448
11449 #if 0
11450   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11451     return;
11452 #endif
11453
11454   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11455                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11456
11457   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11458                            EP_CAN_SMASH_EVERYTHING, direction);
11459
11460   if (IN_LEV_FIELD(hitx, hity))
11461   {
11462     int opposite_direction = MV_DIR_OPPOSITE(direction);
11463     int hitting_side = direction;
11464     int touched_side = opposite_direction;
11465 #if 0
11466     int touched_element = MovingOrBlocked2Element(hitx, hity);
11467 #endif
11468 #if 1
11469     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11470                           MovDir[hitx][hity] != direction ||
11471                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11472
11473     object_hit = TRUE;
11474 #endif
11475
11476     if (object_hit)
11477     {
11478       int i;
11479
11480       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11481                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11482
11483 #if TEST_NEW_STUFF
11484
11485       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11486                                CE_OTHER_IS_SMASHING, touched_side);
11487 #else
11488
11489       if (IS_CUSTOM_ELEMENT(hitting_element) &&
11490           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
11491       {
11492         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
11493         {
11494           struct ElementChangeInfo *change =
11495             &element_info[hitting_element].change_page[i];
11496
11497           if (change->can_change &&
11498               change->has_event[CE_OTHER_IS_SMASHING] &&
11499               change->trigger_side & touched_side &&
11500           
11501 #if 1
11502               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
11503 #else
11504               change->trigger_element == touched_element
11505 #endif
11506               )
11507           {
11508             CheckElementChangeByPage(x, y, hitting_element, touched_element,
11509                                      CE_OTHER_IS_SMASHING, i);
11510             break;
11511           }
11512         }
11513       }
11514
11515 #endif
11516
11517 #if TEST_NEW_STUFF
11518
11519       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11520                                CE_OTHER_GETS_SMASHED, hitting_side);
11521 #else
11522
11523       if (IS_CUSTOM_ELEMENT(touched_element) &&
11524           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
11525       {
11526         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
11527         {
11528           struct ElementChangeInfo *change =
11529             &element_info[touched_element].change_page[i];
11530
11531           if (change->can_change &&
11532               change->has_event[CE_OTHER_GETS_SMASHED] &&
11533               change->trigger_side & hitting_side &&
11534 #if 1
11535               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
11536 #else
11537               change->trigger_element == hitting_element
11538 #endif
11539               )
11540           {
11541             CheckElementChangeByPage(hitx, hity, touched_element,
11542                                      hitting_element, CE_OTHER_GETS_SMASHED,i);
11543             break;
11544           }
11545         }
11546       }
11547
11548 #endif
11549
11550     }
11551   }
11552 }
11553 #endif
11554
11555 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11556 {
11557   int i, kill_x = -1, kill_y = -1;
11558   int bad_element = -1;
11559   static int test_xy[4][2] =
11560   {
11561     { 0, -1 },
11562     { -1, 0 },
11563     { +1, 0 },
11564     { 0, +1 }
11565   };
11566   static int test_dir[4] =
11567   {
11568     MV_UP,
11569     MV_LEFT,
11570     MV_RIGHT,
11571     MV_DOWN
11572   };
11573
11574   for (i = 0; i < NUM_DIRECTIONS; i++)
11575   {
11576     int test_x, test_y, test_move_dir, test_element;
11577
11578     test_x = good_x + test_xy[i][0];
11579     test_y = good_y + test_xy[i][1];
11580
11581     if (!IN_LEV_FIELD(test_x, test_y))
11582       continue;
11583
11584     test_move_dir =
11585       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11586
11587 #if 0
11588     test_element = Feld[test_x][test_y];
11589 #else
11590     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11591 #endif
11592
11593     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11594        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11595     */
11596     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11597         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11598     {
11599       kill_x = test_x;
11600       kill_y = test_y;
11601       bad_element = test_element;
11602
11603       break;
11604     }
11605   }
11606
11607   if (kill_x != -1 || kill_y != -1)
11608   {
11609     if (IS_PLAYER(good_x, good_y))
11610     {
11611       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11612
11613 #if 1
11614       if (player->shield_deadly_time_left > 0 &&
11615           !IS_INDESTRUCTIBLE(bad_element))
11616         Bang(kill_x, kill_y);
11617       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11618         KillHero(player);
11619 #else
11620       if (player->shield_deadly_time_left > 0)
11621         Bang(kill_x, kill_y);
11622       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11623         KillHero(player);
11624 #endif
11625     }
11626     else
11627       Bang(good_x, good_y);
11628   }
11629 }
11630
11631 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11632 {
11633   int i, kill_x = -1, kill_y = -1;
11634   int bad_element = Feld[bad_x][bad_y];
11635   static int test_xy[4][2] =
11636   {
11637     { 0, -1 },
11638     { -1, 0 },
11639     { +1, 0 },
11640     { 0, +1 }
11641   };
11642   static int touch_dir[4] =
11643   {
11644     MV_LEFT | MV_RIGHT,
11645     MV_UP   | MV_DOWN,
11646     MV_UP   | MV_DOWN,
11647     MV_LEFT | MV_RIGHT
11648   };
11649   static int test_dir[4] =
11650   {
11651     MV_UP,
11652     MV_LEFT,
11653     MV_RIGHT,
11654     MV_DOWN
11655   };
11656
11657   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11658     return;
11659
11660   for (i = 0; i < NUM_DIRECTIONS; i++)
11661   {
11662     int test_x, test_y, test_move_dir, test_element;
11663
11664     test_x = bad_x + test_xy[i][0];
11665     test_y = bad_y + test_xy[i][1];
11666     if (!IN_LEV_FIELD(test_x, test_y))
11667       continue;
11668
11669     test_move_dir =
11670       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
11671
11672     test_element = Feld[test_x][test_y];
11673
11674     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11675        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11676     */
11677     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11678         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11679     {
11680       /* good thing is player or penguin that does not move away */
11681       if (IS_PLAYER(test_x, test_y))
11682       {
11683         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11684
11685         if (bad_element == EL_ROBOT && player->is_moving)
11686           continue;     /* robot does not kill player if he is moving */
11687
11688         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11689         {
11690           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11691             continue;           /* center and border element do not touch */
11692         }
11693
11694         kill_x = test_x;
11695         kill_y = test_y;
11696         break;
11697       }
11698       else if (test_element == EL_PENGUIN)
11699       {
11700         kill_x = test_x;
11701         kill_y = test_y;
11702         break;
11703       }
11704     }
11705   }
11706
11707   if (kill_x != -1 || kill_y != -1)
11708   {
11709     if (IS_PLAYER(kill_x, kill_y))
11710     {
11711       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11712
11713 #if 1
11714       if (player->shield_deadly_time_left > 0 &&
11715           !IS_INDESTRUCTIBLE(bad_element))
11716         Bang(bad_x, bad_y);
11717       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11718         KillHero(player);
11719 #else
11720       if (player->shield_deadly_time_left > 0)
11721         Bang(bad_x, bad_y);
11722       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11723         KillHero(player);
11724 #endif
11725     }
11726     else
11727       Bang(kill_x, kill_y);
11728   }
11729 }
11730
11731 void TestIfHeroTouchesBadThing(int x, int y)
11732 {
11733   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11734 }
11735
11736 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
11737 {
11738   TestIfGoodThingHitsBadThing(x, y, move_dir);
11739 }
11740
11741 void TestIfBadThingTouchesHero(int x, int y)
11742 {
11743   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11744 }
11745
11746 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
11747 {
11748   TestIfBadThingHitsGoodThing(x, y, move_dir);
11749 }
11750
11751 void TestIfFriendTouchesBadThing(int x, int y)
11752 {
11753   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
11754 }
11755
11756 void TestIfBadThingTouchesFriend(int x, int y)
11757 {
11758   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
11759 }
11760
11761 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11762 {
11763   int i, kill_x = bad_x, kill_y = bad_y;
11764   static int xy[4][2] =
11765   {
11766     { 0, -1 },
11767     { -1, 0 },
11768     { +1, 0 },
11769     { 0, +1 }
11770   };
11771
11772   for (i = 0; i < NUM_DIRECTIONS; i++)
11773   {
11774     int x, y, element;
11775
11776     x = bad_x + xy[i][0];
11777     y = bad_y + xy[i][1];
11778     if (!IN_LEV_FIELD(x, y))
11779       continue;
11780
11781     element = Feld[x][y];
11782     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11783         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11784     {
11785       kill_x = x;
11786       kill_y = y;
11787       break;
11788     }
11789   }
11790
11791   if (kill_x != bad_x || kill_y != bad_y)
11792     Bang(bad_x, bad_y);
11793 }
11794
11795 void KillHero(struct PlayerInfo *player)
11796 {
11797   int jx = player->jx, jy = player->jy;
11798
11799   if (!player->active)
11800     return;
11801
11802   /* remove accessible field at the player's position */
11803   Feld[jx][jy] = EL_EMPTY;
11804
11805   /* deactivate shield (else Bang()/Explode() would not work right) */
11806   player->shield_normal_time_left = 0;
11807   player->shield_deadly_time_left = 0;
11808
11809   Bang(jx, jy);
11810   BuryHero(player);
11811 }
11812
11813 static void KillHeroUnlessEnemyProtected(int x, int y)
11814 {
11815   if (!PLAYER_ENEMY_PROTECTED(x, y))
11816     KillHero(PLAYERINFO(x, y));
11817 }
11818
11819 static void KillHeroUnlessExplosionProtected(int x, int y)
11820 {
11821   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11822     KillHero(PLAYERINFO(x, y));
11823 }
11824
11825 void BuryHero(struct PlayerInfo *player)
11826 {
11827   int jx = player->jx, jy = player->jy;
11828
11829   if (!player->active)
11830     return;
11831
11832 #if 1
11833   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
11834 #else
11835   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
11836 #endif
11837   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11838
11839   player->GameOver = TRUE;
11840   RemoveHero(player);
11841 }
11842
11843 void RemoveHero(struct PlayerInfo *player)
11844 {
11845   int jx = player->jx, jy = player->jy;
11846   int i, found = FALSE;
11847
11848   player->present = FALSE;
11849   player->active = FALSE;
11850
11851   if (!ExplodeField[jx][jy])
11852     StorePlayer[jx][jy] = 0;
11853
11854   for (i = 0; i < MAX_PLAYERS; i++)
11855     if (stored_player[i].active)
11856       found = TRUE;
11857
11858   if (!found)
11859     AllPlayersGone = TRUE;
11860
11861   ExitX = ZX = jx;
11862   ExitY = ZY = jy;
11863 }
11864
11865 /*
11866   =============================================================================
11867   checkDiagonalPushing()
11868   -----------------------------------------------------------------------------
11869   check if diagonal input device direction results in pushing of object
11870   (by checking if the alternative direction is walkable, diggable, ...)
11871   =============================================================================
11872 */
11873
11874 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11875                                     int x, int y, int real_dx, int real_dy)
11876 {
11877   int jx, jy, dx, dy, xx, yy;
11878
11879   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11880     return TRUE;
11881
11882   /* diagonal direction: check alternative direction */
11883   jx = player->jx;
11884   jy = player->jy;
11885   dx = x - jx;
11886   dy = y - jy;
11887   xx = jx + (dx == 0 ? real_dx : 0);
11888   yy = jy + (dy == 0 ? real_dy : 0);
11889
11890   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11891 }
11892
11893 /*
11894   =============================================================================
11895   DigField()
11896   -----------------------------------------------------------------------------
11897   x, y:                 field next to player (non-diagonal) to try to dig to
11898   real_dx, real_dy:     direction as read from input device (can be diagonal)
11899   =============================================================================
11900 */
11901
11902 int DigField(struct PlayerInfo *player,
11903              int oldx, int oldy, int x, int y,
11904              int real_dx, int real_dy, int mode)
11905 {
11906 #if 0
11907   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
11908 #endif
11909   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11910   boolean player_was_pushing = player->is_pushing;
11911   int jx = oldx, jy = oldy;
11912   int dx = x - jx, dy = y - jy;
11913   int nextx = x + dx, nexty = y + dy;
11914   int move_direction = (dx == -1 ? MV_LEFT :
11915                         dx == +1 ? MV_RIGHT :
11916                         dy == -1 ? MV_UP :
11917                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
11918   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11919 #if 1
11920   int dig_side = MV_DIR_OPPOSITE(move_direction);
11921 #else
11922   static int trigger_sides[4] =
11923   {
11924     CH_SIDE_RIGHT,      /* moving left  */
11925     CH_SIDE_LEFT,       /* moving right */
11926     CH_SIDE_BOTTOM,     /* moving up    */
11927     CH_SIDE_TOP,        /* moving down  */
11928   };
11929   int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
11930 #endif
11931   int old_element = Feld[jx][jy];
11932   int element;
11933
11934   if (is_player)                /* function can also be called by EL_PENGUIN */
11935   {
11936     if (player->MovPos == 0)
11937     {
11938       player->is_digging = FALSE;
11939       player->is_collecting = FALSE;
11940     }
11941
11942     if (player->MovPos == 0)    /* last pushing move finished */
11943       player->is_pushing = FALSE;
11944
11945     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11946     {
11947       player->is_switching = FALSE;
11948 #if USE_NEW_PUSH_DELAY
11949       player->push_delay = -1;
11950 #else
11951       player->push_delay = 0;
11952 #endif
11953
11954       return MF_NO_ACTION;
11955     }
11956   }
11957
11958   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11959     return MF_NO_ACTION;
11960
11961 #if 0
11962
11963 #if 0
11964   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
11965 #else
11966   if (IS_TUBE(Feld[jx][jy]) ||
11967       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
11968 #endif
11969   {
11970     int i = 0;
11971     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
11972     int tube_leave_directions[][2] =
11973     {
11974       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
11975       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
11976       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
11977       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
11978       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
11979       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
11980       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
11981       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
11982       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
11983       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
11984       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
11985       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
11986     };
11987
11988     while (tube_leave_directions[i][0] != tube_element)
11989     {
11990       i++;
11991       if (tube_leave_directions[i][0] == -1)    /* should not happen */
11992         break;
11993     }
11994
11995     if (!(tube_leave_directions[i][1] & move_direction))
11996       return MF_NO_ACTION;      /* tube has no opening in this direction */
11997   }
11998
11999 #else
12000
12001   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
12002     old_element = Back[jx][jy];
12003 #if USE_BACK_WALKABLE_BUGFIX
12004
12005   /* in case of element dropped at player position, check background */
12006   else if (Back[jx][jy] != EL_EMPTY &&
12007            game.engine_version >= VERSION_IDENT(2,2,0,0))
12008     old_element = Back[jx][jy];
12009 #endif
12010
12011 #endif
12012
12013   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
12014     return MF_NO_ACTION;        /* field has no opening in this direction */
12015
12016   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
12017     return MF_NO_ACTION;        /* field has no opening in this direction */
12018
12019   element = Feld[x][y];
12020
12021   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
12022     return MF_NO_ACTION;
12023
12024   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
12025       game.engine_version >= VERSION_IDENT(2,2,0,0))
12026     return MF_NO_ACTION;
12027
12028 #if 1
12029   if (game.gravity && is_player && !player->is_auto_moving &&
12030       canFallDown(player) && move_direction != MV_DOWN &&
12031       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
12032     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
12033 #endif
12034
12035 #if 0
12036   if (element == EL_EMPTY_SPACE &&
12037       game.gravity && !player->is_auto_moving &&
12038       canFallDown(player) && move_direction != MV_DOWN)
12039     return MF_NO_ACTION;        /* player cannot walk here due to gravity */
12040 #endif
12041
12042   switch (element)
12043   {
12044 #if 0
12045     case EL_SP_PORT_LEFT:
12046     case EL_SP_PORT_RIGHT:
12047     case EL_SP_PORT_UP:
12048     case EL_SP_PORT_DOWN:
12049     case EL_SP_PORT_HORIZONTAL:
12050     case EL_SP_PORT_VERTICAL:
12051     case EL_SP_PORT_ANY:
12052     case EL_SP_GRAVITY_PORT_LEFT:
12053     case EL_SP_GRAVITY_PORT_RIGHT:
12054     case EL_SP_GRAVITY_PORT_UP:
12055     case EL_SP_GRAVITY_PORT_DOWN:
12056 #if 1
12057       if (!canEnterSupaplexPort(x, y, dx, dy))
12058         return MF_NO_ACTION;
12059 #else
12060       if ((dx == -1 &&
12061            element != EL_SP_PORT_LEFT &&
12062            element != EL_SP_GRAVITY_PORT_LEFT &&
12063            element != EL_SP_PORT_HORIZONTAL &&
12064            element != EL_SP_PORT_ANY) ||
12065           (dx == +1 &&
12066            element != EL_SP_PORT_RIGHT &&
12067            element != EL_SP_GRAVITY_PORT_RIGHT &&
12068            element != EL_SP_PORT_HORIZONTAL &&
12069            element != EL_SP_PORT_ANY) ||
12070           (dy == -1 &&
12071            element != EL_SP_PORT_UP &&
12072            element != EL_SP_GRAVITY_PORT_UP &&
12073            element != EL_SP_PORT_VERTICAL &&
12074            element != EL_SP_PORT_ANY) ||
12075           (dy == +1 &&
12076            element != EL_SP_PORT_DOWN &&
12077            element != EL_SP_GRAVITY_PORT_DOWN &&
12078            element != EL_SP_PORT_VERTICAL &&
12079            element != EL_SP_PORT_ANY) ||
12080           !IN_LEV_FIELD(nextx, nexty) ||
12081           !IS_FREE(nextx, nexty))
12082         return MF_NO_ACTION;
12083 #endif
12084
12085       if (element == EL_SP_GRAVITY_PORT_LEFT ||
12086           element == EL_SP_GRAVITY_PORT_RIGHT ||
12087           element == EL_SP_GRAVITY_PORT_UP ||
12088           element == EL_SP_GRAVITY_PORT_DOWN)
12089         game.gravity = !game.gravity;
12090
12091       /* automatically move to the next field with double speed */
12092       player->programmed_action = move_direction;
12093 #if 1
12094       if (player->move_delay_reset_counter == 0)
12095       {
12096         player->move_delay_reset_counter = 2;   /* two double speed steps */
12097
12098         DOUBLE_PLAYER_SPEED(player);
12099       }
12100 #else
12101       player->move_delay_reset_counter = 2;
12102
12103       DOUBLE_PLAYER_SPEED(player);
12104 #endif
12105
12106 #if 0
12107       printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
12108 #endif
12109
12110       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
12111       break;
12112 #endif
12113
12114 #if 0
12115     case EL_TUBE_ANY:
12116     case EL_TUBE_VERTICAL:
12117     case EL_TUBE_HORIZONTAL:
12118     case EL_TUBE_VERTICAL_LEFT:
12119     case EL_TUBE_VERTICAL_RIGHT:
12120     case EL_TUBE_HORIZONTAL_UP:
12121     case EL_TUBE_HORIZONTAL_DOWN:
12122     case EL_TUBE_LEFT_UP:
12123     case EL_TUBE_LEFT_DOWN:
12124     case EL_TUBE_RIGHT_UP:
12125     case EL_TUBE_RIGHT_DOWN:
12126       {
12127         int i = 0;
12128         int tube_enter_directions[][2] =
12129         {
12130           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
12131           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
12132           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
12133           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
12134           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
12135           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
12136           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
12137           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
12138           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
12139           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
12140           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
12141           { -1,                         MV_NO_MOVING                         }
12142         };
12143
12144         while (tube_enter_directions[i][0] != element)
12145         {
12146           i++;
12147           if (tube_enter_directions[i][0] == -1)        /* should not happen */
12148             break;
12149         }
12150
12151         if (!(tube_enter_directions[i][1] & move_direction))
12152           return MF_NO_ACTION;  /* tube has no opening in this direction */
12153
12154         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
12155       }
12156       break;
12157 #endif
12158
12159     default:
12160
12161 #if 1
12162       if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
12163 #else
12164       if (IS_WALKABLE(element))
12165 #endif
12166       {
12167         int sound_element = SND_ELEMENT(element);
12168         int sound_action = ACTION_WALKING;
12169
12170 #if 0
12171         if (!ACCESS_FROM(element, opposite_direction))
12172           return MF_NO_ACTION;  /* field not accessible from this direction */
12173 #endif
12174
12175 #if 0
12176         if (element == EL_EMPTY_SPACE &&
12177             game.gravity && !player->is_auto_moving &&
12178             canFallDown(player) && move_direction != MV_DOWN)
12179           return MF_NO_ACTION;  /* player cannot walk here due to gravity */
12180 #endif
12181
12182         if (IS_RND_GATE(element))
12183         {
12184           if (!player->key[RND_GATE_NR(element)])
12185             return MF_NO_ACTION;
12186         }
12187         else if (IS_RND_GATE_GRAY(element))
12188         {
12189           if (!player->key[RND_GATE_GRAY_NR(element)])
12190             return MF_NO_ACTION;
12191         }
12192         else if (element == EL_EXIT_OPEN ||
12193                  element == EL_SP_EXIT_OPEN ||
12194                  element == EL_SP_EXIT_OPENING)
12195         {
12196           sound_action = ACTION_PASSING;        /* player is passing exit */
12197         }
12198         else if (element == EL_EMPTY)
12199         {
12200           sound_action = ACTION_MOVING;         /* nothing to walk on */
12201         }
12202
12203         /* play sound from background or player, whatever is available */
12204         if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
12205           PlayLevelSoundElementAction(x, y, sound_element, sound_action);
12206         else
12207           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
12208
12209         break;
12210       }
12211 #if 1
12212       else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
12213 #else
12214       else if (IS_PASSABLE(element))
12215 #endif
12216       {
12217 #if 0
12218         if (!canPassField(x, y, move_direction))
12219           return MF_NO_ACTION;
12220 #else
12221
12222 #if 0
12223 #if 1
12224         if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
12225             !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
12226             (!level.can_pass_to_walkable && !IS_FREE(nextx, nexty)))
12227           return MF_NO_ACTION;
12228 #else
12229         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
12230           return MF_NO_ACTION;
12231 #endif
12232 #endif
12233
12234 #if 1
12235         if (!ACCESS_FROM(element, opposite_direction))
12236           return MF_NO_ACTION;  /* field not accessible from this direction */
12237 #else
12238         if (IS_CUSTOM_ELEMENT(element) &&
12239             !ACCESS_FROM(element, opposite_direction))
12240           return MF_NO_ACTION;  /* field not accessible from this direction */
12241 #endif
12242
12243 #if 1
12244         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
12245           return MF_NO_ACTION;
12246 #endif
12247
12248 #endif
12249
12250         if (IS_EM_GATE(element))
12251         {
12252           if (!player->key[EM_GATE_NR(element)])
12253             return MF_NO_ACTION;
12254         }
12255         else if (IS_EM_GATE_GRAY(element))
12256         {
12257           if (!player->key[EM_GATE_GRAY_NR(element)])
12258             return MF_NO_ACTION;
12259         }
12260         else if (IS_SP_PORT(element))
12261         {
12262           if (element == EL_SP_GRAVITY_PORT_LEFT ||
12263               element == EL_SP_GRAVITY_PORT_RIGHT ||
12264               element == EL_SP_GRAVITY_PORT_UP ||
12265               element == EL_SP_GRAVITY_PORT_DOWN)
12266             game.gravity = !game.gravity;
12267           else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
12268                    element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
12269                    element == EL_SP_GRAVITY_ON_PORT_UP ||
12270                    element == EL_SP_GRAVITY_ON_PORT_DOWN)
12271             game.gravity = TRUE;
12272           else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
12273                    element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
12274                    element == EL_SP_GRAVITY_OFF_PORT_UP ||
12275                    element == EL_SP_GRAVITY_OFF_PORT_DOWN)
12276             game.gravity = FALSE;
12277         }
12278
12279         /* automatically move to the next field with double speed */
12280         player->programmed_action = move_direction;
12281 #if 1
12282         if (player->move_delay_reset_counter == 0)
12283         {
12284           player->move_delay_reset_counter = 2; /* two double speed steps */
12285
12286           DOUBLE_PLAYER_SPEED(player);
12287         }
12288 #else
12289         player->move_delay_reset_counter = 2;
12290
12291         DOUBLE_PLAYER_SPEED(player);
12292 #endif
12293
12294         PlayLevelSoundAction(x, y, ACTION_PASSING);
12295
12296         break;
12297       }
12298       else if (IS_DIGGABLE(element))
12299       {
12300         RemoveField(x, y);
12301
12302         if (mode != DF_SNAP)
12303         {
12304 #if 1
12305           GfxElement[x][y] = GFX_ELEMENT(element);
12306 #else
12307           GfxElement[x][y] =
12308             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
12309 #endif
12310           player->is_digging = TRUE;
12311         }
12312
12313         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12314
12315         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
12316                                             player->index_bit, dig_side);
12317
12318 #if 1
12319         if (mode == DF_SNAP)
12320           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
12321 #endif
12322
12323         break;
12324       }
12325       else if (IS_COLLECTIBLE(element))
12326       {
12327         RemoveField(x, y);
12328
12329         if (is_player && mode != DF_SNAP)
12330         {
12331           GfxElement[x][y] = element;
12332           player->is_collecting = TRUE;
12333         }
12334
12335         if (element == EL_SPEED_PILL)
12336         {
12337           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
12338         }
12339         else if (element == EL_EXTRA_TIME && level.time > 0)
12340         {
12341           TimeLeft += 10;
12342           DrawGameValue_Time(TimeLeft);
12343         }
12344         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
12345         {
12346           player->shield_normal_time_left += 10;
12347           if (element == EL_SHIELD_DEADLY)
12348             player->shield_deadly_time_left += 10;
12349         }
12350         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
12351         {
12352           if (player->inventory_size < MAX_INVENTORY_SIZE)
12353             player->inventory_element[player->inventory_size++] = element;
12354
12355           DrawGameValue_Dynamite(local_player->inventory_size);
12356         }
12357         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
12358         {
12359           player->dynabomb_count++;
12360           player->dynabombs_left++;
12361         }
12362         else if (element == EL_DYNABOMB_INCREASE_SIZE)
12363         {
12364           player->dynabomb_size++;
12365         }
12366         else if (element == EL_DYNABOMB_INCREASE_POWER)
12367         {
12368           player->dynabomb_xl = TRUE;
12369         }
12370         else if (IS_KEY(element))
12371         {
12372           player->key[KEY_NR(element)] = TRUE;
12373
12374           DrawGameValue_Keys(player->key);
12375
12376           redraw_mask |= REDRAW_DOOR_1;
12377         }
12378         else if (IS_ENVELOPE(element))
12379         {
12380 #if 1
12381           player->show_envelope = element;
12382 #else
12383           ShowEnvelope(element - EL_ENVELOPE_1);
12384 #endif
12385         }
12386         else if (IS_DROPPABLE(element) ||
12387                  IS_THROWABLE(element)) /* can be collected and dropped */
12388         {
12389           int i;
12390
12391           if (element_info[element].collect_count == 0)
12392             player->inventory_infinite_element = element;
12393           else
12394             for (i = 0; i < element_info[element].collect_count; i++)
12395               if (player->inventory_size < MAX_INVENTORY_SIZE)
12396                 player->inventory_element[player->inventory_size++] = element;
12397
12398           DrawGameValue_Dynamite(local_player->inventory_size);
12399         }
12400         else if (element_info[element].collect_count > 0)
12401         {
12402           local_player->gems_still_needed -=
12403             element_info[element].collect_count;
12404           if (local_player->gems_still_needed < 0)
12405             local_player->gems_still_needed = 0;
12406
12407           DrawGameValue_Emeralds(local_player->gems_still_needed);
12408         }
12409
12410         RaiseScoreElement(element);
12411         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12412
12413         if (is_player)
12414           CheckTriggeredElementChangeByPlayer(x, y, element,
12415                                               CE_PLAYER_COLLECTS_X,
12416                                               player->index_bit, dig_side);
12417
12418 #if 1
12419         if (mode == DF_SNAP)
12420           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
12421 #endif
12422
12423         break;
12424       }
12425       else if (IS_PUSHABLE(element))
12426       {
12427         if (mode == DF_SNAP && element != EL_BD_ROCK)
12428           return MF_NO_ACTION;
12429
12430         if (CAN_FALL(element) && dy)
12431           return MF_NO_ACTION;
12432
12433         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
12434             !(element == EL_SPRING && level.use_spring_bug))
12435           return MF_NO_ACTION;
12436
12437 #if 1
12438         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
12439             ((move_direction & MV_VERTICAL &&
12440               ((element_info[element].move_pattern & MV_LEFT &&
12441                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
12442                (element_info[element].move_pattern & MV_RIGHT &&
12443                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
12444              (move_direction & MV_HORIZONTAL &&
12445               ((element_info[element].move_pattern & MV_UP &&
12446                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
12447                (element_info[element].move_pattern & MV_DOWN &&
12448                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
12449           return MF_NO_ACTION;
12450 #endif
12451
12452 #if 1
12453         /* do not push elements already moving away faster than player */
12454         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
12455             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
12456           return MF_NO_ACTION;
12457 #else
12458         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
12459           return MF_NO_ACTION;
12460 #endif
12461
12462 #if 1
12463
12464 #if 1
12465         if (game.engine_version >= VERSION_IDENT(3,1,0,0))
12466         {
12467           if (player->push_delay_value == -1 || !player_was_pushing)
12468             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12469         }
12470         else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12471         {
12472           if (player->push_delay_value == -1)
12473             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12474         }
12475 #else
12476         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12477         {
12478           if (player->push_delay_value == -1 || !player_was_pushing)
12479             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12480         }
12481 #endif
12482         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
12483         {
12484           if (!player->is_pushing)
12485             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12486         }
12487
12488         /*
12489         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
12490             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
12491              !player_is_pushing))
12492           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12493         */
12494 #else
12495         if (!player->is_pushing &&
12496             game.engine_version >= VERSION_IDENT(2,2,0,7))
12497           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12498 #endif
12499
12500 #if 0
12501         printf("::: push delay: %ld -> %ld [%d, %d] [%d / %d] [%d '%s': %d]\n",
12502                player->push_delay, player->push_delay_value,
12503                FrameCounter, game.engine_version,
12504                player_was_pushing, player->is_pushing,
12505                element, element_info[element].token_name,
12506                GET_NEW_PUSH_DELAY(element));
12507 #endif
12508
12509         player->is_pushing = TRUE;
12510
12511         if (!(IN_LEV_FIELD(nextx, nexty) &&
12512               (IS_FREE(nextx, nexty) ||
12513                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
12514                 IS_SB_ELEMENT(element)))))
12515           return MF_NO_ACTION;
12516
12517         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
12518           return MF_NO_ACTION;
12519
12520 #if USE_NEW_PUSH_DELAY
12521
12522 #if 0
12523         if ( (player->push_delay == -1) != (player->push_delay2 == 0) )
12524           printf("::: ALERT: %d, %d [%d / %d]\n",
12525                  player->push_delay, player->push_delay2,
12526                  FrameCounter, FrameCounter / 50);
12527 #endif
12528
12529         if (player->push_delay == -1)   /* new pushing; restart delay */
12530           player->push_delay = 0;
12531 #else
12532         if (player->push_delay == 0)    /* new pushing; restart delay */
12533           player->push_delay = FrameCounter;
12534 #endif
12535
12536 #if USE_NEW_PUSH_DELAY
12537 #if 0
12538         if ( (player->push_delay > 0) != (!xxx_fr) )
12539           printf("::: PUSH BUG! %d, (%d -> %d) %d [%d / %d]\n",
12540                  player->push_delay,
12541                  xxx_pdv2, player->push_delay2, player->push_delay_value,
12542                  FrameCounter, FrameCounter / 50);
12543 #endif
12544
12545 #if 0
12546         if (player->push_delay > 0 &&
12547             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12548             element != EL_SPRING && element != EL_BALLOON)
12549 #else
12550         /* !!! */
12551         if (player->push_delay < player->push_delay_value &&
12552             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12553             element != EL_SPRING && element != EL_BALLOON)
12554 #endif
12555
12556 #else
12557         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
12558             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12559             element != EL_SPRING && element != EL_BALLOON)
12560 #endif
12561         {
12562           /* make sure that there is no move delay before next try to push */
12563 #if USE_NEW_MOVE_DELAY
12564           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12565             player->move_delay = 0;
12566 #else
12567           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12568             player->move_delay = INITIAL_MOVE_DELAY_OFF;
12569 #endif
12570
12571           return MF_NO_ACTION;
12572         }
12573
12574 #if 0
12575         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
12576 #endif
12577
12578         if (IS_SB_ELEMENT(element))
12579         {
12580           if (element == EL_SOKOBAN_FIELD_FULL)
12581           {
12582             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12583             local_player->sokobanfields_still_needed++;
12584           }
12585
12586           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12587           {
12588             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12589             local_player->sokobanfields_still_needed--;
12590           }
12591
12592           Feld[x][y] = EL_SOKOBAN_OBJECT;
12593
12594           if (Back[x][y] == Back[nextx][nexty])
12595             PlayLevelSoundAction(x, y, ACTION_PUSHING);
12596           else if (Back[x][y] != 0)
12597             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12598                                         ACTION_EMPTYING);
12599           else
12600             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12601                                         ACTION_FILLING);
12602
12603           if (local_player->sokobanfields_still_needed == 0 &&
12604               game.emulation == EMU_SOKOBAN)
12605           {
12606             player->LevelSolved = player->GameOver = TRUE;
12607             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12608           }
12609         }
12610         else
12611           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12612
12613         InitMovingField(x, y, move_direction);
12614         GfxAction[x][y] = ACTION_PUSHING;
12615
12616         if (mode == DF_SNAP)
12617           ContinueMoving(x, y);
12618         else
12619           MovPos[x][y] = (dx != 0 ? dx : dy);
12620
12621         Pushed[x][y] = TRUE;
12622         Pushed[nextx][nexty] = TRUE;
12623
12624         if (game.engine_version < VERSION_IDENT(2,2,0,7))
12625           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12626         else
12627           player->push_delay_value = -1;        /* get new value later */
12628
12629 #if USE_PUSH_BUGFIX
12630         /* now: check for element change _after_ element has been pushed! */
12631 #if 1
12632         if (game.use_change_when_pushing_bug)
12633 #else
12634         if (game.engine_version < VERSION_IDENT(3,1,0,0))
12635 #endif
12636         {
12637           CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12638                                      player->index_bit, dig_side);
12639           CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
12640                                               player->index_bit, dig_side);
12641         }
12642
12643 #else
12644
12645 #if 1
12646         /* check for element change _after_ element has been pushed! */
12647 #else
12648
12649 #if 1
12650         /* !!! TEST ONLY !!! */
12651         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12652                                    player->index_bit, dig_side);
12653         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12654                                             player->index_bit, dig_side);
12655 #else
12656         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12657                                             player->index_bit, dig_side);
12658         CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12659                                    player->index_bit, dig_side);
12660 #endif
12661 #endif
12662
12663 #endif
12664
12665         break;
12666       }
12667       else if (IS_SWITCHABLE(element))
12668       {
12669         if (PLAYER_SWITCHING(player, x, y))
12670         {
12671           CheckTriggeredElementChangeByPlayer(x,y, element,
12672                                               CE_PLAYER_PRESSES_X,
12673                                               player->index_bit, dig_side);
12674
12675           return MF_ACTION;
12676         }
12677
12678         player->is_switching = TRUE;
12679         player->switch_x = x;
12680         player->switch_y = y;
12681
12682         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12683
12684         if (element == EL_ROBOT_WHEEL)
12685         {
12686           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12687           ZX = x;
12688           ZY = y;
12689
12690           DrawLevelField(x, y);
12691         }
12692         else if (element == EL_SP_TERMINAL)
12693         {
12694           int xx, yy;
12695
12696           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
12697           {
12698             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12699               Bang(xx, yy);
12700             else if (Feld[xx][yy] == EL_SP_TERMINAL)
12701               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12702           }
12703         }
12704         else if (IS_BELT_SWITCH(element))
12705         {
12706           ToggleBeltSwitch(x, y);
12707         }
12708         else if (element == EL_SWITCHGATE_SWITCH_UP ||
12709                  element == EL_SWITCHGATE_SWITCH_DOWN)
12710         {
12711           ToggleSwitchgateSwitch(x, y);
12712         }
12713         else if (element == EL_LIGHT_SWITCH ||
12714                  element == EL_LIGHT_SWITCH_ACTIVE)
12715         {
12716           ToggleLightSwitch(x, y);
12717
12718 #if 0
12719           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
12720                          SND_LIGHT_SWITCH_ACTIVATING :
12721                          SND_LIGHT_SWITCH_DEACTIVATING);
12722 #endif
12723         }
12724         else if (element == EL_TIMEGATE_SWITCH)
12725         {
12726           ActivateTimegateSwitch(x, y);
12727         }
12728         else if (element == EL_BALLOON_SWITCH_LEFT ||
12729                  element == EL_BALLOON_SWITCH_RIGHT ||
12730                  element == EL_BALLOON_SWITCH_UP ||
12731                  element == EL_BALLOON_SWITCH_DOWN ||
12732                  element == EL_BALLOON_SWITCH_ANY)
12733         {
12734           if (element == EL_BALLOON_SWITCH_ANY)
12735             game.balloon_dir = move_direction;
12736           else
12737             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
12738                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12739                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
12740                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
12741                                 MV_NO_MOVING);
12742         }
12743         else if (element == EL_LAMP)
12744         {
12745           Feld[x][y] = EL_LAMP_ACTIVE;
12746           local_player->lights_still_needed--;
12747
12748           ResetGfxAnimation(x, y);
12749           DrawLevelField(x, y);
12750         }
12751         else if (element == EL_TIME_ORB_FULL)
12752         {
12753           Feld[x][y] = EL_TIME_ORB_EMPTY;
12754           TimeLeft += 10;
12755           DrawGameValue_Time(TimeLeft);
12756
12757           ResetGfxAnimation(x, y);
12758           DrawLevelField(x, y);
12759
12760 #if 0
12761           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
12762 #endif
12763         }
12764
12765         CheckTriggeredElementChangeByPlayer(x, y, element,
12766                                             CE_SWITCH_OF_X,
12767                                             player->index_bit, dig_side);
12768
12769         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12770                                             player->index_bit, dig_side);
12771
12772         return MF_ACTION;
12773       }
12774       else
12775       {
12776         if (!PLAYER_SWITCHING(player, x, y))
12777         {
12778           player->is_switching = TRUE;
12779           player->switch_x = x;
12780           player->switch_y = y;
12781
12782 #if 1
12783           /* !!! TEST ONLY !!! */
12784           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12785                                      player->index_bit, dig_side);
12786           CheckTriggeredElementChangeByPlayer(x, y, element,
12787                                               CE_SWITCH_OF_X,
12788                                               player->index_bit, dig_side);
12789 #else
12790           CheckTriggeredElementChangeByPlayer(x, y, element,
12791                                               CE_SWITCH_OF_X,
12792                                               player->index_bit, dig_side);
12793           CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12794                                      player->index_bit, dig_side);
12795 #endif
12796         }
12797
12798 #if 1
12799         /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
12800         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12801                                    player->index_bit, dig_side);
12802         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12803                                             player->index_bit, dig_side);
12804 #else
12805         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12806                                             player->index_bit, dig_side);
12807         CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12808                                    player->index_bit, dig_side);
12809 #endif
12810       }
12811
12812       return MF_NO_ACTION;
12813   }
12814
12815 #if USE_NEW_PUSH_DELAY
12816   player->push_delay = -1;
12817 #else
12818   player->push_delay = 0;
12819 #endif
12820
12821 #if USE_PENGUIN_COLLECT_BUGFIX
12822   if (is_player)                /* function can also be called by EL_PENGUIN */
12823 #endif
12824   {
12825     if (Feld[x][y] != element)          /* really digged/collected something */
12826       player->is_collecting = !player->is_digging;
12827   }
12828
12829   return MF_MOVING;
12830 }
12831
12832 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12833 {
12834   int jx = player->jx, jy = player->jy;
12835   int x = jx + dx, y = jy + dy;
12836   int snap_direction = (dx == -1 ? MV_LEFT :
12837                         dx == +1 ? MV_RIGHT :
12838                         dy == -1 ? MV_UP :
12839                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
12840
12841 #if 0
12842   if (player->MovPos != 0)
12843     return FALSE;
12844 #else
12845   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12846     return FALSE;
12847 #endif
12848
12849   if (!player->active || !IN_LEV_FIELD(x, y))
12850     return FALSE;
12851
12852   if (dx && dy)
12853     return FALSE;
12854
12855   if (!dx && !dy)
12856   {
12857     if (player->MovPos == 0)
12858       player->is_pushing = FALSE;
12859
12860     player->is_snapping = FALSE;
12861
12862     if (player->MovPos == 0)
12863     {
12864       player->is_moving = FALSE;
12865       player->is_digging = FALSE;
12866       player->is_collecting = FALSE;
12867     }
12868
12869     return FALSE;
12870   }
12871
12872   if (player->is_snapping)
12873     return FALSE;
12874
12875   player->MovDir = snap_direction;
12876
12877 #if 1
12878   if (player->MovPos == 0)
12879 #endif
12880   {
12881     player->is_moving = FALSE;
12882     player->is_digging = FALSE;
12883     player->is_collecting = FALSE;
12884   }
12885
12886   player->is_dropping = FALSE;
12887
12888   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
12889     return FALSE;
12890
12891   player->is_snapping = TRUE;
12892
12893 #if 1
12894   if (player->MovPos == 0)
12895 #endif
12896   {
12897     player->is_moving = FALSE;
12898     player->is_digging = FALSE;
12899     player->is_collecting = FALSE;
12900   }
12901
12902 #if 1
12903   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12904     DrawLevelField(player->last_jx, player->last_jy);
12905 #endif
12906
12907   DrawLevelField(x, y);
12908
12909 #if 0
12910   BackToFront();
12911 #endif
12912
12913   return TRUE;
12914 }
12915
12916 boolean DropElement(struct PlayerInfo *player)
12917 {
12918   int old_element, new_element;
12919   int dropx = player->jx, dropy = player->jy;
12920   int drop_direction = player->MovDir;
12921 #if 1
12922   int drop_side = drop_direction;
12923 #else
12924   static int trigger_sides[4] =
12925   {
12926     CH_SIDE_LEFT,       /* dropping left  */
12927     CH_SIDE_RIGHT,      /* dropping right */
12928     CH_SIDE_TOP,        /* dropping up    */
12929     CH_SIDE_BOTTOM,     /* dropping down  */
12930   };
12931   int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
12932 #endif
12933   int drop_element = (player->inventory_size > 0 ?
12934                       player->inventory_element[player->inventory_size - 1] :
12935                       player->inventory_infinite_element != EL_UNDEFINED ?
12936                       player->inventory_infinite_element :
12937                       player->dynabombs_left > 0 ?
12938                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12939                       EL_UNDEFINED);
12940
12941 #if USE_DROP_BUGFIX
12942   /* do not drop an element on top of another element; when holding drop key
12943      pressed without moving, dropped element must move away before the next
12944      element can be dropped (this is especially important if the next element
12945      is dynamite, which can be placed on background for historical reasons) */
12946   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12947     return MF_ACTION;
12948 #endif
12949
12950   if (IS_THROWABLE(drop_element))
12951   {
12952     dropx += GET_DX_FROM_DIR(drop_direction);
12953     dropy += GET_DY_FROM_DIR(drop_direction);
12954
12955     if (!IN_LEV_FIELD(dropx, dropy))
12956       return FALSE;
12957   }
12958
12959   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12960   new_element = drop_element;           /* default: no change when dropping */
12961
12962   /* check if player is active, not moving and ready to drop */
12963   if (!player->active || player->MovPos || player->drop_delay > 0)
12964     return FALSE;
12965
12966   /* check if player has anything that can be dropped */
12967 #if 1
12968   if (new_element == EL_UNDEFINED)
12969     return FALSE;
12970 #else
12971   if (player->inventory_size == 0 &&
12972       player->inventory_infinite_element == EL_UNDEFINED &&
12973       player->dynabombs_left == 0)
12974     return FALSE;
12975 #endif
12976
12977   /* check if anything can be dropped at the current position */
12978   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12979     return FALSE;
12980
12981   /* collected custom elements can only be dropped on empty fields */
12982 #if 1
12983   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12984     return FALSE;
12985 #else
12986   if (player->inventory_size > 0 &&
12987       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
12988       && old_element != EL_EMPTY)
12989     return FALSE;
12990 #endif
12991
12992   if (old_element != EL_EMPTY)
12993     Back[dropx][dropy] = old_element;   /* store old element on this field */
12994
12995   ResetGfxAnimation(dropx, dropy);
12996   ResetRandomAnimationValue(dropx, dropy);
12997
12998   if (player->inventory_size > 0 ||
12999       player->inventory_infinite_element != EL_UNDEFINED)
13000   {
13001     if (player->inventory_size > 0)
13002     {
13003       player->inventory_size--;
13004
13005 #if 0
13006       new_element = player->inventory_element[player->inventory_size];
13007 #endif
13008
13009       DrawGameValue_Dynamite(local_player->inventory_size);
13010
13011       if (new_element == EL_DYNAMITE)
13012         new_element = EL_DYNAMITE_ACTIVE;
13013       else if (new_element == EL_SP_DISK_RED)
13014         new_element = EL_SP_DISK_RED_ACTIVE;
13015     }
13016
13017     Feld[dropx][dropy] = new_element;
13018
13019     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13020       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13021                           el2img(Feld[dropx][dropy]), 0);
13022
13023     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13024
13025 #if 1
13026     /* needed if previous element just changed to "empty" in the last frame */
13027     Changed[dropx][dropy] = FALSE;              /* allow another change */
13028 #endif
13029
13030 #if 1
13031     /* !!! TEST ONLY !!! */
13032     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13033                                player->index_bit, drop_side);
13034     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13035                                         CE_PLAYER_DROPS_X,
13036                                         player->index_bit, drop_side);
13037 #else
13038     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
13039                                         CE_PLAYER_DROPS_X,
13040                                         player->index_bit, drop_side);
13041     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
13042                                player->index_bit, drop_side);
13043 #endif
13044
13045     TestIfElementTouchesCustomElement(dropx, dropy);
13046   }
13047   else          /* player is dropping a dyna bomb */
13048   {
13049     player->dynabombs_left--;
13050
13051 #if 0
13052     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
13053 #endif
13054
13055     Feld[dropx][dropy] = new_element;
13056
13057     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
13058       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
13059                           el2img(Feld[dropx][dropy]), 0);
13060
13061     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
13062   }
13063
13064
13065
13066 #if 1
13067
13068   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
13069   {
13070 #if 1
13071     InitField_WithBug1(dropx, dropy, FALSE);
13072 #else
13073     InitField(dropx, dropy, FALSE);
13074     if (CAN_MOVE(Feld[dropx][dropy]))
13075       InitMovDir(dropx, dropy);
13076 #endif
13077   }
13078
13079   new_element = Feld[dropx][dropy];     /* element might have changed */
13080
13081   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
13082       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
13083   {
13084 #if 0
13085     int move_stepsize = element_info[new_element].move_stepsize;
13086 #endif
13087     int move_direction, nextx, nexty;
13088
13089     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
13090       MovDir[dropx][dropy] = drop_direction;
13091
13092     move_direction = MovDir[dropx][dropy];
13093     nextx = dropx + GET_DX_FROM_DIR(move_direction);
13094     nexty = dropy + GET_DY_FROM_DIR(move_direction);
13095
13096 #if 1
13097     Changed[dropx][dropy] = FALSE;              /* allow another change */
13098     CheckCollision[dropx][dropy] = 2;
13099 #else
13100
13101     if (IN_LEV_FIELD_AND_IS_FREE(nextx, nexty))
13102     {
13103 #if 0
13104       WasJustMoving[dropx][dropy] = 3;
13105 #else
13106 #if 1
13107       InitMovingField(dropx, dropy, move_direction);
13108       ContinueMoving(dropx, dropy);
13109 #endif
13110 #endif
13111     }
13112 #if 0
13113     /* !!! commented out from 3.1.0-4 to 3.1.0-5 !!! */
13114     else
13115     {
13116       Changed[dropx][dropy] = FALSE;    /* allow another change */
13117
13118 #if 1
13119       TestIfElementHitsCustomElement(dropx, dropy, move_direction);
13120 #else
13121       CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
13122                                CE_HITTING_SOMETHING, move_direction);
13123 #endif
13124     }
13125 #endif
13126
13127 #endif
13128
13129 #if 0
13130     player->drop_delay = 2 * TILEX / move_stepsize + 1;
13131 #endif
13132   }
13133
13134 #if 0
13135   player->drop_delay = 8 + 8 + 8;
13136 #endif
13137
13138 #if 1
13139   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
13140 #endif
13141
13142 #endif
13143
13144   player->is_dropping = TRUE;
13145
13146 #if USE_DROP_BUGFIX
13147   player->drop_x = dropx;
13148   player->drop_y = dropy;
13149 #endif
13150
13151   return TRUE;
13152 }
13153
13154 /* ------------------------------------------------------------------------- */
13155 /* game sound playing functions                                              */
13156 /* ------------------------------------------------------------------------- */
13157
13158 static int *loop_sound_frame = NULL;
13159 static int *loop_sound_volume = NULL;
13160
13161 void InitPlayLevelSound()
13162 {
13163   int num_sounds = getSoundListSize();
13164
13165   checked_free(loop_sound_frame);
13166   checked_free(loop_sound_volume);
13167
13168   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
13169   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
13170 }
13171
13172 static void PlayLevelSound(int x, int y, int nr)
13173 {
13174   int sx = SCREENX(x), sy = SCREENY(y);
13175   int volume, stereo_position;
13176   int max_distance = 8;
13177   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
13178
13179   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
13180       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
13181     return;
13182
13183   if (!IN_LEV_FIELD(x, y) ||
13184       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
13185       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
13186     return;
13187
13188   volume = SOUND_MAX_VOLUME;
13189
13190   if (!IN_SCR_FIELD(sx, sy))
13191   {
13192     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
13193     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
13194
13195     volume -= volume * (dx > dy ? dx : dy) / max_distance;
13196   }
13197
13198   stereo_position = (SOUND_MAX_LEFT +
13199                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
13200                      (SCR_FIELDX + 2 * max_distance));
13201
13202   if (IS_LOOP_SOUND(nr))
13203   {
13204     /* This assures that quieter loop sounds do not overwrite louder ones,
13205        while restarting sound volume comparison with each new game frame. */
13206
13207     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
13208       return;
13209
13210     loop_sound_volume[nr] = volume;
13211     loop_sound_frame[nr] = FrameCounter;
13212   }
13213
13214   PlaySoundExt(nr, volume, stereo_position, type);
13215 }
13216
13217 static void PlayLevelSoundNearest(int x, int y, int sound_action)
13218 {
13219   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
13220                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
13221                  y < LEVELY(BY1) ? LEVELY(BY1) :
13222                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
13223                  sound_action);
13224 }
13225
13226 static void PlayLevelSoundAction(int x, int y, int action)
13227 {
13228   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
13229 }
13230
13231 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
13232 {
13233   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13234
13235   if (sound_effect != SND_UNDEFINED)
13236     PlayLevelSound(x, y, sound_effect);
13237 }
13238
13239 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
13240                                               int action)
13241 {
13242   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
13243
13244   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13245     PlayLevelSound(x, y, sound_effect);
13246 }
13247
13248 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
13249 {
13250   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13251
13252   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13253     PlayLevelSound(x, y, sound_effect);
13254 }
13255
13256 static void StopLevelSoundActionIfLoop(int x, int y, int action)
13257 {
13258   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
13259
13260   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
13261     StopSound(sound_effect);
13262 }
13263
13264 static void PlayLevelMusic()
13265 {
13266   if (levelset.music[level_nr] != MUS_UNDEFINED)
13267     PlayMusic(levelset.music[level_nr]);        /* from config file */
13268   else
13269     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
13270 }
13271
13272 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
13273 {
13274   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
13275
13276 #if 0
13277   if (sample == SAMPLE_bug)
13278     printf("::: PlayLevelSound_EM: %d, %d: %d\n", x, y, sample);
13279 #endif
13280
13281   switch (sample)
13282   {
13283     case SAMPLE_blank:
13284       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
13285       break;
13286
13287     case SAMPLE_roll:
13288       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13289       break;
13290
13291     case SAMPLE_stone:
13292       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13293       break;
13294
13295     case SAMPLE_nut:
13296       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13297       break;
13298
13299     case SAMPLE_crack:
13300       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13301       break;
13302
13303     case SAMPLE_bug:
13304       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13305       break;
13306
13307     case SAMPLE_tank:
13308       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13309       break;
13310
13311     case SAMPLE_android_clone:
13312       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13313       break;
13314
13315     case SAMPLE_android_move:
13316       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13317       break;
13318
13319     case SAMPLE_spring:
13320       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13321       break;
13322
13323     case SAMPLE_slurp:
13324       PlayLevelSoundElementAction(x, y, element, ACTION_SLURPED_BY_SPRING);
13325       break;
13326
13327     case SAMPLE_eater:
13328       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
13329       break;
13330
13331     case SAMPLE_eater_eat:
13332       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13333       break;
13334
13335     case SAMPLE_alien:
13336       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
13337       break;
13338
13339     case SAMPLE_collect:
13340       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13341       break;
13342
13343     case SAMPLE_diamond:
13344       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13345       break;
13346
13347     case SAMPLE_squash:
13348       /* !!! CHECK THIS !!! */
13349 #if 1
13350       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
13351 #else
13352       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
13353 #endif
13354       break;
13355
13356     case SAMPLE_wonderfall:
13357       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
13358       break;
13359
13360     case SAMPLE_drip:
13361       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
13362       break;
13363
13364     case SAMPLE_push:
13365       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13366       break;
13367
13368     case SAMPLE_dirt:
13369       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13370       break;
13371
13372     case SAMPLE_acid:
13373       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
13374       break;
13375
13376     case SAMPLE_ball:
13377       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13378       break;
13379
13380     case SAMPLE_grow:
13381       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
13382       break;
13383
13384     case SAMPLE_wonder:
13385       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13386       break;
13387
13388     case SAMPLE_door:
13389       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13390       break;
13391
13392     case SAMPLE_exit_open:
13393       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
13394       break;
13395
13396     case SAMPLE_exit_leave:
13397       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
13398       break;
13399
13400     case SAMPLE_dynamite:
13401       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
13402       break;
13403
13404     case SAMPLE_tick:
13405       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13406       break;
13407
13408     case SAMPLE_press:
13409       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13410       break;
13411
13412     case SAMPLE_wheel:
13413       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
13414       break;
13415
13416     case SAMPLE_boom:
13417       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
13418       break;
13419
13420     case SAMPLE_die:
13421       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
13422       break;
13423
13424     case SAMPLE_time:
13425       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
13426       break;
13427
13428     default:
13429       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
13430       break;
13431   }
13432 }
13433
13434 void RaiseScore(int value)
13435 {
13436   local_player->score += value;
13437
13438   DrawGameValue_Score(local_player->score);
13439 }
13440
13441 void RaiseScoreElement(int element)
13442 {
13443   switch(element)
13444   {
13445     case EL_EMERALD:
13446     case EL_BD_DIAMOND:
13447     case EL_EMERALD_YELLOW:
13448     case EL_EMERALD_RED:
13449     case EL_EMERALD_PURPLE:
13450     case EL_SP_INFOTRON:
13451       RaiseScore(level.score[SC_EMERALD]);
13452       break;
13453     case EL_DIAMOND:
13454       RaiseScore(level.score[SC_DIAMOND]);
13455       break;
13456     case EL_CRYSTAL:
13457       RaiseScore(level.score[SC_CRYSTAL]);
13458       break;
13459     case EL_PEARL:
13460       RaiseScore(level.score[SC_PEARL]);
13461       break;
13462     case EL_BUG:
13463     case EL_BD_BUTTERFLY:
13464     case EL_SP_ELECTRON:
13465       RaiseScore(level.score[SC_BUG]);
13466       break;
13467     case EL_SPACESHIP:
13468     case EL_BD_FIREFLY:
13469     case EL_SP_SNIKSNAK:
13470       RaiseScore(level.score[SC_SPACESHIP]);
13471       break;
13472     case EL_YAMYAM:
13473     case EL_DARK_YAMYAM:
13474       RaiseScore(level.score[SC_YAMYAM]);
13475       break;
13476     case EL_ROBOT:
13477       RaiseScore(level.score[SC_ROBOT]);
13478       break;
13479     case EL_PACMAN:
13480       RaiseScore(level.score[SC_PACMAN]);
13481       break;
13482     case EL_NUT:
13483       RaiseScore(level.score[SC_NUT]);
13484       break;
13485     case EL_DYNAMITE:
13486     case EL_SP_DISK_RED:
13487     case EL_DYNABOMB_INCREASE_NUMBER:
13488     case EL_DYNABOMB_INCREASE_SIZE:
13489     case EL_DYNABOMB_INCREASE_POWER:
13490       RaiseScore(level.score[SC_DYNAMITE]);
13491       break;
13492     case EL_SHIELD_NORMAL:
13493     case EL_SHIELD_DEADLY:
13494       RaiseScore(level.score[SC_SHIELD]);
13495       break;
13496     case EL_EXTRA_TIME:
13497       RaiseScore(level.score[SC_TIME_BONUS]);
13498       break;
13499     case EL_KEY_1:
13500     case EL_KEY_2:
13501     case EL_KEY_3:
13502     case EL_KEY_4:
13503     case EL_EM_KEY_1:
13504     case EL_EM_KEY_2:
13505     case EL_EM_KEY_3:
13506     case EL_EM_KEY_4:
13507     case EL_EMC_KEY_5:
13508     case EL_EMC_KEY_6:
13509     case EL_EMC_KEY_7:
13510     case EL_EMC_KEY_8:
13511       RaiseScore(level.score[SC_KEY]);
13512       break;
13513     default:
13514       RaiseScore(element_info[element].collect_score);
13515       break;
13516   }
13517 }
13518
13519 void RequestQuitGame(boolean ask_if_really_quit)
13520 {
13521   if (AllPlayersGone ||
13522       !ask_if_really_quit ||
13523       level_editor_test_game ||
13524       Request("Do you really want to quit the game ?",
13525               REQ_ASK | REQ_STAY_CLOSED))
13526   {
13527 #if defined(NETWORK_AVALIABLE)
13528     if (options.network)
13529       SendToServer_StopPlaying();
13530     else
13531 #endif
13532     {
13533       game_status = GAME_MODE_MAIN;
13534       DrawMainMenu();
13535     }
13536   }
13537   else
13538   {
13539
13540 #if 1
13541     if (tape.playing && tape.deactivate_display)
13542       TapeDeactivateDisplayOff(TRUE);
13543 #endif
13544
13545     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
13546
13547 #if 1
13548     if (tape.playing && tape.deactivate_display)
13549       TapeDeactivateDisplayOn();
13550 #endif
13551
13552   }
13553 }
13554
13555
13556 /* ---------- new game button stuff ---------------------------------------- */
13557
13558 /* graphic position values for game buttons */
13559 #define GAME_BUTTON_XSIZE       30
13560 #define GAME_BUTTON_YSIZE       30
13561 #define GAME_BUTTON_XPOS        5
13562 #define GAME_BUTTON_YPOS        215
13563 #define SOUND_BUTTON_XPOS       5
13564 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
13565
13566 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13567 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13568 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13569 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
13570 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
13571 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
13572
13573 static struct
13574 {
13575   int x, y;
13576   int gadget_id;
13577   char *infotext;
13578 } gamebutton_info[NUM_GAME_BUTTONS] =
13579 {
13580   {
13581     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
13582     GAME_CTRL_ID_STOP,
13583     "stop game"
13584   },
13585   {
13586     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
13587     GAME_CTRL_ID_PAUSE,
13588     "pause game"
13589   },
13590   {
13591     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
13592     GAME_CTRL_ID_PLAY,
13593     "play game"
13594   },
13595   {
13596     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
13597     SOUND_CTRL_ID_MUSIC,
13598     "background music on/off"
13599   },
13600   {
13601     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
13602     SOUND_CTRL_ID_LOOPS,
13603     "sound loops on/off"
13604   },
13605   {
13606     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
13607     SOUND_CTRL_ID_SIMPLE,
13608     "normal sounds on/off"
13609   }
13610 };
13611
13612 void CreateGameButtons()
13613 {
13614   int i;
13615
13616   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13617   {
13618     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
13619     struct GadgetInfo *gi;
13620     int button_type;
13621     boolean checked;
13622     unsigned long event_mask;
13623     int gd_xoffset, gd_yoffset;
13624     int gd_x1, gd_x2, gd_y1, gd_y2;
13625     int id = i;
13626
13627     gd_xoffset = gamebutton_info[i].x;
13628     gd_yoffset = gamebutton_info[i].y;
13629     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
13630     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
13631
13632     if (id == GAME_CTRL_ID_STOP ||
13633         id == GAME_CTRL_ID_PAUSE ||
13634         id == GAME_CTRL_ID_PLAY)
13635     {
13636       button_type = GD_TYPE_NORMAL_BUTTON;
13637       checked = FALSE;
13638       event_mask = GD_EVENT_RELEASED;
13639       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13640       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13641     }
13642     else
13643     {
13644       button_type = GD_TYPE_CHECK_BUTTON;
13645       checked =
13646         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
13647          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
13648          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
13649       event_mask = GD_EVENT_PRESSED;
13650       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
13651       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
13652     }
13653
13654     gi = CreateGadget(GDI_CUSTOM_ID, id,
13655                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
13656                       GDI_X, DX + gd_xoffset,
13657                       GDI_Y, DY + gd_yoffset,
13658                       GDI_WIDTH, GAME_BUTTON_XSIZE,
13659                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
13660                       GDI_TYPE, button_type,
13661                       GDI_STATE, GD_BUTTON_UNPRESSED,
13662                       GDI_CHECKED, checked,
13663                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
13664                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
13665                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
13666                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
13667                       GDI_EVENT_MASK, event_mask,
13668                       GDI_CALLBACK_ACTION, HandleGameButtons,
13669                       GDI_END);
13670
13671     if (gi == NULL)
13672       Error(ERR_EXIT, "cannot create gadget");
13673
13674     game_gadget[id] = gi;
13675   }
13676 }
13677
13678 void FreeGameButtons()
13679 {
13680   int i;
13681
13682   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13683     FreeGadget(game_gadget[i]);
13684 }
13685
13686 static void MapGameButtons()
13687 {
13688   int i;
13689
13690   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13691     MapGadget(game_gadget[i]);
13692 }
13693
13694 void UnmapGameButtons()
13695 {
13696   int i;
13697
13698   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13699     UnmapGadget(game_gadget[i]);
13700 }
13701
13702 static void HandleGameButtons(struct GadgetInfo *gi)
13703 {
13704   int id = gi->custom_id;
13705
13706   if (game_status != GAME_MODE_PLAYING)
13707     return;
13708
13709   switch (id)
13710   {
13711     case GAME_CTRL_ID_STOP:
13712       RequestQuitGame(TRUE);
13713       break;
13714
13715     case GAME_CTRL_ID_PAUSE:
13716       if (options.network)
13717       {
13718 #if defined(NETWORK_AVALIABLE)
13719         if (tape.pausing)
13720           SendToServer_ContinuePlaying();
13721         else
13722           SendToServer_PausePlaying();
13723 #endif
13724       }
13725       else
13726         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13727       break;
13728
13729     case GAME_CTRL_ID_PLAY:
13730       if (tape.pausing)
13731       {
13732 #if defined(NETWORK_AVALIABLE)
13733         if (options.network)
13734           SendToServer_ContinuePlaying();
13735         else
13736 #endif
13737         {
13738           tape.pausing = FALSE;
13739           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
13740         }
13741       }
13742       break;
13743
13744     case SOUND_CTRL_ID_MUSIC:
13745       if (setup.sound_music)
13746       { 
13747         setup.sound_music = FALSE;
13748         FadeMusic();
13749       }
13750       else if (audio.music_available)
13751       { 
13752         setup.sound = setup.sound_music = TRUE;
13753
13754         SetAudioMode(setup.sound);
13755
13756         PlayLevelMusic();
13757       }
13758       break;
13759
13760     case SOUND_CTRL_ID_LOOPS:
13761       if (setup.sound_loops)
13762         setup.sound_loops = FALSE;
13763       else if (audio.loops_available)
13764       {
13765         setup.sound = setup.sound_loops = TRUE;
13766         SetAudioMode(setup.sound);
13767       }
13768       break;
13769
13770     case SOUND_CTRL_ID_SIMPLE:
13771       if (setup.sound_simple)
13772         setup.sound_simple = FALSE;
13773       else if (audio.sound_available)
13774       {
13775         setup.sound = setup.sound_simple = TRUE;
13776         SetAudioMode(setup.sound);
13777       }
13778       break;
13779
13780     default:
13781       break;
13782   }
13783 }