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