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