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