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