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