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