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