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