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