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