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