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