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