rnd-20060319-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 static void CheckLevelTime()
9055 {
9056   int i;
9057
9058   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9059   {
9060     if (level.native_em_level->lev->home == 0)  /* all players at home */
9061     {
9062       local_player->LevelSolved = TRUE;
9063       AllPlayersGone = TRUE;
9064
9065       level.native_em_level->lev->home = -1;
9066     }
9067
9068     if (level.native_em_level->ply[0]->alive == 0 &&
9069         level.native_em_level->ply[1]->alive == 0 &&
9070         level.native_em_level->ply[2]->alive == 0 &&
9071         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9072       AllPlayersGone = TRUE;
9073   }
9074
9075   if (TimeFrames >= FRAMES_PER_SECOND)
9076   {
9077     TimeFrames = 0;
9078     TapeTime++;
9079
9080     for (i = 0; i < MAX_PLAYERS; i++)
9081     {
9082       struct PlayerInfo *player = &stored_player[i];
9083
9084       if (SHIELD_ON(player))
9085       {
9086         player->shield_normal_time_left--;
9087
9088         if (player->shield_deadly_time_left > 0)
9089           player->shield_deadly_time_left--;
9090       }
9091     }
9092
9093     if (!level.use_step_counter)
9094     {
9095       TimePlayed++;
9096
9097       if (TimeLeft > 0)
9098       {
9099         TimeLeft--;
9100
9101         if (TimeLeft <= 10 && setup.time_limit)
9102           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9103
9104         DrawGameValue_Time(TimeLeft);
9105
9106         if (!TimeLeft && setup.time_limit)
9107         {
9108           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9109             level.native_em_level->lev->killed_out_of_time = TRUE;
9110           else
9111             for (i = 0; i < MAX_PLAYERS; i++)
9112               KillPlayer(&stored_player[i]);
9113         }
9114       }
9115       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9116         DrawGameValue_Time(TimePlayed);
9117
9118       level.native_em_level->lev->time =
9119         (level.time == 0 ? TimePlayed : TimeLeft);
9120     }
9121
9122     if (tape.recording || tape.playing)
9123       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9124   }
9125 }
9126
9127 void AdvanceFrameAndPlayerCounters(int player_nr)
9128 {
9129   int i;
9130
9131   /* advance frame counters (global frame counter and time frame counter) */
9132   FrameCounter++;
9133   TimeFrames++;
9134
9135   /* advance player counters (counters for move delay, move animation etc.) */
9136   for (i = 0; i < MAX_PLAYERS; i++)
9137   {
9138     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9139     int move_delay_value = stored_player[i].move_delay_value;
9140     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9141
9142     if (!advance_player_counters)       /* not all players may be affected */
9143       continue;
9144
9145 #if USE_NEW_PLAYER_ANIM
9146     if (move_frames == 0)       /* less than one move per game frame */
9147     {
9148       int stepsize = TILEX / move_delay_value;
9149       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9150       int count = (stored_player[i].is_moving ?
9151                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9152
9153       if (count % delay == 0)
9154         move_frames = 1;
9155     }
9156 #endif
9157
9158     stored_player[i].Frame += move_frames;
9159
9160     if (stored_player[i].MovPos != 0)
9161       stored_player[i].StepFrame += move_frames;
9162
9163     if (stored_player[i].move_delay > 0)
9164       stored_player[i].move_delay--;
9165
9166     /* due to bugs in previous versions, counter must count up, not down */
9167     if (stored_player[i].push_delay != -1)
9168       stored_player[i].push_delay++;
9169
9170     if (stored_player[i].drop_delay > 0)
9171       stored_player[i].drop_delay--;
9172
9173     if (stored_player[i].is_dropping_pressed)
9174       stored_player[i].drop_pressed_delay++;
9175   }
9176 }
9177
9178 void StartGameActions(boolean init_network_game, boolean record_tape,
9179                       long random_seed)
9180 {
9181 #if 1
9182   unsigned long new_random_seed = InitRND(random_seed);
9183
9184   if (record_tape)
9185     TapeStartRecording(new_random_seed);
9186 #else
9187   if (record_tape)
9188     TapeStartRecording(random_seed);
9189 #endif
9190
9191 #if defined(NETWORK_AVALIABLE)
9192   if (init_network_game)
9193   {
9194     SendToServer_StartPlaying();
9195
9196     return;
9197   }
9198 #endif
9199
9200   StopAnimation();
9201
9202   game_status = GAME_MODE_PLAYING;
9203
9204 #if 0
9205   InitRND(random_seed);
9206 #endif
9207
9208   InitGame();
9209 }
9210
9211 void GameActions()
9212 {
9213   static unsigned long game_frame_delay = 0;
9214   unsigned long game_frame_delay_value;
9215   byte *recorded_player_action;
9216   byte summarized_player_action = 0;
9217   byte tape_action[MAX_PLAYERS];
9218   int i;
9219
9220   if (game.restart_level)
9221     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9222
9223   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9224   {
9225     if (level.native_em_level->lev->home == 0)  /* all players at home */
9226     {
9227       local_player->LevelSolved = TRUE;
9228       AllPlayersGone = TRUE;
9229
9230       level.native_em_level->lev->home = -1;
9231     }
9232
9233     if (level.native_em_level->ply[0]->alive == 0 &&
9234         level.native_em_level->ply[1]->alive == 0 &&
9235         level.native_em_level->ply[2]->alive == 0 &&
9236         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9237       AllPlayersGone = TRUE;
9238   }
9239
9240   if (local_player->LevelSolved)
9241     GameWon();
9242
9243   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9244     TapeStop();
9245
9246   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9247     return;
9248
9249   game_frame_delay_value =
9250     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9251
9252   if (tape.playing && tape.warp_forward && !tape.pausing)
9253     game_frame_delay_value = 0;
9254
9255   /* ---------- main game synchronization point ---------- */
9256
9257   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9258
9259   if (network_playing && !network_player_action_received)
9260   {
9261     /* try to get network player actions in time */
9262
9263 #if defined(NETWORK_AVALIABLE)
9264     /* last chance to get network player actions without main loop delay */
9265     HandleNetworking();
9266 #endif
9267
9268     /* game was quit by network peer */
9269     if (game_status != GAME_MODE_PLAYING)
9270       return;
9271
9272     if (!network_player_action_received)
9273       return;           /* failed to get network player actions in time */
9274   }
9275
9276   if (tape.pausing)
9277     return;
9278
9279   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9280
9281   if (tape.set_centered_player)
9282   {
9283     game.centered_player_nr_next = tape.centered_player_nr_next;
9284     game.set_centered_player = TRUE;
9285   }
9286
9287   for (i = 0; i < MAX_PLAYERS; i++)
9288   {
9289     summarized_player_action |= stored_player[i].action;
9290
9291     if (!network_playing)
9292       stored_player[i].effective_action = stored_player[i].action;
9293   }
9294
9295 #if defined(NETWORK_AVALIABLE)
9296   if (network_playing)
9297     SendToServer_MovePlayer(summarized_player_action);
9298 #endif
9299
9300   if (!options.network && !setup.team_mode)
9301     local_player->effective_action = summarized_player_action;
9302
9303   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9304   {
9305     for (i = 0; i < MAX_PLAYERS; i++)
9306       stored_player[i].effective_action =
9307         (i == game.centered_player_nr ? summarized_player_action : 0);
9308   }
9309
9310   if (recorded_player_action != NULL)
9311     for (i = 0; i < MAX_PLAYERS; i++)
9312       stored_player[i].effective_action = recorded_player_action[i];
9313
9314   for (i = 0; i < MAX_PLAYERS; i++)
9315   {
9316     tape_action[i] = stored_player[i].effective_action;
9317
9318     /* (this can only happen in the R'n'D game engine) */
9319     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9320       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9321   }
9322
9323   /* only record actions from input devices, but not programmed actions */
9324   if (tape.recording)
9325     TapeRecordAction(tape_action);
9326
9327   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9328   {
9329     GameActions_EM_Main();
9330   }
9331   else
9332   {
9333     GameActions_RND();
9334   }
9335 }
9336
9337 void GameActions_EM_Main()
9338 {
9339   byte effective_action[MAX_PLAYERS];
9340   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9341   int i;
9342
9343   for (i = 0; i < MAX_PLAYERS; i++)
9344     effective_action[i] = stored_player[i].effective_action;
9345
9346   GameActions_EM(effective_action, warp_mode);
9347
9348   CheckLevelTime();
9349
9350   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9351 }
9352
9353 void GameActions_RND()
9354 {
9355   int magic_wall_x = 0, magic_wall_y = 0;
9356   int i, x, y, element, graphic;
9357
9358   InitPlayfieldScanModeVars();
9359
9360 #if USE_ONE_MORE_CHANGE_PER_FRAME
9361   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9362   {
9363     SCAN_PLAYFIELD(x, y)
9364     {
9365       ChangeCount[x][y] = 0;
9366       ChangeEvent[x][y] = -1;
9367     }
9368   }
9369 #endif
9370
9371 #if 1
9372   if (game.set_centered_player)
9373   {
9374     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9375
9376     /* switching to "all players" only possible if all players fit to screen */
9377     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9378     {
9379       game.centered_player_nr_next = game.centered_player_nr;
9380       game.set_centered_player = FALSE;
9381     }
9382
9383     /* do not switch focus to non-existing (or non-active) player */
9384     if (game.centered_player_nr_next >= 0 &&
9385         !stored_player[game.centered_player_nr_next].active)
9386     {
9387       game.centered_player_nr_next = game.centered_player_nr;
9388       game.set_centered_player = FALSE;
9389     }
9390   }
9391
9392   if (game.set_centered_player &&
9393       ScreenMovPos == 0)        /* screen currently aligned at tile position */
9394   {
9395     int sx, sy;
9396
9397     if (game.centered_player_nr_next == -1)
9398     {
9399       setScreenCenteredToAllPlayers(&sx, &sy);
9400     }
9401     else
9402     {
9403       sx = stored_player[game.centered_player_nr_next].jx;
9404       sy = stored_player[game.centered_player_nr_next].jy;
9405     }
9406
9407     DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9408
9409     game.centered_player_nr = game.centered_player_nr_next;
9410     game.set_centered_player = FALSE;
9411   }
9412 #endif
9413
9414   for (i = 0; i < MAX_PLAYERS; i++)
9415   {
9416     int actual_player_action = stored_player[i].effective_action;
9417
9418 #if 1
9419     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9420        - rnd_equinox_tetrachloride 048
9421        - rnd_equinox_tetrachloride_ii 096
9422        - rnd_emanuel_schmieg 002
9423        - doctor_sloan_ww 001, 020
9424     */
9425     if (stored_player[i].MovPos == 0)
9426       CheckGravityMovement(&stored_player[i]);
9427 #endif
9428
9429     /* overwrite programmed action with tape action */
9430     if (stored_player[i].programmed_action)
9431       actual_player_action = stored_player[i].programmed_action;
9432
9433 #if 1
9434     PlayerActions(&stored_player[i], actual_player_action);
9435 #else
9436     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9437
9438     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9439       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9440 #endif
9441
9442     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9443   }
9444
9445   network_player_action_received = FALSE;
9446
9447   ScrollScreen(NULL, SCROLL_GO_ON);
9448
9449   /* for backwards compatibility, the following code emulates a fixed bug that
9450      occured when pushing elements (causing elements that just made their last
9451      pushing step to already (if possible) make their first falling step in the
9452      same game frame, which is bad); this code is also needed to use the famous
9453      "spring push bug" which is used in older levels and might be wanted to be
9454      used also in newer levels, but in this case the buggy pushing code is only
9455      affecting the "spring" element and no other elements */
9456
9457   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9458   {
9459     for (i = 0; i < MAX_PLAYERS; i++)
9460     {
9461       struct PlayerInfo *player = &stored_player[i];
9462       int x = player->jx;
9463       int y = player->jy;
9464
9465       if (player->active && player->is_pushing && player->is_moving &&
9466           IS_MOVING(x, y) &&
9467           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9468            Feld[x][y] == EL_SPRING))
9469       {
9470         ContinueMoving(x, y);
9471
9472         /* continue moving after pushing (this is actually a bug) */
9473         if (!IS_MOVING(x, y))
9474         {
9475           Stop[x][y] = FALSE;
9476         }
9477       }
9478     }
9479   }
9480
9481 #if 1
9482   SCAN_PLAYFIELD(x, y)
9483 #else
9484   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9485 #endif
9486   {
9487     ChangeCount[x][y] = 0;
9488     ChangeEvent[x][y] = -1;
9489
9490     /* this must be handled before main playfield loop */
9491     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9492     {
9493       MovDelay[x][y]--;
9494       if (MovDelay[x][y] <= 0)
9495         RemoveField(x, y);
9496     }
9497
9498 #if USE_NEW_SNAP_DELAY
9499     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9500     {
9501       MovDelay[x][y]--;
9502       if (MovDelay[x][y] <= 0)
9503       {
9504         RemoveField(x, y);
9505         DrawLevelField(x, y);
9506
9507         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
9508       }
9509     }
9510 #endif
9511
9512 #if DEBUG
9513     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9514     {
9515       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9516       printf("GameActions(): This should never happen!\n");
9517
9518       ChangePage[x][y] = -1;
9519     }
9520 #endif
9521
9522     Stop[x][y] = FALSE;
9523     if (WasJustMoving[x][y] > 0)
9524       WasJustMoving[x][y]--;
9525     if (WasJustFalling[x][y] > 0)
9526       WasJustFalling[x][y]--;
9527     if (CheckCollision[x][y] > 0)
9528       CheckCollision[x][y]--;
9529
9530     GfxFrame[x][y]++;
9531
9532     /* reset finished pushing action (not done in ContinueMoving() to allow
9533        continuous pushing animation for elements with zero push delay) */
9534     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9535     {
9536       ResetGfxAnimation(x, y);
9537       DrawLevelField(x, y);
9538     }
9539
9540 #if DEBUG
9541     if (IS_BLOCKED(x, y))
9542     {
9543       int oldx, oldy;
9544
9545       Blocked2Moving(x, y, &oldx, &oldy);
9546       if (!IS_MOVING(oldx, oldy))
9547       {
9548         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9549         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9550         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9551         printf("GameActions(): This should never happen!\n");
9552       }
9553     }
9554 #endif
9555   }
9556
9557 #if 1
9558   SCAN_PLAYFIELD(x, y)
9559 #else
9560   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9561 #endif
9562   {
9563     element = Feld[x][y];
9564     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9565
9566 #if 0
9567     printf("::: %d,%d\n", x, y);
9568
9569     if (element == EL_ROCK)
9570       printf("::: Yo man! Rocks can fall!\n");
9571 #endif
9572
9573     if (graphic_info[graphic].anim_global_sync)
9574       GfxFrame[x][y] = FrameCounter;
9575     else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9576     {
9577       int old_gfx_frame = GfxFrame[x][y];
9578
9579       GfxFrame[x][y] = CustomValue[x][y];
9580
9581 #if 1
9582       if (GfxFrame[x][y] != old_gfx_frame)
9583 #endif
9584         DrawLevelGraphicAnimation(x, y, graphic);
9585     }
9586     else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9587     {
9588       int old_gfx_frame = GfxFrame[x][y];
9589
9590       GfxFrame[x][y] = element_info[element].collect_score;
9591
9592 #if 1
9593       if (GfxFrame[x][y] != old_gfx_frame)
9594 #endif
9595         DrawLevelGraphicAnimation(x, y, graphic);
9596     }
9597
9598     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9599         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9600       ResetRandomAnimationValue(x, y);
9601
9602     SetRandomAnimationValue(x, y);
9603
9604     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9605
9606     if (IS_INACTIVE(element))
9607     {
9608       if (IS_ANIMATED(graphic))
9609         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9610
9611       continue;
9612     }
9613
9614     /* this may take place after moving, so 'element' may have changed */
9615     if (IS_CHANGING(x, y) &&
9616         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9617     {
9618       int page = element_info[element].event_page_nr[CE_DELAY];
9619 #if 0
9620       HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9621 #else
9622
9623 #if 0
9624       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9625 #endif
9626
9627 #if 0
9628       if (element == EL_CUSTOM_255)
9629         printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9630 #endif
9631
9632 #if 1
9633       HandleElementChange(x, y, page);
9634 #else
9635       if (CAN_CHANGE(element))
9636         HandleElementChange(x, y, page);
9637
9638       if (HAS_ACTION(element))
9639         ExecuteCustomElementAction(x, y, element, page);
9640 #endif
9641
9642 #endif
9643
9644       element = Feld[x][y];
9645       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9646     }
9647
9648     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9649     {
9650       StartMoving(x, y);
9651
9652       element = Feld[x][y];
9653       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9654
9655       if (IS_ANIMATED(graphic) &&
9656           !IS_MOVING(x, y) &&
9657           !Stop[x][y])
9658         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9659
9660       if (IS_GEM(element) || element == EL_SP_INFOTRON)
9661         EdelsteinFunkeln(x, y);
9662     }
9663     else if ((element == EL_ACID ||
9664               element == EL_EXIT_OPEN ||
9665               element == EL_SP_EXIT_OPEN ||
9666               element == EL_SP_TERMINAL ||
9667               element == EL_SP_TERMINAL_ACTIVE ||
9668               element == EL_EXTRA_TIME ||
9669               element == EL_SHIELD_NORMAL ||
9670               element == EL_SHIELD_DEADLY) &&
9671              IS_ANIMATED(graphic))
9672       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9673     else if (IS_MOVING(x, y))
9674       ContinueMoving(x, y);
9675     else if (IS_ACTIVE_BOMB(element))
9676       CheckDynamite(x, y);
9677     else if (element == EL_AMOEBA_GROWING)
9678       AmoebeWaechst(x, y);
9679     else if (element == EL_AMOEBA_SHRINKING)
9680       AmoebaDisappearing(x, y);
9681
9682 #if !USE_NEW_AMOEBA_CODE
9683     else if (IS_AMOEBALIVE(element))
9684       AmoebeAbleger(x, y);
9685 #endif
9686
9687     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9688       Life(x, y);
9689     else if (element == EL_EXIT_CLOSED)
9690       CheckExit(x, y);
9691     else if (element == EL_SP_EXIT_CLOSED)
9692       CheckExitSP(x, y);
9693     else if (element == EL_EXPANDABLE_WALL_GROWING)
9694       MauerWaechst(x, y);
9695     else if (element == EL_EXPANDABLE_WALL ||
9696              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9697              element == EL_EXPANDABLE_WALL_VERTICAL ||
9698              element == EL_EXPANDABLE_WALL_ANY)
9699       MauerAbleger(x, y);
9700     else if (element == EL_FLAMES)
9701       CheckForDragon(x, y);
9702     else if (element == EL_EXPLOSION)
9703       ; /* drawing of correct explosion animation is handled separately */
9704     else if (element == EL_ELEMENT_SNAPPING ||
9705              element == EL_DIAGONAL_SHRINKING ||
9706              element == EL_DIAGONAL_GROWING)
9707     {
9708 #if 1
9709       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9710
9711       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9712 #endif
9713     }
9714     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9715       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9716
9717 #if 0
9718     if (element == EL_CUSTOM_255 ||
9719         element == EL_CUSTOM_256)
9720       DrawLevelGraphicAnimation(x, y, graphic);
9721 #endif
9722
9723     if (IS_BELT_ACTIVE(element))
9724       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9725
9726     if (game.magic_wall_active)
9727     {
9728       int jx = local_player->jx, jy = local_player->jy;
9729
9730       /* play the element sound at the position nearest to the player */
9731       if ((element == EL_MAGIC_WALL_FULL ||
9732            element == EL_MAGIC_WALL_ACTIVE ||
9733            element == EL_MAGIC_WALL_EMPTYING ||
9734            element == EL_BD_MAGIC_WALL_FULL ||
9735            element == EL_BD_MAGIC_WALL_ACTIVE ||
9736            element == EL_BD_MAGIC_WALL_EMPTYING) &&
9737           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9738       {
9739         magic_wall_x = x;
9740         magic_wall_y = y;
9741       }
9742     }
9743   }
9744
9745 #if USE_NEW_AMOEBA_CODE
9746   /* new experimental amoeba growth stuff */
9747   if (!(FrameCounter % 8))
9748   {
9749     static unsigned long random = 1684108901;
9750
9751     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9752     {
9753       x = RND(lev_fieldx);
9754       y = RND(lev_fieldy);
9755       element = Feld[x][y];
9756
9757       if (!IS_PLAYER(x,y) &&
9758           (element == EL_EMPTY ||
9759            CAN_GROW_INTO(element) ||
9760            element == EL_QUICKSAND_EMPTY ||
9761            element == EL_ACID_SPLASH_LEFT ||
9762            element == EL_ACID_SPLASH_RIGHT))
9763       {
9764         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9765             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9766             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9767             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9768           Feld[x][y] = EL_AMOEBA_DROP;
9769       }
9770
9771       random = random * 129 + 1;
9772     }
9773   }
9774 #endif
9775
9776 #if 0
9777   if (game.explosions_delayed)
9778 #endif
9779   {
9780     game.explosions_delayed = FALSE;
9781
9782 #if 1
9783     SCAN_PLAYFIELD(x, y)
9784 #else
9785     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9786 #endif
9787     {
9788       element = Feld[x][y];
9789
9790       if (ExplodeField[x][y])
9791         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9792       else if (element == EL_EXPLOSION)
9793         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9794
9795       ExplodeField[x][y] = EX_TYPE_NONE;
9796     }
9797
9798     game.explosions_delayed = TRUE;
9799   }
9800
9801   if (game.magic_wall_active)
9802   {
9803     if (!(game.magic_wall_time_left % 4))
9804     {
9805       int element = Feld[magic_wall_x][magic_wall_y];
9806
9807       if (element == EL_BD_MAGIC_WALL_FULL ||
9808           element == EL_BD_MAGIC_WALL_ACTIVE ||
9809           element == EL_BD_MAGIC_WALL_EMPTYING)
9810         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9811       else
9812         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9813     }
9814
9815     if (game.magic_wall_time_left > 0)
9816     {
9817       game.magic_wall_time_left--;
9818       if (!game.magic_wall_time_left)
9819       {
9820 #if 1
9821         SCAN_PLAYFIELD(x, y)
9822 #else
9823         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9824 #endif
9825         {
9826           element = Feld[x][y];
9827
9828           if (element == EL_MAGIC_WALL_ACTIVE ||
9829               element == EL_MAGIC_WALL_FULL)
9830           {
9831             Feld[x][y] = EL_MAGIC_WALL_DEAD;
9832             DrawLevelField(x, y);
9833           }
9834           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9835                    element == EL_BD_MAGIC_WALL_FULL)
9836           {
9837             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9838             DrawLevelField(x, y);
9839           }
9840         }
9841
9842         game.magic_wall_active = FALSE;
9843       }
9844     }
9845   }
9846
9847   if (game.light_time_left > 0)
9848   {
9849     game.light_time_left--;
9850
9851     if (game.light_time_left == 0)
9852       RedrawAllLightSwitchesAndInvisibleElements();
9853   }
9854
9855   if (game.timegate_time_left > 0)
9856   {
9857     game.timegate_time_left--;
9858
9859     if (game.timegate_time_left == 0)
9860       CloseAllOpenTimegates();
9861   }
9862
9863   if (game.lenses_time_left > 0)
9864   {
9865     game.lenses_time_left--;
9866
9867     if (game.lenses_time_left == 0)
9868       RedrawAllInvisibleElementsForLenses();
9869   }
9870
9871   if (game.magnify_time_left > 0)
9872   {
9873     game.magnify_time_left--;
9874
9875     if (game.magnify_time_left == 0)
9876       RedrawAllInvisibleElementsForMagnifier();
9877   }
9878
9879   for (i = 0; i < MAX_PLAYERS; i++)
9880   {
9881     struct PlayerInfo *player = &stored_player[i];
9882
9883     if (SHIELD_ON(player))
9884     {
9885       if (player->shield_deadly_time_left)
9886         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
9887       else if (player->shield_normal_time_left)
9888         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
9889     }
9890   }
9891
9892   CheckLevelTime();
9893
9894   DrawAllPlayers();
9895   PlayAllPlayersSound();
9896
9897   if (options.debug)                    /* calculate frames per second */
9898   {
9899     static unsigned long fps_counter = 0;
9900     static int fps_frames = 0;
9901     unsigned long fps_delay_ms = Counter() - fps_counter;
9902
9903     fps_frames++;
9904
9905     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
9906     {
9907       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
9908
9909       fps_frames = 0;
9910       fps_counter = Counter();
9911     }
9912
9913     redraw_mask |= REDRAW_FPS;
9914   }
9915
9916   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9917
9918   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
9919   {
9920     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
9921
9922     local_player->show_envelope = 0;
9923   }
9924
9925   /* use random number generator in every frame to make it less predictable */
9926   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
9927     RND(1);
9928 }
9929
9930 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
9931 {
9932   int min_x = x, min_y = y, max_x = x, max_y = y;
9933   int i;
9934
9935   for (i = 0; i < MAX_PLAYERS; i++)
9936   {
9937     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9938
9939     if (!stored_player[i].active || &stored_player[i] == player)
9940       continue;
9941
9942     min_x = MIN(min_x, jx);
9943     min_y = MIN(min_y, jy);
9944     max_x = MAX(max_x, jx);
9945     max_y = MAX(max_y, jy);
9946   }
9947
9948   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
9949 }
9950
9951 static boolean AllPlayersInVisibleScreen()
9952 {
9953   int i;
9954
9955   for (i = 0; i < MAX_PLAYERS; i++)
9956   {
9957     int jx = stored_player[i].jx, jy = stored_player[i].jy;
9958
9959     if (!stored_player[i].active)
9960       continue;
9961
9962     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
9963       return FALSE;
9964   }
9965
9966   return TRUE;
9967 }
9968
9969 void ScrollLevel(int dx, int dy)
9970 {
9971   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
9972   int x, y;
9973
9974   BlitBitmap(drawto_field, drawto_field,
9975              FX + TILEX * (dx == -1) - softscroll_offset,
9976              FY + TILEY * (dy == -1) - softscroll_offset,
9977              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
9978              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
9979              FX + TILEX * (dx == 1) - softscroll_offset,
9980              FY + TILEY * (dy == 1) - softscroll_offset);
9981
9982   if (dx)
9983   {
9984     x = (dx == 1 ? BX1 : BX2);
9985     for (y = BY1; y <= BY2; y++)
9986       DrawScreenField(x, y);
9987   }
9988
9989   if (dy)
9990   {
9991     y = (dy == 1 ? BY1 : BY2);
9992     for (x = BX1; x <= BX2; x++)
9993       DrawScreenField(x, y);
9994   }
9995
9996   redraw_mask |= REDRAW_FIELD;
9997 }
9998
9999 static boolean canFallDown(struct PlayerInfo *player)
10000 {
10001   int jx = player->jx, jy = player->jy;
10002
10003   return (IN_LEV_FIELD(jx, jy + 1) &&
10004           (IS_FREE(jx, jy + 1) ||
10005            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10006           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10007           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10008 }
10009
10010 static boolean canPassField(int x, int y, int move_dir)
10011 {
10012   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10013   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10014   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10015   int nextx = x + dx;
10016   int nexty = y + dy;
10017   int element = Feld[x][y];
10018
10019   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10020           !CAN_MOVE(element) &&
10021           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10022           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10023           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10024 }
10025
10026 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10027 {
10028   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10029   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10030   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10031   int newx = x + dx;
10032   int newy = y + dy;
10033
10034   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10035           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10036           (IS_DIGGABLE(Feld[newx][newy]) ||
10037            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10038            canPassField(newx, newy, move_dir)));
10039 }
10040
10041 static void CheckGravityMovement(struct PlayerInfo *player)
10042 {
10043   if (game.gravity && !player->programmed_action)
10044   {
10045     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10046     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10047     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10048     int jx = player->jx, jy = player->jy;
10049     boolean player_is_moving_to_valid_field =
10050       (!player_is_snapping &&
10051        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10052         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10053     boolean player_can_fall_down = canFallDown(player);
10054
10055     if (player_can_fall_down &&
10056         !player_is_moving_to_valid_field)
10057       player->programmed_action = MV_DOWN;
10058   }
10059 }
10060
10061 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10062 {
10063   return CheckGravityMovement(player);
10064
10065   if (game.gravity && !player->programmed_action)
10066   {
10067     int jx = player->jx, jy = player->jy;
10068     boolean field_under_player_is_free =
10069       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10070     boolean player_is_standing_on_valid_field =
10071       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10072        (IS_WALKABLE(Feld[jx][jy]) &&
10073         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10074
10075     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10076       player->programmed_action = MV_DOWN;
10077   }
10078 }
10079
10080 /*
10081   MovePlayerOneStep()
10082   -----------------------------------------------------------------------------
10083   dx, dy:               direction (non-diagonal) to try to move the player to
10084   real_dx, real_dy:     direction as read from input device (can be diagonal)
10085 */
10086
10087 boolean MovePlayerOneStep(struct PlayerInfo *player,
10088                           int dx, int dy, int real_dx, int real_dy)
10089 {
10090   int jx = player->jx, jy = player->jy;
10091   int new_jx = jx + dx, new_jy = jy + dy;
10092 #if !USE_FIXED_DONT_RUN_INTO
10093   int element;
10094 #endif
10095   int can_move;
10096   boolean player_can_move = !player->cannot_move;
10097
10098   if (!player->active || (!dx && !dy))
10099     return MP_NO_ACTION;
10100
10101   player->MovDir = (dx < 0 ? MV_LEFT :
10102                     dx > 0 ? MV_RIGHT :
10103                     dy < 0 ? MV_UP :
10104                     dy > 0 ? MV_DOWN :  MV_NONE);
10105
10106   if (!IN_LEV_FIELD(new_jx, new_jy))
10107     return MP_NO_ACTION;
10108
10109   if (!player_can_move)
10110   {
10111 #if 1
10112     if (player->MovPos == 0)
10113     {
10114       player->is_moving = FALSE;
10115       player->is_digging = FALSE;
10116       player->is_collecting = FALSE;
10117       player->is_snapping = FALSE;
10118       player->is_pushing = FALSE;
10119     }
10120 #else
10121     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10122     SnapField(player, 0, 0);
10123 #endif
10124
10125 #if 0
10126     return MP_NO_ACTION;
10127 #endif
10128   }
10129
10130 #if 1
10131   if (!options.network && game.centered_player_nr == -1 &&
10132       !AllPlayersInSight(player, new_jx, new_jy))
10133     return MP_NO_ACTION;
10134 #else
10135   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10136     return MP_NO_ACTION;
10137 #endif
10138
10139 #if !USE_FIXED_DONT_RUN_INTO
10140   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10141
10142   /* (moved to DigField()) */
10143   if (player_can_move && DONT_RUN_INTO(element))
10144   {
10145     if (element == EL_ACID && dx == 0 && dy == 1)
10146     {
10147       SplashAcid(new_jx, new_jy);
10148       Feld[jx][jy] = EL_PLAYER_1;
10149       InitMovingField(jx, jy, MV_DOWN);
10150       Store[jx][jy] = EL_ACID;
10151       ContinueMoving(jx, jy);
10152       BuryPlayer(player);
10153     }
10154     else
10155       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10156
10157     return MP_MOVING;
10158   }
10159 #endif
10160
10161   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10162 #if 0
10163 #if USE_FIXED_DONT_RUN_INTO
10164   if (can_move == MP_DONT_RUN_INTO)
10165     return MP_MOVING;
10166 #endif
10167 #endif
10168   if (can_move != MP_MOVING)
10169     return can_move;
10170
10171 #if USE_FIXED_DONT_RUN_INTO
10172 #endif
10173
10174   /* check if DigField() has caused relocation of the player */
10175   if (player->jx != jx || player->jy != jy)
10176     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10177
10178   StorePlayer[jx][jy] = 0;
10179   player->last_jx = jx;
10180   player->last_jy = jy;
10181   player->jx = new_jx;
10182   player->jy = new_jy;
10183   StorePlayer[new_jx][new_jy] = player->element_nr;
10184
10185   if (player->move_delay_value_next != -1)
10186   {
10187     player->move_delay_value = player->move_delay_value_next;
10188     player->move_delay_value_next = -1;
10189   }
10190
10191   player->MovPos =
10192     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10193
10194   player->step_counter++;
10195
10196   PlayerVisit[jx][jy] = FrameCounter;
10197
10198   ScrollPlayer(player, SCROLL_INIT);
10199
10200   return MP_MOVING;
10201 }
10202
10203 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10204 {
10205   int jx = player->jx, jy = player->jy;
10206   int old_jx = jx, old_jy = jy;
10207   int moved = MP_NO_ACTION;
10208
10209   if (!player->active)
10210     return FALSE;
10211
10212   if (!dx && !dy)
10213   {
10214     if (player->MovPos == 0)
10215     {
10216       player->is_moving = FALSE;
10217       player->is_digging = FALSE;
10218       player->is_collecting = FALSE;
10219       player->is_snapping = FALSE;
10220       player->is_pushing = FALSE;
10221     }
10222
10223     return FALSE;
10224   }
10225
10226   if (player->move_delay > 0)
10227     return FALSE;
10228
10229   player->move_delay = -1;              /* set to "uninitialized" value */
10230
10231   /* store if player is automatically moved to next field */
10232   player->is_auto_moving = (player->programmed_action != MV_NONE);
10233
10234   /* remove the last programmed player action */
10235   player->programmed_action = 0;
10236
10237   if (player->MovPos)
10238   {
10239     /* should only happen if pre-1.2 tape recordings are played */
10240     /* this is only for backward compatibility */
10241
10242     int original_move_delay_value = player->move_delay_value;
10243
10244 #if DEBUG
10245     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10246            tape.counter);
10247 #endif
10248
10249     /* scroll remaining steps with finest movement resolution */
10250     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10251
10252     while (player->MovPos)
10253     {
10254       ScrollPlayer(player, SCROLL_GO_ON);
10255       ScrollScreen(NULL, SCROLL_GO_ON);
10256
10257       AdvanceFrameAndPlayerCounters(player->index_nr);
10258
10259       DrawAllPlayers();
10260       BackToFront();
10261     }
10262
10263     player->move_delay_value = original_move_delay_value;
10264   }
10265
10266   if (player->last_move_dir & MV_HORIZONTAL)
10267   {
10268     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10269       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10270   }
10271   else
10272   {
10273     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10274       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10275   }
10276
10277   jx = player->jx;
10278   jy = player->jy;
10279
10280 #if 1
10281   if (moved & MP_MOVING && !ScreenMovPos &&
10282       (player->index_nr == game.centered_player_nr ||
10283        game.centered_player_nr == -1))
10284 #else
10285   if (moved & MP_MOVING && !ScreenMovPos &&
10286       (player == local_player || !options.network))
10287 #endif
10288   {
10289     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10290     int offset = (setup.scroll_delay ? 3 : 0);
10291
10292     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10293     {
10294       /* actual player has left the screen -- scroll in that direction */
10295       if (jx != old_jx)         /* player has moved horizontally */
10296         scroll_x += (jx - old_jx);
10297       else                      /* player has moved vertically */
10298         scroll_y += (jy - old_jy);
10299     }
10300     else
10301     {
10302       if (jx != old_jx)         /* player has moved horizontally */
10303       {
10304         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10305             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10306           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10307
10308         /* don't scroll over playfield boundaries */
10309         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10310           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10311
10312         /* don't scroll more than one field at a time */
10313         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10314
10315         /* don't scroll against the player's moving direction */
10316         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10317             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10318           scroll_x = old_scroll_x;
10319       }
10320       else                      /* player has moved vertically */
10321       {
10322         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10323             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10324           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10325
10326         /* don't scroll over playfield boundaries */
10327         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10328           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10329
10330         /* don't scroll more than one field at a time */
10331         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10332
10333         /* don't scroll against the player's moving direction */
10334         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10335             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10336           scroll_y = old_scroll_y;
10337       }
10338     }
10339
10340     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10341     {
10342 #if 1
10343       if (!options.network && game.centered_player_nr == -1 &&
10344           !AllPlayersInVisibleScreen())
10345       {
10346         scroll_x = old_scroll_x;
10347         scroll_y = old_scroll_y;
10348       }
10349       else
10350 #else
10351       if (!options.network && !AllPlayersInVisibleScreen())
10352       {
10353         scroll_x = old_scroll_x;
10354         scroll_y = old_scroll_y;
10355       }
10356       else
10357 #endif
10358       {
10359         ScrollScreen(player, SCROLL_INIT);
10360         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10361       }
10362     }
10363   }
10364
10365   player->StepFrame = 0;
10366
10367   if (moved & MP_MOVING)
10368   {
10369     if (old_jx != jx && old_jy == jy)
10370       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10371     else if (old_jx == jx && old_jy != jy)
10372       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10373
10374     DrawLevelField(jx, jy);     /* for "crumbled sand" */
10375
10376     player->last_move_dir = player->MovDir;
10377     player->is_moving = TRUE;
10378     player->is_snapping = FALSE;
10379     player->is_switching = FALSE;
10380     player->is_dropping = FALSE;
10381     player->is_dropping_pressed = FALSE;
10382     player->drop_pressed_delay = 0;
10383   }
10384   else
10385   {
10386     CheckGravityMovementWhenNotMoving(player);
10387
10388     player->is_moving = FALSE;
10389
10390     /* at this point, the player is allowed to move, but cannot move right now
10391        (e.g. because of something blocking the way) -- ensure that the player
10392        is also allowed to move in the next frame (in old versions before 3.1.1,
10393        the player was forced to wait again for eight frames before next try) */
10394
10395     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10396       player->move_delay = 0;   /* allow direct movement in the next frame */
10397   }
10398
10399   if (player->move_delay == -1)         /* not yet initialized by DigField() */
10400     player->move_delay = player->move_delay_value;
10401
10402   if (game.engine_version < VERSION_IDENT(3,0,7,0))
10403   {
10404     TestIfPlayerTouchesBadThing(jx, jy);
10405     TestIfPlayerTouchesCustomElement(jx, jy);
10406   }
10407
10408   if (!player->active)
10409     RemovePlayer(player);
10410
10411   return moved;
10412 }
10413
10414 void ScrollPlayer(struct PlayerInfo *player, int mode)
10415 {
10416   int jx = player->jx, jy = player->jy;
10417   int last_jx = player->last_jx, last_jy = player->last_jy;
10418   int move_stepsize = TILEX / player->move_delay_value;
10419
10420 #if USE_NEW_PLAYER_SPEED
10421   if (!player->active)
10422     return;
10423
10424   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
10425     return;
10426 #else
10427   if (!player->active || player->MovPos == 0)
10428     return;
10429 #endif
10430
10431   if (mode == SCROLL_INIT)
10432   {
10433     player->actual_frame_counter = FrameCounter;
10434     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10435
10436     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10437         Feld[last_jx][last_jy] == EL_EMPTY)
10438     {
10439       int last_field_block_delay = 0;   /* start with no blocking at all */
10440       int block_delay_adjustment = player->block_delay_adjustment;
10441
10442       /* if player blocks last field, add delay for exactly one move */
10443       if (player->block_last_field)
10444       {
10445         last_field_block_delay += player->move_delay_value;
10446
10447         /* when blocking enabled, prevent moving up despite gravity */
10448         if (game.gravity && player->MovDir == MV_UP)
10449           block_delay_adjustment = -1;
10450       }
10451
10452       /* add block delay adjustment (also possible when not blocking) */
10453       last_field_block_delay += block_delay_adjustment;
10454
10455       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10456       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10457     }
10458
10459 #if USE_NEW_PLAYER_SPEED
10460     if (player->MovPos != 0)    /* player has not yet reached destination */
10461       return;
10462 #else
10463     return;
10464 #endif
10465   }
10466   else if (!FrameReached(&player->actual_frame_counter, 1))
10467     return;
10468
10469 #if 0
10470   printf("::: player->MovPos: %d -> %d\n",
10471          player->MovPos,
10472          player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10473 #endif
10474
10475 #if USE_NEW_PLAYER_SPEED
10476   if (player->MovPos != 0)
10477   {
10478     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10479     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10480
10481     /* before DrawPlayer() to draw correct player graphic for this case */
10482     if (player->MovPos == 0)
10483       CheckGravityMovement(player);
10484   }
10485 #else
10486   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10487   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10488
10489   /* before DrawPlayer() to draw correct player graphic for this case */
10490   if (player->MovPos == 0)
10491     CheckGravityMovement(player);
10492 #endif
10493
10494   if (player->MovPos == 0)      /* player reached destination field */
10495   {
10496 #if 0
10497     printf("::: player reached destination field\n");
10498 #endif
10499
10500     if (player->move_delay_reset_counter > 0)
10501     {
10502       player->move_delay_reset_counter--;
10503
10504       if (player->move_delay_reset_counter == 0)
10505       {
10506         /* continue with normal speed after quickly moving through gate */
10507         HALVE_PLAYER_SPEED(player);
10508
10509         /* be able to make the next move without delay */
10510         player->move_delay = 0;
10511       }
10512     }
10513
10514     player->last_jx = jx;
10515     player->last_jy = jy;
10516
10517     if (Feld[jx][jy] == EL_EXIT_OPEN ||
10518         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10519         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
10520     {
10521       DrawPlayer(player);       /* needed here only to cleanup last field */
10522       RemovePlayer(player);
10523
10524       if (local_player->friends_still_needed == 0 ||
10525           IS_SP_ELEMENT(Feld[jx][jy]))
10526         player->LevelSolved = player->GameOver = TRUE;
10527     }
10528
10529     /* this breaks one level: "machine", level 000 */
10530     {
10531       int move_direction = player->MovDir;
10532       int enter_side = MV_DIR_OPPOSITE(move_direction);
10533       int leave_side = move_direction;
10534       int old_jx = last_jx;
10535       int old_jy = last_jy;
10536       int old_element = Feld[old_jx][old_jy];
10537       int new_element = Feld[jx][jy];
10538
10539       if (IS_CUSTOM_ELEMENT(old_element))
10540         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10541                                    CE_LEFT_BY_PLAYER,
10542                                    player->index_bit, leave_side);
10543
10544       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10545                                           CE_PLAYER_LEAVES_X,
10546                                           player->index_bit, leave_side);
10547
10548       if (IS_CUSTOM_ELEMENT(new_element))
10549         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10550                                    player->index_bit, enter_side);
10551
10552       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10553                                           CE_PLAYER_ENTERS_X,
10554                                           player->index_bit, enter_side);
10555
10556       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10557                                         CE_MOVE_OF_X, move_direction);
10558     }
10559
10560     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10561     {
10562       TestIfPlayerTouchesBadThing(jx, jy);
10563       TestIfPlayerTouchesCustomElement(jx, jy);
10564
10565       /* needed because pushed element has not yet reached its destination,
10566          so it would trigger a change event at its previous field location */
10567       if (!player->is_pushing)
10568         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
10569
10570       if (!player->active)
10571         RemovePlayer(player);
10572     }
10573
10574     if (level.use_step_counter)
10575     {
10576       int i;
10577
10578       TimePlayed++;
10579
10580       if (TimeLeft > 0)
10581       {
10582         TimeLeft--;
10583
10584         if (TimeLeft <= 10 && setup.time_limit)
10585           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10586
10587         DrawGameValue_Time(TimeLeft);
10588
10589         if (!TimeLeft && setup.time_limit)
10590           for (i = 0; i < MAX_PLAYERS; i++)
10591             KillPlayer(&stored_player[i]);
10592       }
10593       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10594         DrawGameValue_Time(TimePlayed);
10595     }
10596
10597     if (tape.single_step && tape.recording && !tape.pausing &&
10598         !player->programmed_action)
10599       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10600   }
10601 }
10602
10603 void ScrollScreen(struct PlayerInfo *player, int mode)
10604 {
10605   static unsigned long screen_frame_counter = 0;
10606
10607   if (mode == SCROLL_INIT)
10608   {
10609     /* set scrolling step size according to actual player's moving speed */
10610     ScrollStepSize = TILEX / player->move_delay_value;
10611
10612     screen_frame_counter = FrameCounter;
10613     ScreenMovDir = player->MovDir;
10614     ScreenMovPos = player->MovPos;
10615     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10616     return;
10617   }
10618   else if (!FrameReached(&screen_frame_counter, 1))
10619     return;
10620
10621   if (ScreenMovPos)
10622   {
10623     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10624     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10625     redraw_mask |= REDRAW_FIELD;
10626   }
10627   else
10628     ScreenMovDir = MV_NONE;
10629 }
10630
10631 void TestIfPlayerTouchesCustomElement(int x, int y)
10632 {
10633   static int xy[4][2] =
10634   {
10635     { 0, -1 },
10636     { -1, 0 },
10637     { +1, 0 },
10638     { 0, +1 }
10639   };
10640   static int trigger_sides[4][2] =
10641   {
10642     /* center side       border side */
10643     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10644     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10645     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10646     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10647   };
10648   static int touch_dir[4] =
10649   {
10650     MV_LEFT | MV_RIGHT,
10651     MV_UP   | MV_DOWN,
10652     MV_UP   | MV_DOWN,
10653     MV_LEFT | MV_RIGHT
10654   };
10655   int center_element = Feld[x][y];      /* should always be non-moving! */
10656   int i;
10657
10658   for (i = 0; i < NUM_DIRECTIONS; i++)
10659   {
10660     int xx = x + xy[i][0];
10661     int yy = y + xy[i][1];
10662     int center_side = trigger_sides[i][0];
10663     int border_side = trigger_sides[i][1];
10664     int border_element;
10665
10666     if (!IN_LEV_FIELD(xx, yy))
10667       continue;
10668
10669     if (IS_PLAYER(x, y))
10670     {
10671       struct PlayerInfo *player = PLAYERINFO(x, y);
10672
10673       if (game.engine_version < VERSION_IDENT(3,0,7,0))
10674         border_element = Feld[xx][yy];          /* may be moving! */
10675       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10676         border_element = Feld[xx][yy];
10677       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
10678         border_element = MovingOrBlocked2Element(xx, yy);
10679       else
10680         continue;               /* center and border element do not touch */
10681
10682       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10683                                  player->index_bit, border_side);
10684       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10685                                           CE_PLAYER_TOUCHES_X,
10686                                           player->index_bit, border_side);
10687     }
10688     else if (IS_PLAYER(xx, yy))
10689     {
10690       struct PlayerInfo *player = PLAYERINFO(xx, yy);
10691
10692       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10693       {
10694         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10695           continue;             /* center and border element do not touch */
10696       }
10697
10698       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10699                                  player->index_bit, center_side);
10700       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10701                                           CE_PLAYER_TOUCHES_X,
10702                                           player->index_bit, center_side);
10703       break;
10704     }
10705   }
10706 }
10707
10708 #if USE_ELEMENT_TOUCHING_BUGFIX
10709
10710 void TestIfElementTouchesCustomElement(int x, int y)
10711 {
10712   static int xy[4][2] =
10713   {
10714     { 0, -1 },
10715     { -1, 0 },
10716     { +1, 0 },
10717     { 0, +1 }
10718   };
10719   static int trigger_sides[4][2] =
10720   {
10721     /* center side      border side */
10722     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10723     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10724     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10725     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10726   };
10727   static int touch_dir[4] =
10728   {
10729     MV_LEFT | MV_RIGHT,
10730     MV_UP   | MV_DOWN,
10731     MV_UP   | MV_DOWN,
10732     MV_LEFT | MV_RIGHT
10733   };
10734   boolean change_center_element = FALSE;
10735   int center_element = Feld[x][y];      /* should always be non-moving! */
10736   int border_element_old[NUM_DIRECTIONS];
10737   int i;
10738
10739   for (i = 0; i < NUM_DIRECTIONS; i++)
10740   {
10741     int xx = x + xy[i][0];
10742     int yy = y + xy[i][1];
10743     int border_element;
10744
10745     border_element_old[i] = -1;
10746
10747     if (!IN_LEV_FIELD(xx, yy))
10748       continue;
10749
10750     if (game.engine_version < VERSION_IDENT(3,0,7,0))
10751       border_element = Feld[xx][yy];    /* may be moving! */
10752     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10753       border_element = Feld[xx][yy];
10754     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
10755       border_element = MovingOrBlocked2Element(xx, yy);
10756     else
10757       continue;                 /* center and border element do not touch */
10758
10759     border_element_old[i] = border_element;
10760   }
10761
10762   for (i = 0; i < NUM_DIRECTIONS; i++)
10763   {
10764     int xx = x + xy[i][0];
10765     int yy = y + xy[i][1];
10766     int center_side = trigger_sides[i][0];
10767     int border_element = border_element_old[i];
10768
10769     if (border_element == -1)
10770       continue;
10771
10772     /* check for change of border element */
10773     CheckElementChangeBySide(xx, yy, border_element, center_element,
10774                              CE_TOUCHING_X, center_side);
10775   }
10776
10777   for (i = 0; i < NUM_DIRECTIONS; i++)
10778   {
10779     int border_side = trigger_sides[i][1];
10780     int border_element = border_element_old[i];
10781
10782     if (border_element == -1)
10783       continue;
10784
10785     /* check for change of center element (but change it only once) */
10786     if (!change_center_element)
10787       change_center_element =
10788         CheckElementChangeBySide(x, y, center_element, border_element,
10789                                  CE_TOUCHING_X, border_side);
10790   }
10791 }
10792
10793 #else
10794
10795 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10796 {
10797   static int xy[4][2] =
10798   {
10799     { 0, -1 },
10800     { -1, 0 },
10801     { +1, 0 },
10802     { 0, +1 }
10803   };
10804   static int trigger_sides[4][2] =
10805   {
10806     /* center side      border side */
10807     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10808     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10809     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10810     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10811   };
10812   static int touch_dir[4] =
10813   {
10814     MV_LEFT | MV_RIGHT,
10815     MV_UP   | MV_DOWN,
10816     MV_UP   | MV_DOWN,
10817     MV_LEFT | MV_RIGHT
10818   };
10819   boolean change_center_element = FALSE;
10820   int center_element = Feld[x][y];      /* should always be non-moving! */
10821   int i;
10822
10823   for (i = 0; i < NUM_DIRECTIONS; i++)
10824   {
10825     int xx = x + xy[i][0];
10826     int yy = y + xy[i][1];
10827     int center_side = trigger_sides[i][0];
10828     int border_side = trigger_sides[i][1];
10829     int border_element;
10830
10831     if (!IN_LEV_FIELD(xx, yy))
10832       continue;
10833
10834     if (game.engine_version < VERSION_IDENT(3,0,7,0))
10835       border_element = Feld[xx][yy];    /* may be moving! */
10836     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10837       border_element = Feld[xx][yy];
10838     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
10839       border_element = MovingOrBlocked2Element(xx, yy);
10840     else
10841       continue;                 /* center and border element do not touch */
10842
10843     /* check for change of center element (but change it only once) */
10844     if (!change_center_element)
10845       change_center_element =
10846         CheckElementChangeBySide(x, y, center_element, border_element,
10847                                  CE_TOUCHING_X, border_side);
10848
10849     /* check for change of border element */
10850     CheckElementChangeBySide(xx, yy, border_element, center_element,
10851                              CE_TOUCHING_X, center_side);
10852   }
10853 }
10854
10855 #endif
10856
10857 void TestIfElementHitsCustomElement(int x, int y, int direction)
10858 {
10859   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10860   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10861   int hitx = x + dx, hity = y + dy;
10862   int hitting_element = Feld[x][y];
10863   int touched_element;
10864
10865   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10866     return;
10867
10868   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10869                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10870
10871   if (IN_LEV_FIELD(hitx, hity))
10872   {
10873     int opposite_direction = MV_DIR_OPPOSITE(direction);
10874     int hitting_side = direction;
10875     int touched_side = opposite_direction;
10876     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10877                           MovDir[hitx][hity] != direction ||
10878                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10879
10880     object_hit = TRUE;
10881
10882     if (object_hit)
10883     {
10884       CheckElementChangeBySide(x, y, hitting_element, touched_element,
10885                                CE_HITTING_X, touched_side);
10886
10887       CheckElementChangeBySide(hitx, hity, touched_element,
10888                                hitting_element, CE_HIT_BY_X, hitting_side);
10889
10890       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10891                                CE_HIT_BY_SOMETHING, opposite_direction);
10892     }
10893   }
10894
10895   /* "hitting something" is also true when hitting the playfield border */
10896   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10897                            CE_HITTING_SOMETHING, direction);
10898 }
10899
10900 #if 0
10901 void TestIfElementSmashesCustomElement(int x, int y, int direction)
10902 {
10903   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
10904   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
10905   int hitx = x + dx, hity = y + dy;
10906   int hitting_element = Feld[x][y];
10907   int touched_element;
10908 #if 0
10909   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
10910                         !IS_FREE(hitx, hity) &&
10911                         (!IS_MOVING(hitx, hity) ||
10912                          MovDir[hitx][hity] != direction ||
10913                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
10914 #endif
10915
10916   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
10917     return;
10918
10919 #if 0
10920   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
10921     return;
10922 #endif
10923
10924   touched_element = (IN_LEV_FIELD(hitx, hity) ?
10925                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
10926
10927   CheckElementChangeBySide(x, y, hitting_element, touched_element,
10928                            EP_CAN_SMASH_EVERYTHING, direction);
10929
10930   if (IN_LEV_FIELD(hitx, hity))
10931   {
10932     int opposite_direction = MV_DIR_OPPOSITE(direction);
10933     int hitting_side = direction;
10934     int touched_side = opposite_direction;
10935 #if 0
10936     int touched_element = MovingOrBlocked2Element(hitx, hity);
10937 #endif
10938 #if 1
10939     boolean object_hit = (!IS_MOVING(hitx, hity) ||
10940                           MovDir[hitx][hity] != direction ||
10941                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
10942
10943     object_hit = TRUE;
10944 #endif
10945
10946     if (object_hit)
10947     {
10948       int i;
10949
10950       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10951                                CE_SMASHED_BY_SOMETHING, opposite_direction);
10952
10953       CheckElementChangeBySide(x, y, hitting_element, touched_element,
10954                                CE_OTHER_IS_SMASHING, touched_side);
10955
10956       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
10957                                CE_OTHER_GETS_SMASHED, hitting_side);
10958     }
10959   }
10960 }
10961 #endif
10962
10963 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
10964 {
10965   int i, kill_x = -1, kill_y = -1;
10966
10967   int bad_element = -1;
10968   static int test_xy[4][2] =
10969   {
10970     { 0, -1 },
10971     { -1, 0 },
10972     { +1, 0 },
10973     { 0, +1 }
10974   };
10975   static int test_dir[4] =
10976   {
10977     MV_UP,
10978     MV_LEFT,
10979     MV_RIGHT,
10980     MV_DOWN
10981   };
10982
10983   for (i = 0; i < NUM_DIRECTIONS; i++)
10984   {
10985     int test_x, test_y, test_move_dir, test_element;
10986
10987     test_x = good_x + test_xy[i][0];
10988     test_y = good_y + test_xy[i][1];
10989
10990     if (!IN_LEV_FIELD(test_x, test_y))
10991       continue;
10992
10993     test_move_dir =
10994       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
10995
10996     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
10997
10998     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
10999        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11000     */
11001     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11002         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11003     {
11004       kill_x = test_x;
11005       kill_y = test_y;
11006       bad_element = test_element;
11007
11008       break;
11009     }
11010   }
11011
11012   if (kill_x != -1 || kill_y != -1)
11013   {
11014     if (IS_PLAYER(good_x, good_y))
11015     {
11016       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11017
11018       if (player->shield_deadly_time_left > 0 &&
11019           !IS_INDESTRUCTIBLE(bad_element))
11020         Bang(kill_x, kill_y);
11021       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11022         KillPlayer(player);
11023     }
11024     else
11025       Bang(good_x, good_y);
11026   }
11027 }
11028
11029 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11030 {
11031   int i, kill_x = -1, kill_y = -1;
11032   int bad_element = Feld[bad_x][bad_y];
11033   static int test_xy[4][2] =
11034   {
11035     { 0, -1 },
11036     { -1, 0 },
11037     { +1, 0 },
11038     { 0, +1 }
11039   };
11040   static int touch_dir[4] =
11041   {
11042     MV_LEFT | MV_RIGHT,
11043     MV_UP   | MV_DOWN,
11044     MV_UP   | MV_DOWN,
11045     MV_LEFT | MV_RIGHT
11046   };
11047   static int test_dir[4] =
11048   {
11049     MV_UP,
11050     MV_LEFT,
11051     MV_RIGHT,
11052     MV_DOWN
11053   };
11054
11055   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11056     return;
11057
11058   for (i = 0; i < NUM_DIRECTIONS; i++)
11059   {
11060     int test_x, test_y, test_move_dir, test_element;
11061
11062     test_x = bad_x + test_xy[i][0];
11063     test_y = bad_y + test_xy[i][1];
11064     if (!IN_LEV_FIELD(test_x, test_y))
11065       continue;
11066
11067     test_move_dir =
11068       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11069
11070     test_element = Feld[test_x][test_y];
11071
11072     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11073        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11074     */
11075     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11076         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11077     {
11078       /* good thing is player or penguin that does not move away */
11079       if (IS_PLAYER(test_x, test_y))
11080       {
11081         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11082
11083         if (bad_element == EL_ROBOT && player->is_moving)
11084           continue;     /* robot does not kill player if he is moving */
11085
11086         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11087         {
11088           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11089             continue;           /* center and border element do not touch */
11090         }
11091
11092         kill_x = test_x;
11093         kill_y = test_y;
11094         break;
11095       }
11096       else if (test_element == EL_PENGUIN)
11097       {
11098         kill_x = test_x;
11099         kill_y = test_y;
11100         break;
11101       }
11102     }
11103   }
11104
11105   if (kill_x != -1 || kill_y != -1)
11106   {
11107     if (IS_PLAYER(kill_x, kill_y))
11108     {
11109       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11110
11111       if (player->shield_deadly_time_left > 0 &&
11112           !IS_INDESTRUCTIBLE(bad_element))
11113         Bang(bad_x, bad_y);
11114       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11115         KillPlayer(player);
11116     }
11117     else
11118       Bang(kill_x, kill_y);
11119   }
11120 }
11121
11122 void TestIfPlayerTouchesBadThing(int x, int y)
11123 {
11124   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11125 }
11126
11127 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11128 {
11129   TestIfGoodThingHitsBadThing(x, y, move_dir);
11130 }
11131
11132 void TestIfBadThingTouchesPlayer(int x, int y)
11133 {
11134   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11135 }
11136
11137 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11138 {
11139   TestIfBadThingHitsGoodThing(x, y, move_dir);
11140 }
11141
11142 void TestIfFriendTouchesBadThing(int x, int y)
11143 {
11144   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11145 }
11146
11147 void TestIfBadThingTouchesFriend(int x, int y)
11148 {
11149   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11150 }
11151
11152 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11153 {
11154   int i, kill_x = bad_x, kill_y = bad_y;
11155   static int xy[4][2] =
11156   {
11157     { 0, -1 },
11158     { -1, 0 },
11159     { +1, 0 },
11160     { 0, +1 }
11161   };
11162
11163   for (i = 0; i < NUM_DIRECTIONS; i++)
11164   {
11165     int x, y, element;
11166
11167     x = bad_x + xy[i][0];
11168     y = bad_y + xy[i][1];
11169     if (!IN_LEV_FIELD(x, y))
11170       continue;
11171
11172     element = Feld[x][y];
11173     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11174         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11175     {
11176       kill_x = x;
11177       kill_y = y;
11178       break;
11179     }
11180   }
11181
11182   if (kill_x != bad_x || kill_y != bad_y)
11183     Bang(bad_x, bad_y);
11184 }
11185
11186 void KillPlayer(struct PlayerInfo *player)
11187 {
11188   int jx = player->jx, jy = player->jy;
11189
11190   if (!player->active)
11191     return;
11192
11193   /* remove accessible field at the player's position */
11194   Feld[jx][jy] = EL_EMPTY;
11195
11196   /* deactivate shield (else Bang()/Explode() would not work right) */
11197   player->shield_normal_time_left = 0;
11198   player->shield_deadly_time_left = 0;
11199
11200   Bang(jx, jy);
11201   BuryPlayer(player);
11202 }
11203
11204 static void KillPlayerUnlessEnemyProtected(int x, int y)
11205 {
11206   if (!PLAYER_ENEMY_PROTECTED(x, y))
11207     KillPlayer(PLAYERINFO(x, y));
11208 }
11209
11210 static void KillPlayerUnlessExplosionProtected(int x, int y)
11211 {
11212   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11213     KillPlayer(PLAYERINFO(x, y));
11214 }
11215
11216 void BuryPlayer(struct PlayerInfo *player)
11217 {
11218   int jx = player->jx, jy = player->jy;
11219
11220   if (!player->active)
11221     return;
11222
11223   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11224   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11225
11226   player->GameOver = TRUE;
11227   RemovePlayer(player);
11228 }
11229
11230 void RemovePlayer(struct PlayerInfo *player)
11231 {
11232   int jx = player->jx, jy = player->jy;
11233   int i, found = FALSE;
11234
11235   player->present = FALSE;
11236   player->active = FALSE;
11237
11238   if (!ExplodeField[jx][jy])
11239     StorePlayer[jx][jy] = 0;
11240
11241   if (player->is_moving)
11242     DrawLevelField(player->last_jx, player->last_jy);
11243
11244   for (i = 0; i < MAX_PLAYERS; i++)
11245     if (stored_player[i].active)
11246       found = TRUE;
11247
11248   if (!found)
11249     AllPlayersGone = TRUE;
11250
11251   ExitX = ZX = jx;
11252   ExitY = ZY = jy;
11253 }
11254
11255 #if USE_NEW_SNAP_DELAY
11256 static void setFieldForSnapping(int x, int y, int element, int direction)
11257 {
11258   struct ElementInfo *ei = &element_info[element];
11259   int direction_bit = MV_DIR_TO_BIT(direction);
11260   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11261   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11262                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11263
11264   Feld[x][y] = EL_ELEMENT_SNAPPING;
11265   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11266
11267   ResetGfxAnimation(x, y);
11268
11269   GfxElement[x][y] = element;
11270   GfxAction[x][y] = action;
11271   GfxDir[x][y] = direction;
11272   GfxFrame[x][y] = -1;
11273 }
11274 #endif
11275
11276 /*
11277   =============================================================================
11278   checkDiagonalPushing()
11279   -----------------------------------------------------------------------------
11280   check if diagonal input device direction results in pushing of object
11281   (by checking if the alternative direction is walkable, diggable, ...)
11282   =============================================================================
11283 */
11284
11285 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11286                                     int x, int y, int real_dx, int real_dy)
11287 {
11288   int jx, jy, dx, dy, xx, yy;
11289
11290   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11291     return TRUE;
11292
11293   /* diagonal direction: check alternative direction */
11294   jx = player->jx;
11295   jy = player->jy;
11296   dx = x - jx;
11297   dy = y - jy;
11298   xx = jx + (dx == 0 ? real_dx : 0);
11299   yy = jy + (dy == 0 ? real_dy : 0);
11300
11301   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11302 }
11303
11304 /*
11305   =============================================================================
11306   DigField()
11307   -----------------------------------------------------------------------------
11308   x, y:                 field next to player (non-diagonal) to try to dig to
11309   real_dx, real_dy:     direction as read from input device (can be diagonal)
11310   =============================================================================
11311 */
11312
11313 int DigField(struct PlayerInfo *player,
11314              int oldx, int oldy, int x, int y,
11315              int real_dx, int real_dy, int mode)
11316 {
11317   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11318   boolean player_was_pushing = player->is_pushing;
11319   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11320   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11321   int jx = oldx, jy = oldy;
11322   int dx = x - jx, dy = y - jy;
11323   int nextx = x + dx, nexty = y + dy;
11324   int move_direction = (dx == -1 ? MV_LEFT  :
11325                         dx == +1 ? MV_RIGHT :
11326                         dy == -1 ? MV_UP    :
11327                         dy == +1 ? MV_DOWN  : MV_NONE);
11328   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11329   int dig_side = MV_DIR_OPPOSITE(move_direction);
11330   int old_element = Feld[jx][jy];
11331 #if USE_FIXED_DONT_RUN_INTO
11332   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11333 #else
11334   int element;
11335 #endif
11336   int collect_count;
11337
11338   if (is_player)                /* function can also be called by EL_PENGUIN */
11339   {
11340     if (player->MovPos == 0)
11341     {
11342       player->is_digging = FALSE;
11343       player->is_collecting = FALSE;
11344     }
11345
11346     if (player->MovPos == 0)    /* last pushing move finished */
11347       player->is_pushing = FALSE;
11348
11349     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11350     {
11351       player->is_switching = FALSE;
11352       player->push_delay = -1;
11353
11354       return MP_NO_ACTION;
11355     }
11356   }
11357
11358 #if !USE_FIXED_DONT_RUN_INTO
11359   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11360     return MP_NO_ACTION;
11361 #endif
11362
11363   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11364     old_element = Back[jx][jy];
11365
11366   /* in case of element dropped at player position, check background */
11367   else if (Back[jx][jy] != EL_EMPTY &&
11368            game.engine_version >= VERSION_IDENT(2,2,0,0))
11369     old_element = Back[jx][jy];
11370
11371 #if 0
11372 #if USE_FIXED_DONT_RUN_INTO
11373   if (player_can_move && DONT_RUN_INTO(element))
11374   {
11375     if (element == EL_ACID && dx == 0 && dy == 1)
11376     {
11377       SplashAcid(x, y);
11378       Feld[jx][jy] = EL_PLAYER_1;
11379       InitMovingField(jx, jy, MV_DOWN);
11380       Store[jx][jy] = EL_ACID;
11381       ContinueMoving(jx, jy);
11382       BuryPlayer(player);
11383     }
11384     else
11385       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11386
11387     return MP_DONT_RUN_INTO;
11388   }
11389 #endif
11390 #endif
11391
11392 #if 1
11393 #if USE_FIXED_DONT_RUN_INTO
11394   if (player_can_move && DONT_RUN_INTO(element))
11395   {
11396     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11397
11398     return MP_DONT_RUN_INTO;
11399   }
11400 #endif
11401 #endif
11402
11403   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11404     return MP_NO_ACTION;        /* field has no opening in this direction */
11405
11406   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11407     return MP_NO_ACTION;        /* field has no opening in this direction */
11408
11409 #if 1
11410 #if USE_FIXED_DONT_RUN_INTO
11411   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11412   {
11413     SplashAcid(x, y);
11414     Feld[jx][jy] = EL_PLAYER_1;
11415     InitMovingField(jx, jy, MV_DOWN);
11416     Store[jx][jy] = EL_ACID;
11417     ContinueMoving(jx, jy);
11418     BuryPlayer(player);
11419
11420     return MP_DONT_RUN_INTO;
11421   }
11422 #endif
11423 #endif
11424
11425 #if 0
11426 #if USE_FIXED_DONT_RUN_INTO
11427   if (player_can_move && DONT_RUN_INTO(element))
11428   {
11429     if (element == EL_ACID && dx == 0 && dy == 1)
11430     {
11431       SplashAcid(x, y);
11432       Feld[jx][jy] = EL_PLAYER_1;
11433       InitMovingField(jx, jy, MV_DOWN);
11434       Store[jx][jy] = EL_ACID;
11435       ContinueMoving(jx, jy);
11436       BuryPlayer(player);
11437     }
11438     else
11439       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11440
11441     return MP_DONT_RUN_INTO;
11442   }
11443 #endif
11444 #endif
11445
11446 #if USE_FIXED_DONT_RUN_INTO
11447   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11448     return MP_NO_ACTION;
11449 #endif
11450
11451 #if !USE_FIXED_DONT_RUN_INTO
11452   element = Feld[x][y];
11453 #endif
11454
11455   collect_count = element_info[element].collect_count_initial;
11456
11457   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
11458     return MP_NO_ACTION;
11459
11460   if (game.engine_version < VERSION_IDENT(2,2,0,0))
11461     player_can_move = player_can_move_or_snap;
11462
11463   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11464       game.engine_version >= VERSION_IDENT(2,2,0,0))
11465   {
11466     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11467                                player->index_bit, dig_side);
11468     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11469                                         player->index_bit, dig_side);
11470
11471     if (Feld[x][y] != element)          /* field changed by snapping */
11472       return MP_ACTION;
11473
11474     return MP_NO_ACTION;
11475   }
11476
11477   if (game.gravity && is_player && !player->is_auto_moving &&
11478       canFallDown(player) && move_direction != MV_DOWN &&
11479       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11480     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
11481
11482   if (player_can_move &&
11483       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11484   {
11485     int sound_element = SND_ELEMENT(element);
11486     int sound_action = ACTION_WALKING;
11487
11488     if (IS_RND_GATE(element))
11489     {
11490       if (!player->key[RND_GATE_NR(element)])
11491         return MP_NO_ACTION;
11492     }
11493     else if (IS_RND_GATE_GRAY(element))
11494     {
11495       if (!player->key[RND_GATE_GRAY_NR(element)])
11496         return MP_NO_ACTION;
11497     }
11498     else if (IS_RND_GATE_GRAY_ACTIVE(element))
11499     {
11500       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11501         return MP_NO_ACTION;
11502     }
11503     else if (element == EL_EXIT_OPEN ||
11504              element == EL_SP_EXIT_OPEN ||
11505              element == EL_SP_EXIT_OPENING)
11506     {
11507       sound_action = ACTION_PASSING;    /* player is passing exit */
11508     }
11509     else if (element == EL_EMPTY)
11510     {
11511       sound_action = ACTION_MOVING;             /* nothing to walk on */
11512     }
11513
11514     /* play sound from background or player, whatever is available */
11515     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11516       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11517     else
11518       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11519   }
11520   else if (player_can_move &&
11521            IS_PASSABLE(element) && canPassField(x, y, move_direction))
11522   {
11523     if (!ACCESS_FROM(element, opposite_direction))
11524       return MP_NO_ACTION;      /* field not accessible from this direction */
11525
11526     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
11527       return MP_NO_ACTION;
11528
11529     if (IS_EM_GATE(element))
11530     {
11531       if (!player->key[EM_GATE_NR(element)])
11532         return MP_NO_ACTION;
11533     }
11534     else if (IS_EM_GATE_GRAY(element))
11535     {
11536       if (!player->key[EM_GATE_GRAY_NR(element)])
11537         return MP_NO_ACTION;
11538     }
11539     else if (IS_EM_GATE_GRAY_ACTIVE(element))
11540     {
11541       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11542         return MP_NO_ACTION;
11543     }
11544     else if (IS_SP_PORT(element))
11545     {
11546       if (element == EL_SP_GRAVITY_PORT_LEFT ||
11547           element == EL_SP_GRAVITY_PORT_RIGHT ||
11548           element == EL_SP_GRAVITY_PORT_UP ||
11549           element == EL_SP_GRAVITY_PORT_DOWN)
11550         game.gravity = !game.gravity;
11551       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11552                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11553                element == EL_SP_GRAVITY_ON_PORT_UP ||
11554                element == EL_SP_GRAVITY_ON_PORT_DOWN)
11555         game.gravity = TRUE;
11556       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11557                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11558                element == EL_SP_GRAVITY_OFF_PORT_UP ||
11559                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11560         game.gravity = FALSE;
11561     }
11562
11563     /* automatically move to the next field with double speed */
11564     player->programmed_action = move_direction;
11565
11566     if (player->move_delay_reset_counter == 0)
11567     {
11568       player->move_delay_reset_counter = 2;     /* two double speed steps */
11569
11570       DOUBLE_PLAYER_SPEED(player);
11571     }
11572
11573     PlayLevelSoundAction(x, y, ACTION_PASSING);
11574   }
11575   else if (player_can_move_or_snap && IS_DIGGABLE(element))
11576   {
11577     RemoveField(x, y);
11578
11579     if (mode != DF_SNAP)
11580     {
11581       GfxElement[x][y] = GFX_ELEMENT(element);
11582       player->is_digging = TRUE;
11583     }
11584
11585     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11586
11587     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11588                                         player->index_bit, dig_side);
11589
11590     if (mode == DF_SNAP)
11591     {
11592 #if USE_NEW_SNAP_DELAY
11593       if (level.block_snap_field)
11594         setFieldForSnapping(x, y, element, move_direction);
11595       else
11596         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11597 #else
11598       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
11599 #endif
11600
11601       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11602                                           player->index_bit, dig_side);
11603     }
11604   }
11605   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11606   {
11607     RemoveField(x, y);
11608
11609     if (is_player && mode != DF_SNAP)
11610     {
11611       GfxElement[x][y] = element;
11612       player->is_collecting = TRUE;
11613     }
11614
11615     if (element == EL_SPEED_PILL)
11616     {
11617       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11618     }
11619     else if (element == EL_EXTRA_TIME && level.time > 0)
11620     {
11621       TimeLeft += level.extra_time;
11622       DrawGameValue_Time(TimeLeft);
11623     }
11624     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11625     {
11626       player->shield_normal_time_left += level.shield_normal_time;
11627       if (element == EL_SHIELD_DEADLY)
11628         player->shield_deadly_time_left += level.shield_deadly_time;
11629     }
11630     else if (element == EL_DYNAMITE ||
11631              element == EL_EM_DYNAMITE ||
11632              element == EL_SP_DISK_RED)
11633     {
11634       if (player->inventory_size < MAX_INVENTORY_SIZE)
11635         player->inventory_element[player->inventory_size++] = element;
11636
11637       DrawGameValue_Dynamite(local_player->inventory_size);
11638     }
11639     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11640     {
11641       player->dynabomb_count++;
11642       player->dynabombs_left++;
11643     }
11644     else if (element == EL_DYNABOMB_INCREASE_SIZE)
11645     {
11646       player->dynabomb_size++;
11647     }
11648     else if (element == EL_DYNABOMB_INCREASE_POWER)
11649     {
11650       player->dynabomb_xl = TRUE;
11651     }
11652     else if (IS_KEY(element))
11653     {
11654       player->key[KEY_NR(element)] = TRUE;
11655
11656       DrawGameValue_Keys(player->key);
11657
11658       redraw_mask |= REDRAW_DOOR_1;
11659     }
11660     else if (IS_ENVELOPE(element))
11661     {
11662       player->show_envelope = element;
11663     }
11664     else if (element == EL_EMC_LENSES)
11665     {
11666       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11667
11668       RedrawAllInvisibleElementsForLenses();
11669     }
11670     else if (element == EL_EMC_MAGNIFIER)
11671     {
11672       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11673
11674       RedrawAllInvisibleElementsForMagnifier();
11675     }
11676     else if (IS_DROPPABLE(element) ||
11677              IS_THROWABLE(element))     /* can be collected and dropped */
11678     {
11679       int i;
11680
11681       if (collect_count == 0)
11682         player->inventory_infinite_element = element;
11683       else
11684         for (i = 0; i < collect_count; i++)
11685           if (player->inventory_size < MAX_INVENTORY_SIZE)
11686             player->inventory_element[player->inventory_size++] = element;
11687
11688       DrawGameValue_Dynamite(local_player->inventory_size);
11689     }
11690     else if (collect_count > 0)
11691     {
11692       local_player->gems_still_needed -= collect_count;
11693       if (local_player->gems_still_needed < 0)
11694         local_player->gems_still_needed = 0;
11695
11696       DrawGameValue_Emeralds(local_player->gems_still_needed);
11697     }
11698
11699     RaiseScoreElement(element);
11700     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11701
11702     if (is_player)
11703       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11704                                           player->index_bit, dig_side);
11705
11706     if (mode == DF_SNAP)
11707     {
11708 #if USE_NEW_SNAP_DELAY
11709       if (level.block_snap_field)
11710         setFieldForSnapping(x, y, element, move_direction);
11711       else
11712         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11713 #else
11714       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
11715 #endif
11716
11717       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11718                                           player->index_bit, dig_side);
11719     }
11720   }
11721   else if (player_can_move_or_snap && IS_PUSHABLE(element))
11722   {
11723     if (mode == DF_SNAP && element != EL_BD_ROCK)
11724       return MP_NO_ACTION;
11725
11726     if (CAN_FALL(element) && dy)
11727       return MP_NO_ACTION;
11728
11729     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11730         !(element == EL_SPRING && level.use_spring_bug))
11731       return MP_NO_ACTION;
11732
11733     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11734         ((move_direction & MV_VERTICAL &&
11735           ((element_info[element].move_pattern & MV_LEFT &&
11736             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11737            (element_info[element].move_pattern & MV_RIGHT &&
11738             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11739          (move_direction & MV_HORIZONTAL &&
11740           ((element_info[element].move_pattern & MV_UP &&
11741             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11742            (element_info[element].move_pattern & MV_DOWN &&
11743             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11744       return MP_NO_ACTION;
11745
11746     /* do not push elements already moving away faster than player */
11747     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11748         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11749       return MP_NO_ACTION;
11750
11751     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11752     {
11753       if (player->push_delay_value == -1 || !player_was_pushing)
11754         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11755     }
11756     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11757     {
11758       if (player->push_delay_value == -1)
11759         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11760     }
11761     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11762     {
11763       if (!player->is_pushing)
11764         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11765     }
11766
11767     player->is_pushing = TRUE;
11768
11769     if (!(IN_LEV_FIELD(nextx, nexty) &&
11770           (IS_FREE(nextx, nexty) ||
11771            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11772             IS_SB_ELEMENT(element)))))
11773       return MP_NO_ACTION;
11774
11775     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11776       return MP_NO_ACTION;
11777
11778     if (player->push_delay == -1)       /* new pushing; restart delay */
11779       player->push_delay = 0;
11780
11781     if (player->push_delay < player->push_delay_value &&
11782         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11783         element != EL_SPRING && element != EL_BALLOON)
11784     {
11785       /* make sure that there is no move delay before next try to push */
11786       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11787         player->move_delay = 0;
11788
11789       return MP_NO_ACTION;
11790     }
11791
11792     if (IS_SB_ELEMENT(element))
11793     {
11794       if (element == EL_SOKOBAN_FIELD_FULL)
11795       {
11796         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11797         local_player->sokobanfields_still_needed++;
11798       }
11799
11800       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11801       {
11802         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11803         local_player->sokobanfields_still_needed--;
11804       }
11805
11806       Feld[x][y] = EL_SOKOBAN_OBJECT;
11807
11808       if (Back[x][y] == Back[nextx][nexty])
11809         PlayLevelSoundAction(x, y, ACTION_PUSHING);
11810       else if (Back[x][y] != 0)
11811         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11812                                     ACTION_EMPTYING);
11813       else
11814         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11815                                     ACTION_FILLING);
11816
11817       if (local_player->sokobanfields_still_needed == 0 &&
11818           game.emulation == EMU_SOKOBAN)
11819       {
11820         player->LevelSolved = player->GameOver = TRUE;
11821         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11822       }
11823     }
11824     else
11825       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11826
11827     InitMovingField(x, y, move_direction);
11828     GfxAction[x][y] = ACTION_PUSHING;
11829
11830     if (mode == DF_SNAP)
11831       ContinueMoving(x, y);
11832     else
11833       MovPos[x][y] = (dx != 0 ? dx : dy);
11834
11835     Pushed[x][y] = TRUE;
11836     Pushed[nextx][nexty] = TRUE;
11837
11838     if (game.engine_version < VERSION_IDENT(2,2,0,7))
11839       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11840     else
11841       player->push_delay_value = -1;    /* get new value later */
11842
11843     /* check for element change _after_ element has been pushed */
11844     if (game.use_change_when_pushing_bug)
11845     {
11846       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
11847                                  player->index_bit, dig_side);
11848       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
11849                                           player->index_bit, dig_side);
11850     }
11851   }
11852   else if (IS_SWITCHABLE(element))
11853   {
11854     if (PLAYER_SWITCHING(player, x, y))
11855     {
11856       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11857                                           player->index_bit, dig_side);
11858
11859       return MP_ACTION;
11860     }
11861
11862     player->is_switching = TRUE;
11863     player->switch_x = x;
11864     player->switch_y = y;
11865
11866     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
11867
11868     if (element == EL_ROBOT_WHEEL)
11869     {
11870       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
11871       ZX = x;
11872       ZY = y;
11873
11874       DrawLevelField(x, y);
11875     }
11876     else if (element == EL_SP_TERMINAL)
11877     {
11878       int xx, yy;
11879
11880 #if 1
11881       SCAN_PLAYFIELD(xx, yy)
11882 #else
11883       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11884 #endif
11885       {
11886         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
11887           Bang(xx, yy);
11888         else if (Feld[xx][yy] == EL_SP_TERMINAL)
11889           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
11890       }
11891     }
11892     else if (IS_BELT_SWITCH(element))
11893     {
11894       ToggleBeltSwitch(x, y);
11895     }
11896     else if (element == EL_SWITCHGATE_SWITCH_UP ||
11897              element == EL_SWITCHGATE_SWITCH_DOWN)
11898     {
11899       ToggleSwitchgateSwitch(x, y);
11900     }
11901     else if (element == EL_LIGHT_SWITCH ||
11902              element == EL_LIGHT_SWITCH_ACTIVE)
11903     {
11904       ToggleLightSwitch(x, y);
11905     }
11906     else if (element == EL_TIMEGATE_SWITCH)
11907     {
11908       ActivateTimegateSwitch(x, y);
11909     }
11910     else if (element == EL_BALLOON_SWITCH_LEFT  ||
11911              element == EL_BALLOON_SWITCH_RIGHT ||
11912              element == EL_BALLOON_SWITCH_UP    ||
11913              element == EL_BALLOON_SWITCH_DOWN  ||
11914              element == EL_BALLOON_SWITCH_NONE  ||
11915              element == EL_BALLOON_SWITCH_ANY)
11916     {
11917       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
11918                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
11919                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
11920                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
11921                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
11922                              move_direction);
11923     }
11924     else if (element == EL_LAMP)
11925     {
11926       Feld[x][y] = EL_LAMP_ACTIVE;
11927       local_player->lights_still_needed--;
11928
11929       ResetGfxAnimation(x, y);
11930       DrawLevelField(x, y);
11931     }
11932     else if (element == EL_TIME_ORB_FULL)
11933     {
11934       Feld[x][y] = EL_TIME_ORB_EMPTY;
11935
11936       if (level.time > 0 || level.use_time_orb_bug)
11937       {
11938         TimeLeft += level.time_orb_time;
11939         DrawGameValue_Time(TimeLeft);
11940       }
11941
11942       ResetGfxAnimation(x, y);
11943       DrawLevelField(x, y);
11944     }
11945     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
11946              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11947     {
11948       int xx, yy;
11949
11950       game.ball_state = !game.ball_state;
11951
11952 #if 1
11953       SCAN_PLAYFIELD(xx, yy)
11954 #else
11955       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
11956 #endif
11957       {
11958         int e = Feld[xx][yy];
11959
11960         if (game.ball_state)
11961         {
11962           if (e == EL_EMC_MAGIC_BALL)
11963             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
11964           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
11965             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
11966         }
11967         else
11968         {
11969           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
11970             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
11971           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
11972             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
11973         }
11974       }
11975     }
11976
11977     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11978                                         player->index_bit, dig_side);
11979
11980     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
11981                                         player->index_bit, dig_side);
11982
11983     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
11984                                         player->index_bit, dig_side);
11985
11986     return MP_ACTION;
11987   }
11988   else
11989   {
11990     if (!PLAYER_SWITCHING(player, x, y))
11991     {
11992       player->is_switching = TRUE;
11993       player->switch_x = x;
11994       player->switch_y = y;
11995
11996       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
11997                                  player->index_bit, dig_side);
11998       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
11999                                           player->index_bit, dig_side);
12000
12001       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12002                                  player->index_bit, dig_side);
12003       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12004                                           player->index_bit, dig_side);
12005     }
12006
12007     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12008                                player->index_bit, dig_side);
12009     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12010                                         player->index_bit, dig_side);
12011
12012     return MP_NO_ACTION;
12013   }
12014
12015   player->push_delay = -1;
12016
12017   if (is_player)                /* function can also be called by EL_PENGUIN */
12018   {
12019     if (Feld[x][y] != element)          /* really digged/collected something */
12020       player->is_collecting = !player->is_digging;
12021   }
12022
12023   return MP_MOVING;
12024 }
12025
12026 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12027 {
12028   int jx = player->jx, jy = player->jy;
12029   int x = jx + dx, y = jy + dy;
12030   int snap_direction = (dx == -1 ? MV_LEFT  :
12031                         dx == +1 ? MV_RIGHT :
12032                         dy == -1 ? MV_UP    :
12033                         dy == +1 ? MV_DOWN  : MV_NONE);
12034   boolean can_continue_snapping = (level.continuous_snapping &&
12035                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12036
12037   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12038     return FALSE;
12039
12040   if (!player->active || !IN_LEV_FIELD(x, y))
12041     return FALSE;
12042
12043   if (dx && dy)
12044     return FALSE;
12045
12046   if (!dx && !dy)
12047   {
12048     if (player->MovPos == 0)
12049       player->is_pushing = FALSE;
12050
12051     player->is_snapping = FALSE;
12052
12053     if (player->MovPos == 0)
12054     {
12055       player->is_moving = FALSE;
12056       player->is_digging = FALSE;
12057       player->is_collecting = FALSE;
12058     }
12059
12060     return FALSE;
12061   }
12062
12063 #if USE_NEW_CONTINUOUS_SNAPPING
12064   /* prevent snapping with already pressed snap key when not allowed */
12065   if (player->is_snapping && !can_continue_snapping)
12066     return FALSE;
12067 #else
12068   if (player->is_snapping)
12069     return FALSE;
12070 #endif
12071
12072   player->MovDir = snap_direction;
12073
12074   if (player->MovPos == 0)
12075   {
12076     player->is_moving = FALSE;
12077     player->is_digging = FALSE;
12078     player->is_collecting = FALSE;
12079   }
12080
12081   player->is_dropping = FALSE;
12082   player->is_dropping_pressed = FALSE;
12083   player->drop_pressed_delay = 0;
12084
12085   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12086     return FALSE;
12087
12088   player->is_snapping = TRUE;
12089
12090   if (player->MovPos == 0)
12091   {
12092     player->is_moving = FALSE;
12093     player->is_digging = FALSE;
12094     player->is_collecting = FALSE;
12095   }
12096
12097   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12098     DrawLevelField(player->last_jx, player->last_jy);
12099
12100   DrawLevelField(x, y);
12101
12102   return TRUE;
12103 }
12104
12105 boolean DropElement(struct PlayerInfo *player)
12106 {
12107   int old_element, new_element;
12108   int dropx = player->jx, dropy = player->jy;
12109   int drop_direction = player->MovDir;
12110   int drop_side = drop_direction;
12111   int drop_element = (player->inventory_size > 0 ?
12112                       player->inventory_element[player->inventory_size - 1] :
12113                       player->inventory_infinite_element != EL_UNDEFINED ?
12114                       player->inventory_infinite_element :
12115                       player->dynabombs_left > 0 ?
12116                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12117                       EL_UNDEFINED);
12118
12119   player->is_dropping_pressed = TRUE;
12120
12121   /* do not drop an element on top of another element; when holding drop key
12122      pressed without moving, dropped element must move away before the next
12123      element can be dropped (this is especially important if the next element
12124      is dynamite, which can be placed on background for historical reasons) */
12125   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12126     return MP_ACTION;
12127
12128   if (IS_THROWABLE(drop_element))
12129   {
12130     dropx += GET_DX_FROM_DIR(drop_direction);
12131     dropy += GET_DY_FROM_DIR(drop_direction);
12132
12133     if (!IN_LEV_FIELD(dropx, dropy))
12134       return FALSE;
12135   }
12136
12137   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12138   new_element = drop_element;           /* default: no change when dropping */
12139
12140   /* check if player is active, not moving and ready to drop */
12141   if (!player->active || player->MovPos || player->drop_delay > 0)
12142     return FALSE;
12143
12144   /* check if player has anything that can be dropped */
12145   if (new_element == EL_UNDEFINED)
12146     return FALSE;
12147
12148   /* check if drop key was pressed long enough for EM style dynamite */
12149   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12150     return FALSE;
12151
12152   /* check if anything can be dropped at the current position */
12153   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12154     return FALSE;
12155
12156   /* collected custom elements can only be dropped on empty fields */
12157   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12158     return FALSE;
12159
12160   if (old_element != EL_EMPTY)
12161     Back[dropx][dropy] = old_element;   /* store old element on this field */
12162
12163   ResetGfxAnimation(dropx, dropy);
12164   ResetRandomAnimationValue(dropx, dropy);
12165
12166   if (player->inventory_size > 0 ||
12167       player->inventory_infinite_element != EL_UNDEFINED)
12168   {
12169     if (player->inventory_size > 0)
12170     {
12171       player->inventory_size--;
12172
12173       DrawGameValue_Dynamite(local_player->inventory_size);
12174
12175       if (new_element == EL_DYNAMITE)
12176         new_element = EL_DYNAMITE_ACTIVE;
12177       else if (new_element == EL_EM_DYNAMITE)
12178         new_element = EL_EM_DYNAMITE_ACTIVE;
12179       else if (new_element == EL_SP_DISK_RED)
12180         new_element = EL_SP_DISK_RED_ACTIVE;
12181     }
12182
12183     Feld[dropx][dropy] = new_element;
12184
12185     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12186       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12187                           el2img(Feld[dropx][dropy]), 0);
12188
12189     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12190
12191     /* needed if previous element just changed to "empty" in the last frame */
12192     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12193
12194     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12195                                player->index_bit, drop_side);
12196     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12197                                         CE_PLAYER_DROPS_X,
12198                                         player->index_bit, drop_side);
12199
12200     TestIfElementTouchesCustomElement(dropx, dropy);
12201   }
12202   else          /* player is dropping a dyna bomb */
12203   {
12204     player->dynabombs_left--;
12205
12206     Feld[dropx][dropy] = new_element;
12207
12208     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12209       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12210                           el2img(Feld[dropx][dropy]), 0);
12211
12212     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12213   }
12214
12215   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12216     InitField_WithBug1(dropx, dropy, FALSE);
12217
12218   new_element = Feld[dropx][dropy];     /* element might have changed */
12219
12220   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12221       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12222   {
12223     int move_direction, nextx, nexty;
12224
12225     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12226       MovDir[dropx][dropy] = drop_direction;
12227
12228     move_direction = MovDir[dropx][dropy];
12229     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12230     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12231
12232     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12233     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12234   }
12235
12236   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12237   player->is_dropping = TRUE;
12238
12239   player->drop_pressed_delay = 0;
12240   player->is_dropping_pressed = FALSE;
12241
12242   player->drop_x = dropx;
12243   player->drop_y = dropy;
12244
12245   return TRUE;
12246 }
12247
12248 /* ------------------------------------------------------------------------- */
12249 /* game sound playing functions                                              */
12250 /* ------------------------------------------------------------------------- */
12251
12252 static int *loop_sound_frame = NULL;
12253 static int *loop_sound_volume = NULL;
12254
12255 void InitPlayLevelSound()
12256 {
12257   int num_sounds = getSoundListSize();
12258
12259   checked_free(loop_sound_frame);
12260   checked_free(loop_sound_volume);
12261
12262   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12263   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12264 }
12265
12266 static void PlayLevelSound(int x, int y, int nr)
12267 {
12268   int sx = SCREENX(x), sy = SCREENY(y);
12269   int volume, stereo_position;
12270   int max_distance = 8;
12271   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12272
12273   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12274       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12275     return;
12276
12277   if (!IN_LEV_FIELD(x, y) ||
12278       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12279       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12280     return;
12281
12282   volume = SOUND_MAX_VOLUME;
12283
12284   if (!IN_SCR_FIELD(sx, sy))
12285   {
12286     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12287     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12288
12289     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12290   }
12291
12292   stereo_position = (SOUND_MAX_LEFT +
12293                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12294                      (SCR_FIELDX + 2 * max_distance));
12295
12296   if (IS_LOOP_SOUND(nr))
12297   {
12298     /* This assures that quieter loop sounds do not overwrite louder ones,
12299        while restarting sound volume comparison with each new game frame. */
12300
12301     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12302       return;
12303
12304     loop_sound_volume[nr] = volume;
12305     loop_sound_frame[nr] = FrameCounter;
12306   }
12307
12308   PlaySoundExt(nr, volume, stereo_position, type);
12309 }
12310
12311 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12312 {
12313   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12314                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12315                  y < LEVELY(BY1) ? LEVELY(BY1) :
12316                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12317                  sound_action);
12318 }
12319
12320 static void PlayLevelSoundAction(int x, int y, int action)
12321 {
12322   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12323 }
12324
12325 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12326 {
12327   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12328
12329   if (sound_effect != SND_UNDEFINED)
12330     PlayLevelSound(x, y, sound_effect);
12331 }
12332
12333 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12334                                               int action)
12335 {
12336   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12337
12338   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12339     PlayLevelSound(x, y, sound_effect);
12340 }
12341
12342 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12343 {
12344   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12345
12346   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12347     PlayLevelSound(x, y, sound_effect);
12348 }
12349
12350 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12351 {
12352   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12353
12354   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12355     StopSound(sound_effect);
12356 }
12357
12358 static void PlayLevelMusic()
12359 {
12360   if (levelset.music[level_nr] != MUS_UNDEFINED)
12361     PlayMusic(levelset.music[level_nr]);        /* from config file */
12362   else
12363     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
12364 }
12365
12366 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12367 {
12368   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12369
12370   switch (sample)
12371   {
12372     case SAMPLE_blank:
12373       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12374       break;
12375
12376     case SAMPLE_roll:
12377       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12378       break;
12379
12380     case SAMPLE_stone:
12381       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12382       break;
12383
12384     case SAMPLE_nut:
12385       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12386       break;
12387
12388     case SAMPLE_crack:
12389       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12390       break;
12391
12392     case SAMPLE_bug:
12393       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12394       break;
12395
12396     case SAMPLE_tank:
12397       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12398       break;
12399
12400     case SAMPLE_android_clone:
12401       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12402       break;
12403
12404     case SAMPLE_android_move:
12405       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12406       break;
12407
12408     case SAMPLE_spring:
12409       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12410       break;
12411
12412     case SAMPLE_slurp:
12413       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12414       break;
12415
12416     case SAMPLE_eater:
12417       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12418       break;
12419
12420     case SAMPLE_eater_eat:
12421       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12422       break;
12423
12424     case SAMPLE_alien:
12425       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12426       break;
12427
12428     case SAMPLE_collect:
12429       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12430       break;
12431
12432     case SAMPLE_diamond:
12433       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12434       break;
12435
12436     case SAMPLE_squash:
12437       /* !!! CHECK THIS !!! */
12438 #if 1
12439       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12440 #else
12441       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12442 #endif
12443       break;
12444
12445     case SAMPLE_wonderfall:
12446       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12447       break;
12448
12449     case SAMPLE_drip:
12450       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12451       break;
12452
12453     case SAMPLE_push:
12454       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12455       break;
12456
12457     case SAMPLE_dirt:
12458       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12459       break;
12460
12461     case SAMPLE_acid:
12462       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12463       break;
12464
12465     case SAMPLE_ball:
12466       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12467       break;
12468
12469     case SAMPLE_grow:
12470       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12471       break;
12472
12473     case SAMPLE_wonder:
12474       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12475       break;
12476
12477     case SAMPLE_door:
12478       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12479       break;
12480
12481     case SAMPLE_exit_open:
12482       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12483       break;
12484
12485     case SAMPLE_exit_leave:
12486       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12487       break;
12488
12489     case SAMPLE_dynamite:
12490       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12491       break;
12492
12493     case SAMPLE_tick:
12494       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12495       break;
12496
12497     case SAMPLE_press:
12498       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12499       break;
12500
12501     case SAMPLE_wheel:
12502       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12503       break;
12504
12505     case SAMPLE_boom:
12506       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12507       break;
12508
12509     case SAMPLE_die:
12510       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12511       break;
12512
12513     case SAMPLE_time:
12514       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12515       break;
12516
12517     default:
12518       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12519       break;
12520   }
12521 }
12522
12523 void RaiseScore(int value)
12524 {
12525   local_player->score += value;
12526
12527   DrawGameValue_Score(local_player->score);
12528 }
12529
12530 void RaiseScoreElement(int element)
12531 {
12532   switch(element)
12533   {
12534     case EL_EMERALD:
12535     case EL_BD_DIAMOND:
12536     case EL_EMERALD_YELLOW:
12537     case EL_EMERALD_RED:
12538     case EL_EMERALD_PURPLE:
12539     case EL_SP_INFOTRON:
12540       RaiseScore(level.score[SC_EMERALD]);
12541       break;
12542     case EL_DIAMOND:
12543       RaiseScore(level.score[SC_DIAMOND]);
12544       break;
12545     case EL_CRYSTAL:
12546       RaiseScore(level.score[SC_CRYSTAL]);
12547       break;
12548     case EL_PEARL:
12549       RaiseScore(level.score[SC_PEARL]);
12550       break;
12551     case EL_BUG:
12552     case EL_BD_BUTTERFLY:
12553     case EL_SP_ELECTRON:
12554       RaiseScore(level.score[SC_BUG]);
12555       break;
12556     case EL_SPACESHIP:
12557     case EL_BD_FIREFLY:
12558     case EL_SP_SNIKSNAK:
12559       RaiseScore(level.score[SC_SPACESHIP]);
12560       break;
12561     case EL_YAMYAM:
12562     case EL_DARK_YAMYAM:
12563       RaiseScore(level.score[SC_YAMYAM]);
12564       break;
12565     case EL_ROBOT:
12566       RaiseScore(level.score[SC_ROBOT]);
12567       break;
12568     case EL_PACMAN:
12569       RaiseScore(level.score[SC_PACMAN]);
12570       break;
12571     case EL_NUT:
12572       RaiseScore(level.score[SC_NUT]);
12573       break;
12574     case EL_DYNAMITE:
12575     case EL_EM_DYNAMITE:
12576     case EL_SP_DISK_RED:
12577     case EL_DYNABOMB_INCREASE_NUMBER:
12578     case EL_DYNABOMB_INCREASE_SIZE:
12579     case EL_DYNABOMB_INCREASE_POWER:
12580       RaiseScore(level.score[SC_DYNAMITE]);
12581       break;
12582     case EL_SHIELD_NORMAL:
12583     case EL_SHIELD_DEADLY:
12584       RaiseScore(level.score[SC_SHIELD]);
12585       break;
12586     case EL_EXTRA_TIME:
12587       RaiseScore(level.extra_time_score);
12588       break;
12589     case EL_KEY_1:
12590     case EL_KEY_2:
12591     case EL_KEY_3:
12592     case EL_KEY_4:
12593     case EL_EM_KEY_1:
12594     case EL_EM_KEY_2:
12595     case EL_EM_KEY_3:
12596     case EL_EM_KEY_4:
12597     case EL_EMC_KEY_5:
12598     case EL_EMC_KEY_6:
12599     case EL_EMC_KEY_7:
12600     case EL_EMC_KEY_8:
12601       RaiseScore(level.score[SC_KEY]);
12602       break;
12603     default:
12604       RaiseScore(element_info[element].collect_score);
12605       break;
12606   }
12607 }
12608
12609 void RequestQuitGame(boolean ask_if_really_quit)
12610 {
12611   if (AllPlayersGone ||
12612       !ask_if_really_quit ||
12613       level_editor_test_game ||
12614       Request("Do you really want to quit the game ?",
12615               REQ_ASK | REQ_STAY_CLOSED))
12616   {
12617 #if defined(NETWORK_AVALIABLE)
12618     if (options.network)
12619       SendToServer_StopPlaying();
12620     else
12621 #endif
12622     {
12623       game_status = GAME_MODE_MAIN;
12624       DrawMainMenu();
12625     }
12626   }
12627   else
12628   {
12629     if (tape.playing && tape.deactivate_display)
12630       TapeDeactivateDisplayOff(TRUE);
12631
12632     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12633
12634     if (tape.playing && tape.deactivate_display)
12635       TapeDeactivateDisplayOn();
12636   }
12637 }
12638
12639
12640 /* ---------- new game button stuff ---------------------------------------- */
12641
12642 /* graphic position values for game buttons */
12643 #define GAME_BUTTON_XSIZE       30
12644 #define GAME_BUTTON_YSIZE       30
12645 #define GAME_BUTTON_XPOS        5
12646 #define GAME_BUTTON_YPOS        215
12647 #define SOUND_BUTTON_XPOS       5
12648 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12649
12650 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12651 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12652 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12653 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12654 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12655 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12656
12657 static struct
12658 {
12659   int x, y;
12660   int gadget_id;
12661   char *infotext;
12662 } gamebutton_info[NUM_GAME_BUTTONS] =
12663 {
12664   {
12665     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
12666     GAME_CTRL_ID_STOP,
12667     "stop game"
12668   },
12669   {
12670     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
12671     GAME_CTRL_ID_PAUSE,
12672     "pause game"
12673   },
12674   {
12675     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
12676     GAME_CTRL_ID_PLAY,
12677     "play game"
12678   },
12679   {
12680     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
12681     SOUND_CTRL_ID_MUSIC,
12682     "background music on/off"
12683   },
12684   {
12685     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
12686     SOUND_CTRL_ID_LOOPS,
12687     "sound loops on/off"
12688   },
12689   {
12690     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
12691     SOUND_CTRL_ID_SIMPLE,
12692     "normal sounds on/off"
12693   }
12694 };
12695
12696 void CreateGameButtons()
12697 {
12698   int i;
12699
12700   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12701   {
12702     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12703     struct GadgetInfo *gi;
12704     int button_type;
12705     boolean checked;
12706     unsigned long event_mask;
12707     int gd_xoffset, gd_yoffset;
12708     int gd_x1, gd_x2, gd_y1, gd_y2;
12709     int id = i;
12710
12711     gd_xoffset = gamebutton_info[i].x;
12712     gd_yoffset = gamebutton_info[i].y;
12713     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12714     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12715
12716     if (id == GAME_CTRL_ID_STOP ||
12717         id == GAME_CTRL_ID_PAUSE ||
12718         id == GAME_CTRL_ID_PLAY)
12719     {
12720       button_type = GD_TYPE_NORMAL_BUTTON;
12721       checked = FALSE;
12722       event_mask = GD_EVENT_RELEASED;
12723       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12724       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12725     }
12726     else
12727     {
12728       button_type = GD_TYPE_CHECK_BUTTON;
12729       checked =
12730         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12731          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12732          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12733       event_mask = GD_EVENT_PRESSED;
12734       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
12735       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12736     }
12737
12738     gi = CreateGadget(GDI_CUSTOM_ID, id,
12739                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
12740                       GDI_X, DX + gd_xoffset,
12741                       GDI_Y, DY + gd_yoffset,
12742                       GDI_WIDTH, GAME_BUTTON_XSIZE,
12743                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
12744                       GDI_TYPE, button_type,
12745                       GDI_STATE, GD_BUTTON_UNPRESSED,
12746                       GDI_CHECKED, checked,
12747                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12748                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12749                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12750                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12751                       GDI_EVENT_MASK, event_mask,
12752                       GDI_CALLBACK_ACTION, HandleGameButtons,
12753                       GDI_END);
12754
12755     if (gi == NULL)
12756       Error(ERR_EXIT, "cannot create gadget");
12757
12758     game_gadget[id] = gi;
12759   }
12760 }
12761
12762 void FreeGameButtons()
12763 {
12764   int i;
12765
12766   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12767     FreeGadget(game_gadget[i]);
12768 }
12769
12770 static void MapGameButtons()
12771 {
12772   int i;
12773
12774   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12775     MapGadget(game_gadget[i]);
12776 }
12777
12778 void UnmapGameButtons()
12779 {
12780   int i;
12781
12782   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12783     UnmapGadget(game_gadget[i]);
12784 }
12785
12786 static void HandleGameButtons(struct GadgetInfo *gi)
12787 {
12788   int id = gi->custom_id;
12789
12790   if (game_status != GAME_MODE_PLAYING)
12791     return;
12792
12793   switch (id)
12794   {
12795     case GAME_CTRL_ID_STOP:
12796       RequestQuitGame(TRUE);
12797       break;
12798
12799     case GAME_CTRL_ID_PAUSE:
12800       if (options.network)
12801       {
12802 #if defined(NETWORK_AVALIABLE)
12803         if (tape.pausing)
12804           SendToServer_ContinuePlaying();
12805         else
12806           SendToServer_PausePlaying();
12807 #endif
12808       }
12809       else
12810         TapeTogglePause(TAPE_TOGGLE_MANUAL);
12811       break;
12812
12813     case GAME_CTRL_ID_PLAY:
12814       if (tape.pausing)
12815       {
12816 #if defined(NETWORK_AVALIABLE)
12817         if (options.network)
12818           SendToServer_ContinuePlaying();
12819         else
12820 #endif
12821         {
12822           tape.pausing = FALSE;
12823           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
12824         }
12825       }
12826       break;
12827
12828     case SOUND_CTRL_ID_MUSIC:
12829       if (setup.sound_music)
12830       { 
12831         setup.sound_music = FALSE;
12832         FadeMusic();
12833       }
12834       else if (audio.music_available)
12835       { 
12836         setup.sound = setup.sound_music = TRUE;
12837
12838         SetAudioMode(setup.sound);
12839
12840         PlayLevelMusic();
12841       }
12842       break;
12843
12844     case SOUND_CTRL_ID_LOOPS:
12845       if (setup.sound_loops)
12846         setup.sound_loops = FALSE;
12847       else if (audio.loops_available)
12848       {
12849         setup.sound = setup.sound_loops = TRUE;
12850         SetAudioMode(setup.sound);
12851       }
12852       break;
12853
12854     case SOUND_CTRL_ID_SIMPLE:
12855       if (setup.sound_simple)
12856         setup.sound_simple = FALSE;
12857       else if (audio.sound_available)
12858       {
12859         setup.sound = setup.sound_simple = TRUE;
12860         SetAudioMode(setup.sound);
12861       }
12862       break;
12863
12864     default:
12865       break;
12866   }
12867 }