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