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