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