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