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