rnd-20060415-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #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 static void ResetRandomAnimationValue(int x, int y)
3016 {
3017   GfxRandom[x][y] = INIT_GFX_RANDOM();
3018 }
3019
3020 static void ResetGfxAnimation(int x, int y)
3021 {
3022 #if USE_GFX_RESET_GFX_ANIMATION
3023   int element, graphic;
3024 #endif
3025
3026   GfxFrame[x][y] = 0;
3027   GfxAction[x][y] = ACTION_DEFAULT;
3028   GfxDir[x][y] = MovDir[x][y];
3029
3030 #if USE_GFX_RESET_GFX_ANIMATION
3031   element = Feld[x][y];
3032   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3033
3034   if (graphic_info[graphic].anim_global_sync)
3035     GfxFrame[x][y] = FrameCounter;
3036   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3037     GfxFrame[x][y] = CustomValue[x][y];
3038   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3039     GfxFrame[x][y] = element_info[element].collect_score;
3040 #endif
3041 }
3042
3043 void InitMovingField(int x, int y, int direction)
3044 {
3045   int element = Feld[x][y];
3046 #if 0
3047   int graphic;
3048 #endif
3049   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3050   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3051   int newx = x + dx;
3052   int newy = y + dy;
3053
3054   if (!WasJustMoving[x][y] || direction != MovDir[x][y])
3055     ResetGfxAnimation(x, y);
3056
3057   MovDir[x][y] = direction;
3058   GfxDir[x][y] = direction;
3059   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
3060                      ACTION_FALLING : ACTION_MOVING);
3061
3062 #if 0
3063   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3064
3065   if (graphic_info[graphic].anim_global_sync)
3066     GfxFrame[x][y] = FrameCounter;
3067   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
3068     GfxFrame[x][y] = CustomValue[x][y];
3069   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
3070     GfxFrame[x][y] = element_info[element].collect_score;
3071 #endif
3072
3073   /* this is needed for CEs with property "can move" / "not moving" */
3074
3075   if (getElementMoveStepsize(x, y) != 0)        /* moving or being moved */
3076   {
3077     if (Feld[newx][newy] == EL_EMPTY)
3078       Feld[newx][newy] = EL_BLOCKED;
3079
3080     MovDir[newx][newy] = MovDir[x][y];
3081
3082 #if USE_NEW_CUSTOM_VALUE
3083     CustomValue[newx][newy] = CustomValue[x][y];
3084 #endif
3085
3086     GfxFrame[newx][newy] = GfxFrame[x][y];
3087     GfxRandom[newx][newy] = GfxRandom[x][y];
3088     GfxAction[newx][newy] = GfxAction[x][y];
3089     GfxDir[newx][newy] = GfxDir[x][y];
3090   }
3091 }
3092
3093 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
3094 {
3095   int direction = MovDir[x][y];
3096 #if 1
3097   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
3098   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
3099 #else
3100   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3101   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3102 #endif
3103
3104   *goes_to_x = newx;
3105   *goes_to_y = newy;
3106 }
3107
3108 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
3109 {
3110   int oldx = x, oldy = y;
3111   int direction = MovDir[x][y];
3112
3113   if (direction == MV_LEFT)
3114     oldx++;
3115   else if (direction == MV_RIGHT)
3116     oldx--;
3117   else if (direction == MV_UP)
3118     oldy++;
3119   else if (direction == MV_DOWN)
3120     oldy--;
3121
3122   *comes_from_x = oldx;
3123   *comes_from_y = oldy;
3124 }
3125
3126 int MovingOrBlocked2Element(int x, int y)
3127 {
3128   int element = Feld[x][y];
3129
3130   if (element == EL_BLOCKED)
3131   {
3132     int oldx, oldy;
3133
3134     Blocked2Moving(x, y, &oldx, &oldy);
3135     return Feld[oldx][oldy];
3136   }
3137   else
3138     return element;
3139 }
3140
3141 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
3142 {
3143   /* like MovingOrBlocked2Element(), but if element is moving
3144      and (x,y) is the field the moving element is just leaving,
3145      return EL_BLOCKED instead of the element value */
3146   int element = Feld[x][y];
3147
3148   if (IS_MOVING(x, y))
3149   {
3150     if (element == EL_BLOCKED)
3151     {
3152       int oldx, oldy;
3153
3154       Blocked2Moving(x, y, &oldx, &oldy);
3155       return Feld[oldx][oldy];
3156     }
3157     else
3158       return EL_BLOCKED;
3159   }
3160   else
3161     return element;
3162 }
3163
3164 static void RemoveField(int x, int y)
3165 {
3166   Feld[x][y] = EL_EMPTY;
3167
3168   MovPos[x][y] = 0;
3169   MovDir[x][y] = 0;
3170   MovDelay[x][y] = 0;
3171
3172 #if USE_NEW_CUSTOM_VALUE
3173   CustomValue[x][y] = 0;
3174 #endif
3175
3176   AmoebaNr[x][y] = 0;
3177   ChangeDelay[x][y] = 0;
3178   ChangePage[x][y] = -1;
3179   Pushed[x][y] = FALSE;
3180
3181 #if 0
3182   ExplodeField[x][y] = EX_TYPE_NONE;
3183 #endif
3184
3185   GfxElement[x][y] = EL_UNDEFINED;
3186   GfxAction[x][y] = ACTION_DEFAULT;
3187   GfxDir[x][y] = MV_NONE;
3188 }
3189
3190 void RemoveMovingField(int x, int y)
3191 {
3192   int oldx = x, oldy = y, newx = x, newy = y;
3193   int element = Feld[x][y];
3194   int next_element = EL_UNDEFINED;
3195
3196   if (element != EL_BLOCKED && !IS_MOVING(x, y))
3197     return;
3198
3199   if (IS_MOVING(x, y))
3200   {
3201     Moving2Blocked(x, y, &newx, &newy);
3202
3203     if (Feld[newx][newy] != EL_BLOCKED)
3204     {
3205       /* element is moving, but target field is not free (blocked), but
3206          already occupied by something different (example: acid pool);
3207          in this case, only remove the moving field, but not the target */
3208
3209       RemoveField(oldx, oldy);
3210
3211       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3212
3213       DrawLevelField(oldx, oldy);
3214
3215       return;
3216     }
3217   }
3218   else if (element == EL_BLOCKED)
3219   {
3220     Blocked2Moving(x, y, &oldx, &oldy);
3221     if (!IS_MOVING(oldx, oldy))
3222       return;
3223   }
3224
3225   if (element == EL_BLOCKED &&
3226       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
3227        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
3228        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
3229        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
3230     next_element = get_next_element(Feld[oldx][oldy]);
3231
3232   RemoveField(oldx, oldy);
3233   RemoveField(newx, newy);
3234
3235   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
3236
3237   if (next_element != EL_UNDEFINED)
3238     Feld[oldx][oldy] = next_element;
3239
3240   DrawLevelField(oldx, oldy);
3241   DrawLevelField(newx, newy);
3242 }
3243
3244 void DrawDynamite(int x, int y)
3245 {
3246   int sx = SCREENX(x), sy = SCREENY(y);
3247   int graphic = el2img(Feld[x][y]);
3248   int frame;
3249
3250   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
3251     return;
3252
3253   if (IS_WALKABLE_INSIDE(Back[x][y]))
3254     return;
3255
3256   if (Back[x][y])
3257     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
3258   else if (Store[x][y])
3259     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
3260
3261   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
3262
3263   if (Back[x][y] || Store[x][y])
3264     DrawGraphicThruMask(sx, sy, graphic, frame);
3265   else
3266     DrawGraphic(sx, sy, graphic, frame);
3267 }
3268
3269 void CheckDynamite(int x, int y)
3270 {
3271   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
3272   {
3273     MovDelay[x][y]--;
3274
3275     if (MovDelay[x][y] != 0)
3276     {
3277       DrawDynamite(x, y);
3278       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3279
3280       return;
3281     }
3282   }
3283
3284   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
3285
3286   Bang(x, y);
3287 }
3288
3289 #if 1
3290
3291 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
3292 {
3293   boolean num_checked_players = 0;
3294   int i;
3295
3296   for (i = 0; i < MAX_PLAYERS; i++)
3297   {
3298     if (stored_player[i].active)
3299     {
3300       int sx = stored_player[i].jx;
3301       int sy = stored_player[i].jy;
3302
3303       if (num_checked_players == 0)
3304       {
3305         *sx1 = *sx2 = sx;
3306         *sy1 = *sy2 = sy;
3307       }
3308       else
3309       {
3310         *sx1 = MIN(*sx1, sx);
3311         *sy1 = MIN(*sy1, sy);
3312         *sx2 = MAX(*sx2, sx);
3313         *sy2 = MAX(*sy2, sy);
3314       }
3315
3316       num_checked_players++;
3317     }
3318   }
3319 }
3320
3321 static boolean checkIfAllPlayersFitToScreen_RND()
3322 {
3323   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
3324
3325   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3326
3327   return (sx2 - sx1 < SCR_FIELDX &&
3328           sy2 - sy1 < SCR_FIELDY);
3329 }
3330
3331 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
3332 {
3333   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
3334
3335   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3336
3337   *sx = (sx1 + sx2) / 2;
3338   *sy = (sy1 + sy2) / 2;
3339 }
3340
3341 #if 0
3342 static void setMaxCenterDistanceForAllPlayers(int *max_dx, int *max_dy,
3343                                               int center_x, int center_y)
3344 {
3345   int sx1 = center_x, sy1 = center_y, sx2 = center_x, sy2 = center_y;
3346
3347   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
3348
3349   *max_dx = MAX(ABS(sx1 - center_x), ABS(sx2 - center_x));
3350   *max_dy = MAX(ABS(sy1 - center_y), ABS(sy2 - center_y));
3351 }
3352
3353 static boolean checkIfAllPlayersAreVisible(int center_x, int center_y)
3354 {
3355   int max_dx, max_dy;
3356
3357   setMaxCenterDistanceForAllPlayers(&max_dx, &max_dy, center_x, center_y);
3358
3359   return (max_dx <= SCR_FIELDX / 2 &&
3360           max_dy <= SCR_FIELDY / 2);
3361 }
3362 #endif
3363
3364 #endif
3365
3366 #if 1
3367
3368 void DrawRelocateScreen(int x, int y, int move_dir, boolean center_screen,
3369                         boolean quick_relocation)
3370 {
3371   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3372   boolean no_delay = (tape.warp_forward);
3373   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3374   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3375
3376   if (quick_relocation)
3377   {
3378     int offset = (setup.scroll_delay ? 3 : 0);
3379
3380 #if 0
3381     if (center_screen)
3382       offset = 0;
3383 #endif
3384
3385     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
3386     {
3387       scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3388                   x > SBX_Right + MIDPOSX ? SBX_Right :
3389                   x - MIDPOSX);
3390
3391       scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3392                   y > SBY_Lower + MIDPOSY ? SBY_Lower :
3393                   y - MIDPOSY);
3394     }
3395     else
3396     {
3397       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
3398           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
3399         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
3400
3401       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
3402           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
3403         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
3404
3405       /* don't scroll over playfield boundaries */
3406       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3407         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3408
3409       /* don't scroll over playfield boundaries */
3410       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3411         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3412     }
3413
3414     RedrawPlayfield(TRUE, 0,0,0,0);
3415   }
3416   else
3417   {
3418     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
3419                      x > SBX_Right + MIDPOSX ? SBX_Right :
3420                      x - MIDPOSX);
3421
3422     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
3423                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
3424                      y - MIDPOSY);
3425
3426     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3427
3428     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3429     {
3430       int dx = 0, dy = 0;
3431       int fx = FX, fy = FY;
3432
3433       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3434       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3435
3436       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3437         break;
3438
3439       scroll_x -= dx;
3440       scroll_y -= dy;
3441
3442       fx += dx * TILEX / 2;
3443       fy += dy * TILEY / 2;
3444
3445       ScrollLevel(dx, dy);
3446       DrawAllPlayers();
3447
3448       /* scroll in two steps of half tile size to make things smoother */
3449       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3450       FlushDisplay();
3451       Delay(wait_delay_value);
3452
3453       /* scroll second step to align at full tile size */
3454       BackToFront();
3455       Delay(wait_delay_value);
3456     }
3457
3458     DrawAllPlayers();
3459     BackToFront();
3460     Delay(wait_delay_value);
3461   }
3462 }
3463
3464 #else
3465
3466 void DrawRelocatePlayer(struct PlayerInfo *player, boolean quick_relocation)
3467 {
3468   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3469   boolean no_delay = (tape.warp_forward);
3470   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3471   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3472   int jx = player->jx;
3473   int jy = player->jy;
3474
3475   if (quick_relocation)
3476   {
3477     int offset = (setup.scroll_delay ? 3 : 0);
3478
3479     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3480     {
3481       scroll_x = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3482                   player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3483                   player->jx - MIDPOSX);
3484
3485       scroll_y = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3486                   player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3487                   player->jy - MIDPOSY);
3488     }
3489     else
3490     {
3491       if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
3492           (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3493         scroll_x = jx - MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3494
3495       if ((player->MovDir == MV_UP  && scroll_y > jy - MIDPOSY + offset) ||
3496           (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3497         scroll_y = jy - MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3498
3499       /* don't scroll over playfield boundaries */
3500       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3501         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3502
3503       /* don't scroll over playfield boundaries */
3504       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3505         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3506     }
3507
3508     RedrawPlayfield(TRUE, 0,0,0,0);
3509   }
3510   else
3511   {
3512     int scroll_xx = (player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3513                      player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3514                      player->jx - MIDPOSX);
3515
3516     int scroll_yy = (player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3517                      player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3518                      player->jy - MIDPOSY);
3519
3520     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
3521
3522     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
3523     {
3524       int dx = 0, dy = 0;
3525       int fx = FX, fy = FY;
3526
3527       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
3528       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
3529
3530       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
3531         break;
3532
3533       scroll_x -= dx;
3534       scroll_y -= dy;
3535
3536       fx += dx * TILEX / 2;
3537       fy += dy * TILEY / 2;
3538
3539       ScrollLevel(dx, dy);
3540       DrawAllPlayers();
3541
3542       /* scroll in two steps of half tile size to make things smoother */
3543       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
3544       FlushDisplay();
3545       Delay(wait_delay_value);
3546
3547       /* scroll second step to align at full tile size */
3548       BackToFront();
3549       Delay(wait_delay_value);
3550     }
3551
3552     DrawPlayer(player);
3553     BackToFront();
3554     Delay(wait_delay_value);
3555   }
3556 }
3557
3558 #endif
3559
3560 void RelocatePlayer(int jx, int jy, int el_player_raw)
3561 {
3562   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
3563   int player_nr = GET_PLAYER_NR(el_player);
3564   struct PlayerInfo *player = &stored_player[player_nr];
3565   boolean ffwd_delay = (tape.playing && tape.fast_forward);
3566   boolean no_delay = (tape.warp_forward);
3567   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
3568   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
3569   int old_jx = player->jx;
3570   int old_jy = player->jy;
3571   int old_element = Feld[old_jx][old_jy];
3572   int element = Feld[jx][jy];
3573   boolean player_relocated = (old_jx != jx || old_jy != jy);
3574
3575   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
3576   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
3577   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
3578   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
3579   int leave_side_horiz = move_dir_horiz;
3580   int leave_side_vert  = move_dir_vert;
3581   int enter_side = enter_side_horiz | enter_side_vert;
3582   int leave_side = leave_side_horiz | leave_side_vert;
3583
3584   if (player->GameOver)         /* do not reanimate dead player */
3585     return;
3586
3587   if (!player_relocated)        /* no need to relocate the player */
3588     return;
3589
3590   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
3591   {
3592     RemoveField(jx, jy);        /* temporarily remove newly placed player */
3593     DrawLevelField(jx, jy);
3594   }
3595
3596   if (player->present)
3597   {
3598     while (player->MovPos)
3599     {
3600       ScrollPlayer(player, SCROLL_GO_ON);
3601       ScrollScreen(NULL, SCROLL_GO_ON);
3602
3603       AdvanceFrameAndPlayerCounters(player->index_nr);
3604
3605       DrawPlayer(player);
3606
3607       BackToFront();
3608       Delay(wait_delay_value);
3609     }
3610
3611     DrawPlayer(player);         /* needed here only to cleanup last field */
3612     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
3613
3614     player->is_moving = FALSE;
3615   }
3616
3617   if (IS_CUSTOM_ELEMENT(old_element))
3618     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
3619                                CE_LEFT_BY_PLAYER,
3620                                player->index_bit, leave_side);
3621
3622   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
3623                                       CE_PLAYER_LEAVES_X,
3624                                       player->index_bit, leave_side);
3625
3626   Feld[jx][jy] = el_player;
3627   InitPlayerField(jx, jy, el_player, TRUE);
3628
3629   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
3630   {
3631     Feld[jx][jy] = element;
3632     InitField(jx, jy, FALSE);
3633   }
3634
3635 #if 1
3636   /* only visually relocate centered player */
3637 #if 1
3638   DrawRelocateScreen(player->jx, player->jy, player->MovDir, FALSE,
3639                      level.instant_relocation);
3640 #else
3641   if (player->index_nr == game.centered_player_nr)
3642     DrawRelocatePlayer(player, level.instant_relocation);
3643 #endif
3644 #else
3645   if (player == local_player)   /* only visually relocate local player */
3646     DrawRelocatePlayer(player, level.instant_relocation);
3647 #endif
3648
3649   TestIfPlayerTouchesBadThing(jx, jy);
3650   TestIfPlayerTouchesCustomElement(jx, jy);
3651
3652   if (IS_CUSTOM_ELEMENT(element))
3653     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
3654                                player->index_bit, enter_side);
3655
3656   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
3657                                       player->index_bit, enter_side);
3658 }
3659
3660 void Explode(int ex, int ey, int phase, int mode)
3661 {
3662   int x, y;
3663   int last_phase;
3664   int border_element;
3665
3666   /* !!! eliminate this variable !!! */
3667   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3668
3669   if (game.explosions_delayed)
3670   {
3671     ExplodeField[ex][ey] = mode;
3672     return;
3673   }
3674
3675   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
3676   {
3677     int center_element = Feld[ex][ey];
3678     int artwork_element, explosion_element;     /* set these values later */
3679
3680 #if 0
3681     /* --- This is only really needed (and now handled) in "Impact()". --- */
3682     /* do not explode moving elements that left the explode field in time */
3683     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
3684         center_element == EL_EMPTY &&
3685         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
3686       return;
3687 #endif
3688
3689 #if 0
3690     /* !!! at this place, the center element may be EL_BLOCKED !!! */
3691     if (mode == EX_TYPE_NORMAL ||
3692         mode == EX_TYPE_CENTER ||
3693         mode == EX_TYPE_CROSS)
3694       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3695 #endif
3696
3697     /* remove things displayed in background while burning dynamite */
3698     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
3699       Back[ex][ey] = 0;
3700
3701     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
3702     {
3703       /* put moving element to center field (and let it explode there) */
3704       center_element = MovingOrBlocked2Element(ex, ey);
3705       RemoveMovingField(ex, ey);
3706       Feld[ex][ey] = center_element;
3707     }
3708
3709     /* now "center_element" is finally determined -- set related values now */
3710     artwork_element = center_element;           /* for custom player artwork */
3711     explosion_element = center_element;         /* for custom player artwork */
3712
3713     if (IS_PLAYER(ex, ey))
3714     {
3715       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
3716
3717       artwork_element = stored_player[player_nr].artwork_element;
3718
3719       if (level.use_explosion_element[player_nr])
3720       {
3721         explosion_element = level.explosion_element[player_nr];
3722         artwork_element = explosion_element;
3723       }
3724     }
3725
3726 #if 1
3727     if (mode == EX_TYPE_NORMAL ||
3728         mode == EX_TYPE_CENTER ||
3729         mode == EX_TYPE_CROSS)
3730       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
3731 #endif
3732
3733 #if 1
3734     last_phase = element_info[explosion_element].explosion_delay + 1;
3735 #else
3736     last_phase = element_info[center_element].explosion_delay + 1;
3737 #endif
3738
3739     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
3740     {
3741       int xx = x - ex + 1;
3742       int yy = y - ey + 1;
3743       int element;
3744
3745       if (!IN_LEV_FIELD(x, y) ||
3746           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
3747           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
3748         continue;
3749
3750       element = Feld[x][y];
3751
3752       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
3753       {
3754         element = MovingOrBlocked2Element(x, y);
3755
3756         if (!IS_EXPLOSION_PROOF(element))
3757           RemoveMovingField(x, y);
3758       }
3759
3760       /* indestructible elements can only explode in center (but not flames) */
3761       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
3762                                            mode == EX_TYPE_BORDER)) ||
3763           element == EL_FLAMES)
3764         continue;
3765
3766       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
3767          behaviour, for example when touching a yamyam that explodes to rocks
3768          with active deadly shield, a rock is created under the player !!! */
3769       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
3770 #if 0
3771       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
3772           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
3773            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
3774 #else
3775       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
3776 #endif
3777       {
3778         if (IS_ACTIVE_BOMB(element))
3779         {
3780           /* re-activate things under the bomb like gate or penguin */
3781           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
3782           Back[x][y] = 0;
3783         }
3784
3785         continue;
3786       }
3787
3788       /* save walkable background elements while explosion on same tile */
3789       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
3790           (x != ex || y != ey || mode == EX_TYPE_BORDER))
3791         Back[x][y] = element;
3792
3793       /* ignite explodable elements reached by other explosion */
3794       if (element == EL_EXPLOSION)
3795         element = Store2[x][y];
3796
3797       if (AmoebaNr[x][y] &&
3798           (element == EL_AMOEBA_FULL ||
3799            element == EL_BD_AMOEBA ||
3800            element == EL_AMOEBA_GROWING))
3801       {
3802         AmoebaCnt[AmoebaNr[x][y]]--;
3803         AmoebaCnt2[AmoebaNr[x][y]]--;
3804       }
3805
3806       RemoveField(x, y);
3807
3808       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
3809       {
3810 #if 1
3811         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
3812
3813         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
3814 #else
3815         switch(StorePlayer[ex][ey])
3816         {
3817           case EL_PLAYER_2:
3818             Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
3819             break;
3820           case EL_PLAYER_3:
3821             Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
3822             break;
3823           case EL_PLAYER_4:
3824             Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
3825             break;
3826           case EL_PLAYER_1:
3827           default:
3828             Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
3829             break;
3830         }
3831 #endif
3832
3833         if (PLAYERINFO(ex, ey)->use_murphy)
3834           Store[x][y] = EL_EMPTY;
3835       }
3836 #if 1
3837       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
3838          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
3839       else if (ELEM_IS_PLAYER(center_element))
3840         Store[x][y] = EL_EMPTY;
3841       else if (center_element == EL_YAMYAM)
3842         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3843       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3844         Store[x][y] = element_info[center_element].content.e[xx][yy];
3845 #if 1
3846       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
3847          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
3848          otherwise) -- FIX THIS !!! */
3849       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
3850         Store[x][y] = element_info[element].content.e[1][1];
3851 #else
3852       else if (!CAN_EXPLODE(element))
3853         Store[x][y] = element_info[element].content.e[1][1];
3854 #endif
3855       else
3856         Store[x][y] = EL_EMPTY;
3857 #else
3858       else if (center_element == EL_MOLE)
3859         Store[x][y] = EL_EMERALD_RED;
3860       else if (center_element == EL_PENGUIN)
3861         Store[x][y] = EL_EMERALD_PURPLE;
3862       else if (center_element == EL_BUG)
3863         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
3864       else if (center_element == EL_BD_BUTTERFLY)
3865         Store[x][y] = EL_BD_DIAMOND;
3866       else if (center_element == EL_SP_ELECTRON)
3867         Store[x][y] = EL_SP_INFOTRON;
3868       else if (center_element == EL_AMOEBA_TO_DIAMOND)
3869         Store[x][y] = level.amoeba_content;
3870       else if (center_element == EL_YAMYAM)
3871         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
3872       else if (IS_CUSTOM_ELEMENT(center_element) &&
3873                element_info[center_element].content.e[xx][yy] != EL_EMPTY)
3874         Store[x][y] = element_info[center_element].content.e[xx][yy];
3875       else if (element == EL_WALL_EMERALD)
3876         Store[x][y] = EL_EMERALD;
3877       else if (element == EL_WALL_DIAMOND)
3878         Store[x][y] = EL_DIAMOND;
3879       else if (element == EL_WALL_BD_DIAMOND)
3880         Store[x][y] = EL_BD_DIAMOND;
3881       else if (element == EL_WALL_EMERALD_YELLOW)
3882         Store[x][y] = EL_EMERALD_YELLOW;
3883       else if (element == EL_WALL_EMERALD_RED)
3884         Store[x][y] = EL_EMERALD_RED;
3885       else if (element == EL_WALL_EMERALD_PURPLE)
3886         Store[x][y] = EL_EMERALD_PURPLE;
3887       else if (element == EL_WALL_PEARL)
3888         Store[x][y] = EL_PEARL;
3889       else if (element == EL_WALL_CRYSTAL)
3890         Store[x][y] = EL_CRYSTAL;
3891       else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
3892         Store[x][y] = element_info[element].content.e[1][1];
3893       else
3894         Store[x][y] = EL_EMPTY;
3895 #endif
3896
3897       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
3898           center_element == EL_AMOEBA_TO_DIAMOND)
3899         Store2[x][y] = element;
3900
3901       Feld[x][y] = EL_EXPLOSION;
3902       GfxElement[x][y] = artwork_element;
3903
3904 #if 0
3905       printf(":: setting gfx(%d,%d) to %d ['%s']\n",
3906              x, y, artwork_element, EL_NAME(artwork_element));
3907 #endif
3908
3909       ExplodePhase[x][y] = 1;
3910       ExplodeDelay[x][y] = last_phase;
3911
3912       Stop[x][y] = TRUE;
3913     }
3914
3915     if (center_element == EL_YAMYAM)
3916       game.yamyam_content_nr =
3917         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
3918
3919     return;
3920   }
3921
3922   if (Stop[ex][ey])
3923     return;
3924
3925   x = ex;
3926   y = ey;
3927
3928   if (phase == 1)
3929     GfxFrame[x][y] = 0;         /* restart explosion animation */
3930
3931   last_phase = ExplodeDelay[x][y];
3932
3933   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
3934
3935 #ifdef DEBUG
3936
3937   /* activate this even in non-DEBUG version until cause for crash in
3938      getGraphicAnimationFrame() (see below) is found and eliminated */
3939
3940 #endif
3941 #if 1
3942
3943 #if 1
3944   /* this can happen if the player leaves an explosion just in time */
3945   if (GfxElement[x][y] == EL_UNDEFINED)
3946     GfxElement[x][y] = EL_EMPTY;
3947 #else
3948   if (GfxElement[x][y] == EL_UNDEFINED)
3949   {
3950     printf("\n\n");
3951     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
3952     printf("Explode(): This should never happen!\n");
3953     printf("\n\n");
3954
3955     GfxElement[x][y] = EL_EMPTY;
3956   }
3957 #endif
3958
3959 #endif
3960
3961   border_element = Store2[x][y];
3962   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3963     border_element = StorePlayer[x][y];
3964
3965   if (phase == element_info[border_element].ignition_delay ||
3966       phase == last_phase)
3967   {
3968     boolean border_explosion = FALSE;
3969
3970     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
3971         !PLAYER_EXPLOSION_PROTECTED(x, y))
3972     {
3973       KillPlayerUnlessExplosionProtected(x, y);
3974       border_explosion = TRUE;
3975     }
3976     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
3977     {
3978       Feld[x][y] = Store2[x][y];
3979       Store2[x][y] = 0;
3980       Bang(x, y);
3981       border_explosion = TRUE;
3982     }
3983     else if (border_element == EL_AMOEBA_TO_DIAMOND)
3984     {
3985       AmoebeUmwandeln(x, y);
3986       Store2[x][y] = 0;
3987       border_explosion = TRUE;
3988     }
3989
3990     /* if an element just explodes due to another explosion (chain-reaction),
3991        do not immediately end the new explosion when it was the last frame of
3992        the explosion (as it would be done in the following "if"-statement!) */
3993     if (border_explosion && phase == last_phase)
3994       return;
3995   }
3996
3997   if (phase == last_phase)
3998   {
3999     int element;
4000
4001     element = Feld[x][y] = Store[x][y];
4002     Store[x][y] = Store2[x][y] = 0;
4003     GfxElement[x][y] = EL_UNDEFINED;
4004
4005     /* player can escape from explosions and might therefore be still alive */
4006     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
4007         element <= EL_PLAYER_IS_EXPLODING_4)
4008     {
4009       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
4010       int explosion_element = EL_PLAYER_1 + player_nr;
4011       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
4012       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
4013
4014       if (level.use_explosion_element[player_nr])
4015         explosion_element = level.explosion_element[player_nr];
4016
4017       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
4018                     element_info[explosion_element].content.e[xx][yy]);
4019     }
4020
4021     /* restore probably existing indestructible background element */
4022     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
4023       element = Feld[x][y] = Back[x][y];
4024     Back[x][y] = 0;
4025
4026     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
4027     GfxDir[x][y] = MV_NONE;
4028     ChangeDelay[x][y] = 0;
4029     ChangePage[x][y] = -1;
4030
4031 #if USE_NEW_CUSTOM_VALUE
4032     CustomValue[x][y] = 0;
4033 #endif
4034
4035     InitField_WithBug2(x, y, FALSE);
4036
4037     DrawLevelField(x, y);
4038
4039     TestIfElementTouchesCustomElement(x, y);
4040
4041     if (GFX_CRUMBLED(element))
4042       DrawLevelFieldCrumbledSandNeighbours(x, y);
4043
4044     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
4045       StorePlayer[x][y] = 0;
4046
4047     if (ELEM_IS_PLAYER(element))
4048       RelocatePlayer(x, y, element);
4049   }
4050   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
4051   {
4052     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
4053     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4054
4055     if (phase == delay)
4056       DrawLevelFieldCrumbledSand(x, y);
4057
4058     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
4059     {
4060       DrawLevelElement(x, y, Back[x][y]);
4061       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
4062     }
4063     else if (IS_WALKABLE_UNDER(Back[x][y]))
4064     {
4065       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4066       DrawLevelElementThruMask(x, y, Back[x][y]);
4067     }
4068     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
4069       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
4070   }
4071 }
4072
4073 void DynaExplode(int ex, int ey)
4074 {
4075   int i, j;
4076   int dynabomb_element = Feld[ex][ey];
4077   int dynabomb_size = 1;
4078   boolean dynabomb_xl = FALSE;
4079   struct PlayerInfo *player;
4080   static int xy[4][2] =
4081   {
4082     { 0, -1 },
4083     { -1, 0 },
4084     { +1, 0 },
4085     { 0, +1 }
4086   };
4087
4088   if (IS_ACTIVE_BOMB(dynabomb_element))
4089   {
4090     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
4091     dynabomb_size = player->dynabomb_size;
4092     dynabomb_xl = player->dynabomb_xl;
4093     player->dynabombs_left++;
4094   }
4095
4096   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
4097
4098   for (i = 0; i < NUM_DIRECTIONS; i++)
4099   {
4100     for (j = 1; j <= dynabomb_size; j++)
4101     {
4102       int x = ex + j * xy[i][0];
4103       int y = ey + j * xy[i][1];
4104       int element;
4105
4106       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
4107         break;
4108
4109       element = Feld[x][y];
4110
4111       /* do not restart explosions of fields with active bombs */
4112       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
4113         continue;
4114
4115       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
4116
4117       if (element != EL_EMPTY && element != EL_EXPLOSION &&
4118           !IS_DIGGABLE(element) && !dynabomb_xl)
4119         break;
4120     }
4121   }
4122 }
4123
4124 void Bang(int x, int y)
4125 {
4126   int element = MovingOrBlocked2Element(x, y);
4127   int explosion_type = EX_TYPE_NORMAL;
4128
4129   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
4130   {
4131     struct PlayerInfo *player = PLAYERINFO(x, y);
4132
4133     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
4134                             player->element_nr);
4135
4136     if (level.use_explosion_element[player->index_nr])
4137     {
4138       int explosion_element = level.explosion_element[player->index_nr];
4139
4140       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
4141         explosion_type = EX_TYPE_CROSS;
4142       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
4143         explosion_type = EX_TYPE_CENTER;
4144     }
4145   }
4146
4147   switch(element)
4148   {
4149     case EL_BUG:
4150     case EL_SPACESHIP:
4151     case EL_BD_BUTTERFLY:
4152     case EL_BD_FIREFLY:
4153     case EL_YAMYAM:
4154     case EL_DARK_YAMYAM:
4155     case EL_ROBOT:
4156     case EL_PACMAN:
4157     case EL_MOLE:
4158       RaiseScoreElement(element);
4159       break;
4160
4161     case EL_DYNABOMB_PLAYER_1_ACTIVE:
4162     case EL_DYNABOMB_PLAYER_2_ACTIVE:
4163     case EL_DYNABOMB_PLAYER_3_ACTIVE:
4164     case EL_DYNABOMB_PLAYER_4_ACTIVE:
4165     case EL_DYNABOMB_INCREASE_NUMBER:
4166     case EL_DYNABOMB_INCREASE_SIZE:
4167     case EL_DYNABOMB_INCREASE_POWER:
4168       explosion_type = EX_TYPE_DYNA;
4169       break;
4170
4171     case EL_PENGUIN:
4172     case EL_LAMP:
4173     case EL_LAMP_ACTIVE:
4174     case EL_AMOEBA_TO_DIAMOND:
4175       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
4176         explosion_type = EX_TYPE_CENTER;
4177       break;
4178
4179     default:
4180       if (element_info[element].explosion_type == EXPLODES_CROSS)
4181         explosion_type = EX_TYPE_CROSS;
4182       else if (element_info[element].explosion_type == EXPLODES_1X1)
4183         explosion_type = EX_TYPE_CENTER;
4184       break;
4185   }
4186
4187   if (explosion_type == EX_TYPE_DYNA)
4188     DynaExplode(x, y);
4189   else
4190     Explode(x, y, EX_PHASE_START, explosion_type);
4191
4192   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
4193 }
4194
4195 void SplashAcid(int x, int y)
4196 {
4197   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
4198       (!IN_LEV_FIELD(x - 1, y - 2) ||
4199        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
4200     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
4201
4202   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
4203       (!IN_LEV_FIELD(x + 1, y - 2) ||
4204        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
4205     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
4206
4207   PlayLevelSound(x, y, SND_ACID_SPLASHING);
4208 }
4209
4210 static void InitBeltMovement()
4211 {
4212   static int belt_base_element[4] =
4213   {
4214     EL_CONVEYOR_BELT_1_LEFT,
4215     EL_CONVEYOR_BELT_2_LEFT,
4216     EL_CONVEYOR_BELT_3_LEFT,
4217     EL_CONVEYOR_BELT_4_LEFT
4218   };
4219   static int belt_base_active_element[4] =
4220   {
4221     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4222     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4223     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4224     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4225   };
4226
4227   int x, y, i, j;
4228
4229   /* set frame order for belt animation graphic according to belt direction */
4230   for (i = 0; i < NUM_BELTS; i++)
4231   {
4232     int belt_nr = i;
4233
4234     for (j = 0; j < NUM_BELT_PARTS; j++)
4235     {
4236       int element = belt_base_active_element[belt_nr] + j;
4237       int graphic = el2img(element);
4238
4239       if (game.belt_dir[i] == MV_LEFT)
4240         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4241       else
4242         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4243     }
4244   }
4245
4246 #if 1
4247   SCAN_PLAYFIELD(x, y)
4248 #else
4249   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4250 #endif
4251   {
4252     int element = Feld[x][y];
4253
4254     for (i = 0; i < NUM_BELTS; i++)
4255     {
4256       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
4257       {
4258         int e_belt_nr = getBeltNrFromBeltElement(element);
4259         int belt_nr = i;
4260
4261         if (e_belt_nr == belt_nr)
4262         {
4263           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
4264
4265           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
4266         }
4267       }
4268     }
4269   }
4270 }
4271
4272 static void ToggleBeltSwitch(int x, int y)
4273 {
4274   static int belt_base_element[4] =
4275   {
4276     EL_CONVEYOR_BELT_1_LEFT,
4277     EL_CONVEYOR_BELT_2_LEFT,
4278     EL_CONVEYOR_BELT_3_LEFT,
4279     EL_CONVEYOR_BELT_4_LEFT
4280   };
4281   static int belt_base_active_element[4] =
4282   {
4283     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
4284     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
4285     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
4286     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
4287   };
4288   static int belt_base_switch_element[4] =
4289   {
4290     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
4291     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
4292     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
4293     EL_CONVEYOR_BELT_4_SWITCH_LEFT
4294   };
4295   static int belt_move_dir[4] =
4296   {
4297     MV_LEFT,
4298     MV_NONE,
4299     MV_RIGHT,
4300     MV_NONE,
4301   };
4302
4303   int element = Feld[x][y];
4304   int belt_nr = getBeltNrFromBeltSwitchElement(element);
4305   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
4306   int belt_dir = belt_move_dir[belt_dir_nr];
4307   int xx, yy, i;
4308
4309   if (!IS_BELT_SWITCH(element))
4310     return;
4311
4312   game.belt_dir_nr[belt_nr] = belt_dir_nr;
4313   game.belt_dir[belt_nr] = belt_dir;
4314
4315   if (belt_dir_nr == 3)
4316     belt_dir_nr = 1;
4317
4318   /* set frame order for belt animation graphic according to belt direction */
4319   for (i = 0; i < NUM_BELT_PARTS; i++)
4320   {
4321     int element = belt_base_active_element[belt_nr] + i;
4322     int graphic = el2img(element);
4323
4324     if (belt_dir == MV_LEFT)
4325       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
4326     else
4327       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
4328   }
4329
4330 #if 1
4331   SCAN_PLAYFIELD(xx, yy)
4332 #else
4333   for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4334 #endif
4335   {
4336     int element = Feld[xx][yy];
4337
4338     if (IS_BELT_SWITCH(element))
4339     {
4340       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
4341
4342       if (e_belt_nr == belt_nr)
4343       {
4344         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
4345         DrawLevelField(xx, yy);
4346       }
4347     }
4348     else if (IS_BELT(element) && belt_dir != MV_NONE)
4349     {
4350       int e_belt_nr = getBeltNrFromBeltElement(element);
4351
4352       if (e_belt_nr == belt_nr)
4353       {
4354         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
4355
4356         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
4357         DrawLevelField(xx, yy);
4358       }
4359     }
4360     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
4361     {
4362       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
4363
4364       if (e_belt_nr == belt_nr)
4365       {
4366         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
4367
4368         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
4369         DrawLevelField(xx, yy);
4370       }
4371     }
4372   }
4373 }
4374
4375 static void ToggleSwitchgateSwitch(int x, int y)
4376 {
4377   int xx, yy;
4378
4379   game.switchgate_pos = !game.switchgate_pos;
4380
4381 #if 1
4382   SCAN_PLAYFIELD(xx, yy)
4383 #else
4384   for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4385 #endif
4386   {
4387     int element = Feld[xx][yy];
4388
4389     if (element == EL_SWITCHGATE_SWITCH_UP ||
4390         element == EL_SWITCHGATE_SWITCH_DOWN)
4391     {
4392       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
4393       DrawLevelField(xx, yy);
4394     }
4395     else if (element == EL_SWITCHGATE_OPEN ||
4396              element == EL_SWITCHGATE_OPENING)
4397     {
4398       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
4399
4400       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
4401     }
4402     else if (element == EL_SWITCHGATE_CLOSED ||
4403              element == EL_SWITCHGATE_CLOSING)
4404     {
4405       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
4406
4407       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
4408     }
4409   }
4410 }
4411
4412 static int getInvisibleActiveFromInvisibleElement(int element)
4413 {
4414   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
4415           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
4416           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
4417           element);
4418 }
4419
4420 static int getInvisibleFromInvisibleActiveElement(int element)
4421 {
4422   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
4423           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
4424           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
4425           element);
4426 }
4427
4428 static void RedrawAllLightSwitchesAndInvisibleElements()
4429 {
4430   int x, y;
4431
4432 #if 1
4433   SCAN_PLAYFIELD(x, y)
4434 #else
4435   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4436 #endif
4437   {
4438     int element = Feld[x][y];
4439
4440     if (element == EL_LIGHT_SWITCH &&
4441         game.light_time_left > 0)
4442     {
4443       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
4444       DrawLevelField(x, y);
4445     }
4446     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
4447              game.light_time_left == 0)
4448     {
4449       Feld[x][y] = EL_LIGHT_SWITCH;
4450       DrawLevelField(x, y);
4451     }
4452     else if (element == EL_EMC_DRIPPER &&
4453              game.light_time_left > 0)
4454     {
4455       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4456       DrawLevelField(x, y);
4457     }
4458     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4459              game.light_time_left == 0)
4460     {
4461       Feld[x][y] = EL_EMC_DRIPPER;
4462       DrawLevelField(x, y);
4463     }
4464     else if (element == EL_INVISIBLE_STEELWALL ||
4465              element == EL_INVISIBLE_WALL ||
4466              element == EL_INVISIBLE_SAND)
4467     {
4468       if (game.light_time_left > 0)
4469         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4470
4471       DrawLevelField(x, y);
4472
4473       /* uncrumble neighbour fields, if needed */
4474       if (element == EL_INVISIBLE_SAND)
4475         DrawLevelFieldCrumbledSandNeighbours(x, y);
4476     }
4477     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4478              element == EL_INVISIBLE_WALL_ACTIVE ||
4479              element == EL_INVISIBLE_SAND_ACTIVE)
4480     {
4481       if (game.light_time_left == 0)
4482         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4483
4484       DrawLevelField(x, y);
4485
4486       /* re-crumble neighbour fields, if needed */
4487       if (element == EL_INVISIBLE_SAND)
4488         DrawLevelFieldCrumbledSandNeighbours(x, y);
4489     }
4490   }
4491 }
4492
4493 static void RedrawAllInvisibleElementsForLenses()
4494 {
4495   int x, y;
4496
4497 #if 1
4498   SCAN_PLAYFIELD(x, y)
4499 #else
4500   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4501 #endif
4502   {
4503     int element = Feld[x][y];
4504
4505     if (element == EL_EMC_DRIPPER &&
4506         game.lenses_time_left > 0)
4507     {
4508       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
4509       DrawLevelField(x, y);
4510     }
4511     else if (element == EL_EMC_DRIPPER_ACTIVE &&
4512              game.lenses_time_left == 0)
4513     {
4514       Feld[x][y] = EL_EMC_DRIPPER;
4515       DrawLevelField(x, y);
4516     }
4517     else if (element == EL_INVISIBLE_STEELWALL ||
4518              element == EL_INVISIBLE_WALL ||
4519              element == EL_INVISIBLE_SAND)
4520     {
4521       if (game.lenses_time_left > 0)
4522         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
4523
4524       DrawLevelField(x, y);
4525
4526       /* uncrumble neighbour fields, if needed */
4527       if (element == EL_INVISIBLE_SAND)
4528         DrawLevelFieldCrumbledSandNeighbours(x, y);
4529     }
4530     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
4531              element == EL_INVISIBLE_WALL_ACTIVE ||
4532              element == EL_INVISIBLE_SAND_ACTIVE)
4533     {
4534       if (game.lenses_time_left == 0)
4535         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
4536
4537       DrawLevelField(x, y);
4538
4539       /* re-crumble neighbour fields, if needed */
4540       if (element == EL_INVISIBLE_SAND)
4541         DrawLevelFieldCrumbledSandNeighbours(x, y);
4542     }
4543   }
4544 }
4545
4546 static void RedrawAllInvisibleElementsForMagnifier()
4547 {
4548   int x, y;
4549
4550 #if 1
4551   SCAN_PLAYFIELD(x, y)
4552 #else
4553   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
4554 #endif
4555   {
4556     int element = Feld[x][y];
4557
4558     if (element == EL_EMC_FAKE_GRASS &&
4559         game.magnify_time_left > 0)
4560     {
4561       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
4562       DrawLevelField(x, y);
4563     }
4564     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
4565              game.magnify_time_left == 0)
4566     {
4567       Feld[x][y] = EL_EMC_FAKE_GRASS;
4568       DrawLevelField(x, y);
4569     }
4570     else if (IS_GATE_GRAY(element) &&
4571              game.magnify_time_left > 0)
4572     {
4573       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
4574                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
4575                     IS_EM_GATE_GRAY(element) ?
4576                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
4577                     IS_EMC_GATE_GRAY(element) ?
4578                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
4579                     element);
4580       DrawLevelField(x, y);
4581     }
4582     else if (IS_GATE_GRAY_ACTIVE(element) &&
4583              game.magnify_time_left == 0)
4584     {
4585       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
4586                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
4587                     IS_EM_GATE_GRAY_ACTIVE(element) ?
4588                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
4589                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
4590                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
4591                     element);
4592       DrawLevelField(x, y);
4593     }
4594   }
4595 }
4596
4597 static void ToggleLightSwitch(int x, int y)
4598 {
4599   int element = Feld[x][y];
4600
4601   game.light_time_left =
4602     (element == EL_LIGHT_SWITCH ?
4603      level.time_light * FRAMES_PER_SECOND : 0);
4604
4605   RedrawAllLightSwitchesAndInvisibleElements();
4606 }
4607
4608 static void ActivateTimegateSwitch(int x, int y)
4609 {
4610   int xx, yy;
4611
4612   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
4613
4614 #if 1
4615   SCAN_PLAYFIELD(xx, yy)
4616 #else
4617   for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4618 #endif
4619   {
4620     int element = Feld[xx][yy];
4621
4622     if (element == EL_TIMEGATE_CLOSED ||
4623         element == EL_TIMEGATE_CLOSING)
4624     {
4625       Feld[xx][yy] = EL_TIMEGATE_OPENING;
4626       PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
4627     }
4628
4629     /*
4630     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
4631     {
4632       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
4633       DrawLevelField(xx, yy);
4634     }
4635     */
4636
4637   }
4638
4639   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
4640 }
4641
4642 void Impact(int x, int y)
4643 {
4644   boolean last_line = (y == lev_fieldy - 1);
4645   boolean object_hit = FALSE;
4646   boolean impact = (last_line || object_hit);
4647   int element = Feld[x][y];
4648   int smashed = EL_STEELWALL;
4649
4650   if (!last_line)       /* check if element below was hit */
4651   {
4652     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
4653       return;
4654
4655     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
4656                                          MovDir[x][y + 1] != MV_DOWN ||
4657                                          MovPos[x][y + 1] <= TILEY / 2));
4658
4659     /* do not smash moving elements that left the smashed field in time */
4660     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
4661         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
4662       object_hit = FALSE;
4663
4664 #if USE_QUICKSAND_IMPACT_BUGFIX
4665     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
4666     {
4667       RemoveMovingField(x, y + 1);
4668       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
4669       Feld[x][y + 2] = EL_ROCK;
4670       DrawLevelField(x, y + 2);
4671
4672       object_hit = TRUE;
4673     }
4674 #endif
4675
4676     if (object_hit)
4677       smashed = MovingOrBlocked2Element(x, y + 1);
4678
4679     impact = (last_line || object_hit);
4680   }
4681
4682   if (!last_line && smashed == EL_ACID) /* element falls into acid */
4683   {
4684     SplashAcid(x, y + 1);
4685     return;
4686   }
4687
4688   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
4689   /* only reset graphic animation if graphic really changes after impact */
4690   if (impact &&
4691       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
4692   {
4693     ResetGfxAnimation(x, y);
4694     DrawLevelField(x, y);
4695   }
4696
4697   if (impact && CAN_EXPLODE_IMPACT(element))
4698   {
4699     Bang(x, y);
4700     return;
4701   }
4702   else if (impact && element == EL_PEARL)
4703   {
4704     ResetGfxAnimation(x, y);
4705
4706     Feld[x][y] = EL_PEARL_BREAKING;
4707     PlayLevelSound(x, y, SND_PEARL_BREAKING);
4708     return;
4709   }
4710   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
4711   {
4712     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4713
4714     return;
4715   }
4716
4717   if (impact && element == EL_AMOEBA_DROP)
4718   {
4719     if (object_hit && IS_PLAYER(x, y + 1))
4720       KillPlayerUnlessEnemyProtected(x, y + 1);
4721     else if (object_hit && smashed == EL_PENGUIN)
4722       Bang(x, y + 1);
4723     else
4724     {
4725       Feld[x][y] = EL_AMOEBA_GROWING;
4726       Store[x][y] = EL_AMOEBA_WET;
4727
4728       ResetRandomAnimationValue(x, y);
4729     }
4730     return;
4731   }
4732
4733   if (object_hit)               /* check which object was hit */
4734   {
4735     if (CAN_PASS_MAGIC_WALL(element) && 
4736         (smashed == EL_MAGIC_WALL ||
4737          smashed == EL_BD_MAGIC_WALL))
4738     {
4739       int xx, yy;
4740       int activated_magic_wall =
4741         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
4742          EL_BD_MAGIC_WALL_ACTIVE);
4743
4744       /* activate magic wall / mill */
4745 #if 1
4746       SCAN_PLAYFIELD(xx, yy)
4747 #else
4748       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
4749 #endif
4750         if (Feld[xx][yy] == smashed)
4751           Feld[xx][yy] = activated_magic_wall;
4752
4753       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
4754       game.magic_wall_active = TRUE;
4755
4756       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
4757                             SND_MAGIC_WALL_ACTIVATING :
4758                             SND_BD_MAGIC_WALL_ACTIVATING));
4759     }
4760
4761     if (IS_PLAYER(x, y + 1))
4762     {
4763       if (CAN_SMASH_PLAYER(element))
4764       {
4765         KillPlayerUnlessEnemyProtected(x, y + 1);
4766         return;
4767       }
4768     }
4769     else if (smashed == EL_PENGUIN)
4770     {
4771       if (CAN_SMASH_PLAYER(element))
4772       {
4773         Bang(x, y + 1);
4774         return;
4775       }
4776     }
4777     else if (element == EL_BD_DIAMOND)
4778     {
4779       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
4780       {
4781         Bang(x, y + 1);
4782         return;
4783       }
4784     }
4785     else if (((element == EL_SP_INFOTRON ||
4786                element == EL_SP_ZONK) &&
4787               (smashed == EL_SP_SNIKSNAK ||
4788                smashed == EL_SP_ELECTRON ||
4789                smashed == EL_SP_DISK_ORANGE)) ||
4790              (element == EL_SP_INFOTRON &&
4791               smashed == EL_SP_DISK_YELLOW))
4792     {
4793       Bang(x, y + 1);
4794       return;
4795     }
4796     else if (CAN_SMASH_EVERYTHING(element))
4797     {
4798       if (IS_CLASSIC_ENEMY(smashed) ||
4799           CAN_EXPLODE_SMASHED(smashed))
4800       {
4801         Bang(x, y + 1);
4802         return;
4803       }
4804       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
4805       {
4806         if (smashed == EL_LAMP ||
4807             smashed == EL_LAMP_ACTIVE)
4808         {
4809           Bang(x, y + 1);
4810           return;
4811         }
4812         else if (smashed == EL_NUT)
4813         {
4814           Feld[x][y + 1] = EL_NUT_BREAKING;
4815           PlayLevelSound(x, y, SND_NUT_BREAKING);
4816           RaiseScoreElement(EL_NUT);
4817           return;
4818         }
4819         else if (smashed == EL_PEARL)
4820         {
4821           ResetGfxAnimation(x, y);
4822
4823           Feld[x][y + 1] = EL_PEARL_BREAKING;
4824           PlayLevelSound(x, y, SND_PEARL_BREAKING);
4825           return;
4826         }
4827         else if (smashed == EL_DIAMOND)
4828         {
4829           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
4830           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
4831           return;
4832         }
4833         else if (IS_BELT_SWITCH(smashed))
4834         {
4835           ToggleBeltSwitch(x, y + 1);
4836         }
4837         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
4838                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
4839         {
4840           ToggleSwitchgateSwitch(x, y + 1);
4841         }
4842         else if (smashed == EL_LIGHT_SWITCH ||
4843                  smashed == EL_LIGHT_SWITCH_ACTIVE)
4844         {
4845           ToggleLightSwitch(x, y + 1);
4846         }
4847         else
4848         {
4849 #if 0
4850           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
4851 #endif
4852
4853           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4854
4855           CheckElementChangeBySide(x, y + 1, smashed, element,
4856                                    CE_SWITCHED, CH_SIDE_TOP);
4857           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
4858                                             CH_SIDE_TOP);
4859         }
4860       }
4861       else
4862       {
4863         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
4864       }
4865     }
4866   }
4867
4868   /* play sound of magic wall / mill */
4869   if (!last_line &&
4870       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4871        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4872   {
4873     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4874       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
4875     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4876       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
4877
4878     return;
4879   }
4880
4881   /* play sound of object that hits the ground */
4882   if (last_line || object_hit)
4883     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
4884 }
4885
4886 inline static void TurnRoundExt(int x, int y)
4887 {
4888   static struct
4889   {
4890     int dx, dy;
4891   } move_xy[] =
4892   {
4893     {  0,  0 },
4894     { -1,  0 },
4895     { +1,  0 },
4896     {  0,  0 },
4897     {  0, -1 },
4898     {  0,  0 }, { 0, 0 }, { 0, 0 },
4899     {  0, +1 }
4900   };
4901   static struct
4902   {
4903     int left, right, back;
4904   } turn[] =
4905   {
4906     { 0,        0,              0        },
4907     { MV_DOWN,  MV_UP,          MV_RIGHT },
4908     { MV_UP,    MV_DOWN,        MV_LEFT  },
4909     { 0,        0,              0        },
4910     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
4911     { 0,        0,              0        },
4912     { 0,        0,              0        },
4913     { 0,        0,              0        },
4914     { MV_RIGHT, MV_LEFT,        MV_UP    }
4915   };
4916
4917   int element = Feld[x][y];
4918   int move_pattern = element_info[element].move_pattern;
4919
4920   int old_move_dir = MovDir[x][y];
4921   int left_dir  = turn[old_move_dir].left;
4922   int right_dir = turn[old_move_dir].right;
4923   int back_dir  = turn[old_move_dir].back;
4924
4925   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
4926   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
4927   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
4928   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
4929
4930   int left_x  = x + left_dx,  left_y  = y + left_dy;
4931   int right_x = x + right_dx, right_y = y + right_dy;
4932   int move_x  = x + move_dx,  move_y  = y + move_dy;
4933
4934   int xx, yy;
4935
4936   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4937   {
4938     TestIfBadThingTouchesOtherBadThing(x, y);
4939
4940     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
4941       MovDir[x][y] = right_dir;
4942     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4943       MovDir[x][y] = left_dir;
4944
4945     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
4946       MovDelay[x][y] = 9;
4947     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
4948       MovDelay[x][y] = 1;
4949   }
4950   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
4951   {
4952     TestIfBadThingTouchesOtherBadThing(x, y);
4953
4954     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
4955       MovDir[x][y] = left_dir;
4956     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
4957       MovDir[x][y] = right_dir;
4958
4959     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
4960       MovDelay[x][y] = 9;
4961     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
4962       MovDelay[x][y] = 1;
4963   }
4964   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4965   {
4966     TestIfBadThingTouchesOtherBadThing(x, y);
4967
4968     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
4969       MovDir[x][y] = left_dir;
4970     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
4971       MovDir[x][y] = right_dir;
4972
4973     if (MovDir[x][y] != old_move_dir)
4974       MovDelay[x][y] = 9;
4975   }
4976   else if (element == EL_YAMYAM)
4977   {
4978     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
4979     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
4980
4981     if (can_turn_left && can_turn_right)
4982       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4983     else if (can_turn_left)
4984       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4985     else if (can_turn_right)
4986       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4987     else
4988       MovDir[x][y] = back_dir;
4989
4990     MovDelay[x][y] = 16 + 16 * RND(3);
4991   }
4992   else if (element == EL_DARK_YAMYAM)
4993   {
4994     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4995                                                          left_x, left_y);
4996     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
4997                                                          right_x, right_y);
4998
4999     if (can_turn_left && can_turn_right)
5000       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5001     else if (can_turn_left)
5002       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5003     else if (can_turn_right)
5004       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5005     else
5006       MovDir[x][y] = back_dir;
5007
5008     MovDelay[x][y] = 16 + 16 * RND(3);
5009   }
5010   else if (element == EL_PACMAN)
5011   {
5012     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
5013     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
5014
5015     if (can_turn_left && can_turn_right)
5016       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5017     else if (can_turn_left)
5018       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5019     else if (can_turn_right)
5020       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5021     else
5022       MovDir[x][y] = back_dir;
5023
5024     MovDelay[x][y] = 6 + RND(40);
5025   }
5026   else if (element == EL_PIG)
5027   {
5028     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
5029     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
5030     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
5031     boolean should_turn_left, should_turn_right, should_move_on;
5032     int rnd_value = 24;
5033     int rnd = RND(rnd_value);
5034
5035     should_turn_left = (can_turn_left &&
5036                         (!can_move_on ||
5037                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
5038                                                    y + back_dy + left_dy)));
5039     should_turn_right = (can_turn_right &&
5040                          (!can_move_on ||
5041                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
5042                                                     y + back_dy + right_dy)));
5043     should_move_on = (can_move_on &&
5044                       (!can_turn_left ||
5045                        !can_turn_right ||
5046                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
5047                                                  y + move_dy + left_dy) ||
5048                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
5049                                                  y + move_dy + right_dy)));
5050
5051     if (should_turn_left || should_turn_right || should_move_on)
5052     {
5053       if (should_turn_left && should_turn_right && should_move_on)
5054         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
5055                         rnd < 2 * rnd_value / 3 ? right_dir :
5056                         old_move_dir);
5057       else if (should_turn_left && should_turn_right)
5058         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5059       else if (should_turn_left && should_move_on)
5060         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
5061       else if (should_turn_right && should_move_on)
5062         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
5063       else if (should_turn_left)
5064         MovDir[x][y] = left_dir;
5065       else if (should_turn_right)
5066         MovDir[x][y] = right_dir;
5067       else if (should_move_on)
5068         MovDir[x][y] = old_move_dir;
5069     }
5070     else if (can_move_on && rnd > rnd_value / 8)
5071       MovDir[x][y] = old_move_dir;
5072     else if (can_turn_left && can_turn_right)
5073       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5074     else if (can_turn_left && rnd > rnd_value / 8)
5075       MovDir[x][y] = left_dir;
5076     else if (can_turn_right && rnd > rnd_value/8)
5077       MovDir[x][y] = right_dir;
5078     else
5079       MovDir[x][y] = back_dir;
5080
5081     xx = x + move_xy[MovDir[x][y]].dx;
5082     yy = y + move_xy[MovDir[x][y]].dy;
5083
5084     if (!IN_LEV_FIELD(xx, yy) ||
5085         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
5086       MovDir[x][y] = old_move_dir;
5087
5088     MovDelay[x][y] = 0;
5089   }
5090   else if (element == EL_DRAGON)
5091   {
5092     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
5093     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
5094     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
5095     int rnd_value = 24;
5096     int rnd = RND(rnd_value);
5097
5098     if (can_move_on && rnd > rnd_value / 8)
5099       MovDir[x][y] = old_move_dir;
5100     else if (can_turn_left && can_turn_right)
5101       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
5102     else if (can_turn_left && rnd > rnd_value / 8)
5103       MovDir[x][y] = left_dir;
5104     else if (can_turn_right && rnd > rnd_value / 8)
5105       MovDir[x][y] = right_dir;
5106     else
5107       MovDir[x][y] = back_dir;
5108
5109     xx = x + move_xy[MovDir[x][y]].dx;
5110     yy = y + move_xy[MovDir[x][y]].dy;
5111
5112     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
5113       MovDir[x][y] = old_move_dir;
5114
5115     MovDelay[x][y] = 0;
5116   }
5117   else if (element == EL_MOLE)
5118   {
5119     boolean can_move_on =
5120       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
5121                             IS_AMOEBOID(Feld[move_x][move_y]) ||
5122                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
5123     if (!can_move_on)
5124     {
5125       boolean can_turn_left =
5126         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
5127                               IS_AMOEBOID(Feld[left_x][left_y])));
5128
5129       boolean can_turn_right =
5130         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
5131                               IS_AMOEBOID(Feld[right_x][right_y])));
5132
5133       if (can_turn_left && can_turn_right)
5134         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
5135       else if (can_turn_left)
5136         MovDir[x][y] = left_dir;
5137       else
5138         MovDir[x][y] = right_dir;
5139     }
5140
5141     if (MovDir[x][y] != old_move_dir)
5142       MovDelay[x][y] = 9;
5143   }
5144   else if (element == EL_BALLOON)
5145   {
5146     MovDir[x][y] = game.wind_direction;
5147     MovDelay[x][y] = 0;
5148   }
5149   else if (element == EL_SPRING)
5150   {
5151 #if USE_NEW_SPRING_BUMPER
5152     if (MovDir[x][y] & MV_HORIZONTAL)
5153     {
5154       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
5155           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5156       {
5157         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
5158         ResetGfxAnimation(move_x, move_y);
5159         DrawLevelField(move_x, move_y);
5160
5161         MovDir[x][y] = back_dir;
5162       }
5163       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5164                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
5165         MovDir[x][y] = MV_NONE;
5166     }
5167 #else
5168     if (MovDir[x][y] & MV_HORIZONTAL &&
5169         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
5170          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
5171       MovDir[x][y] = MV_NONE;
5172 #endif
5173
5174     MovDelay[x][y] = 0;
5175   }
5176   else if (element == EL_ROBOT ||
5177            element == EL_SATELLITE ||
5178            element == EL_PENGUIN ||
5179            element == EL_EMC_ANDROID)
5180   {
5181     int attr_x = -1, attr_y = -1;
5182
5183     if (AllPlayersGone)
5184     {
5185       attr_x = ExitX;
5186       attr_y = ExitY;
5187     }
5188     else
5189     {
5190       int i;
5191
5192       for (i = 0; i < MAX_PLAYERS; i++)
5193       {
5194         struct PlayerInfo *player = &stored_player[i];
5195         int jx = player->jx, jy = player->jy;
5196
5197         if (!player->active)
5198           continue;
5199
5200         if (attr_x == -1 ||
5201             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5202         {
5203           attr_x = jx;
5204           attr_y = jy;
5205         }
5206       }
5207     }
5208
5209     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
5210         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
5211          game.engine_version < VERSION_IDENT(3,1,0,0)))
5212     {
5213       attr_x = ZX;
5214       attr_y = ZY;
5215     }
5216
5217     if (element == EL_PENGUIN)
5218     {
5219       int i;
5220       static int xy[4][2] =
5221       {
5222         { 0, -1 },
5223         { -1, 0 },
5224         { +1, 0 },
5225         { 0, +1 }
5226       };
5227
5228       for (i = 0; i < NUM_DIRECTIONS; i++)
5229       {
5230         int ex = x + xy[i][0];
5231         int ey = y + xy[i][1];
5232
5233         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
5234         {
5235           attr_x = ex;
5236           attr_y = ey;
5237           break;
5238         }
5239       }
5240     }
5241
5242     MovDir[x][y] = MV_NONE;
5243     if (attr_x < x)
5244       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
5245     else if (attr_x > x)
5246       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
5247     if (attr_y < y)
5248       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
5249     else if (attr_y > y)
5250       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
5251
5252     if (element == EL_ROBOT)
5253     {
5254       int newx, newy;
5255
5256       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5257         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
5258       Moving2Blocked(x, y, &newx, &newy);
5259
5260       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
5261         MovDelay[x][y] = 8 + 8 * !RND(3);
5262       else
5263         MovDelay[x][y] = 16;
5264     }
5265     else if (element == EL_PENGUIN)
5266     {
5267       int newx, newy;
5268
5269       MovDelay[x][y] = 1;
5270
5271       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5272       {
5273         boolean first_horiz = RND(2);
5274         int new_move_dir = MovDir[x][y];
5275
5276         MovDir[x][y] =
5277           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5278         Moving2Blocked(x, y, &newx, &newy);
5279
5280         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5281           return;
5282
5283         MovDir[x][y] =
5284           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5285         Moving2Blocked(x, y, &newx, &newy);
5286
5287         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
5288           return;
5289
5290         MovDir[x][y] = old_move_dir;
5291         return;
5292       }
5293     }
5294     else if (element == EL_SATELLITE)
5295     {
5296       int newx, newy;
5297
5298       MovDelay[x][y] = 1;
5299
5300       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5301       {
5302         boolean first_horiz = RND(2);
5303         int new_move_dir = MovDir[x][y];
5304
5305         MovDir[x][y] =
5306           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5307         Moving2Blocked(x, y, &newx, &newy);
5308
5309         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5310           return;
5311
5312         MovDir[x][y] =
5313           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5314         Moving2Blocked(x, y, &newx, &newy);
5315
5316         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
5317           return;
5318
5319         MovDir[x][y] = old_move_dir;
5320         return;
5321       }
5322     }
5323     else if (element == EL_EMC_ANDROID)
5324     {
5325       static int check_pos[16] =
5326       {
5327         -1,             /*  0 => (invalid)          */
5328         7,              /*  1 => MV_LEFT            */
5329         3,              /*  2 => MV_RIGHT           */
5330         -1,             /*  3 => (invalid)          */
5331         1,              /*  4 =>            MV_UP   */
5332         0,              /*  5 => MV_LEFT  | MV_UP   */
5333         2,              /*  6 => MV_RIGHT | MV_UP   */
5334         -1,             /*  7 => (invalid)          */
5335         5,              /*  8 =>            MV_DOWN */
5336         6,              /*  9 => MV_LEFT  | MV_DOWN */
5337         4,              /* 10 => MV_RIGHT | MV_DOWN */
5338         -1,             /* 11 => (invalid)          */
5339         -1,             /* 12 => (invalid)          */
5340         -1,             /* 13 => (invalid)          */
5341         -1,             /* 14 => (invalid)          */
5342         -1,             /* 15 => (invalid)          */
5343       };
5344       static struct
5345       {
5346         int dx, dy;
5347         int dir;
5348       } check_xy[8] =
5349       {
5350         { -1, -1,       MV_LEFT  | MV_UP   },
5351         {  0, -1,                  MV_UP   },
5352         { +1, -1,       MV_RIGHT | MV_UP   },
5353         { +1,  0,       MV_RIGHT           },
5354         { +1, +1,       MV_RIGHT | MV_DOWN },
5355         {  0, +1,                  MV_DOWN },
5356         { -1, +1,       MV_LEFT  | MV_DOWN },
5357         { -1,  0,       MV_LEFT            },
5358       };
5359       int start_pos, check_order;
5360       boolean can_clone = FALSE;
5361       int i;
5362
5363       /* check if there is any free field around current position */
5364       for (i = 0; i < 8; i++)
5365       {
5366         int newx = x + check_xy[i].dx;
5367         int newy = y + check_xy[i].dy;
5368
5369         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5370         {
5371           can_clone = TRUE;
5372
5373           break;
5374         }
5375       }
5376
5377       if (can_clone)            /* randomly find an element to clone */
5378       {
5379         can_clone = FALSE;
5380
5381         start_pos = check_pos[RND(8)];
5382         check_order = (RND(2) ? -1 : +1);
5383
5384         for (i = 0; i < 8; i++)
5385         {
5386           int pos_raw = start_pos + i * check_order;
5387           int pos = (pos_raw + 8) % 8;
5388           int newx = x + check_xy[pos].dx;
5389           int newy = y + check_xy[pos].dy;
5390
5391           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
5392           {
5393             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
5394             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
5395
5396             Store[x][y] = Feld[newx][newy];
5397
5398             can_clone = TRUE;
5399
5400             break;
5401           }
5402         }
5403       }
5404
5405       if (can_clone)            /* randomly find a direction to move */
5406       {
5407         can_clone = FALSE;
5408
5409         start_pos = check_pos[RND(8)];
5410         check_order = (RND(2) ? -1 : +1);
5411
5412         for (i = 0; i < 8; i++)
5413         {
5414           int pos_raw = start_pos + i * check_order;
5415           int pos = (pos_raw + 8) % 8;
5416           int newx = x + check_xy[pos].dx;
5417           int newy = y + check_xy[pos].dy;
5418           int new_move_dir = check_xy[pos].dir;
5419
5420           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
5421           {
5422             MovDir[x][y] = new_move_dir;
5423             MovDelay[x][y] = level.android_clone_time * 8 + 1;
5424
5425             can_clone = TRUE;
5426
5427             break;
5428           }
5429         }
5430       }
5431
5432       if (can_clone)            /* cloning and moving successful */
5433         return;
5434
5435       /* cannot clone -- try to move towards player */
5436
5437       start_pos = check_pos[MovDir[x][y] & 0x0f];
5438       check_order = (RND(2) ? -1 : +1);
5439
5440       for (i = 0; i < 3; i++)
5441       {
5442         /* first check start_pos, then previous/next or (next/previous) pos */
5443         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
5444         int pos = (pos_raw + 8) % 8;
5445         int newx = x + check_xy[pos].dx;
5446         int newy = y + check_xy[pos].dy;
5447         int new_move_dir = check_xy[pos].dir;
5448
5449         if (IS_PLAYER(newx, newy))
5450           break;
5451
5452         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
5453         {
5454           MovDir[x][y] = new_move_dir;
5455           MovDelay[x][y] = level.android_move_time * 8 + 1;
5456
5457           break;
5458         }
5459       }
5460     }
5461   }
5462   else if (move_pattern == MV_TURNING_LEFT ||
5463            move_pattern == MV_TURNING_RIGHT ||
5464            move_pattern == MV_TURNING_LEFT_RIGHT ||
5465            move_pattern == MV_TURNING_RIGHT_LEFT ||
5466            move_pattern == MV_TURNING_RANDOM ||
5467            move_pattern == MV_ALL_DIRECTIONS)
5468   {
5469     boolean can_turn_left =
5470       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
5471     boolean can_turn_right =
5472       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
5473
5474     if (element_info[element].move_stepsize == 0)       /* "not moving" */
5475       return;
5476
5477     if (move_pattern == MV_TURNING_LEFT)
5478       MovDir[x][y] = left_dir;
5479     else if (move_pattern == MV_TURNING_RIGHT)
5480       MovDir[x][y] = right_dir;
5481     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
5482       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
5483     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
5484       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
5485     else if (move_pattern == MV_TURNING_RANDOM)
5486       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
5487                       can_turn_right && !can_turn_left ? right_dir :
5488                       RND(2) ? left_dir : right_dir);
5489     else if (can_turn_left && can_turn_right)
5490       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
5491     else if (can_turn_left)
5492       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
5493     else if (can_turn_right)
5494       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
5495     else
5496       MovDir[x][y] = back_dir;
5497
5498     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5499   }
5500   else if (move_pattern == MV_HORIZONTAL ||
5501            move_pattern == MV_VERTICAL)
5502   {
5503     if (move_pattern & old_move_dir)
5504       MovDir[x][y] = back_dir;
5505     else if (move_pattern == MV_HORIZONTAL)
5506       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
5507     else if (move_pattern == MV_VERTICAL)
5508       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
5509
5510     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5511   }
5512   else if (move_pattern & MV_ANY_DIRECTION)
5513   {
5514     MovDir[x][y] = move_pattern;
5515     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5516   }
5517   else if (move_pattern & MV_WIND_DIRECTION)
5518   {
5519     MovDir[x][y] = game.wind_direction;
5520     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5521   }
5522   else if (move_pattern == MV_ALONG_LEFT_SIDE)
5523   {
5524     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
5525       MovDir[x][y] = left_dir;
5526     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5527       MovDir[x][y] = right_dir;
5528
5529     if (MovDir[x][y] != old_move_dir)
5530       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5531   }
5532   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
5533   {
5534     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
5535       MovDir[x][y] = right_dir;
5536     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5537       MovDir[x][y] = left_dir;
5538
5539     if (MovDir[x][y] != old_move_dir)
5540       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5541   }
5542   else if (move_pattern == MV_TOWARDS_PLAYER ||
5543            move_pattern == MV_AWAY_FROM_PLAYER)
5544   {
5545     int attr_x = -1, attr_y = -1;
5546     int newx, newy;
5547     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
5548
5549     if (AllPlayersGone)
5550     {
5551       attr_x = ExitX;
5552       attr_y = ExitY;
5553     }
5554     else
5555     {
5556       int i;
5557
5558       for (i = 0; i < MAX_PLAYERS; i++)
5559       {
5560         struct PlayerInfo *player = &stored_player[i];
5561         int jx = player->jx, jy = player->jy;
5562
5563         if (!player->active)
5564           continue;
5565
5566         if (attr_x == -1 ||
5567             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
5568         {
5569           attr_x = jx;
5570           attr_y = jy;
5571         }
5572       }
5573     }
5574
5575     MovDir[x][y] = MV_NONE;
5576     if (attr_x < x)
5577       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
5578     else if (attr_x > x)
5579       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
5580     if (attr_y < y)
5581       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
5582     else if (attr_y > y)
5583       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
5584
5585     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5586
5587     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
5588     {
5589       boolean first_horiz = RND(2);
5590       int new_move_dir = MovDir[x][y];
5591
5592       if (element_info[element].move_stepsize == 0)     /* "not moving" */
5593       {
5594         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
5595         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5596
5597         return;
5598       }
5599
5600       MovDir[x][y] =
5601         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5602       Moving2Blocked(x, y, &newx, &newy);
5603
5604       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5605         return;
5606
5607       MovDir[x][y] =
5608         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
5609       Moving2Blocked(x, y, &newx, &newy);
5610
5611       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
5612         return;
5613
5614       MovDir[x][y] = old_move_dir;
5615     }
5616   }
5617   else if (move_pattern == MV_WHEN_PUSHED ||
5618            move_pattern == MV_WHEN_DROPPED)
5619   {
5620     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
5621       MovDir[x][y] = MV_NONE;
5622
5623     MovDelay[x][y] = 0;
5624   }
5625   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
5626   {
5627     static int test_xy[7][2] =
5628     {
5629       { 0, -1 },
5630       { -1, 0 },
5631       { +1, 0 },
5632       { 0, +1 },
5633       { 0, -1 },
5634       { -1, 0 },
5635       { +1, 0 },
5636     };
5637     static int test_dir[7] =
5638     {
5639       MV_UP,
5640       MV_LEFT,
5641       MV_RIGHT,
5642       MV_DOWN,
5643       MV_UP,
5644       MV_LEFT,
5645       MV_RIGHT,
5646     };
5647     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
5648     int move_preference = -1000000;     /* start with very low preference */
5649     int new_move_dir = MV_NONE;
5650     int start_test = RND(4);
5651     int i;
5652
5653     for (i = 0; i < NUM_DIRECTIONS; i++)
5654     {
5655       int move_dir = test_dir[start_test + i];
5656       int move_dir_preference;
5657
5658       xx = x + test_xy[start_test + i][0];
5659       yy = y + test_xy[start_test + i][1];
5660
5661       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
5662           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
5663       {
5664         new_move_dir = move_dir;
5665
5666         break;
5667       }
5668
5669       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
5670         continue;
5671
5672       move_dir_preference = -1 * RunnerVisit[xx][yy];
5673       if (hunter_mode && PlayerVisit[xx][yy] > 0)
5674         move_dir_preference = PlayerVisit[xx][yy];
5675
5676       if (move_dir_preference > move_preference)
5677       {
5678         /* prefer field that has not been visited for the longest time */
5679         move_preference = move_dir_preference;
5680         new_move_dir = move_dir;
5681       }
5682       else if (move_dir_preference == move_preference &&
5683                move_dir == old_move_dir)
5684       {
5685         /* prefer last direction when all directions are preferred equally */
5686         move_preference = move_dir_preference;
5687         new_move_dir = move_dir;
5688       }
5689     }
5690
5691     MovDir[x][y] = new_move_dir;
5692     if (old_move_dir != new_move_dir)
5693       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
5694   }
5695 }
5696
5697 static void TurnRound(int x, int y)
5698 {
5699   int direction = MovDir[x][y];
5700 #if 1
5701   int element, graphic;
5702 #endif
5703
5704   TurnRoundExt(x, y);
5705
5706   GfxDir[x][y] = MovDir[x][y];
5707
5708   if (direction != MovDir[x][y])
5709     GfxFrame[x][y] = 0;
5710
5711   if (MovDelay[x][y])
5712     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
5713
5714 #if 1
5715   element = Feld[x][y];
5716   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5717
5718   if (graphic_info[graphic].anim_global_sync)
5719     GfxFrame[x][y] = FrameCounter;
5720   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5721     GfxFrame[x][y] = CustomValue[x][y];
5722   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5723     GfxFrame[x][y] = element_info[element].collect_score;
5724 #endif
5725 }
5726
5727 static boolean JustBeingPushed(int x, int y)
5728 {
5729   int i;
5730
5731   for (i = 0; i < MAX_PLAYERS; i++)
5732   {
5733     struct PlayerInfo *player = &stored_player[i];
5734
5735     if (player->active && player->is_pushing && player->MovPos)
5736     {
5737       int next_jx = player->jx + (player->jx - player->last_jx);
5738       int next_jy = player->jy + (player->jy - player->last_jy);
5739
5740       if (x == next_jx && y == next_jy)
5741         return TRUE;
5742     }
5743   }
5744
5745   return FALSE;
5746 }
5747
5748 void StartMoving(int x, int y)
5749 {
5750   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
5751   int element = Feld[x][y];
5752
5753   if (Stop[x][y])
5754     return;
5755
5756   if (MovDelay[x][y] == 0)
5757     GfxAction[x][y] = ACTION_DEFAULT;
5758
5759   if (CAN_FALL(element) && y < lev_fieldy - 1)
5760   {
5761     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
5762         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
5763       if (JustBeingPushed(x, y))
5764         return;
5765
5766     if (element == EL_QUICKSAND_FULL)
5767     {
5768       if (IS_FREE(x, y + 1))
5769       {
5770         InitMovingField(x, y, MV_DOWN);
5771         started_moving = TRUE;
5772
5773         Feld[x][y] = EL_QUICKSAND_EMPTYING;
5774         Store[x][y] = EL_ROCK;
5775
5776         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
5777       }
5778       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5779       {
5780         if (!MovDelay[x][y])
5781           MovDelay[x][y] = TILEY + 1;
5782
5783         if (MovDelay[x][y])
5784         {
5785           MovDelay[x][y]--;
5786           if (MovDelay[x][y])
5787             return;
5788         }
5789
5790         Feld[x][y] = EL_QUICKSAND_EMPTY;
5791         Feld[x][y + 1] = EL_QUICKSAND_FULL;
5792         Store[x][y + 1] = Store[x][y];
5793         Store[x][y] = 0;
5794
5795         PlayLevelSoundAction(x, y, ACTION_FILLING);
5796       }
5797     }
5798     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
5799              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
5800     {
5801       InitMovingField(x, y, MV_DOWN);
5802       started_moving = TRUE;
5803
5804       Feld[x][y] = EL_QUICKSAND_FILLING;
5805       Store[x][y] = element;
5806
5807       PlayLevelSoundAction(x, y, ACTION_FILLING);
5808     }
5809     else if (element == EL_MAGIC_WALL_FULL)
5810     {
5811       if (IS_FREE(x, y + 1))
5812       {
5813         InitMovingField(x, y, MV_DOWN);
5814         started_moving = TRUE;
5815
5816         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
5817         Store[x][y] = EL_CHANGED(Store[x][y]);
5818       }
5819       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
5820       {
5821         if (!MovDelay[x][y])
5822           MovDelay[x][y] = TILEY/4 + 1;
5823
5824         if (MovDelay[x][y])
5825         {
5826           MovDelay[x][y]--;
5827           if (MovDelay[x][y])
5828             return;
5829         }
5830
5831         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
5832         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
5833         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
5834         Store[x][y] = 0;
5835       }
5836     }
5837     else if (element == EL_BD_MAGIC_WALL_FULL)
5838     {
5839       if (IS_FREE(x, y + 1))
5840       {
5841         InitMovingField(x, y, MV_DOWN);
5842         started_moving = TRUE;
5843
5844         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
5845         Store[x][y] = EL_CHANGED2(Store[x][y]);
5846       }
5847       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
5848       {
5849         if (!MovDelay[x][y])
5850           MovDelay[x][y] = TILEY/4 + 1;
5851
5852         if (MovDelay[x][y])
5853         {
5854           MovDelay[x][y]--;
5855           if (MovDelay[x][y])
5856             return;
5857         }
5858
5859         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
5860         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
5861         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
5862         Store[x][y] = 0;
5863       }
5864     }
5865     else if (CAN_PASS_MAGIC_WALL(element) &&
5866              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
5867               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
5868     {
5869       InitMovingField(x, y, MV_DOWN);
5870       started_moving = TRUE;
5871
5872       Feld[x][y] =
5873         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
5874          EL_BD_MAGIC_WALL_FILLING);
5875       Store[x][y] = element;
5876     }
5877     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
5878     {
5879       SplashAcid(x, y + 1);
5880
5881       InitMovingField(x, y, MV_DOWN);
5882       started_moving = TRUE;
5883
5884       Store[x][y] = EL_ACID;
5885     }
5886     else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
5887               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
5888
5889              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
5890               CAN_FALL(element) && WasJustFalling[x][y] &&
5891               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
5892
5893              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
5894               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
5895               (Feld[x][y + 1] == EL_BLOCKED)))
5896     {
5897       /* this is needed for a special case not covered by calling "Impact()"
5898          from "ContinueMoving()": if an element moves to a tile directly below
5899          another element which was just falling on that tile (which was empty
5900          in the previous frame), the falling element above would just stop
5901          instead of smashing the element below (in previous version, the above
5902          element was just checked for "moving" instead of "falling", resulting
5903          in incorrect smashes caused by horizontal movement of the above
5904          element; also, the case of the player being the element to smash was
5905          simply not covered here... :-/ ) */
5906
5907       CheckCollision[x][y] = 0;
5908
5909       Impact(x, y);
5910     }
5911     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
5912     {
5913       if (MovDir[x][y] == MV_NONE)
5914       {
5915         InitMovingField(x, y, MV_DOWN);
5916         started_moving = TRUE;
5917       }
5918     }
5919     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
5920     {
5921       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
5922         MovDir[x][y] = MV_DOWN;
5923
5924       InitMovingField(x, y, MV_DOWN);
5925       started_moving = TRUE;
5926     }
5927     else if (element == EL_AMOEBA_DROP)
5928     {
5929       Feld[x][y] = EL_AMOEBA_GROWING;
5930       Store[x][y] = EL_AMOEBA_WET;
5931     }
5932     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
5933               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
5934              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
5935              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
5936     {
5937       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
5938                                 (IS_FREE(x - 1, y + 1) ||
5939                                  Feld[x - 1][y + 1] == EL_ACID));
5940       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
5941                                 (IS_FREE(x + 1, y + 1) ||
5942                                  Feld[x + 1][y + 1] == EL_ACID));
5943       boolean can_fall_any  = (can_fall_left || can_fall_right);
5944       boolean can_fall_both = (can_fall_left && can_fall_right);
5945       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
5946
5947 #if USE_NEW_ALL_SLIPPERY
5948       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
5949       {
5950         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5951           can_fall_right = FALSE;
5952         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5953           can_fall_left = FALSE;
5954         else if (slippery_type == SLIPPERY_ONLY_LEFT)
5955           can_fall_right = FALSE;
5956         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5957           can_fall_left = FALSE;
5958
5959         can_fall_any  = (can_fall_left || can_fall_right);
5960         can_fall_both = FALSE;
5961       }
5962 #else
5963       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
5964       {
5965         if (slippery_type == SLIPPERY_ONLY_LEFT)
5966           can_fall_right = FALSE;
5967         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
5968           can_fall_left = FALSE;
5969         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
5970           can_fall_right = FALSE;
5971         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
5972           can_fall_left = FALSE;
5973
5974         can_fall_any  = (can_fall_left || can_fall_right);
5975         can_fall_both = (can_fall_left && can_fall_right);
5976       }
5977 #endif
5978
5979 #if USE_NEW_ALL_SLIPPERY
5980 #else
5981 #if USE_NEW_SP_SLIPPERY
5982       /* !!! better use the same properties as for custom elements here !!! */
5983       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
5984                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
5985       {
5986         can_fall_right = FALSE;         /* slip down on left side */
5987         can_fall_both = FALSE;
5988       }
5989 #endif
5990 #endif
5991
5992 #if USE_NEW_ALL_SLIPPERY
5993       if (can_fall_both)
5994       {
5995         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
5996           can_fall_right = FALSE;       /* slip down on left side */
5997         else
5998           can_fall_left = !(can_fall_right = RND(2));
5999
6000         can_fall_both = FALSE;
6001       }
6002 #else
6003       if (can_fall_both)
6004       {
6005         if (game.emulation == EMU_BOULDERDASH ||
6006             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
6007           can_fall_right = FALSE;       /* slip down on left side */
6008         else
6009           can_fall_left = !(can_fall_right = RND(2));
6010
6011         can_fall_both = FALSE;
6012       }
6013 #endif
6014
6015       if (can_fall_any)
6016       {
6017         /* if not determined otherwise, prefer left side for slipping down */
6018         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
6019         started_moving = TRUE;
6020       }
6021     }
6022 #if 0
6023     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
6024 #else
6025     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
6026 #endif
6027     {
6028       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
6029       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
6030       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
6031       int belt_dir = game.belt_dir[belt_nr];
6032
6033       if ((belt_dir == MV_LEFT  && left_is_free) ||
6034           (belt_dir == MV_RIGHT && right_is_free))
6035       {
6036         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
6037
6038         InitMovingField(x, y, belt_dir);
6039         started_moving = TRUE;
6040
6041         Pushed[x][y] = TRUE;
6042         Pushed[nextx][y] = TRUE;
6043
6044         GfxAction[x][y] = ACTION_DEFAULT;
6045       }
6046       else
6047       {
6048         MovDir[x][y] = 0;       /* if element was moving, stop it */
6049       }
6050     }
6051   }
6052
6053   /* not "else if" because of elements that can fall and move (EL_SPRING) */
6054 #if 0
6055   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
6056 #else
6057   if (CAN_MOVE(element) && !started_moving)
6058 #endif
6059   {
6060     int move_pattern = element_info[element].move_pattern;
6061     int newx, newy;
6062
6063 #if 0
6064 #if DEBUG
6065     if (MovDir[x][y] == MV_NONE)
6066     {
6067       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
6068              x, y, element, element_info[element].token_name);
6069       printf("StartMoving(): This should never happen!\n");
6070     }
6071 #endif
6072 #endif
6073
6074     Moving2Blocked(x, y, &newx, &newy);
6075
6076     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
6077       return;
6078
6079     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
6080         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6081     {
6082       WasJustMoving[x][y] = 0;
6083       CheckCollision[x][y] = 0;
6084
6085       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
6086
6087       if (Feld[x][y] != element)        /* element has changed */
6088         return;
6089     }
6090
6091     if (!MovDelay[x][y])        /* start new movement phase */
6092     {
6093       /* all objects that can change their move direction after each step
6094          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
6095
6096       if (element != EL_YAMYAM &&
6097           element != EL_DARK_YAMYAM &&
6098           element != EL_PACMAN &&
6099           !(move_pattern & MV_ANY_DIRECTION) &&
6100           move_pattern != MV_TURNING_LEFT &&
6101           move_pattern != MV_TURNING_RIGHT &&
6102           move_pattern != MV_TURNING_LEFT_RIGHT &&
6103           move_pattern != MV_TURNING_RIGHT_LEFT &&
6104           move_pattern != MV_TURNING_RANDOM)
6105       {
6106         TurnRound(x, y);
6107
6108         if (MovDelay[x][y] && (element == EL_BUG ||
6109                                element == EL_SPACESHIP ||
6110                                element == EL_SP_SNIKSNAK ||
6111                                element == EL_SP_ELECTRON ||
6112                                element == EL_MOLE))
6113           DrawLevelField(x, y);
6114       }
6115     }
6116
6117     if (MovDelay[x][y])         /* wait some time before next movement */
6118     {
6119       MovDelay[x][y]--;
6120
6121       if (element == EL_ROBOT ||
6122           element == EL_YAMYAM ||
6123           element == EL_DARK_YAMYAM)
6124       {
6125         DrawLevelElementAnimationIfNeeded(x, y, element);
6126         PlayLevelSoundAction(x, y, ACTION_WAITING);
6127       }
6128       else if (element == EL_SP_ELECTRON)
6129         DrawLevelElementAnimationIfNeeded(x, y, element);
6130       else if (element == EL_DRAGON)
6131       {
6132         int i;
6133         int dir = MovDir[x][y];
6134         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
6135         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
6136         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
6137                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
6138                        dir == MV_UP     ? IMG_FLAMES_1_UP :
6139                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
6140         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6141
6142         GfxAction[x][y] = ACTION_ATTACKING;
6143
6144         if (IS_PLAYER(x, y))
6145           DrawPlayerField(x, y);
6146         else
6147           DrawLevelField(x, y);
6148
6149         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
6150
6151         for (i = 1; i <= 3; i++)
6152         {
6153           int xx = x + i * dx;
6154           int yy = y + i * dy;
6155           int sx = SCREENX(xx);
6156           int sy = SCREENY(yy);
6157           int flame_graphic = graphic + (i - 1);
6158
6159           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
6160             break;
6161
6162           if (MovDelay[x][y])
6163           {
6164             int flamed = MovingOrBlocked2Element(xx, yy);
6165
6166             /* !!! */
6167 #if 0
6168             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6169               Bang(xx, yy);
6170             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
6171               RemoveMovingField(xx, yy);
6172             else
6173               RemoveField(xx, yy);
6174 #else
6175             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
6176               Bang(xx, yy);
6177             else
6178               RemoveMovingField(xx, yy);
6179 #endif
6180
6181             ChangeDelay[xx][yy] = 0;
6182
6183             Feld[xx][yy] = EL_FLAMES;
6184
6185             if (IN_SCR_FIELD(sx, sy))
6186             {
6187               DrawLevelFieldCrumbledSand(xx, yy);
6188               DrawGraphic(sx, sy, flame_graphic, frame);
6189             }
6190           }
6191           else
6192           {
6193             if (Feld[xx][yy] == EL_FLAMES)
6194               Feld[xx][yy] = EL_EMPTY;
6195             DrawLevelField(xx, yy);
6196           }
6197         }
6198       }
6199
6200       if (MovDelay[x][y])       /* element still has to wait some time */
6201       {
6202         PlayLevelSoundAction(x, y, ACTION_WAITING);
6203
6204         return;
6205       }
6206     }
6207
6208     /* now make next step */
6209
6210     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
6211
6212     if (DONT_COLLIDE_WITH(element) &&
6213         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
6214         !PLAYER_ENEMY_PROTECTED(newx, newy))
6215     {
6216       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
6217
6218       return;
6219     }
6220
6221     else if (CAN_MOVE_INTO_ACID(element) &&
6222              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
6223              !IS_MV_DIAGONAL(MovDir[x][y]) &&
6224              (MovDir[x][y] == MV_DOWN ||
6225               game.engine_version >= VERSION_IDENT(3,1,0,0)))
6226     {
6227       SplashAcid(newx, newy);
6228       Store[x][y] = EL_ACID;
6229     }
6230     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
6231     {
6232       if (Feld[newx][newy] == EL_EXIT_OPEN)
6233       {
6234         RemoveField(x, y);
6235         DrawLevelField(x, y);
6236
6237         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
6238         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
6239           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
6240
6241         local_player->friends_still_needed--;
6242         if (!local_player->friends_still_needed &&
6243             !local_player->GameOver && AllPlayersGone)
6244           local_player->LevelSolved = local_player->GameOver = TRUE;
6245
6246         return;
6247       }
6248       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
6249       {
6250         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
6251           DrawLevelField(newx, newy);
6252         else
6253           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
6254       }
6255       else if (!IS_FREE(newx, newy))
6256       {
6257         GfxAction[x][y] = ACTION_WAITING;
6258
6259         if (IS_PLAYER(x, y))
6260           DrawPlayerField(x, y);
6261         else
6262           DrawLevelField(x, y);
6263
6264         return;
6265       }
6266     }
6267     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
6268     {
6269       if (IS_FOOD_PIG(Feld[newx][newy]))
6270       {
6271         if (IS_MOVING(newx, newy))
6272           RemoveMovingField(newx, newy);
6273         else
6274         {
6275           Feld[newx][newy] = EL_EMPTY;
6276           DrawLevelField(newx, newy);
6277         }
6278
6279         PlayLevelSound(x, y, SND_PIG_DIGGING);
6280       }
6281       else if (!IS_FREE(newx, newy))
6282       {
6283         if (IS_PLAYER(x, y))
6284           DrawPlayerField(x, y);
6285         else
6286           DrawLevelField(x, y);
6287
6288         return;
6289       }
6290     }
6291     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
6292     {
6293       if (Store[x][y] != EL_EMPTY)
6294       {
6295         boolean can_clone = FALSE;
6296         int xx, yy;
6297
6298         /* check if element to clone is still there */
6299         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
6300         {
6301           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
6302           {
6303             can_clone = TRUE;
6304
6305             break;
6306           }
6307         }
6308
6309         /* cannot clone or target field not free anymore -- do not clone */
6310         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6311           Store[x][y] = EL_EMPTY;
6312       }
6313
6314       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6315       {
6316         if (IS_MV_DIAGONAL(MovDir[x][y]))
6317         {
6318           int diagonal_move_dir = MovDir[x][y];
6319           int stored = Store[x][y];
6320           int change_delay = 8;
6321           int graphic;
6322
6323           /* android is moving diagonally */
6324
6325           CreateField(x, y, EL_DIAGONAL_SHRINKING);
6326
6327           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
6328           GfxElement[x][y] = EL_EMC_ANDROID;
6329           GfxAction[x][y] = ACTION_SHRINKING;
6330           GfxDir[x][y] = diagonal_move_dir;
6331           ChangeDelay[x][y] = change_delay;
6332
6333           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
6334                                    GfxDir[x][y]);
6335
6336           DrawLevelGraphicAnimation(x, y, graphic);
6337           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
6338
6339           if (Feld[newx][newy] == EL_ACID)
6340           {
6341             SplashAcid(newx, newy);
6342
6343             return;
6344           }
6345
6346           CreateField(newx, newy, EL_DIAGONAL_GROWING);
6347
6348           Store[newx][newy] = EL_EMC_ANDROID;
6349           GfxElement[newx][newy] = EL_EMC_ANDROID;
6350           GfxAction[newx][newy] = ACTION_GROWING;
6351           GfxDir[newx][newy] = diagonal_move_dir;
6352           ChangeDelay[newx][newy] = change_delay;
6353
6354           graphic = el_act_dir2img(GfxElement[newx][newy],
6355                                    GfxAction[newx][newy], GfxDir[newx][newy]);
6356
6357           DrawLevelGraphicAnimation(newx, newy, graphic);
6358           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
6359
6360           return;
6361         }
6362         else
6363         {
6364           Feld[newx][newy] = EL_EMPTY;
6365           DrawLevelField(newx, newy);
6366
6367           PlayLevelSoundAction(x, y, ACTION_DIGGING);
6368         }
6369       }
6370       else if (!IS_FREE(newx, newy))
6371       {
6372 #if 0
6373         if (IS_PLAYER(x, y))
6374           DrawPlayerField(x, y);
6375         else
6376           DrawLevelField(x, y);
6377 #endif
6378
6379         return;
6380       }
6381     }
6382     else if (IS_CUSTOM_ELEMENT(element) &&
6383              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6384     {
6385       int new_element = Feld[newx][newy];
6386
6387       if (!IS_FREE(newx, newy))
6388       {
6389         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
6390                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
6391                       ACTION_BREAKING);
6392
6393         /* no element can dig solid indestructible elements */
6394         if (IS_INDESTRUCTIBLE(new_element) &&
6395             !IS_DIGGABLE(new_element) &&
6396             !IS_COLLECTIBLE(new_element))
6397           return;
6398
6399         if (AmoebaNr[newx][newy] &&
6400             (new_element == EL_AMOEBA_FULL ||
6401              new_element == EL_BD_AMOEBA ||
6402              new_element == EL_AMOEBA_GROWING))
6403         {
6404           AmoebaCnt[AmoebaNr[newx][newy]]--;
6405           AmoebaCnt2[AmoebaNr[newx][newy]]--;
6406         }
6407
6408         if (IS_MOVING(newx, newy))
6409           RemoveMovingField(newx, newy);
6410         else
6411         {
6412           RemoveField(newx, newy);
6413           DrawLevelField(newx, newy);
6414         }
6415
6416         /* if digged element was about to explode, prevent the explosion */
6417         ExplodeField[newx][newy] = EX_TYPE_NONE;
6418
6419         PlayLevelSoundAction(x, y, action);
6420       }
6421
6422       Store[newx][newy] = EL_EMPTY;
6423 #if 1
6424       /* this makes it possible to leave the removed element again */
6425       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6426         Store[newx][newy] = new_element;
6427 #else
6428       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
6429       {
6430         int move_leave_element = element_info[element].move_leave_element;
6431
6432         /* this makes it possible to leave the removed element again */
6433         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
6434                              new_element : move_leave_element);
6435       }
6436 #endif
6437
6438       if (move_pattern & MV_MAZE_RUNNER_STYLE)
6439       {
6440         RunnerVisit[x][y] = FrameCounter;
6441         PlayerVisit[x][y] /= 8;         /* expire player visit path */
6442       }
6443     }
6444     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
6445     {
6446       if (!IS_FREE(newx, newy))
6447       {
6448         if (IS_PLAYER(x, y))
6449           DrawPlayerField(x, y);
6450         else
6451           DrawLevelField(x, y);
6452
6453         return;
6454       }
6455       else
6456       {
6457         boolean wanna_flame = !RND(10);
6458         int dx = newx - x, dy = newy - y;
6459         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
6460         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
6461         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
6462                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
6463         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
6464                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
6465
6466         if ((wanna_flame ||
6467              IS_CLASSIC_ENEMY(element1) ||
6468              IS_CLASSIC_ENEMY(element2)) &&
6469             element1 != EL_DRAGON && element2 != EL_DRAGON &&
6470             element1 != EL_FLAMES && element2 != EL_FLAMES)
6471         {
6472           ResetGfxAnimation(x, y);
6473           GfxAction[x][y] = ACTION_ATTACKING;
6474
6475           if (IS_PLAYER(x, y))
6476             DrawPlayerField(x, y);
6477           else
6478             DrawLevelField(x, y);
6479
6480           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
6481
6482           MovDelay[x][y] = 50;
6483
6484           /* !!! */
6485 #if 0
6486           RemoveField(newx, newy);
6487 #endif
6488           Feld[newx][newy] = EL_FLAMES;
6489           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
6490           {
6491 #if 0
6492             RemoveField(newx1, newy1);
6493 #endif
6494             Feld[newx1][newy1] = EL_FLAMES;
6495           }
6496           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
6497           {
6498 #if 0
6499             RemoveField(newx2, newy2);
6500 #endif
6501             Feld[newx2][newy2] = EL_FLAMES;
6502           }
6503
6504           return;
6505         }
6506       }
6507     }
6508     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6509              Feld[newx][newy] == EL_DIAMOND)
6510     {
6511       if (IS_MOVING(newx, newy))
6512         RemoveMovingField(newx, newy);
6513       else
6514       {
6515         Feld[newx][newy] = EL_EMPTY;
6516         DrawLevelField(newx, newy);
6517       }
6518
6519       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
6520     }
6521     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
6522              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
6523     {
6524       if (AmoebaNr[newx][newy])
6525       {
6526         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6527         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6528             Feld[newx][newy] == EL_BD_AMOEBA)
6529           AmoebaCnt[AmoebaNr[newx][newy]]--;
6530       }
6531
6532 #if 0
6533       /* !!! test !!! */
6534       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
6535       {
6536         RemoveMovingField(newx, newy);
6537       }
6538 #else
6539       if (IS_MOVING(newx, newy))
6540       {
6541         RemoveMovingField(newx, newy);
6542       }
6543 #endif
6544       else
6545       {
6546         Feld[newx][newy] = EL_EMPTY;
6547         DrawLevelField(newx, newy);
6548       }
6549
6550       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
6551     }
6552     else if ((element == EL_PACMAN || element == EL_MOLE)
6553              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
6554     {
6555       if (AmoebaNr[newx][newy])
6556       {
6557         AmoebaCnt2[AmoebaNr[newx][newy]]--;
6558         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
6559             Feld[newx][newy] == EL_BD_AMOEBA)
6560           AmoebaCnt[AmoebaNr[newx][newy]]--;
6561       }
6562
6563       if (element == EL_MOLE)
6564       {
6565         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
6566         PlayLevelSound(x, y, SND_MOLE_DIGGING);
6567
6568         ResetGfxAnimation(x, y);
6569         GfxAction[x][y] = ACTION_DIGGING;
6570         DrawLevelField(x, y);
6571
6572         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
6573
6574         return;                         /* wait for shrinking amoeba */
6575       }
6576       else      /* element == EL_PACMAN */
6577       {
6578         Feld[newx][newy] = EL_EMPTY;
6579         DrawLevelField(newx, newy);
6580         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
6581       }
6582     }
6583     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
6584              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
6585               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
6586     {
6587       /* wait for shrinking amoeba to completely disappear */
6588       return;
6589     }
6590     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
6591     {
6592       /* object was running against a wall */
6593
6594       TurnRound(x, y);
6595
6596 #if 0
6597       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
6598       if (move_pattern & MV_ANY_DIRECTION &&
6599           move_pattern == MovDir[x][y])
6600       {
6601         int blocking_element =
6602           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
6603
6604         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
6605                                  MovDir[x][y]);
6606
6607         element = Feld[x][y];   /* element might have changed */
6608       }
6609 #endif
6610
6611       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
6612         DrawLevelElementAnimation(x, y, element);
6613
6614       if (DONT_TOUCH(element))
6615         TestIfBadThingTouchesPlayer(x, y);
6616
6617       return;
6618     }
6619
6620     InitMovingField(x, y, MovDir[x][y]);
6621
6622     PlayLevelSoundAction(x, y, ACTION_MOVING);
6623   }
6624
6625   if (MovDir[x][y])
6626     ContinueMoving(x, y);
6627 }
6628
6629 void ContinueMoving(int x, int y)
6630 {
6631   int element = Feld[x][y];
6632   struct ElementInfo *ei = &element_info[element];
6633   int direction = MovDir[x][y];
6634   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
6635   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
6636   int newx = x + dx, newy = y + dy;
6637   int stored = Store[x][y];
6638   int stored_new = Store[newx][newy];
6639   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
6640   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
6641   boolean last_line = (newy == lev_fieldy - 1);
6642
6643   MovPos[x][y] += getElementMoveStepsize(x, y);
6644
6645   if (pushed_by_player) /* special case: moving object pushed by player */
6646     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
6647
6648   if (ABS(MovPos[x][y]) < TILEX)
6649   {
6650     DrawLevelField(x, y);
6651
6652     return;     /* element is still moving */
6653   }
6654
6655   /* element reached destination field */
6656
6657   Feld[x][y] = EL_EMPTY;
6658   Feld[newx][newy] = element;
6659   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
6660
6661   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
6662   {
6663     element = Feld[newx][newy] = EL_ACID;
6664   }
6665   else if (element == EL_MOLE)
6666   {
6667     Feld[x][y] = EL_SAND;
6668
6669     DrawLevelFieldCrumbledSandNeighbours(x, y);
6670   }
6671   else if (element == EL_QUICKSAND_FILLING)
6672   {
6673     element = Feld[newx][newy] = get_next_element(element);
6674     Store[newx][newy] = Store[x][y];
6675   }
6676   else if (element == EL_QUICKSAND_EMPTYING)
6677   {
6678     Feld[x][y] = get_next_element(element);
6679     element = Feld[newx][newy] = Store[x][y];
6680   }
6681   else if (element == EL_MAGIC_WALL_FILLING)
6682   {
6683     element = Feld[newx][newy] = get_next_element(element);
6684     if (!game.magic_wall_active)
6685       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
6686     Store[newx][newy] = Store[x][y];
6687   }
6688   else if (element == EL_MAGIC_WALL_EMPTYING)
6689   {
6690     Feld[x][y] = get_next_element(element);
6691     if (!game.magic_wall_active)
6692       Feld[x][y] = EL_MAGIC_WALL_DEAD;
6693     element = Feld[newx][newy] = Store[x][y];
6694
6695 #if USE_NEW_CUSTOM_VALUE
6696     InitField(newx, newy, FALSE);
6697 #endif
6698   }
6699   else if (element == EL_BD_MAGIC_WALL_FILLING)
6700   {
6701     element = Feld[newx][newy] = get_next_element(element);
6702     if (!game.magic_wall_active)
6703       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
6704     Store[newx][newy] = Store[x][y];
6705   }
6706   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
6707   {
6708     Feld[x][y] = get_next_element(element);
6709     if (!game.magic_wall_active)
6710       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
6711     element = Feld[newx][newy] = Store[x][y];
6712
6713 #if USE_NEW_CUSTOM_VALUE
6714     InitField(newx, newy, FALSE);
6715 #endif
6716   }
6717   else if (element == EL_AMOEBA_DROPPING)
6718   {
6719     Feld[x][y] = get_next_element(element);
6720     element = Feld[newx][newy] = Store[x][y];
6721   }
6722   else if (element == EL_SOKOBAN_OBJECT)
6723   {
6724     if (Back[x][y])
6725       Feld[x][y] = Back[x][y];
6726
6727     if (Back[newx][newy])
6728       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
6729
6730     Back[x][y] = Back[newx][newy] = 0;
6731   }
6732
6733   Store[x][y] = EL_EMPTY;
6734   MovPos[x][y] = 0;
6735   MovDir[x][y] = 0;
6736   MovDelay[x][y] = 0;
6737
6738   MovDelay[newx][newy] = 0;
6739
6740 #if 1
6741   if (CAN_CHANGE_OR_HAS_ACTION(element))
6742 #else
6743   if (CAN_CHANGE(element))
6744 #endif
6745   {
6746     /* copy element change control values to new field */
6747     ChangeDelay[newx][newy] = ChangeDelay[x][y];
6748     ChangePage[newx][newy]  = ChangePage[x][y];
6749     ChangeCount[newx][newy] = ChangeCount[x][y];
6750     ChangeEvent[newx][newy] = ChangeEvent[x][y];
6751
6752 #if 0
6753 #if USE_NEW_CUSTOM_VALUE
6754     CustomValue[newx][newy] = CustomValue[x][y];
6755 #endif
6756 #endif
6757   }
6758
6759 #if 1
6760 #if USE_NEW_CUSTOM_VALUE
6761     CustomValue[newx][newy] = CustomValue[x][y];
6762 #endif
6763 #endif
6764
6765   ChangeDelay[x][y] = 0;
6766   ChangePage[x][y] = -1;
6767   ChangeCount[x][y] = 0;
6768   ChangeEvent[x][y] = -1;
6769
6770 #if USE_NEW_CUSTOM_VALUE
6771   CustomValue[x][y] = 0;
6772 #endif
6773
6774   /* copy animation control values to new field */
6775   GfxFrame[newx][newy]  = GfxFrame[x][y];
6776   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
6777   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
6778   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
6779
6780   Pushed[x][y] = Pushed[newx][newy] = FALSE;
6781
6782   /* some elements can leave other elements behind after moving */
6783 #if 1
6784   if (ei->move_leave_element != EL_EMPTY &&
6785       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6786       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6787 #else
6788   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
6789       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
6790       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
6791 #endif
6792   {
6793     int move_leave_element = ei->move_leave_element;
6794
6795 #if 1
6796 #if 1
6797     /* this makes it possible to leave the removed element again */
6798     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6799       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
6800 #else
6801     /* this makes it possible to leave the removed element again */
6802     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
6803       move_leave_element = stored;
6804 #endif
6805 #else
6806     /* this makes it possible to leave the removed element again */
6807     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
6808         ei->move_leave_element == EL_TRIGGER_ELEMENT)
6809       move_leave_element = stored;
6810 #endif
6811
6812     Feld[x][y] = move_leave_element;
6813
6814     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6815       MovDir[x][y] = direction;
6816
6817     InitField(x, y, FALSE);
6818
6819     if (GFX_CRUMBLED(Feld[x][y]))
6820       DrawLevelFieldCrumbledSandNeighbours(x, y);
6821
6822     if (ELEM_IS_PLAYER(move_leave_element))
6823       RelocatePlayer(x, y, move_leave_element);
6824   }
6825
6826   /* do this after checking for left-behind element */
6827   ResetGfxAnimation(x, y);      /* reset animation values for old field */
6828
6829   if (!CAN_MOVE(element) ||
6830       (CAN_FALL(element) && direction == MV_DOWN &&
6831        (element == EL_SPRING ||
6832         element_info[element].move_pattern == MV_WHEN_PUSHED ||
6833         element_info[element].move_pattern == MV_WHEN_DROPPED)))
6834     GfxDir[x][y] = MovDir[newx][newy] = 0;
6835
6836   DrawLevelField(x, y);
6837   DrawLevelField(newx, newy);
6838
6839   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
6840
6841   /* prevent pushed element from moving on in pushed direction */
6842   if (pushed_by_player && CAN_MOVE(element) &&
6843       element_info[element].move_pattern & MV_ANY_DIRECTION &&
6844       !(element_info[element].move_pattern & direction))
6845     TurnRound(newx, newy);
6846
6847   /* prevent elements on conveyor belt from moving on in last direction */
6848   if (pushed_by_conveyor && CAN_FALL(element) &&
6849       direction & MV_HORIZONTAL)
6850     MovDir[newx][newy] = 0;
6851
6852   if (!pushed_by_player)
6853   {
6854     int nextx = newx + dx, nexty = newy + dy;
6855     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
6856
6857     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
6858
6859     if (CAN_FALL(element) && direction == MV_DOWN)
6860       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
6861
6862     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
6863       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
6864   }
6865
6866   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
6867   {
6868     TestIfBadThingTouchesPlayer(newx, newy);
6869     TestIfBadThingTouchesFriend(newx, newy);
6870
6871     if (!IS_CUSTOM_ELEMENT(element))
6872       TestIfBadThingTouchesOtherBadThing(newx, newy);
6873   }
6874   else if (element == EL_PENGUIN)
6875     TestIfFriendTouchesBadThing(newx, newy);
6876
6877   /* give the player one last chance (one more frame) to move away */
6878   if (CAN_FALL(element) && direction == MV_DOWN &&
6879       (last_line || (!IS_FREE(x, newy + 1) &&
6880                      (!IS_PLAYER(x, newy + 1) ||
6881                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
6882     Impact(x, newy);
6883
6884   if (pushed_by_player && !game.use_change_when_pushing_bug)
6885   {
6886     int push_side = MV_DIR_OPPOSITE(direction);
6887     struct PlayerInfo *player = PLAYERINFO(x, y);
6888
6889     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
6890                                player->index_bit, push_side);
6891     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
6892                                         player->index_bit, push_side);
6893   }
6894
6895   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
6896     MovDelay[newx][newy] = 1;
6897
6898   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
6899
6900   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
6901
6902 #if 0
6903   if (ChangePage[newx][newy] != -1)             /* delayed change */
6904   {
6905     int page = ChangePage[newx][newy];
6906     struct ElementChangeInfo *change = &ei->change_page[page];
6907
6908     ChangePage[newx][newy] = -1;
6909
6910     if (change->can_change)
6911     {
6912       if (ChangeElement(newx, newy, element, page))
6913       {
6914         if (change->post_change_function)
6915           change->post_change_function(newx, newy);
6916       }
6917     }
6918
6919     if (change->has_action)
6920       ExecuteCustomElementAction(newx, newy, element, page);
6921   }
6922 #endif
6923
6924   TestIfElementHitsCustomElement(newx, newy, direction);
6925   TestIfPlayerTouchesCustomElement(newx, newy);
6926   TestIfElementTouchesCustomElement(newx, newy);
6927
6928 #if 1
6929   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
6930       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
6931     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
6932                                       MV_DIR_OPPOSITE(direction));
6933 #endif
6934 }
6935
6936 int AmoebeNachbarNr(int ax, int ay)
6937 {
6938   int i;
6939   int element = Feld[ax][ay];
6940   int group_nr = 0;
6941   static int xy[4][2] =
6942   {
6943     { 0, -1 },
6944     { -1, 0 },
6945     { +1, 0 },
6946     { 0, +1 }
6947   };
6948
6949   for (i = 0; i < NUM_DIRECTIONS; i++)
6950   {
6951     int x = ax + xy[i][0];
6952     int y = ay + xy[i][1];
6953
6954     if (!IN_LEV_FIELD(x, y))
6955       continue;
6956
6957     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
6958       group_nr = AmoebaNr[x][y];
6959   }
6960
6961   return group_nr;
6962 }
6963
6964 void AmoebenVereinigen(int ax, int ay)
6965 {
6966   int i, x, y, xx, yy;
6967   int new_group_nr = AmoebaNr[ax][ay];
6968   static int xy[4][2] =
6969   {
6970     { 0, -1 },
6971     { -1, 0 },
6972     { +1, 0 },
6973     { 0, +1 }
6974   };
6975
6976   if (new_group_nr == 0)
6977     return;
6978
6979   for (i = 0; i < NUM_DIRECTIONS; i++)
6980   {
6981     x = ax + xy[i][0];
6982     y = ay + xy[i][1];
6983
6984     if (!IN_LEV_FIELD(x, y))
6985       continue;
6986
6987     if ((Feld[x][y] == EL_AMOEBA_FULL ||
6988          Feld[x][y] == EL_BD_AMOEBA ||
6989          Feld[x][y] == EL_AMOEBA_DEAD) &&
6990         AmoebaNr[x][y] != new_group_nr)
6991     {
6992       int old_group_nr = AmoebaNr[x][y];
6993
6994       if (old_group_nr == 0)
6995         return;
6996
6997       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
6998       AmoebaCnt[old_group_nr] = 0;
6999       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
7000       AmoebaCnt2[old_group_nr] = 0;
7001
7002 #if 1
7003       SCAN_PLAYFIELD(xx, yy)
7004 #else
7005       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
7006 #endif
7007       {
7008         if (AmoebaNr[xx][yy] == old_group_nr)
7009           AmoebaNr[xx][yy] = new_group_nr;
7010       }
7011     }
7012   }
7013 }
7014
7015 void AmoebeUmwandeln(int ax, int ay)
7016 {
7017   int i, x, y;
7018
7019   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
7020   {
7021     int group_nr = AmoebaNr[ax][ay];
7022
7023 #ifdef DEBUG
7024     if (group_nr == 0)
7025     {
7026       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
7027       printf("AmoebeUmwandeln(): This should never happen!\n");
7028       return;
7029     }
7030 #endif
7031
7032 #if 1
7033     SCAN_PLAYFIELD(x, y)
7034 #else
7035     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7036 #endif
7037     {
7038       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
7039       {
7040         AmoebaNr[x][y] = 0;
7041         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
7042       }
7043     }
7044
7045     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
7046                             SND_AMOEBA_TURNING_TO_GEM :
7047                             SND_AMOEBA_TURNING_TO_ROCK));
7048     Bang(ax, ay);
7049   }
7050   else
7051   {
7052     static int xy[4][2] =
7053     {
7054       { 0, -1 },
7055       { -1, 0 },
7056       { +1, 0 },
7057       { 0, +1 }
7058     };
7059
7060     for (i = 0; i < NUM_DIRECTIONS; i++)
7061     {
7062       x = ax + xy[i][0];
7063       y = ay + xy[i][1];
7064
7065       if (!IN_LEV_FIELD(x, y))
7066         continue;
7067
7068       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
7069       {
7070         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
7071                               SND_AMOEBA_TURNING_TO_GEM :
7072                               SND_AMOEBA_TURNING_TO_ROCK));
7073         Bang(x, y);
7074       }
7075     }
7076   }
7077 }
7078
7079 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
7080 {
7081   int x, y;
7082   int group_nr = AmoebaNr[ax][ay];
7083   boolean done = FALSE;
7084
7085 #ifdef DEBUG
7086   if (group_nr == 0)
7087   {
7088     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
7089     printf("AmoebeUmwandelnBD(): This should never happen!\n");
7090     return;
7091   }
7092 #endif
7093
7094 #if 1
7095   SCAN_PLAYFIELD(x, y)
7096 #else
7097   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7098 #endif
7099   {
7100     if (AmoebaNr[x][y] == group_nr &&
7101         (Feld[x][y] == EL_AMOEBA_DEAD ||
7102          Feld[x][y] == EL_BD_AMOEBA ||
7103          Feld[x][y] == EL_AMOEBA_GROWING))
7104     {
7105       AmoebaNr[x][y] = 0;
7106       Feld[x][y] = new_element;
7107       InitField(x, y, FALSE);
7108       DrawLevelField(x, y);
7109       done = TRUE;
7110     }
7111   }
7112
7113   if (done)
7114     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
7115                             SND_BD_AMOEBA_TURNING_TO_ROCK :
7116                             SND_BD_AMOEBA_TURNING_TO_GEM));
7117 }
7118
7119 void AmoebeWaechst(int x, int y)
7120 {
7121   static unsigned long sound_delay = 0;
7122   static unsigned long sound_delay_value = 0;
7123
7124   if (!MovDelay[x][y])          /* start new growing cycle */
7125   {
7126     MovDelay[x][y] = 7;
7127
7128     if (DelayReached(&sound_delay, sound_delay_value))
7129     {
7130       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
7131       sound_delay_value = 30;
7132     }
7133   }
7134
7135   if (MovDelay[x][y])           /* wait some time before growing bigger */
7136   {
7137     MovDelay[x][y]--;
7138     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7139     {
7140       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
7141                                            6 - MovDelay[x][y]);
7142
7143       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
7144     }
7145
7146     if (!MovDelay[x][y])
7147     {
7148       Feld[x][y] = Store[x][y];
7149       Store[x][y] = 0;
7150       DrawLevelField(x, y);
7151     }
7152   }
7153 }
7154
7155 void AmoebaDisappearing(int x, int y)
7156 {
7157   static unsigned long sound_delay = 0;
7158   static unsigned long sound_delay_value = 0;
7159
7160   if (!MovDelay[x][y])          /* start new shrinking cycle */
7161   {
7162     MovDelay[x][y] = 7;
7163
7164     if (DelayReached(&sound_delay, sound_delay_value))
7165       sound_delay_value = 30;
7166   }
7167
7168   if (MovDelay[x][y])           /* wait some time before shrinking */
7169   {
7170     MovDelay[x][y]--;
7171     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7172     {
7173       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
7174                                            6 - MovDelay[x][y]);
7175
7176       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
7177     }
7178
7179     if (!MovDelay[x][y])
7180     {
7181       Feld[x][y] = EL_EMPTY;
7182       DrawLevelField(x, y);
7183
7184       /* don't let mole enter this field in this cycle;
7185          (give priority to objects falling to this field from above) */
7186       Stop[x][y] = TRUE;
7187     }
7188   }
7189 }
7190
7191 void AmoebeAbleger(int ax, int ay)
7192 {
7193   int i;
7194   int element = Feld[ax][ay];
7195   int graphic = el2img(element);
7196   int newax = ax, neway = ay;
7197   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
7198   static int xy[4][2] =
7199   {
7200     { 0, -1 },
7201     { -1, 0 },
7202     { +1, 0 },
7203     { 0, +1 }
7204   };
7205
7206   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
7207   {
7208     Feld[ax][ay] = EL_AMOEBA_DEAD;
7209     DrawLevelField(ax, ay);
7210     return;
7211   }
7212
7213   if (IS_ANIMATED(graphic))
7214     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7215
7216   if (!MovDelay[ax][ay])        /* start making new amoeba field */
7217     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
7218
7219   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
7220   {
7221     MovDelay[ax][ay]--;
7222     if (MovDelay[ax][ay])
7223       return;
7224   }
7225
7226   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
7227   {
7228     int start = RND(4);
7229     int x = ax + xy[start][0];
7230     int y = ay + xy[start][1];
7231
7232     if (!IN_LEV_FIELD(x, y))
7233       return;
7234
7235     if (IS_FREE(x, y) ||
7236         CAN_GROW_INTO(Feld[x][y]) ||
7237         Feld[x][y] == EL_QUICKSAND_EMPTY)
7238     {
7239       newax = x;
7240       neway = y;
7241     }
7242
7243     if (newax == ax && neway == ay)
7244       return;
7245   }
7246   else                          /* normal or "filled" (BD style) amoeba */
7247   {
7248     int start = RND(4);
7249     boolean waiting_for_player = FALSE;
7250
7251     for (i = 0; i < NUM_DIRECTIONS; i++)
7252     {
7253       int j = (start + i) % 4;
7254       int x = ax + xy[j][0];
7255       int y = ay + xy[j][1];
7256
7257       if (!IN_LEV_FIELD(x, y))
7258         continue;
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         break;
7267       }
7268       else if (IS_PLAYER(x, y))
7269         waiting_for_player = TRUE;
7270     }
7271
7272     if (newax == ax && neway == ay)             /* amoeba cannot grow */
7273     {
7274       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
7275       {
7276         Feld[ax][ay] = EL_AMOEBA_DEAD;
7277         DrawLevelField(ax, ay);
7278         AmoebaCnt[AmoebaNr[ax][ay]]--;
7279
7280         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
7281         {
7282           if (element == EL_AMOEBA_FULL)
7283             AmoebeUmwandeln(ax, ay);
7284           else if (element == EL_BD_AMOEBA)
7285             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
7286         }
7287       }
7288       return;
7289     }
7290     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
7291     {
7292       /* amoeba gets larger by growing in some direction */
7293
7294       int new_group_nr = AmoebaNr[ax][ay];
7295
7296 #ifdef DEBUG
7297   if (new_group_nr == 0)
7298   {
7299     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
7300     printf("AmoebeAbleger(): This should never happen!\n");
7301     return;
7302   }
7303 #endif
7304
7305       AmoebaNr[newax][neway] = new_group_nr;
7306       AmoebaCnt[new_group_nr]++;
7307       AmoebaCnt2[new_group_nr]++;
7308
7309       /* if amoeba touches other amoeba(s) after growing, unify them */
7310       AmoebenVereinigen(newax, neway);
7311
7312       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
7313       {
7314         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
7315         return;
7316       }
7317     }
7318   }
7319
7320   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
7321       (neway == lev_fieldy - 1 && newax != ax))
7322   {
7323     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
7324     Store[newax][neway] = element;
7325   }
7326   else if (neway == ay || element == EL_EMC_DRIPPER)
7327   {
7328     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
7329
7330     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
7331   }
7332   else
7333   {
7334     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
7335     Feld[ax][ay] = EL_AMOEBA_DROPPING;
7336     Store[ax][ay] = EL_AMOEBA_DROP;
7337     ContinueMoving(ax, ay);
7338     return;
7339   }
7340
7341   DrawLevelField(newax, neway);
7342 }
7343
7344 void Life(int ax, int ay)
7345 {
7346   int x1, y1, x2, y2;
7347 #if 0
7348   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
7349 #endif
7350   int life_time = 40;
7351   int element = Feld[ax][ay];
7352   int graphic = el2img(element);
7353   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
7354                          level.biomaze);
7355   boolean changed = FALSE;
7356
7357   if (IS_ANIMATED(graphic))
7358     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7359
7360   if (Stop[ax][ay])
7361     return;
7362
7363   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
7364     MovDelay[ax][ay] = life_time;
7365
7366   if (MovDelay[ax][ay])         /* wait some time before next cycle */
7367   {
7368     MovDelay[ax][ay]--;
7369     if (MovDelay[ax][ay])
7370       return;
7371   }
7372
7373   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
7374   {
7375     int xx = ax+x1, yy = ay+y1;
7376     int nachbarn = 0;
7377
7378     if (!IN_LEV_FIELD(xx, yy))
7379       continue;
7380
7381     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
7382     {
7383       int x = xx+x2, y = yy+y2;
7384
7385       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
7386         continue;
7387
7388       if (((Feld[x][y] == element ||
7389             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
7390            !Stop[x][y]) ||
7391           (IS_FREE(x, y) && Stop[x][y]))
7392         nachbarn++;
7393     }
7394
7395     if (xx == ax && yy == ay)           /* field in the middle */
7396     {
7397       if (nachbarn < life_parameter[0] ||
7398           nachbarn > life_parameter[1])
7399       {
7400         Feld[xx][yy] = EL_EMPTY;
7401         if (!Stop[xx][yy])
7402           DrawLevelField(xx, yy);
7403         Stop[xx][yy] = TRUE;
7404         changed = TRUE;
7405       }
7406     }
7407     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
7408     {                                   /* free border field */
7409       if (nachbarn >= life_parameter[2] &&
7410           nachbarn <= life_parameter[3])
7411       {
7412         Feld[xx][yy] = element;
7413         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
7414         if (!Stop[xx][yy])
7415           DrawLevelField(xx, yy);
7416         Stop[xx][yy] = TRUE;
7417         changed = TRUE;
7418       }
7419     }
7420   }
7421
7422   if (changed)
7423     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
7424                    SND_GAME_OF_LIFE_GROWING);
7425 }
7426
7427 static void InitRobotWheel(int x, int y)
7428 {
7429   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
7430 }
7431
7432 static void RunRobotWheel(int x, int y)
7433 {
7434   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
7435 }
7436
7437 static void StopRobotWheel(int x, int y)
7438 {
7439   if (ZX == x && ZY == y)
7440     ZX = ZY = -1;
7441 }
7442
7443 static void InitTimegateWheel(int x, int y)
7444 {
7445   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
7446 }
7447
7448 static void RunTimegateWheel(int x, int y)
7449 {
7450   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
7451 }
7452
7453 static void InitMagicBallDelay(int x, int y)
7454 {
7455 #if 1
7456   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
7457 #else
7458   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
7459 #endif
7460 }
7461
7462 static void ActivateMagicBall(int bx, int by)
7463 {
7464   int x, y;
7465
7466   if (level.ball_random)
7467   {
7468     int pos_border = RND(8);    /* select one of the eight border elements */
7469     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
7470     int xx = pos_content % 3;
7471     int yy = pos_content / 3;
7472
7473     x = bx - 1 + xx;
7474     y = by - 1 + yy;
7475
7476     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7477       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7478   }
7479   else
7480   {
7481     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
7482     {
7483       int xx = x - bx + 1;
7484       int yy = y - by + 1;
7485
7486       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
7487         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
7488     }
7489   }
7490
7491   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
7492 }
7493
7494 static void InitDiagonalMovingElement(int x, int y)
7495 {
7496 #if 0
7497   MovDelay[x][y] = level.android_move_time;
7498 #endif
7499 }
7500
7501 void CheckExit(int x, int y)
7502 {
7503   if (local_player->gems_still_needed > 0 ||
7504       local_player->sokobanfields_still_needed > 0 ||
7505       local_player->lights_still_needed > 0)
7506   {
7507     int element = Feld[x][y];
7508     int graphic = el2img(element);
7509
7510     if (IS_ANIMATED(graphic))
7511       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7512
7513     return;
7514   }
7515
7516   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7517     return;
7518
7519   Feld[x][y] = EL_EXIT_OPENING;
7520
7521   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
7522 }
7523
7524 void CheckExitSP(int x, int y)
7525 {
7526   if (local_player->gems_still_needed > 0)
7527   {
7528     int element = Feld[x][y];
7529     int graphic = el2img(element);
7530
7531     if (IS_ANIMATED(graphic))
7532       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7533
7534     return;
7535   }
7536
7537   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
7538     return;
7539
7540   Feld[x][y] = EL_SP_EXIT_OPENING;
7541
7542   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
7543 }
7544
7545 static void CloseAllOpenTimegates()
7546 {
7547   int x, y;
7548
7549 #if 1
7550   SCAN_PLAYFIELD(x, y)
7551 #else
7552   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7553 #endif
7554   {
7555     int element = Feld[x][y];
7556
7557     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
7558     {
7559       Feld[x][y] = EL_TIMEGATE_CLOSING;
7560
7561       PlayLevelSoundAction(x, y, ACTION_CLOSING);
7562     }
7563   }
7564 }
7565
7566 void EdelsteinFunkeln(int x, int y)
7567 {
7568   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
7569     return;
7570
7571   if (Feld[x][y] == EL_BD_DIAMOND)
7572     return;
7573
7574   if (MovDelay[x][y] == 0)      /* next animation frame */
7575     MovDelay[x][y] = 11 * !SimpleRND(500);
7576
7577   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
7578   {
7579     MovDelay[x][y]--;
7580
7581     if (setup.direct_draw && MovDelay[x][y])
7582       SetDrawtoField(DRAW_BUFFERED);
7583
7584     DrawLevelElementAnimation(x, y, Feld[x][y]);
7585
7586     if (MovDelay[x][y] != 0)
7587     {
7588       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
7589                                            10 - MovDelay[x][y]);
7590
7591       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
7592
7593       if (setup.direct_draw)
7594       {
7595         int dest_x, dest_y;
7596
7597         dest_x = FX + SCREENX(x) * TILEX;
7598         dest_y = FY + SCREENY(y) * TILEY;
7599
7600         BlitBitmap(drawto_field, window,
7601                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
7602         SetDrawtoField(DRAW_DIRECT);
7603       }
7604     }
7605   }
7606 }
7607
7608 void MauerWaechst(int x, int y)
7609 {
7610   int delay = 6;
7611
7612   if (!MovDelay[x][y])          /* next animation frame */
7613     MovDelay[x][y] = 3 * delay;
7614
7615   if (MovDelay[x][y])           /* wait some time before next frame */
7616   {
7617     MovDelay[x][y]--;
7618
7619     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
7620     {
7621       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
7622       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
7623
7624       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
7625     }
7626
7627     if (!MovDelay[x][y])
7628     {
7629       if (MovDir[x][y] == MV_LEFT)
7630       {
7631         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
7632           DrawLevelField(x - 1, y);
7633       }
7634       else if (MovDir[x][y] == MV_RIGHT)
7635       {
7636         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
7637           DrawLevelField(x + 1, y);
7638       }
7639       else if (MovDir[x][y] == MV_UP)
7640       {
7641         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
7642           DrawLevelField(x, y - 1);
7643       }
7644       else
7645       {
7646         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
7647           DrawLevelField(x, y + 1);
7648       }
7649
7650       Feld[x][y] = Store[x][y];
7651       Store[x][y] = 0;
7652       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7653       DrawLevelField(x, y);
7654     }
7655   }
7656 }
7657
7658 void MauerAbleger(int ax, int ay)
7659 {
7660   int element = Feld[ax][ay];
7661   int graphic = el2img(element);
7662   boolean oben_frei = FALSE, unten_frei = FALSE;
7663   boolean links_frei = FALSE, rechts_frei = FALSE;
7664   boolean oben_massiv = FALSE, unten_massiv = FALSE;
7665   boolean links_massiv = FALSE, rechts_massiv = FALSE;
7666   boolean new_wall = FALSE;
7667
7668   if (IS_ANIMATED(graphic))
7669     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
7670
7671   if (!MovDelay[ax][ay])        /* start building new wall */
7672     MovDelay[ax][ay] = 6;
7673
7674   if (MovDelay[ax][ay])         /* wait some time before building new wall */
7675   {
7676     MovDelay[ax][ay]--;
7677     if (MovDelay[ax][ay])
7678       return;
7679   }
7680
7681   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
7682     oben_frei = TRUE;
7683   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
7684     unten_frei = TRUE;
7685   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
7686     links_frei = TRUE;
7687   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
7688     rechts_frei = TRUE;
7689
7690   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
7691       element == EL_EXPANDABLE_WALL_ANY)
7692   {
7693     if (oben_frei)
7694     {
7695       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
7696       Store[ax][ay-1] = element;
7697       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
7698       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
7699         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
7700                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
7701       new_wall = TRUE;
7702     }
7703     if (unten_frei)
7704     {
7705       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
7706       Store[ax][ay+1] = element;
7707       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
7708       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
7709         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
7710                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
7711       new_wall = TRUE;
7712     }
7713   }
7714
7715   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7716       element == EL_EXPANDABLE_WALL_ANY ||
7717       element == EL_EXPANDABLE_WALL)
7718   {
7719     if (links_frei)
7720     {
7721       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
7722       Store[ax-1][ay] = element;
7723       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
7724       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
7725         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
7726                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
7727       new_wall = TRUE;
7728     }
7729
7730     if (rechts_frei)
7731     {
7732       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
7733       Store[ax+1][ay] = element;
7734       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
7735       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
7736         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
7737                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
7738       new_wall = TRUE;
7739     }
7740   }
7741
7742   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
7743     DrawLevelField(ax, ay);
7744
7745   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
7746     oben_massiv = TRUE;
7747   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
7748     unten_massiv = TRUE;
7749   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
7750     links_massiv = TRUE;
7751   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
7752     rechts_massiv = TRUE;
7753
7754   if (((oben_massiv && unten_massiv) ||
7755        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7756        element == EL_EXPANDABLE_WALL) &&
7757       ((links_massiv && rechts_massiv) ||
7758        element == EL_EXPANDABLE_WALL_VERTICAL))
7759     Feld[ax][ay] = EL_WALL;
7760
7761   if (new_wall)
7762     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
7763 }
7764
7765 void CheckForDragon(int x, int y)
7766 {
7767   int i, j;
7768   boolean dragon_found = FALSE;
7769   static int xy[4][2] =
7770   {
7771     { 0, -1 },
7772     { -1, 0 },
7773     { +1, 0 },
7774     { 0, +1 }
7775   };
7776
7777   for (i = 0; i < NUM_DIRECTIONS; i++)
7778   {
7779     for (j = 0; j < 4; j++)
7780     {
7781       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7782
7783       if (IN_LEV_FIELD(xx, yy) &&
7784           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
7785       {
7786         if (Feld[xx][yy] == EL_DRAGON)
7787           dragon_found = TRUE;
7788       }
7789       else
7790         break;
7791     }
7792   }
7793
7794   if (!dragon_found)
7795   {
7796     for (i = 0; i < NUM_DIRECTIONS; i++)
7797     {
7798       for (j = 0; j < 3; j++)
7799       {
7800         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
7801   
7802         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
7803         {
7804           Feld[xx][yy] = EL_EMPTY;
7805           DrawLevelField(xx, yy);
7806         }
7807         else
7808           break;
7809       }
7810     }
7811   }
7812 }
7813
7814 static void InitBuggyBase(int x, int y)
7815 {
7816   int element = Feld[x][y];
7817   int activating_delay = FRAMES_PER_SECOND / 4;
7818
7819   ChangeDelay[x][y] =
7820     (element == EL_SP_BUGGY_BASE ?
7821      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
7822      element == EL_SP_BUGGY_BASE_ACTIVATING ?
7823      activating_delay :
7824      element == EL_SP_BUGGY_BASE_ACTIVE ?
7825      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
7826 }
7827
7828 static void WarnBuggyBase(int x, int y)
7829 {
7830   int i;
7831   static int xy[4][2] =
7832   {
7833     { 0, -1 },
7834     { -1, 0 },
7835     { +1, 0 },
7836     { 0, +1 }
7837   };
7838
7839   for (i = 0; i < NUM_DIRECTIONS; i++)
7840   {
7841     int xx = x + xy[i][0];
7842     int yy = y + xy[i][1];
7843
7844     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
7845     {
7846       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
7847
7848       break;
7849     }
7850   }
7851 }
7852
7853 static void InitTrap(int x, int y)
7854 {
7855   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
7856 }
7857
7858 static void ActivateTrap(int x, int y)
7859 {
7860   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
7861 }
7862
7863 static void ChangeActiveTrap(int x, int y)
7864 {
7865   int graphic = IMG_TRAP_ACTIVE;
7866
7867   /* if new animation frame was drawn, correct crumbled sand border */
7868   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
7869     DrawLevelFieldCrumbledSand(x, y);
7870 }
7871
7872 static int getSpecialActionElement(int element, int number, int base_element)
7873 {
7874   return (element != EL_EMPTY ? element :
7875           number != -1 ? base_element + number - 1 :
7876           EL_EMPTY);
7877 }
7878
7879 static int getModifiedActionNumber(int value_old, int operator, int operand,
7880                                    int value_min, int value_max)
7881 {
7882   int value_new = (operator == CA_MODE_SET      ? operand :
7883                    operator == CA_MODE_ADD      ? value_old + operand :
7884                    operator == CA_MODE_SUBTRACT ? value_old - operand :
7885                    operator == CA_MODE_MULTIPLY ? value_old * operand :
7886                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
7887                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
7888                    value_old);
7889
7890   return (value_new < value_min ? value_min :
7891           value_new > value_max ? value_max :
7892           value_new);
7893 }
7894
7895 static void ExecuteCustomElementAction(int x, int y, int element, int page)
7896 {
7897   struct ElementInfo *ei = &element_info[element];
7898   struct ElementChangeInfo *change = &ei->change_page[page];
7899   int target_element = change->target_element;
7900   int action_type = change->action_type;
7901   int action_mode = change->action_mode;
7902   int action_arg = change->action_arg;
7903   int i;
7904
7905   if (!change->has_action)
7906     return;
7907
7908   /* ---------- determine action paramater values -------------------------- */
7909
7910   int level_time_value =
7911     (level.time > 0 ? TimeLeft :
7912      TimePlayed);
7913
7914   int action_arg_element =
7915     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
7916      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
7917      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
7918      EL_EMPTY);
7919
7920   int action_arg_direction =
7921     (action_arg >= CA_ARG_DIRECTION_LEFT &&
7922      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
7923      action_arg == CA_ARG_DIRECTION_TRIGGER ?
7924      change->actual_trigger_side :
7925      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
7926      MV_DIR_OPPOSITE(change->actual_trigger_side) :
7927      MV_NONE);
7928
7929   int action_arg_number_min =
7930     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
7931      CA_ARG_MIN);
7932
7933   int action_arg_number_max =
7934     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
7935      action_type == CA_SET_LEVEL_GEMS ? 999 :
7936      action_type == CA_SET_LEVEL_TIME ? 9999 :
7937      action_type == CA_SET_LEVEL_SCORE ? 99999 :
7938      action_type == CA_SET_CE_VALUE ? 9999 :
7939      action_type == CA_SET_CE_SCORE ? 9999 :
7940      CA_ARG_MAX);
7941
7942   int action_arg_number_reset =
7943     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
7944      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
7945      action_type == CA_SET_LEVEL_TIME ? level.time :
7946      action_type == CA_SET_LEVEL_SCORE ? 0 :
7947 #if 1
7948      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
7949 #else
7950      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
7951 #endif
7952      action_type == CA_SET_CE_SCORE ? 0 :
7953      0);
7954
7955   int action_arg_number =
7956     (action_arg <= CA_ARG_MAX ? action_arg :
7957      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
7958      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
7959      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
7960      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
7961      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
7962      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
7963 #if USE_NEW_CUSTOM_VALUE
7964      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
7965 #else
7966      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
7967 #endif
7968      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
7969      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
7970      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
7971      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
7972      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
7973      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
7974      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
7975      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
7976      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
7977      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
7978      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
7979      -1);
7980
7981   int action_arg_number_old =
7982     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
7983      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
7984      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
7985      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
7986      action_type == CA_SET_CE_SCORE ? ei->collect_score :
7987      0);
7988
7989   int action_arg_number_new =
7990     getModifiedActionNumber(action_arg_number_old,
7991                             action_mode, action_arg_number,
7992                             action_arg_number_min, action_arg_number_max);
7993
7994   int trigger_player_bits =
7995     (change->actual_trigger_player >= EL_PLAYER_1 &&
7996      change->actual_trigger_player <= EL_PLAYER_4 ?
7997      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
7998      PLAYER_BITS_ANY);
7999
8000   int action_arg_player_bits =
8001     (action_arg >= CA_ARG_PLAYER_1 &&
8002      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
8003      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
8004      PLAYER_BITS_ANY);
8005
8006   /* ---------- execute action  -------------------------------------------- */
8007
8008   switch(action_type)
8009   {
8010     case CA_NO_ACTION:
8011     {
8012       return;
8013     }
8014
8015     /* ---------- level actions  ------------------------------------------- */
8016
8017     case CA_RESTART_LEVEL:
8018     {
8019       game.restart_level = TRUE;
8020
8021       break;
8022     }
8023
8024     case CA_SHOW_ENVELOPE:
8025     {
8026       int element = getSpecialActionElement(action_arg_element,
8027                                             action_arg_number, EL_ENVELOPE_1);
8028
8029       if (IS_ENVELOPE(element))
8030         local_player->show_envelope = element;
8031
8032       break;
8033     }
8034
8035     case CA_SET_LEVEL_TIME:
8036     {
8037       if (level.time > 0)       /* only modify limited time value */
8038       {
8039         TimeLeft = action_arg_number_new;
8040
8041         DrawGameValue_Time(TimeLeft);
8042
8043         if (!TimeLeft && setup.time_limit)
8044           for (i = 0; i < MAX_PLAYERS; i++)
8045             KillPlayer(&stored_player[i]);
8046       }
8047
8048       break;
8049     }
8050
8051     case CA_SET_LEVEL_SCORE:
8052     {
8053       local_player->score = action_arg_number_new;
8054
8055       DrawGameValue_Score(local_player->score);
8056
8057       break;
8058     }
8059
8060     case CA_SET_LEVEL_GEMS:
8061     {
8062       local_player->gems_still_needed = action_arg_number_new;
8063
8064       DrawGameValue_Emeralds(local_player->gems_still_needed);
8065
8066       break;
8067     }
8068
8069     case CA_SET_LEVEL_GRAVITY:
8070     {
8071       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
8072                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
8073                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
8074                       game.gravity);
8075       break;
8076     }
8077
8078     case CA_SET_LEVEL_WIND:
8079     {
8080       game.wind_direction = action_arg_direction;
8081
8082       break;
8083     }
8084
8085     /* ---------- player actions  ------------------------------------------ */
8086
8087     case CA_MOVE_PLAYER:
8088     {
8089       /* automatically move to the next field in specified direction */
8090       for (i = 0; i < MAX_PLAYERS; i++)
8091         if (trigger_player_bits & (1 << i))
8092           stored_player[i].programmed_action = action_arg_direction;
8093
8094       break;
8095     }
8096
8097     case CA_EXIT_PLAYER:
8098     {
8099       for (i = 0; i < MAX_PLAYERS; i++)
8100         if (action_arg_player_bits & (1 << i))
8101           stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
8102
8103       break;
8104     }
8105
8106     case CA_KILL_PLAYER:
8107     {
8108       for (i = 0; i < MAX_PLAYERS; i++)
8109         if (action_arg_player_bits & (1 << i))
8110           KillPlayer(&stored_player[i]);
8111
8112       break;
8113     }
8114
8115     case CA_SET_PLAYER_KEYS:
8116     {
8117       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
8118       int element = getSpecialActionElement(action_arg_element,
8119                                             action_arg_number, EL_KEY_1);
8120
8121       if (IS_KEY(element))
8122       {
8123         for (i = 0; i < MAX_PLAYERS; i++)
8124         {
8125           if (trigger_player_bits & (1 << i))
8126           {
8127             stored_player[i].key[KEY_NR(element)] = key_state;
8128
8129 #if 1
8130             DrawGameDoorValues();
8131 #else
8132             DrawGameValue_Keys(stored_player[i].key);
8133 #endif
8134
8135             redraw_mask |= REDRAW_DOOR_1;
8136           }
8137         }
8138       }
8139
8140       break;
8141     }
8142
8143     case CA_SET_PLAYER_SPEED:
8144     {
8145       for (i = 0; i < MAX_PLAYERS; i++)
8146       {
8147         if (trigger_player_bits & (1 << i))
8148         {
8149           int move_stepsize = TILEX / stored_player[i].move_delay_value;
8150
8151           if (action_arg == CA_ARG_SPEED_FASTER &&
8152               stored_player[i].cannot_move)
8153           {
8154             action_arg_number = STEPSIZE_VERY_SLOW;
8155           }
8156           else if (action_arg == CA_ARG_SPEED_SLOWER ||
8157                    action_arg == CA_ARG_SPEED_FASTER)
8158           {
8159             action_arg_number = 2;
8160             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
8161                            CA_MODE_MULTIPLY);
8162           }
8163
8164           move_stepsize =
8165             getModifiedActionNumber(move_stepsize,
8166                                     action_mode,
8167                                     action_arg_number,
8168                                     action_arg_number_min,
8169                                     action_arg_number_max);
8170
8171 #if 1
8172           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
8173 #else
8174           /* make sure that value is power of 2 */
8175           move_stepsize = (1 << log_2(move_stepsize));
8176
8177           /* do no immediately change -- the player might just be moving */
8178           stored_player[i].move_delay_value_next = TILEX / move_stepsize;
8179
8180           stored_player[i].cannot_move =
8181             (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
8182 #endif
8183         }
8184       }
8185
8186       break;
8187     }
8188
8189     case CA_SET_PLAYER_SHIELD:
8190     {
8191       for (i = 0; i < MAX_PLAYERS; i++)
8192       {
8193         if (trigger_player_bits & (1 << i))
8194         {
8195           if (action_arg == CA_ARG_SHIELD_OFF)
8196           {
8197             stored_player[i].shield_normal_time_left = 0;
8198             stored_player[i].shield_deadly_time_left = 0;
8199           }
8200           else if (action_arg == CA_ARG_SHIELD_NORMAL)
8201           {
8202             stored_player[i].shield_normal_time_left = 999999;
8203           }
8204           else if (action_arg == CA_ARG_SHIELD_DEADLY)
8205           {
8206             stored_player[i].shield_normal_time_left = 999999;
8207             stored_player[i].shield_deadly_time_left = 999999;
8208           }
8209         }
8210       }
8211
8212       break;
8213     }
8214
8215     case CA_SET_PLAYER_ARTWORK:
8216     {
8217       for (i = 0; i < MAX_PLAYERS; i++)
8218       {
8219         if (trigger_player_bits & (1 << i))
8220         {
8221           int artwork_element = action_arg_element;
8222
8223           if (action_arg == CA_ARG_ELEMENT_RESET)
8224             artwork_element =
8225               (level.use_artwork_element[i] ? level.artwork_element[i] :
8226                stored_player[i].element_nr);
8227
8228           stored_player[i].artwork_element = artwork_element;
8229
8230           SetPlayerWaiting(&stored_player[i], FALSE);
8231
8232           /* set number of special actions for bored and sleeping animation */
8233           stored_player[i].num_special_action_bored =
8234             get_num_special_action(artwork_element,
8235                                    ACTION_BORING_1, ACTION_BORING_LAST);
8236           stored_player[i].num_special_action_sleeping =
8237             get_num_special_action(artwork_element,
8238                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
8239         }
8240       }
8241
8242       break;
8243     }
8244
8245     /* ---------- CE actions  ---------------------------------------------- */
8246
8247     case CA_SET_CE_VALUE:
8248     {
8249 #if USE_NEW_CUSTOM_VALUE
8250       int last_custom_value = CustomValue[x][y];
8251
8252       CustomValue[x][y] = action_arg_number_new;
8253
8254 #if 0
8255       printf("::: Count == %d\n", CustomValue[x][y]);
8256 #endif
8257
8258       if (CustomValue[x][y] == 0 && last_custom_value > 0)
8259       {
8260 #if 0
8261         printf("::: CE_VALUE_GETS_ZERO\n");
8262 #endif
8263
8264         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
8265         CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
8266
8267 #if 0
8268         printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8269 #endif
8270       }
8271 #endif
8272
8273       break;
8274     }
8275
8276     case CA_SET_CE_SCORE:
8277     {
8278       ei->collect_score = action_arg_number_new;
8279
8280       break;
8281     }
8282
8283     /* ---------- engine actions  ------------------------------------------ */
8284
8285     case CA_SET_ENGINE_SCAN_MODE:
8286     {
8287       InitPlayfieldScanMode(action_arg);
8288
8289       break;
8290     }
8291
8292     default:
8293       break;
8294   }
8295 }
8296
8297 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8298 {
8299   int old_element = Feld[x][y];
8300   int new_element = get_element_from_group_element(element);
8301   int previous_move_direction = MovDir[x][y];
8302 #if USE_NEW_CUSTOM_VALUE
8303   int last_ce_value = CustomValue[x][y];
8304 #endif
8305   boolean add_player = (ELEM_IS_PLAYER(new_element) &&
8306                         IS_WALKABLE(old_element));
8307
8308 #if 0
8309   /* check if element under player changes from accessible to unaccessible
8310      (needed for special case of dropping element which then changes) */
8311   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8312       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8313   {
8314     Bang(x, y);
8315
8316     return;
8317   }
8318 #endif
8319
8320   if (!add_player)
8321   {
8322     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8323       RemoveMovingField(x, y);
8324     else
8325       RemoveField(x, y);
8326
8327     Feld[x][y] = new_element;
8328
8329 #if !USE_GFX_RESET_GFX_ANIMATION
8330     ResetGfxAnimation(x, y);
8331     ResetRandomAnimationValue(x, y);
8332 #endif
8333
8334     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8335       MovDir[x][y] = previous_move_direction;
8336
8337 #if USE_NEW_CUSTOM_VALUE
8338     if (element_info[new_element].use_last_ce_value)
8339       CustomValue[x][y] = last_ce_value;
8340 #endif
8341
8342     InitField_WithBug1(x, y, FALSE);
8343
8344     new_element = Feld[x][y];   /* element may have changed */
8345
8346 #if USE_GFX_RESET_GFX_ANIMATION
8347     ResetGfxAnimation(x, y);
8348     ResetRandomAnimationValue(x, y);
8349 #endif
8350
8351     DrawLevelField(x, y);
8352
8353     if (GFX_CRUMBLED(new_element))
8354       DrawLevelFieldCrumbledSandNeighbours(x, y);
8355   }
8356
8357 #if 1
8358   /* check if element under player changes from accessible to unaccessible
8359      (needed for special case of dropping element which then changes) */
8360   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8361       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8362   {
8363     Bang(x, y);
8364
8365     return;
8366   }
8367 #endif
8368
8369   /* "ChangeCount" not set yet to allow "entered by player" change one time */
8370   if (ELEM_IS_PLAYER(new_element))
8371     RelocatePlayer(x, y, new_element);
8372
8373   if (is_change)
8374     ChangeCount[x][y]++;        /* count number of changes in the same frame */
8375
8376   TestIfBadThingTouchesPlayer(x, y);
8377   TestIfPlayerTouchesCustomElement(x, y);
8378   TestIfElementTouchesCustomElement(x, y);
8379 }
8380
8381 static void CreateField(int x, int y, int element)
8382 {
8383   CreateFieldExt(x, y, element, FALSE);
8384 }
8385
8386 static void CreateElementFromChange(int x, int y, int element)
8387 {
8388   element = GET_VALID_RUNTIME_ELEMENT(element);
8389
8390 #if USE_STOP_CHANGED_ELEMENTS
8391   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8392   {
8393     int old_element = Feld[x][y];
8394
8395     /* prevent changed element from moving in same engine frame
8396        unless both old and new element can either fall or move */
8397     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8398         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8399       Stop[x][y] = TRUE;
8400   }
8401 #endif
8402
8403   CreateFieldExt(x, y, element, TRUE);
8404 }
8405
8406 static boolean ChangeElement(int x, int y, int element, int page)
8407 {
8408   struct ElementInfo *ei = &element_info[element];
8409   struct ElementChangeInfo *change = &ei->change_page[page];
8410   int ce_value = CustomValue[x][y];
8411   int ce_score = ei->collect_score;
8412   int target_element;
8413   int old_element = Feld[x][y];
8414
8415   /* always use default change event to prevent running into a loop */
8416   if (ChangeEvent[x][y] == -1)
8417     ChangeEvent[x][y] = CE_DELAY;
8418
8419   if (ChangeEvent[x][y] == CE_DELAY)
8420   {
8421     /* reset actual trigger element, trigger player and action element */
8422     change->actual_trigger_element = EL_EMPTY;
8423     change->actual_trigger_player = EL_PLAYER_1;
8424     change->actual_trigger_side = CH_SIDE_NONE;
8425     change->actual_trigger_ce_value = 0;
8426     change->actual_trigger_ce_score = 0;
8427   }
8428
8429   /* do not change elements more than a specified maximum number of changes */
8430   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8431     return FALSE;
8432
8433   ChangeCount[x][y]++;          /* count number of changes in the same frame */
8434
8435   if (change->explode)
8436   {
8437     Bang(x, y);
8438
8439     return TRUE;
8440   }
8441
8442   if (change->use_target_content)
8443   {
8444     boolean complete_replace = TRUE;
8445     boolean can_replace[3][3];
8446     int xx, yy;
8447
8448     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8449     {
8450       boolean is_empty;
8451       boolean is_walkable;
8452       boolean is_diggable;
8453       boolean is_collectible;
8454       boolean is_removable;
8455       boolean is_destructible;
8456       int ex = x + xx - 1;
8457       int ey = y + yy - 1;
8458       int content_element = change->target_content.e[xx][yy];
8459       int e;
8460
8461       can_replace[xx][yy] = TRUE;
8462
8463       if (ex == x && ey == y)   /* do not check changing element itself */
8464         continue;
8465
8466       if (content_element == EL_EMPTY_SPACE)
8467       {
8468         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8469
8470         continue;
8471       }
8472
8473       if (!IN_LEV_FIELD(ex, ey))
8474       {
8475         can_replace[xx][yy] = FALSE;
8476         complete_replace = FALSE;
8477
8478         continue;
8479       }
8480
8481       e = Feld[ex][ey];
8482
8483       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8484         e = MovingOrBlocked2Element(ex, ey);
8485
8486       is_empty = (IS_FREE(ex, ey) ||
8487                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8488
8489       is_walkable     = (is_empty || IS_WALKABLE(e));
8490       is_diggable     = (is_empty || IS_DIGGABLE(e));
8491       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8492       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8493       is_removable    = (is_diggable || is_collectible);
8494
8495       can_replace[xx][yy] =
8496         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
8497           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
8498           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
8499           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
8500           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
8501           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8502          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8503
8504       if (!can_replace[xx][yy])
8505         complete_replace = FALSE;
8506     }
8507
8508     if (!change->only_if_complete || complete_replace)
8509     {
8510       boolean something_has_changed = FALSE;
8511
8512       if (change->only_if_complete && change->use_random_replace &&
8513           RND(100) < change->random_percentage)
8514         return FALSE;
8515
8516       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8517       {
8518         int ex = x + xx - 1;
8519         int ey = y + yy - 1;
8520         int content_element;
8521
8522         if (can_replace[xx][yy] && (!change->use_random_replace ||
8523                                     RND(100) < change->random_percentage))
8524         {
8525           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8526             RemoveMovingField(ex, ey);
8527
8528           ChangeEvent[ex][ey] = ChangeEvent[x][y];
8529
8530           content_element = change->target_content.e[xx][yy];
8531           target_element = GET_TARGET_ELEMENT(content_element, change,
8532                                               ce_value, ce_score);
8533
8534           CreateElementFromChange(ex, ey, target_element);
8535
8536           something_has_changed = TRUE;
8537
8538           /* for symmetry reasons, freeze newly created border elements */
8539           if (ex != x || ey != y)
8540             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
8541         }
8542       }
8543
8544       if (something_has_changed)
8545       {
8546         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8547         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8548       }
8549     }
8550   }
8551   else
8552   {
8553     target_element = GET_TARGET_ELEMENT(change->target_element, change,
8554                                         ce_value, ce_score);
8555
8556     if (element == EL_DIAGONAL_GROWING ||
8557         element == EL_DIAGONAL_SHRINKING)
8558     {
8559       target_element = Store[x][y];
8560
8561       Store[x][y] = EL_EMPTY;
8562     }
8563
8564     CreateElementFromChange(x, y, target_element);
8565
8566     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8567     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8568   }
8569
8570   /* this uses direct change before indirect change */
8571   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8572
8573   return TRUE;
8574 }
8575
8576 #if USE_NEW_DELAYED_ACTION
8577
8578 static void HandleElementChange(int x, int y, int page)
8579 {
8580   int element = MovingOrBlocked2Element(x, y);
8581   struct ElementInfo *ei = &element_info[element];
8582   struct ElementChangeInfo *change = &ei->change_page[page];
8583
8584 #ifdef DEBUG
8585   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8586       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8587   {
8588     printf("\n\n");
8589     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8590            x, y, element, element_info[element].token_name);
8591     printf("HandleElementChange(): This should never happen!\n");
8592     printf("\n\n");
8593   }
8594 #endif
8595
8596   /* this can happen with classic bombs on walkable, changing elements */
8597   if (!CAN_CHANGE_OR_HAS_ACTION(element))
8598   {
8599 #if 0
8600     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
8601       ChangeDelay[x][y] = 0;
8602 #endif
8603
8604     return;
8605   }
8606
8607   if (ChangeDelay[x][y] == 0)           /* initialize element change */
8608   {
8609     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8610
8611     if (change->can_change)
8612     {
8613       ResetGfxAnimation(x, y);
8614       ResetRandomAnimationValue(x, y);
8615
8616       if (change->pre_change_function)
8617         change->pre_change_function(x, y);
8618     }
8619   }
8620
8621   ChangeDelay[x][y]--;
8622
8623   if (ChangeDelay[x][y] != 0)           /* continue element change */
8624   {
8625     if (change->can_change)
8626     {
8627       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8628
8629       if (IS_ANIMATED(graphic))
8630         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8631
8632       if (change->change_function)
8633         change->change_function(x, y);
8634     }
8635   }
8636   else                                  /* finish element change */
8637   {
8638     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
8639     {
8640       page = ChangePage[x][y];
8641       ChangePage[x][y] = -1;
8642
8643       change = &ei->change_page[page];
8644     }
8645
8646     if (IS_MOVING(x, y))                /* never change a running system ;-) */
8647     {
8648       ChangeDelay[x][y] = 1;            /* try change after next move step */
8649       ChangePage[x][y] = page;          /* remember page to use for change */
8650
8651       return;
8652     }
8653
8654     if (change->can_change)
8655     {
8656       if (ChangeElement(x, y, element, page))
8657       {
8658         if (change->post_change_function)
8659           change->post_change_function(x, y);
8660       }
8661     }
8662
8663     if (change->has_action)
8664       ExecuteCustomElementAction(x, y, element, page);
8665   }
8666 }
8667
8668 #else
8669
8670 static void HandleElementChange(int x, int y, int page)
8671 {
8672   int element = MovingOrBlocked2Element(x, y);
8673   struct ElementInfo *ei = &element_info[element];
8674   struct ElementChangeInfo *change = &ei->change_page[page];
8675
8676 #ifdef DEBUG
8677   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8678   {
8679     printf("\n\n");
8680     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8681            x, y, element, element_info[element].token_name);
8682     printf("HandleElementChange(): This should never happen!\n");
8683     printf("\n\n");
8684   }
8685 #endif
8686
8687   /* this can happen with classic bombs on walkable, changing elements */
8688   if (!CAN_CHANGE(element))
8689   {
8690 #if 0
8691     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
8692       ChangeDelay[x][y] = 0;
8693 #endif
8694
8695     return;
8696   }
8697
8698   if (ChangeDelay[x][y] == 0)           /* initialize element change */
8699   {
8700     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8701
8702     ResetGfxAnimation(x, y);
8703     ResetRandomAnimationValue(x, y);
8704
8705     if (change->pre_change_function)
8706       change->pre_change_function(x, y);
8707   }
8708
8709   ChangeDelay[x][y]--;
8710
8711   if (ChangeDelay[x][y] != 0)           /* continue element change */
8712   {
8713     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8714
8715     if (IS_ANIMATED(graphic))
8716       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8717
8718     if (change->change_function)
8719       change->change_function(x, y);
8720   }
8721   else                                  /* finish element change */
8722   {
8723     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
8724     {
8725       page = ChangePage[x][y];
8726       ChangePage[x][y] = -1;
8727
8728       change = &ei->change_page[page];
8729     }
8730
8731     if (IS_MOVING(x, y))                /* never change a running system ;-) */
8732     {
8733       ChangeDelay[x][y] = 1;            /* try change after next move step */
8734       ChangePage[x][y] = page;          /* remember page to use for change */
8735
8736       return;
8737     }
8738
8739     if (ChangeElement(x, y, element, page))
8740     {
8741       if (change->post_change_function)
8742         change->post_change_function(x, y);
8743     }
8744   }
8745 }
8746
8747 #endif
8748
8749 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8750                                               int trigger_element,
8751                                               int trigger_event,
8752                                               int trigger_player,
8753                                               int trigger_side,
8754                                               int trigger_page)
8755 {
8756   boolean change_done_any = FALSE;
8757   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8758   int i;
8759
8760   if (!(trigger_events[trigger_element][trigger_event]))
8761     return FALSE;
8762
8763   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8764   {
8765     int element = EL_CUSTOM_START + i;
8766     boolean change_done = FALSE;
8767     int p;
8768
8769     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8770         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8771       continue;
8772
8773     for (p = 0; p < element_info[element].num_change_pages; p++)
8774     {
8775       struct ElementChangeInfo *change = &element_info[element].change_page[p];
8776
8777       if (change->can_change_or_has_action &&
8778           change->has_event[trigger_event] &&
8779           change->trigger_side & trigger_side &&
8780           change->trigger_player & trigger_player &&
8781           change->trigger_page & trigger_page_bits &&
8782           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8783       {
8784         change->actual_trigger_element = trigger_element;
8785         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8786         change->actual_trigger_side = trigger_side;
8787         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8788         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8789
8790         if ((change->can_change && !change_done) || change->has_action)
8791         {
8792           int x, y;
8793
8794 #if 1
8795           SCAN_PLAYFIELD(x, y)
8796 #else
8797           for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8798 #endif
8799           {
8800             if (Feld[x][y] == element)
8801             {
8802               if (change->can_change && !change_done)
8803               {
8804                 ChangeDelay[x][y] = 1;
8805                 ChangeEvent[x][y] = trigger_event;
8806
8807                 HandleElementChange(x, y, p);
8808               }
8809 #if USE_NEW_DELAYED_ACTION
8810               else if (change->has_action)
8811               {
8812                 ExecuteCustomElementAction(x, y, element, p);
8813                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8814               }
8815 #else
8816               if (change->has_action)
8817               {
8818                 ExecuteCustomElementAction(x, y, element, p);
8819                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8820               }
8821 #endif
8822             }
8823           }
8824
8825           if (change->can_change)
8826           {
8827             change_done = TRUE;
8828             change_done_any = TRUE;
8829           }
8830         }
8831       }
8832     }
8833   }
8834
8835   return change_done_any;
8836 }
8837
8838 static boolean CheckElementChangeExt(int x, int y,
8839                                      int element,
8840                                      int trigger_element,
8841                                      int trigger_event,
8842                                      int trigger_player,
8843                                      int trigger_side)
8844 {
8845   boolean change_done = FALSE;
8846   int p;
8847
8848   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8849       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8850     return FALSE;
8851
8852   if (Feld[x][y] == EL_BLOCKED)
8853   {
8854     Blocked2Moving(x, y, &x, &y);
8855     element = Feld[x][y];
8856   }
8857
8858 #if 0
8859   /* check if element has already changed */
8860   if (Feld[x][y] != element)
8861     return FALSE;
8862 #else
8863   /* check if element has already changed or is about to change after moving */
8864   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8865        Feld[x][y] != element) ||
8866
8867       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8868        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8869         ChangePage[x][y] != -1)))
8870     return FALSE;
8871 #endif
8872
8873   for (p = 0; p < element_info[element].num_change_pages; p++)
8874   {
8875     struct ElementChangeInfo *change = &element_info[element].change_page[p];
8876
8877     boolean check_trigger_element =
8878       (trigger_event == CE_TOUCHING_X ||
8879        trigger_event == CE_HITTING_X ||
8880        trigger_event == CE_HIT_BY_X);
8881
8882     if (change->can_change_or_has_action &&
8883         change->has_event[trigger_event] &&
8884         change->trigger_side & trigger_side &&
8885         change->trigger_player & trigger_player &&
8886         (!check_trigger_element ||
8887          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8888     {
8889       change->actual_trigger_element = trigger_element;
8890       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8891       change->actual_trigger_side = trigger_side;
8892       change->actual_trigger_ce_value = CustomValue[x][y];
8893       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8894
8895       /* special case: trigger element not at (x,y) position for some events */
8896       if (check_trigger_element)
8897       {
8898         static struct
8899         {
8900           int dx, dy;
8901         } move_xy[] =
8902           {
8903             {  0,  0 },
8904             { -1,  0 },
8905             { +1,  0 },
8906             {  0,  0 },
8907             {  0, -1 },
8908             {  0,  0 }, { 0, 0 }, { 0, 0 },
8909             {  0, +1 }
8910           };
8911
8912         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8913         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8914
8915         change->actual_trigger_ce_value = CustomValue[xx][yy];
8916         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8917       }
8918
8919       if (change->can_change && !change_done)
8920       {
8921         ChangeDelay[x][y] = 1;
8922         ChangeEvent[x][y] = trigger_event;
8923
8924         HandleElementChange(x, y, p);
8925
8926         change_done = TRUE;
8927       }
8928 #if USE_NEW_DELAYED_ACTION
8929       else if (change->has_action)
8930       {
8931         ExecuteCustomElementAction(x, y, element, p);
8932         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8933       }
8934 #else
8935       if (change->has_action)
8936       {
8937         ExecuteCustomElementAction(x, y, element, p);
8938         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8939       }
8940 #endif
8941     }
8942   }
8943
8944   return change_done;
8945 }
8946
8947 static void PlayPlayerSound(struct PlayerInfo *player)
8948 {
8949   int jx = player->jx, jy = player->jy;
8950   int sound_element = player->artwork_element;
8951   int last_action = player->last_action_waiting;
8952   int action = player->action_waiting;
8953
8954   if (player->is_waiting)
8955   {
8956     if (action != last_action)
8957       PlayLevelSoundElementAction(jx, jy, sound_element, action);
8958     else
8959       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
8960   }
8961   else
8962   {
8963     if (action != last_action)
8964       StopSound(element_info[sound_element].sound[last_action]);
8965
8966     if (last_action == ACTION_SLEEPING)
8967       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
8968   }
8969 }
8970
8971 static void PlayAllPlayersSound()
8972 {
8973   int i;
8974
8975   for (i = 0; i < MAX_PLAYERS; i++)
8976     if (stored_player[i].active)
8977       PlayPlayerSound(&stored_player[i]);
8978 }
8979
8980 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
8981 {
8982   boolean last_waiting = player->is_waiting;
8983   int move_dir = player->MovDir;
8984
8985   player->dir_waiting = move_dir;
8986   player->last_action_waiting = player->action_waiting;
8987
8988   if (is_waiting)
8989   {
8990     if (!last_waiting)          /* not waiting -> waiting */
8991     {
8992       player->is_waiting = TRUE;
8993
8994       player->frame_counter_bored =
8995         FrameCounter +
8996         game.player_boring_delay_fixed +
8997         SimpleRND(game.player_boring_delay_random);
8998       player->frame_counter_sleeping =
8999         FrameCounter +
9000         game.player_sleeping_delay_fixed +
9001         SimpleRND(game.player_sleeping_delay_random);
9002
9003 #if 1
9004       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9005 #else
9006       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9007 #endif
9008     }
9009
9010     if (game.player_sleeping_delay_fixed +
9011         game.player_sleeping_delay_random > 0 &&
9012         player->anim_delay_counter == 0 &&
9013         player->post_delay_counter == 0 &&
9014         FrameCounter >= player->frame_counter_sleeping)
9015       player->is_sleeping = TRUE;
9016     else if (game.player_boring_delay_fixed +
9017              game.player_boring_delay_random > 0 &&
9018              FrameCounter >= player->frame_counter_bored)
9019       player->is_bored = TRUE;
9020
9021     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9022                               player->is_bored ? ACTION_BORING :
9023                               ACTION_WAITING);
9024
9025 #if 1
9026     if (player->is_sleeping && player->use_murphy)
9027     {
9028       /* special case for sleeping Murphy when leaning against non-free tile */
9029
9030       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9031           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9032            !IS_MOVING(player->jx - 1, player->jy)))
9033         move_dir = MV_LEFT;
9034       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9035                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9036                 !IS_MOVING(player->jx + 1, player->jy)))
9037         move_dir = MV_RIGHT;
9038       else
9039         player->is_sleeping = FALSE;
9040
9041       player->dir_waiting = move_dir;
9042     }
9043 #endif
9044
9045     if (player->is_sleeping)
9046     {
9047       if (player->num_special_action_sleeping > 0)
9048       {
9049         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9050         {
9051           int last_special_action = player->special_action_sleeping;
9052           int num_special_action = player->num_special_action_sleeping;
9053           int special_action =
9054             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9055              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9056              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9057              last_special_action + 1 : ACTION_SLEEPING);
9058           int special_graphic =
9059             el_act_dir2img(player->artwork_element, special_action, move_dir);
9060
9061           player->anim_delay_counter =
9062             graphic_info[special_graphic].anim_delay_fixed +
9063             SimpleRND(graphic_info[special_graphic].anim_delay_random);
9064           player->post_delay_counter =
9065             graphic_info[special_graphic].post_delay_fixed +
9066             SimpleRND(graphic_info[special_graphic].post_delay_random);
9067
9068           player->special_action_sleeping = special_action;
9069         }
9070
9071         if (player->anim_delay_counter > 0)
9072         {
9073           player->action_waiting = player->special_action_sleeping;
9074           player->anim_delay_counter--;
9075         }
9076         else if (player->post_delay_counter > 0)
9077         {
9078           player->post_delay_counter--;
9079         }
9080       }
9081     }
9082     else if (player->is_bored)
9083     {
9084       if (player->num_special_action_bored > 0)
9085       {
9086         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9087         {
9088           int special_action =
9089             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9090           int special_graphic =
9091             el_act_dir2img(player->artwork_element, special_action, move_dir);
9092
9093           player->anim_delay_counter =
9094             graphic_info[special_graphic].anim_delay_fixed +
9095             SimpleRND(graphic_info[special_graphic].anim_delay_random);
9096           player->post_delay_counter =
9097             graphic_info[special_graphic].post_delay_fixed +
9098             SimpleRND(graphic_info[special_graphic].post_delay_random);
9099
9100           player->special_action_bored = special_action;
9101         }
9102
9103         if (player->anim_delay_counter > 0)
9104         {
9105           player->action_waiting = player->special_action_bored;
9106           player->anim_delay_counter--;
9107         }
9108         else if (player->post_delay_counter > 0)
9109         {
9110           player->post_delay_counter--;
9111         }
9112       }
9113     }
9114   }
9115   else if (last_waiting)        /* waiting -> not waiting */
9116   {
9117     player->is_waiting = FALSE;
9118     player->is_bored = FALSE;
9119     player->is_sleeping = FALSE;
9120
9121     player->frame_counter_bored = -1;
9122     player->frame_counter_sleeping = -1;
9123
9124     player->anim_delay_counter = 0;
9125     player->post_delay_counter = 0;
9126
9127     player->dir_waiting = player->MovDir;
9128     player->action_waiting = ACTION_DEFAULT;
9129
9130     player->special_action_bored = ACTION_DEFAULT;
9131     player->special_action_sleeping = ACTION_DEFAULT;
9132   }
9133 }
9134
9135 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9136 {
9137   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9138   int left      = player_action & JOY_LEFT;
9139   int right     = player_action & JOY_RIGHT;
9140   int up        = player_action & JOY_UP;
9141   int down      = player_action & JOY_DOWN;
9142   int button1   = player_action & JOY_BUTTON_1;
9143   int button2   = player_action & JOY_BUTTON_2;
9144   int dx        = (left ? -1 : right ? 1 : 0);
9145   int dy        = (up   ? -1 : down  ? 1 : 0);
9146
9147   if (!player->active || tape.pausing)
9148     return 0;
9149
9150   if (player_action)
9151   {
9152     if (button1)
9153       snapped = SnapField(player, dx, dy);
9154     else
9155     {
9156       if (button2)
9157         dropped = DropElement(player);
9158
9159       moved = MovePlayer(player, dx, dy);
9160     }
9161
9162     if (tape.single_step && tape.recording && !tape.pausing)
9163     {
9164       if (button1 || (dropped && !moved))
9165       {
9166         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9167         SnapField(player, 0, 0);                /* stop snapping */
9168       }
9169     }
9170
9171     SetPlayerWaiting(player, FALSE);
9172
9173     return player_action;
9174   }
9175   else
9176   {
9177     /* no actions for this player (no input at player's configured device) */
9178
9179     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9180     SnapField(player, 0, 0);
9181     CheckGravityMovementWhenNotMoving(player);
9182
9183     if (player->MovPos == 0)
9184       SetPlayerWaiting(player, TRUE);
9185
9186     if (player->MovPos == 0)    /* needed for tape.playing */
9187       player->is_moving = FALSE;
9188
9189     player->is_dropping = FALSE;
9190     player->is_dropping_pressed = FALSE;
9191     player->drop_pressed_delay = 0;
9192
9193     return 0;
9194   }
9195 }
9196
9197 static void CheckLevelTime()
9198 {
9199   int i;
9200
9201   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9202   {
9203     if (level.native_em_level->lev->home == 0)  /* all players at home */
9204     {
9205       local_player->LevelSolved = TRUE;
9206       AllPlayersGone = TRUE;
9207
9208       level.native_em_level->lev->home = -1;
9209     }
9210
9211     if (level.native_em_level->ply[0]->alive == 0 &&
9212         level.native_em_level->ply[1]->alive == 0 &&
9213         level.native_em_level->ply[2]->alive == 0 &&
9214         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9215       AllPlayersGone = TRUE;
9216   }
9217
9218   if (TimeFrames >= FRAMES_PER_SECOND)
9219   {
9220     TimeFrames = 0;
9221     TapeTime++;
9222
9223     for (i = 0; i < MAX_PLAYERS; i++)
9224     {
9225       struct PlayerInfo *player = &stored_player[i];
9226
9227       if (SHIELD_ON(player))
9228       {
9229         player->shield_normal_time_left--;
9230
9231         if (player->shield_deadly_time_left > 0)
9232           player->shield_deadly_time_left--;
9233       }
9234     }
9235
9236     if (!level.use_step_counter)
9237     {
9238       TimePlayed++;
9239
9240       if (TimeLeft > 0)
9241       {
9242         TimeLeft--;
9243
9244         if (TimeLeft <= 10 && setup.time_limit)
9245           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9246
9247         DrawGameValue_Time(TimeLeft);
9248
9249         if (!TimeLeft && setup.time_limit)
9250         {
9251           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9252             level.native_em_level->lev->killed_out_of_time = TRUE;
9253           else
9254             for (i = 0; i < MAX_PLAYERS; i++)
9255               KillPlayer(&stored_player[i]);
9256         }
9257       }
9258       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9259         DrawGameValue_Time(TimePlayed);
9260
9261       level.native_em_level->lev->time =
9262         (level.time == 0 ? TimePlayed : TimeLeft);
9263     }
9264
9265     if (tape.recording || tape.playing)
9266       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9267   }
9268 }
9269
9270 void AdvanceFrameAndPlayerCounters(int player_nr)
9271 {
9272   int i;
9273
9274 #if 0
9275   Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9276         FrameCounter, FrameCounter + 1);
9277 #endif
9278
9279   /* advance frame counters (global frame counter and time frame counter) */
9280   FrameCounter++;
9281   TimeFrames++;
9282
9283   /* advance player counters (counters for move delay, move animation etc.) */
9284   for (i = 0; i < MAX_PLAYERS; i++)
9285   {
9286     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9287     int move_delay_value = stored_player[i].move_delay_value;
9288     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9289
9290     if (!advance_player_counters)       /* not all players may be affected */
9291       continue;
9292
9293 #if USE_NEW_PLAYER_ANIM
9294     if (move_frames == 0)       /* less than one move per game frame */
9295     {
9296       int stepsize = TILEX / move_delay_value;
9297       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9298       int count = (stored_player[i].is_moving ?
9299                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9300
9301       if (count % delay == 0)
9302         move_frames = 1;
9303     }
9304 #endif
9305
9306     stored_player[i].Frame += move_frames;
9307
9308     if (stored_player[i].MovPos != 0)
9309       stored_player[i].StepFrame += move_frames;
9310
9311     if (stored_player[i].move_delay > 0)
9312       stored_player[i].move_delay--;
9313
9314     /* due to bugs in previous versions, counter must count up, not down */
9315     if (stored_player[i].push_delay != -1)
9316       stored_player[i].push_delay++;
9317
9318     if (stored_player[i].drop_delay > 0)
9319       stored_player[i].drop_delay--;
9320
9321     if (stored_player[i].is_dropping_pressed)
9322       stored_player[i].drop_pressed_delay++;
9323   }
9324 }
9325
9326 void StartGameActions(boolean init_network_game, boolean record_tape,
9327                       long random_seed)
9328 {
9329   unsigned long new_random_seed = InitRND(random_seed);
9330
9331   if (record_tape)
9332     TapeStartRecording(new_random_seed);
9333
9334 #if defined(NETWORK_AVALIABLE)
9335   if (init_network_game)
9336   {
9337     SendToServer_StartPlaying();
9338
9339     return;
9340   }
9341 #endif
9342
9343   StopAnimation();
9344
9345   game_status = GAME_MODE_PLAYING;
9346
9347   InitGame();
9348 }
9349
9350 void GameActions()
9351 {
9352   static unsigned long game_frame_delay = 0;
9353   unsigned long game_frame_delay_value;
9354   byte *recorded_player_action;
9355   byte summarized_player_action = 0;
9356   byte tape_action[MAX_PLAYERS];
9357   int i;
9358
9359   if (game.restart_level)
9360     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9361
9362   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9363   {
9364     if (level.native_em_level->lev->home == 0)  /* all players at home */
9365     {
9366       local_player->LevelSolved = TRUE;
9367       AllPlayersGone = TRUE;
9368
9369       level.native_em_level->lev->home = -1;
9370     }
9371
9372     if (level.native_em_level->ply[0]->alive == 0 &&
9373         level.native_em_level->ply[1]->alive == 0 &&
9374         level.native_em_level->ply[2]->alive == 0 &&
9375         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9376       AllPlayersGone = TRUE;
9377   }
9378
9379   if (local_player->LevelSolved)
9380     GameWon();
9381
9382   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9383     TapeStop();
9384
9385   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9386     return;
9387
9388   game_frame_delay_value =
9389     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9390
9391   if (tape.playing && tape.warp_forward && !tape.pausing)
9392     game_frame_delay_value = 0;
9393
9394   /* ---------- main game synchronization point ---------- */
9395
9396   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9397
9398   if (network_playing && !network_player_action_received)
9399   {
9400     /* try to get network player actions in time */
9401
9402 #if defined(NETWORK_AVALIABLE)
9403     /* last chance to get network player actions without main loop delay */
9404     HandleNetworking();
9405 #endif
9406
9407     /* game was quit by network peer */
9408     if (game_status != GAME_MODE_PLAYING)
9409       return;
9410
9411     if (!network_player_action_received)
9412       return;           /* failed to get network player actions in time */
9413
9414     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9415   }
9416
9417   if (tape.pausing)
9418     return;
9419
9420   /* at this point we know that we really continue executing the game */
9421
9422 #if 1
9423   network_player_action_received = FALSE;
9424 #endif
9425
9426   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9427
9428   if (tape.set_centered_player)
9429   {
9430     game.centered_player_nr_next = tape.centered_player_nr_next;
9431     game.set_centered_player = TRUE;
9432   }
9433
9434   for (i = 0; i < MAX_PLAYERS; i++)
9435   {
9436     summarized_player_action |= stored_player[i].action;
9437
9438     if (!network_playing)
9439       stored_player[i].effective_action = stored_player[i].action;
9440   }
9441
9442 #if defined(NETWORK_AVALIABLE)
9443   if (network_playing)
9444     SendToServer_MovePlayer(summarized_player_action);
9445 #endif
9446
9447   if (!options.network && !setup.team_mode)
9448     local_player->effective_action = summarized_player_action;
9449
9450   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9451   {
9452     for (i = 0; i < MAX_PLAYERS; i++)
9453       stored_player[i].effective_action =
9454         (i == game.centered_player_nr ? summarized_player_action : 0);
9455   }
9456
9457   if (recorded_player_action != NULL)
9458     for (i = 0; i < MAX_PLAYERS; i++)
9459       stored_player[i].effective_action = recorded_player_action[i];
9460
9461   for (i = 0; i < MAX_PLAYERS; i++)
9462   {
9463     tape_action[i] = stored_player[i].effective_action;
9464
9465     /* (this can only happen in the R'n'D game engine) */
9466     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9467       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9468   }
9469
9470   /* only record actions from input devices, but not programmed actions */
9471   if (tape.recording)
9472     TapeRecordAction(tape_action);
9473
9474   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9475   {
9476     GameActions_EM_Main();
9477   }
9478   else
9479   {
9480     GameActions_RND();
9481   }
9482 }
9483
9484 void GameActions_EM_Main()
9485 {
9486   byte effective_action[MAX_PLAYERS];
9487   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9488   int i;
9489
9490   for (i = 0; i < MAX_PLAYERS; i++)
9491     effective_action[i] = stored_player[i].effective_action;
9492
9493   GameActions_EM(effective_action, warp_mode);
9494
9495   CheckLevelTime();
9496
9497   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9498 }
9499
9500 void GameActions_RND()
9501 {
9502   int magic_wall_x = 0, magic_wall_y = 0;
9503   int i, x, y, element, graphic;
9504
9505   InitPlayfieldScanModeVars();
9506
9507 #if USE_ONE_MORE_CHANGE_PER_FRAME
9508   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9509   {
9510     SCAN_PLAYFIELD(x, y)
9511     {
9512       ChangeCount[x][y] = 0;
9513       ChangeEvent[x][y] = -1;
9514     }
9515   }
9516 #endif
9517
9518 #if 1
9519   if (game.set_centered_player)
9520   {
9521     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9522
9523     /* switching to "all players" only possible if all players fit to screen */
9524     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9525     {
9526       game.centered_player_nr_next = game.centered_player_nr;
9527       game.set_centered_player = FALSE;
9528     }
9529
9530     /* do not switch focus to non-existing (or non-active) player */
9531     if (game.centered_player_nr_next >= 0 &&
9532         !stored_player[game.centered_player_nr_next].active)
9533     {
9534       game.centered_player_nr_next = game.centered_player_nr;
9535       game.set_centered_player = FALSE;
9536     }
9537   }
9538
9539   if (game.set_centered_player &&
9540       ScreenMovPos == 0)        /* screen currently aligned at tile position */
9541   {
9542     int sx, sy;
9543
9544     if (game.centered_player_nr_next == -1)
9545     {
9546       setScreenCenteredToAllPlayers(&sx, &sy);
9547     }
9548     else
9549     {
9550       sx = stored_player[game.centered_player_nr_next].jx;
9551       sy = stored_player[game.centered_player_nr_next].jy;
9552     }
9553
9554     game.centered_player_nr = game.centered_player_nr_next;
9555     game.set_centered_player = FALSE;
9556
9557     DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9558     DrawGameDoorValues();
9559   }
9560 #endif
9561
9562   for (i = 0; i < MAX_PLAYERS; i++)
9563   {
9564     int actual_player_action = stored_player[i].effective_action;
9565
9566 #if 1
9567     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9568        - rnd_equinox_tetrachloride 048
9569        - rnd_equinox_tetrachloride_ii 096
9570        - rnd_emanuel_schmieg 002
9571        - doctor_sloan_ww 001, 020
9572     */
9573     if (stored_player[i].MovPos == 0)
9574       CheckGravityMovement(&stored_player[i]);
9575 #endif
9576
9577     /* overwrite programmed action with tape action */
9578     if (stored_player[i].programmed_action)
9579       actual_player_action = stored_player[i].programmed_action;
9580
9581 #if 1
9582     PlayerActions(&stored_player[i], actual_player_action);
9583 #else
9584     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9585
9586     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9587       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9588 #endif
9589
9590     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9591   }
9592
9593 #if 0
9594   network_player_action_received = FALSE;
9595 #endif
9596
9597   ScrollScreen(NULL, SCROLL_GO_ON);
9598
9599   /* for backwards compatibility, the following code emulates a fixed bug that
9600      occured when pushing elements (causing elements that just made their last
9601      pushing step to already (if possible) make their first falling step in the
9602      same game frame, which is bad); this code is also needed to use the famous
9603      "spring push bug" which is used in older levels and might be wanted to be
9604      used also in newer levels, but in this case the buggy pushing code is only
9605      affecting the "spring" element and no other elements */
9606
9607   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9608   {
9609     for (i = 0; i < MAX_PLAYERS; i++)
9610     {
9611       struct PlayerInfo *player = &stored_player[i];
9612       int x = player->jx;
9613       int y = player->jy;
9614
9615       if (player->active && player->is_pushing && player->is_moving &&
9616           IS_MOVING(x, y) &&
9617           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9618            Feld[x][y] == EL_SPRING))
9619       {
9620         ContinueMoving(x, y);
9621
9622         /* continue moving after pushing (this is actually a bug) */
9623         if (!IS_MOVING(x, y))
9624         {
9625           Stop[x][y] = FALSE;
9626         }
9627       }
9628     }
9629   }
9630
9631 #if 1
9632   SCAN_PLAYFIELD(x, y)
9633 #else
9634   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9635 #endif
9636   {
9637     ChangeCount[x][y] = 0;
9638     ChangeEvent[x][y] = -1;
9639
9640     /* this must be handled before main playfield loop */
9641     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9642     {
9643       MovDelay[x][y]--;
9644       if (MovDelay[x][y] <= 0)
9645         RemoveField(x, y);
9646     }
9647
9648 #if USE_NEW_SNAP_DELAY
9649     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9650     {
9651       MovDelay[x][y]--;
9652       if (MovDelay[x][y] <= 0)
9653       {
9654         RemoveField(x, y);
9655         DrawLevelField(x, y);
9656
9657         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
9658       }
9659     }
9660 #endif
9661
9662 #if DEBUG
9663     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9664     {
9665       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9666       printf("GameActions(): This should never happen!\n");
9667
9668       ChangePage[x][y] = -1;
9669     }
9670 #endif
9671
9672     Stop[x][y] = FALSE;
9673     if (WasJustMoving[x][y] > 0)
9674       WasJustMoving[x][y]--;
9675     if (WasJustFalling[x][y] > 0)
9676       WasJustFalling[x][y]--;
9677     if (CheckCollision[x][y] > 0)
9678       CheckCollision[x][y]--;
9679
9680     GfxFrame[x][y]++;
9681
9682     /* reset finished pushing action (not done in ContinueMoving() to allow
9683        continuous pushing animation for elements with zero push delay) */
9684     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9685     {
9686       ResetGfxAnimation(x, y);
9687       DrawLevelField(x, y);
9688     }
9689
9690 #if DEBUG
9691     if (IS_BLOCKED(x, y))
9692     {
9693       int oldx, oldy;
9694
9695       Blocked2Moving(x, y, &oldx, &oldy);
9696       if (!IS_MOVING(oldx, oldy))
9697       {
9698         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9699         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9700         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9701         printf("GameActions(): This should never happen!\n");
9702       }
9703     }
9704 #endif
9705   }
9706
9707 #if 1
9708   SCAN_PLAYFIELD(x, y)
9709 #else
9710   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9711 #endif
9712   {
9713     element = Feld[x][y];
9714     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9715
9716 #if 0
9717     printf("::: %d,%d\n", x, y);
9718
9719     if (element == EL_ROCK)
9720       printf("::: Yo man! Rocks can fall!\n");
9721 #endif
9722
9723     if (graphic_info[graphic].anim_global_sync)
9724       GfxFrame[x][y] = FrameCounter;
9725     else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9726     {
9727       int old_gfx_frame = GfxFrame[x][y];
9728
9729       GfxFrame[x][y] = CustomValue[x][y];
9730
9731 #if 1
9732       if (GfxFrame[x][y] != old_gfx_frame)
9733 #endif
9734         DrawLevelGraphicAnimation(x, y, graphic);
9735     }
9736     else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9737     {
9738       int old_gfx_frame = GfxFrame[x][y];
9739
9740       GfxFrame[x][y] = element_info[element].collect_score;
9741
9742 #if 1
9743       if (GfxFrame[x][y] != old_gfx_frame)
9744 #endif
9745         DrawLevelGraphicAnimation(x, y, graphic);
9746     }
9747
9748     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9749         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9750       ResetRandomAnimationValue(x, y);
9751
9752     SetRandomAnimationValue(x, y);
9753
9754     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9755
9756     if (IS_INACTIVE(element))
9757     {
9758       if (IS_ANIMATED(graphic))
9759         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9760
9761       continue;
9762     }
9763
9764     /* this may take place after moving, so 'element' may have changed */
9765     if (IS_CHANGING(x, y) &&
9766         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9767     {
9768       int page = element_info[element].event_page_nr[CE_DELAY];
9769 #if 0
9770       HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9771 #else
9772
9773 #if 0
9774       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9775 #endif
9776
9777 #if 0
9778       if (element == EL_CUSTOM_255)
9779         printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9780 #endif
9781
9782 #if 1
9783       HandleElementChange(x, y, page);
9784 #else
9785       if (CAN_CHANGE(element))
9786         HandleElementChange(x, y, page);
9787
9788       if (HAS_ACTION(element))
9789         ExecuteCustomElementAction(x, y, element, page);
9790 #endif
9791
9792 #endif
9793
9794       element = Feld[x][y];
9795       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9796     }
9797
9798     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9799     {
9800       StartMoving(x, y);
9801
9802       element = Feld[x][y];
9803       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9804
9805       if (IS_ANIMATED(graphic) &&
9806           !IS_MOVING(x, y) &&
9807           !Stop[x][y])
9808         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9809
9810       if (IS_GEM(element) || element == EL_SP_INFOTRON)
9811         EdelsteinFunkeln(x, y);
9812     }
9813     else if ((element == EL_ACID ||
9814               element == EL_EXIT_OPEN ||
9815               element == EL_SP_EXIT_OPEN ||
9816               element == EL_SP_TERMINAL ||
9817               element == EL_SP_TERMINAL_ACTIVE ||
9818               element == EL_EXTRA_TIME ||
9819               element == EL_SHIELD_NORMAL ||
9820               element == EL_SHIELD_DEADLY) &&
9821              IS_ANIMATED(graphic))
9822       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9823     else if (IS_MOVING(x, y))
9824       ContinueMoving(x, y);
9825     else if (IS_ACTIVE_BOMB(element))
9826       CheckDynamite(x, y);
9827     else if (element == EL_AMOEBA_GROWING)
9828       AmoebeWaechst(x, y);
9829     else if (element == EL_AMOEBA_SHRINKING)
9830       AmoebaDisappearing(x, y);
9831
9832 #if !USE_NEW_AMOEBA_CODE
9833     else if (IS_AMOEBALIVE(element))
9834       AmoebeAbleger(x, y);
9835 #endif
9836
9837     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9838       Life(x, y);
9839     else if (element == EL_EXIT_CLOSED)
9840       CheckExit(x, y);
9841     else if (element == EL_SP_EXIT_CLOSED)
9842       CheckExitSP(x, y);
9843     else if (element == EL_EXPANDABLE_WALL_GROWING)
9844       MauerWaechst(x, y);
9845     else if (element == EL_EXPANDABLE_WALL ||
9846              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9847              element == EL_EXPANDABLE_WALL_VERTICAL ||
9848              element == EL_EXPANDABLE_WALL_ANY)
9849       MauerAbleger(x, y);
9850     else if (element == EL_FLAMES)
9851       CheckForDragon(x, y);
9852     else if (element == EL_EXPLOSION)
9853       ; /* drawing of correct explosion animation is handled separately */
9854     else if (element == EL_ELEMENT_SNAPPING ||
9855              element == EL_DIAGONAL_SHRINKING ||
9856              element == EL_DIAGONAL_GROWING)
9857     {
9858 #if 1
9859       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9860
9861       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9862 #endif
9863     }
9864     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9865       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9866
9867 #if 0
9868     if (element == EL_CUSTOM_255 ||
9869         element == EL_CUSTOM_256)
9870       DrawLevelGraphicAnimation(x, y, graphic);
9871 #endif
9872
9873     if (IS_BELT_ACTIVE(element))
9874       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9875
9876     if (game.magic_wall_active)
9877     {
9878       int jx = local_player->jx, jy = local_player->jy;
9879
9880       /* play the element sound at the position nearest to the player */
9881       if ((element == EL_MAGIC_WALL_FULL ||
9882            element == EL_MAGIC_WALL_ACTIVE ||
9883            element == EL_MAGIC_WALL_EMPTYING ||
9884            element == EL_BD_MAGIC_WALL_FULL ||
9885            element == EL_BD_MAGIC_WALL_ACTIVE ||
9886            element == EL_BD_MAGIC_WALL_EMPTYING) &&
9887           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9888       {
9889         magic_wall_x = x;
9890         magic_wall_y = y;
9891       }
9892     }
9893   }
9894
9895 #if USE_NEW_AMOEBA_CODE
9896   /* new experimental amoeba growth stuff */
9897   if (!(FrameCounter % 8))
9898   {
9899     static unsigned long random = 1684108901;
9900
9901     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9902     {
9903       x = RND(lev_fieldx);
9904       y = RND(lev_fieldy);
9905       element = Feld[x][y];
9906
9907       if (!IS_PLAYER(x,y) &&
9908           (element == EL_EMPTY ||
9909            CAN_GROW_INTO(element) ||
9910            element == EL_QUICKSAND_EMPTY ||
9911            element == EL_ACID_SPLASH_LEFT ||
9912            element == EL_ACID_SPLASH_RIGHT))
9913       {
9914         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9915             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9916             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9917             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9918           Feld[x][y] = EL_AMOEBA_DROP;
9919       }
9920
9921       random = random * 129 + 1;
9922     }
9923   }
9924 #endif
9925
9926 #if 0
9927   if (game.explosions_delayed)
9928 #endif
9929   {
9930     game.explosions_delayed = FALSE;
9931
9932 #if 1
9933     SCAN_PLAYFIELD(x, y)
9934 #else
9935     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9936 #endif
9937     {
9938       element = Feld[x][y];
9939
9940       if (ExplodeField[x][y])
9941         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
9942       else if (element == EL_EXPLOSION)
9943         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
9944
9945       ExplodeField[x][y] = EX_TYPE_NONE;
9946     }
9947
9948     game.explosions_delayed = TRUE;
9949   }
9950
9951   if (game.magic_wall_active)
9952   {
9953     if (!(game.magic_wall_time_left % 4))
9954     {
9955       int element = Feld[magic_wall_x][magic_wall_y];
9956
9957       if (element == EL_BD_MAGIC_WALL_FULL ||
9958           element == EL_BD_MAGIC_WALL_ACTIVE ||
9959           element == EL_BD_MAGIC_WALL_EMPTYING)
9960         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
9961       else
9962         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
9963     }
9964
9965     if (game.magic_wall_time_left > 0)
9966     {
9967       game.magic_wall_time_left--;
9968       if (!game.magic_wall_time_left)
9969       {
9970 #if 1
9971         SCAN_PLAYFIELD(x, y)
9972 #else
9973         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9974 #endif
9975         {
9976           element = Feld[x][y];
9977
9978           if (element == EL_MAGIC_WALL_ACTIVE ||
9979               element == EL_MAGIC_WALL_FULL)
9980           {
9981             Feld[x][y] = EL_MAGIC_WALL_DEAD;
9982             DrawLevelField(x, y);
9983           }
9984           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
9985                    element == EL_BD_MAGIC_WALL_FULL)
9986           {
9987             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9988             DrawLevelField(x, y);
9989           }
9990         }
9991
9992         game.magic_wall_active = FALSE;
9993       }
9994     }
9995   }
9996
9997   if (game.light_time_left > 0)
9998   {
9999     game.light_time_left--;
10000
10001     if (game.light_time_left == 0)
10002       RedrawAllLightSwitchesAndInvisibleElements();
10003   }
10004
10005   if (game.timegate_time_left > 0)
10006   {
10007     game.timegate_time_left--;
10008
10009     if (game.timegate_time_left == 0)
10010       CloseAllOpenTimegates();
10011   }
10012
10013   if (game.lenses_time_left > 0)
10014   {
10015     game.lenses_time_left--;
10016
10017     if (game.lenses_time_left == 0)
10018       RedrawAllInvisibleElementsForLenses();
10019   }
10020
10021   if (game.magnify_time_left > 0)
10022   {
10023     game.magnify_time_left--;
10024
10025     if (game.magnify_time_left == 0)
10026       RedrawAllInvisibleElementsForMagnifier();
10027   }
10028
10029   for (i = 0; i < MAX_PLAYERS; i++)
10030   {
10031     struct PlayerInfo *player = &stored_player[i];
10032
10033     if (SHIELD_ON(player))
10034     {
10035       if (player->shield_deadly_time_left)
10036         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10037       else if (player->shield_normal_time_left)
10038         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10039     }
10040   }
10041
10042   CheckLevelTime();
10043
10044   DrawAllPlayers();
10045   PlayAllPlayersSound();
10046
10047   if (options.debug)                    /* calculate frames per second */
10048   {
10049     static unsigned long fps_counter = 0;
10050     static int fps_frames = 0;
10051     unsigned long fps_delay_ms = Counter() - fps_counter;
10052
10053     fps_frames++;
10054
10055     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10056     {
10057       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10058
10059       fps_frames = 0;
10060       fps_counter = Counter();
10061     }
10062
10063     redraw_mask |= REDRAW_FPS;
10064   }
10065
10066   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10067
10068   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10069   {
10070     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10071
10072     local_player->show_envelope = 0;
10073   }
10074
10075   /* use random number generator in every frame to make it less predictable */
10076   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10077     RND(1);
10078 }
10079
10080 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10081 {
10082   int min_x = x, min_y = y, max_x = x, max_y = y;
10083   int i;
10084
10085   for (i = 0; i < MAX_PLAYERS; i++)
10086   {
10087     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10088
10089     if (!stored_player[i].active || &stored_player[i] == player)
10090       continue;
10091
10092     min_x = MIN(min_x, jx);
10093     min_y = MIN(min_y, jy);
10094     max_x = MAX(max_x, jx);
10095     max_y = MAX(max_y, jy);
10096   }
10097
10098   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10099 }
10100
10101 static boolean AllPlayersInVisibleScreen()
10102 {
10103   int i;
10104
10105   for (i = 0; i < MAX_PLAYERS; i++)
10106   {
10107     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10108
10109     if (!stored_player[i].active)
10110       continue;
10111
10112     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10113       return FALSE;
10114   }
10115
10116   return TRUE;
10117 }
10118
10119 void ScrollLevel(int dx, int dy)
10120 {
10121   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10122   int x, y;
10123
10124   BlitBitmap(drawto_field, drawto_field,
10125              FX + TILEX * (dx == -1) - softscroll_offset,
10126              FY + TILEY * (dy == -1) - softscroll_offset,
10127              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10128              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10129              FX + TILEX * (dx == 1) - softscroll_offset,
10130              FY + TILEY * (dy == 1) - softscroll_offset);
10131
10132   if (dx)
10133   {
10134     x = (dx == 1 ? BX1 : BX2);
10135     for (y = BY1; y <= BY2; y++)
10136       DrawScreenField(x, y);
10137   }
10138
10139   if (dy)
10140   {
10141     y = (dy == 1 ? BY1 : BY2);
10142     for (x = BX1; x <= BX2; x++)
10143       DrawScreenField(x, y);
10144   }
10145
10146   redraw_mask |= REDRAW_FIELD;
10147 }
10148
10149 static boolean canFallDown(struct PlayerInfo *player)
10150 {
10151   int jx = player->jx, jy = player->jy;
10152
10153   return (IN_LEV_FIELD(jx, jy + 1) &&
10154           (IS_FREE(jx, jy + 1) ||
10155            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10156           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10157           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10158 }
10159
10160 static boolean canPassField(int x, int y, int move_dir)
10161 {
10162   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10163   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10164   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10165   int nextx = x + dx;
10166   int nexty = y + dy;
10167   int element = Feld[x][y];
10168
10169   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10170           !CAN_MOVE(element) &&
10171           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10172           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10173           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10174 }
10175
10176 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10177 {
10178   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10179   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10180   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10181   int newx = x + dx;
10182   int newy = y + dy;
10183
10184   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10185           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10186           (IS_DIGGABLE(Feld[newx][newy]) ||
10187            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10188            canPassField(newx, newy, move_dir)));
10189 }
10190
10191 static void CheckGravityMovement(struct PlayerInfo *player)
10192 {
10193   if (game.gravity && !player->programmed_action)
10194   {
10195     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10196     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10197     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10198     int jx = player->jx, jy = player->jy;
10199     boolean player_is_moving_to_valid_field =
10200       (!player_is_snapping &&
10201        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10202         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10203     boolean player_can_fall_down = canFallDown(player);
10204
10205     if (player_can_fall_down &&
10206         !player_is_moving_to_valid_field)
10207       player->programmed_action = MV_DOWN;
10208   }
10209 }
10210
10211 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10212 {
10213   return CheckGravityMovement(player);
10214
10215   if (game.gravity && !player->programmed_action)
10216   {
10217     int jx = player->jx, jy = player->jy;
10218     boolean field_under_player_is_free =
10219       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10220     boolean player_is_standing_on_valid_field =
10221       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10222        (IS_WALKABLE(Feld[jx][jy]) &&
10223         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10224
10225     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10226       player->programmed_action = MV_DOWN;
10227   }
10228 }
10229
10230 /*
10231   MovePlayerOneStep()
10232   -----------------------------------------------------------------------------
10233   dx, dy:               direction (non-diagonal) to try to move the player to
10234   real_dx, real_dy:     direction as read from input device (can be diagonal)
10235 */
10236
10237 boolean MovePlayerOneStep(struct PlayerInfo *player,
10238                           int dx, int dy, int real_dx, int real_dy)
10239 {
10240   int jx = player->jx, jy = player->jy;
10241   int new_jx = jx + dx, new_jy = jy + dy;
10242 #if !USE_FIXED_DONT_RUN_INTO
10243   int element;
10244 #endif
10245   int can_move;
10246   boolean player_can_move = !player->cannot_move;
10247
10248   if (!player->active || (!dx && !dy))
10249     return MP_NO_ACTION;
10250
10251   player->MovDir = (dx < 0 ? MV_LEFT :
10252                     dx > 0 ? MV_RIGHT :
10253                     dy < 0 ? MV_UP :
10254                     dy > 0 ? MV_DOWN :  MV_NONE);
10255
10256   if (!IN_LEV_FIELD(new_jx, new_jy))
10257     return MP_NO_ACTION;
10258
10259   if (!player_can_move)
10260   {
10261 #if 1
10262     if (player->MovPos == 0)
10263     {
10264       player->is_moving = FALSE;
10265       player->is_digging = FALSE;
10266       player->is_collecting = FALSE;
10267       player->is_snapping = FALSE;
10268       player->is_pushing = FALSE;
10269     }
10270 #else
10271     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10272     SnapField(player, 0, 0);
10273 #endif
10274
10275 #if 0
10276     return MP_NO_ACTION;
10277 #endif
10278   }
10279
10280 #if 1
10281   if (!options.network && game.centered_player_nr == -1 &&
10282       !AllPlayersInSight(player, new_jx, new_jy))
10283     return MP_NO_ACTION;
10284 #else
10285   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10286     return MP_NO_ACTION;
10287 #endif
10288
10289 #if !USE_FIXED_DONT_RUN_INTO
10290   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10291
10292   /* (moved to DigField()) */
10293   if (player_can_move && DONT_RUN_INTO(element))
10294   {
10295     if (element == EL_ACID && dx == 0 && dy == 1)
10296     {
10297       SplashAcid(new_jx, new_jy);
10298       Feld[jx][jy] = EL_PLAYER_1;
10299       InitMovingField(jx, jy, MV_DOWN);
10300       Store[jx][jy] = EL_ACID;
10301       ContinueMoving(jx, jy);
10302       BuryPlayer(player);
10303     }
10304     else
10305       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10306
10307     return MP_MOVING;
10308   }
10309 #endif
10310
10311   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10312 #if 0
10313 #if USE_FIXED_DONT_RUN_INTO
10314   if (can_move == MP_DONT_RUN_INTO)
10315     return MP_MOVING;
10316 #endif
10317 #endif
10318   if (can_move != MP_MOVING)
10319     return can_move;
10320
10321 #if USE_FIXED_DONT_RUN_INTO
10322 #endif
10323
10324   /* check if DigField() has caused relocation of the player */
10325   if (player->jx != jx || player->jy != jy)
10326     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10327
10328   StorePlayer[jx][jy] = 0;
10329   player->last_jx = jx;
10330   player->last_jy = jy;
10331   player->jx = new_jx;
10332   player->jy = new_jy;
10333   StorePlayer[new_jx][new_jy] = player->element_nr;
10334
10335   if (player->move_delay_value_next != -1)
10336   {
10337     player->move_delay_value = player->move_delay_value_next;
10338     player->move_delay_value_next = -1;
10339   }
10340
10341   player->MovPos =
10342     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10343
10344   player->step_counter++;
10345
10346   PlayerVisit[jx][jy] = FrameCounter;
10347
10348   ScrollPlayer(player, SCROLL_INIT);
10349
10350   return MP_MOVING;
10351 }
10352
10353 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10354 {
10355   int jx = player->jx, jy = player->jy;
10356   int old_jx = jx, old_jy = jy;
10357   int moved = MP_NO_ACTION;
10358
10359   if (!player->active)
10360     return FALSE;
10361
10362   if (!dx && !dy)
10363   {
10364     if (player->MovPos == 0)
10365     {
10366       player->is_moving = FALSE;
10367       player->is_digging = FALSE;
10368       player->is_collecting = FALSE;
10369       player->is_snapping = FALSE;
10370       player->is_pushing = FALSE;
10371     }
10372
10373     return FALSE;
10374   }
10375
10376   if (player->move_delay > 0)
10377     return FALSE;
10378
10379   player->move_delay = -1;              /* set to "uninitialized" value */
10380
10381   /* store if player is automatically moved to next field */
10382   player->is_auto_moving = (player->programmed_action != MV_NONE);
10383
10384   /* remove the last programmed player action */
10385   player->programmed_action = 0;
10386
10387   if (player->MovPos)
10388   {
10389     /* should only happen if pre-1.2 tape recordings are played */
10390     /* this is only for backward compatibility */
10391
10392     int original_move_delay_value = player->move_delay_value;
10393
10394 #if DEBUG
10395     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10396            tape.counter);
10397 #endif
10398
10399     /* scroll remaining steps with finest movement resolution */
10400     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10401
10402     while (player->MovPos)
10403     {
10404       ScrollPlayer(player, SCROLL_GO_ON);
10405       ScrollScreen(NULL, SCROLL_GO_ON);
10406
10407       AdvanceFrameAndPlayerCounters(player->index_nr);
10408
10409       DrawAllPlayers();
10410       BackToFront();
10411     }
10412
10413     player->move_delay_value = original_move_delay_value;
10414   }
10415
10416   if (player->last_move_dir & MV_HORIZONTAL)
10417   {
10418     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10419       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10420   }
10421   else
10422   {
10423     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10424       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10425   }
10426
10427   jx = player->jx;
10428   jy = player->jy;
10429
10430 #if 1
10431   if (moved & MP_MOVING && !ScreenMovPos &&
10432       (player->index_nr == game.centered_player_nr ||
10433        game.centered_player_nr == -1))
10434 #else
10435   if (moved & MP_MOVING && !ScreenMovPos &&
10436       (player == local_player || !options.network))
10437 #endif
10438   {
10439     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10440     int offset = (setup.scroll_delay ? 3 : 0);
10441
10442     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10443     {
10444       /* actual player has left the screen -- scroll in that direction */
10445       if (jx != old_jx)         /* player has moved horizontally */
10446         scroll_x += (jx - old_jx);
10447       else                      /* player has moved vertically */
10448         scroll_y += (jy - old_jy);
10449     }
10450     else
10451     {
10452       if (jx != old_jx)         /* player has moved horizontally */
10453       {
10454         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10455             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10456           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10457
10458         /* don't scroll over playfield boundaries */
10459         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10460           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10461
10462         /* don't scroll more than one field at a time */
10463         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10464
10465         /* don't scroll against the player's moving direction */
10466         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10467             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10468           scroll_x = old_scroll_x;
10469       }
10470       else                      /* player has moved vertically */
10471       {
10472         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10473             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10474           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10475
10476         /* don't scroll over playfield boundaries */
10477         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10478           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10479
10480         /* don't scroll more than one field at a time */
10481         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10482
10483         /* don't scroll against the player's moving direction */
10484         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10485             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10486           scroll_y = old_scroll_y;
10487       }
10488     }
10489
10490     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10491     {
10492 #if 1
10493       if (!options.network && game.centered_player_nr == -1 &&
10494           !AllPlayersInVisibleScreen())
10495       {
10496         scroll_x = old_scroll_x;
10497         scroll_y = old_scroll_y;
10498       }
10499       else
10500 #else
10501       if (!options.network && !AllPlayersInVisibleScreen())
10502       {
10503         scroll_x = old_scroll_x;
10504         scroll_y = old_scroll_y;
10505       }
10506       else
10507 #endif
10508       {
10509         ScrollScreen(player, SCROLL_INIT);
10510         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10511       }
10512     }
10513   }
10514
10515   player->StepFrame = 0;
10516
10517   if (moved & MP_MOVING)
10518   {
10519     if (old_jx != jx && old_jy == jy)
10520       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10521     else if (old_jx == jx && old_jy != jy)
10522       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10523
10524     DrawLevelField(jx, jy);     /* for "crumbled sand" */
10525
10526     player->last_move_dir = player->MovDir;
10527     player->is_moving = TRUE;
10528     player->is_snapping = FALSE;
10529     player->is_switching = FALSE;
10530     player->is_dropping = FALSE;
10531     player->is_dropping_pressed = FALSE;
10532     player->drop_pressed_delay = 0;
10533   }
10534   else
10535   {
10536     CheckGravityMovementWhenNotMoving(player);
10537
10538     player->is_moving = FALSE;
10539
10540     /* at this point, the player is allowed to move, but cannot move right now
10541        (e.g. because of something blocking the way) -- ensure that the player
10542        is also allowed to move in the next frame (in old versions before 3.1.1,
10543        the player was forced to wait again for eight frames before next try) */
10544
10545     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10546       player->move_delay = 0;   /* allow direct movement in the next frame */
10547   }
10548
10549   if (player->move_delay == -1)         /* not yet initialized by DigField() */
10550     player->move_delay = player->move_delay_value;
10551
10552   if (game.engine_version < VERSION_IDENT(3,0,7,0))
10553   {
10554     TestIfPlayerTouchesBadThing(jx, jy);
10555     TestIfPlayerTouchesCustomElement(jx, jy);
10556   }
10557
10558   if (!player->active)
10559     RemovePlayer(player);
10560
10561   return moved;
10562 }
10563
10564 void ScrollPlayer(struct PlayerInfo *player, int mode)
10565 {
10566   int jx = player->jx, jy = player->jy;
10567   int last_jx = player->last_jx, last_jy = player->last_jy;
10568   int move_stepsize = TILEX / player->move_delay_value;
10569
10570 #if USE_NEW_PLAYER_SPEED
10571   if (!player->active)
10572     return;
10573
10574   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
10575     return;
10576 #else
10577   if (!player->active || player->MovPos == 0)
10578     return;
10579 #endif
10580
10581   if (mode == SCROLL_INIT)
10582   {
10583     player->actual_frame_counter = FrameCounter;
10584     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10585
10586     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10587         Feld[last_jx][last_jy] == EL_EMPTY)
10588     {
10589       int last_field_block_delay = 0;   /* start with no blocking at all */
10590       int block_delay_adjustment = player->block_delay_adjustment;
10591
10592       /* if player blocks last field, add delay for exactly one move */
10593       if (player->block_last_field)
10594       {
10595         last_field_block_delay += player->move_delay_value;
10596
10597         /* when blocking enabled, prevent moving up despite gravity */
10598         if (game.gravity && player->MovDir == MV_UP)
10599           block_delay_adjustment = -1;
10600       }
10601
10602       /* add block delay adjustment (also possible when not blocking) */
10603       last_field_block_delay += block_delay_adjustment;
10604
10605       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10606       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10607     }
10608
10609 #if USE_NEW_PLAYER_SPEED
10610     if (player->MovPos != 0)    /* player has not yet reached destination */
10611       return;
10612 #else
10613     return;
10614 #endif
10615   }
10616   else if (!FrameReached(&player->actual_frame_counter, 1))
10617     return;
10618
10619 #if 0
10620   printf("::: player->MovPos: %d -> %d\n",
10621          player->MovPos,
10622          player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10623 #endif
10624
10625 #if USE_NEW_PLAYER_SPEED
10626   if (player->MovPos != 0)
10627   {
10628     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10629     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10630
10631     /* before DrawPlayer() to draw correct player graphic for this case */
10632     if (player->MovPos == 0)
10633       CheckGravityMovement(player);
10634   }
10635 #else
10636   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10637   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10638
10639   /* before DrawPlayer() to draw correct player graphic for this case */
10640   if (player->MovPos == 0)
10641     CheckGravityMovement(player);
10642 #endif
10643
10644   if (player->MovPos == 0)      /* player reached destination field */
10645   {
10646 #if 0
10647     printf("::: player reached destination field\n");
10648 #endif
10649
10650     if (player->move_delay_reset_counter > 0)
10651     {
10652       player->move_delay_reset_counter--;
10653
10654       if (player->move_delay_reset_counter == 0)
10655       {
10656         /* continue with normal speed after quickly moving through gate */
10657         HALVE_PLAYER_SPEED(player);
10658
10659         /* be able to make the next move without delay */
10660         player->move_delay = 0;
10661       }
10662     }
10663
10664     player->last_jx = jx;
10665     player->last_jy = jy;
10666
10667     if (Feld[jx][jy] == EL_EXIT_OPEN ||
10668         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10669         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
10670     {
10671       DrawPlayer(player);       /* needed here only to cleanup last field */
10672       RemovePlayer(player);
10673
10674       if (local_player->friends_still_needed == 0 ||
10675           IS_SP_ELEMENT(Feld[jx][jy]))
10676         player->LevelSolved = player->GameOver = TRUE;
10677     }
10678
10679     /* this breaks one level: "machine", level 000 */
10680     {
10681       int move_direction = player->MovDir;
10682       int enter_side = MV_DIR_OPPOSITE(move_direction);
10683       int leave_side = move_direction;
10684       int old_jx = last_jx;
10685       int old_jy = last_jy;
10686       int old_element = Feld[old_jx][old_jy];
10687       int new_element = Feld[jx][jy];
10688
10689       if (IS_CUSTOM_ELEMENT(old_element))
10690         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10691                                    CE_LEFT_BY_PLAYER,
10692                                    player->index_bit, leave_side);
10693
10694       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10695                                           CE_PLAYER_LEAVES_X,
10696                                           player->index_bit, leave_side);
10697
10698       if (IS_CUSTOM_ELEMENT(new_element))
10699         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10700                                    player->index_bit, enter_side);
10701
10702       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10703                                           CE_PLAYER_ENTERS_X,
10704                                           player->index_bit, enter_side);
10705
10706       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10707                                         CE_MOVE_OF_X, move_direction);
10708     }
10709
10710     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10711     {
10712       TestIfPlayerTouchesBadThing(jx, jy);
10713       TestIfPlayerTouchesCustomElement(jx, jy);
10714
10715       /* needed because pushed element has not yet reached its destination,
10716          so it would trigger a change event at its previous field location */
10717       if (!player->is_pushing)
10718         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
10719
10720       if (!player->active)
10721         RemovePlayer(player);
10722     }
10723
10724     if (level.use_step_counter)
10725     {
10726       int i;
10727
10728       TimePlayed++;
10729
10730       if (TimeLeft > 0)
10731       {
10732         TimeLeft--;
10733
10734         if (TimeLeft <= 10 && setup.time_limit)
10735           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10736
10737         DrawGameValue_Time(TimeLeft);
10738
10739         if (!TimeLeft && setup.time_limit)
10740           for (i = 0; i < MAX_PLAYERS; i++)
10741             KillPlayer(&stored_player[i]);
10742       }
10743       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10744         DrawGameValue_Time(TimePlayed);
10745     }
10746
10747     if (tape.single_step && tape.recording && !tape.pausing &&
10748         !player->programmed_action)
10749       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10750   }
10751 }
10752
10753 void ScrollScreen(struct PlayerInfo *player, int mode)
10754 {
10755   static unsigned long screen_frame_counter = 0;
10756
10757   if (mode == SCROLL_INIT)
10758   {
10759     /* set scrolling step size according to actual player's moving speed */
10760     ScrollStepSize = TILEX / player->move_delay_value;
10761
10762     screen_frame_counter = FrameCounter;
10763     ScreenMovDir = player->MovDir;
10764     ScreenMovPos = player->MovPos;
10765     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10766     return;
10767   }
10768   else if (!FrameReached(&screen_frame_counter, 1))
10769     return;
10770
10771   if (ScreenMovPos)
10772   {
10773     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10774     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10775     redraw_mask |= REDRAW_FIELD;
10776   }
10777   else
10778     ScreenMovDir = MV_NONE;
10779 }
10780
10781 void TestIfPlayerTouchesCustomElement(int x, int y)
10782 {
10783   static int xy[4][2] =
10784   {
10785     { 0, -1 },
10786     { -1, 0 },
10787     { +1, 0 },
10788     { 0, +1 }
10789   };
10790   static int trigger_sides[4][2] =
10791   {
10792     /* center side       border side */
10793     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10794     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10795     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10796     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10797   };
10798   static int touch_dir[4] =
10799   {
10800     MV_LEFT | MV_RIGHT,
10801     MV_UP   | MV_DOWN,
10802     MV_UP   | MV_DOWN,
10803     MV_LEFT | MV_RIGHT
10804   };
10805   int center_element = Feld[x][y];      /* should always be non-moving! */
10806   int i;
10807
10808   for (i = 0; i < NUM_DIRECTIONS; i++)
10809   {
10810     int xx = x + xy[i][0];
10811     int yy = y + xy[i][1];
10812     int center_side = trigger_sides[i][0];
10813     int border_side = trigger_sides[i][1];
10814     int border_element;
10815
10816     if (!IN_LEV_FIELD(xx, yy))
10817       continue;
10818
10819     if (IS_PLAYER(x, y))
10820     {
10821       struct PlayerInfo *player = PLAYERINFO(x, y);
10822
10823       if (game.engine_version < VERSION_IDENT(3,0,7,0))
10824         border_element = Feld[xx][yy];          /* may be moving! */
10825       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10826         border_element = Feld[xx][yy];
10827       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
10828         border_element = MovingOrBlocked2Element(xx, yy);
10829       else
10830         continue;               /* center and border element do not touch */
10831
10832       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10833                                  player->index_bit, border_side);
10834       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10835                                           CE_PLAYER_TOUCHES_X,
10836                                           player->index_bit, border_side);
10837     }
10838     else if (IS_PLAYER(xx, yy))
10839     {
10840       struct PlayerInfo *player = PLAYERINFO(xx, yy);
10841
10842       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10843       {
10844         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10845           continue;             /* center and border element do not touch */
10846       }
10847
10848       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10849                                  player->index_bit, center_side);
10850       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10851                                           CE_PLAYER_TOUCHES_X,
10852                                           player->index_bit, center_side);
10853       break;
10854     }
10855   }
10856 }
10857
10858 #if USE_ELEMENT_TOUCHING_BUGFIX
10859
10860 void TestIfElementTouchesCustomElement(int x, int y)
10861 {
10862   static int xy[4][2] =
10863   {
10864     { 0, -1 },
10865     { -1, 0 },
10866     { +1, 0 },
10867     { 0, +1 }
10868   };
10869   static int trigger_sides[4][2] =
10870   {
10871     /* center side      border side */
10872     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10873     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10874     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10875     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10876   };
10877   static int touch_dir[4] =
10878   {
10879     MV_LEFT | MV_RIGHT,
10880     MV_UP   | MV_DOWN,
10881     MV_UP   | MV_DOWN,
10882     MV_LEFT | MV_RIGHT
10883   };
10884   boolean change_center_element = FALSE;
10885   int center_element = Feld[x][y];      /* should always be non-moving! */
10886   int border_element_old[NUM_DIRECTIONS];
10887   int i;
10888
10889   for (i = 0; i < NUM_DIRECTIONS; i++)
10890   {
10891     int xx = x + xy[i][0];
10892     int yy = y + xy[i][1];
10893     int border_element;
10894
10895     border_element_old[i] = -1;
10896
10897     if (!IN_LEV_FIELD(xx, yy))
10898       continue;
10899
10900     if (game.engine_version < VERSION_IDENT(3,0,7,0))
10901       border_element = Feld[xx][yy];    /* may be moving! */
10902     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10903       border_element = Feld[xx][yy];
10904     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
10905       border_element = MovingOrBlocked2Element(xx, yy);
10906     else
10907       continue;                 /* center and border element do not touch */
10908
10909     border_element_old[i] = border_element;
10910   }
10911
10912   for (i = 0; i < NUM_DIRECTIONS; i++)
10913   {
10914     int xx = x + xy[i][0];
10915     int yy = y + xy[i][1];
10916     int center_side = trigger_sides[i][0];
10917     int border_element = border_element_old[i];
10918
10919     if (border_element == -1)
10920       continue;
10921
10922     /* check for change of border element */
10923     CheckElementChangeBySide(xx, yy, border_element, center_element,
10924                              CE_TOUCHING_X, center_side);
10925   }
10926
10927   for (i = 0; i < NUM_DIRECTIONS; i++)
10928   {
10929     int border_side = trigger_sides[i][1];
10930     int border_element = border_element_old[i];
10931
10932     if (border_element == -1)
10933       continue;
10934
10935     /* check for change of center element (but change it only once) */
10936     if (!change_center_element)
10937       change_center_element =
10938         CheckElementChangeBySide(x, y, center_element, border_element,
10939                                  CE_TOUCHING_X, border_side);
10940   }
10941 }
10942
10943 #else
10944
10945 void TestIfElementTouchesCustomElement_OLD(int x, int y)
10946 {
10947   static int xy[4][2] =
10948   {
10949     { 0, -1 },
10950     { -1, 0 },
10951     { +1, 0 },
10952     { 0, +1 }
10953   };
10954   static int trigger_sides[4][2] =
10955   {
10956     /* center side      border side */
10957     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10958     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10959     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10960     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10961   };
10962   static int touch_dir[4] =
10963   {
10964     MV_LEFT | MV_RIGHT,
10965     MV_UP   | MV_DOWN,
10966     MV_UP   | MV_DOWN,
10967     MV_LEFT | MV_RIGHT
10968   };
10969   boolean change_center_element = FALSE;
10970   int center_element = Feld[x][y];      /* should always be non-moving! */
10971   int i;
10972
10973   for (i = 0; i < NUM_DIRECTIONS; i++)
10974   {
10975     int xx = x + xy[i][0];
10976     int yy = y + xy[i][1];
10977     int center_side = trigger_sides[i][0];
10978     int border_side = trigger_sides[i][1];
10979     int border_element;
10980
10981     if (!IN_LEV_FIELD(xx, yy))
10982       continue;
10983
10984     if (game.engine_version < VERSION_IDENT(3,0,7,0))
10985       border_element = Feld[xx][yy];    /* may be moving! */
10986     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10987       border_element = Feld[xx][yy];
10988     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
10989       border_element = MovingOrBlocked2Element(xx, yy);
10990     else
10991       continue;                 /* center and border element do not touch */
10992
10993     /* check for change of center element (but change it only once) */
10994     if (!change_center_element)
10995       change_center_element =
10996         CheckElementChangeBySide(x, y, center_element, border_element,
10997                                  CE_TOUCHING_X, border_side);
10998
10999     /* check for change of border element */
11000     CheckElementChangeBySide(xx, yy, border_element, center_element,
11001                              CE_TOUCHING_X, center_side);
11002   }
11003 }
11004
11005 #endif
11006
11007 void TestIfElementHitsCustomElement(int x, int y, int direction)
11008 {
11009   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11010   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11011   int hitx = x + dx, hity = y + dy;
11012   int hitting_element = Feld[x][y];
11013   int touched_element;
11014
11015   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11016     return;
11017
11018   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11019                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11020
11021   if (IN_LEV_FIELD(hitx, hity))
11022   {
11023     int opposite_direction = MV_DIR_OPPOSITE(direction);
11024     int hitting_side = direction;
11025     int touched_side = opposite_direction;
11026     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11027                           MovDir[hitx][hity] != direction ||
11028                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11029
11030     object_hit = TRUE;
11031
11032     if (object_hit)
11033     {
11034       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11035                                CE_HITTING_X, touched_side);
11036
11037       CheckElementChangeBySide(hitx, hity, touched_element,
11038                                hitting_element, CE_HIT_BY_X, hitting_side);
11039
11040       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11041                                CE_HIT_BY_SOMETHING, opposite_direction);
11042     }
11043   }
11044
11045   /* "hitting something" is also true when hitting the playfield border */
11046   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11047                            CE_HITTING_SOMETHING, direction);
11048 }
11049
11050 #if 0
11051 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11052 {
11053   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11054   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11055   int hitx = x + dx, hity = y + dy;
11056   int hitting_element = Feld[x][y];
11057   int touched_element;
11058 #if 0
11059   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11060                         !IS_FREE(hitx, hity) &&
11061                         (!IS_MOVING(hitx, hity) ||
11062                          MovDir[hitx][hity] != direction ||
11063                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11064 #endif
11065
11066   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11067     return;
11068
11069 #if 0
11070   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11071     return;
11072 #endif
11073
11074   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11075                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11076
11077   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11078                            EP_CAN_SMASH_EVERYTHING, direction);
11079
11080   if (IN_LEV_FIELD(hitx, hity))
11081   {
11082     int opposite_direction = MV_DIR_OPPOSITE(direction);
11083     int hitting_side = direction;
11084     int touched_side = opposite_direction;
11085 #if 0
11086     int touched_element = MovingOrBlocked2Element(hitx, hity);
11087 #endif
11088 #if 1
11089     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11090                           MovDir[hitx][hity] != direction ||
11091                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11092
11093     object_hit = TRUE;
11094 #endif
11095
11096     if (object_hit)
11097     {
11098       int i;
11099
11100       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11101                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11102
11103       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11104                                CE_OTHER_IS_SMASHING, touched_side);
11105
11106       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11107                                CE_OTHER_GETS_SMASHED, hitting_side);
11108     }
11109   }
11110 }
11111 #endif
11112
11113 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11114 {
11115   int i, kill_x = -1, kill_y = -1;
11116
11117   int bad_element = -1;
11118   static int test_xy[4][2] =
11119   {
11120     { 0, -1 },
11121     { -1, 0 },
11122     { +1, 0 },
11123     { 0, +1 }
11124   };
11125   static int test_dir[4] =
11126   {
11127     MV_UP,
11128     MV_LEFT,
11129     MV_RIGHT,
11130     MV_DOWN
11131   };
11132
11133   for (i = 0; i < NUM_DIRECTIONS; i++)
11134   {
11135     int test_x, test_y, test_move_dir, test_element;
11136
11137     test_x = good_x + test_xy[i][0];
11138     test_y = good_y + test_xy[i][1];
11139
11140     if (!IN_LEV_FIELD(test_x, test_y))
11141       continue;
11142
11143     test_move_dir =
11144       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11145
11146     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11147
11148     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11149        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11150     */
11151     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11152         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11153     {
11154       kill_x = test_x;
11155       kill_y = test_y;
11156       bad_element = test_element;
11157
11158       break;
11159     }
11160   }
11161
11162   if (kill_x != -1 || kill_y != -1)
11163   {
11164     if (IS_PLAYER(good_x, good_y))
11165     {
11166       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11167
11168       if (player->shield_deadly_time_left > 0 &&
11169           !IS_INDESTRUCTIBLE(bad_element))
11170         Bang(kill_x, kill_y);
11171       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11172         KillPlayer(player);
11173     }
11174     else
11175       Bang(good_x, good_y);
11176   }
11177 }
11178
11179 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11180 {
11181   int i, kill_x = -1, kill_y = -1;
11182   int bad_element = Feld[bad_x][bad_y];
11183   static int test_xy[4][2] =
11184   {
11185     { 0, -1 },
11186     { -1, 0 },
11187     { +1, 0 },
11188     { 0, +1 }
11189   };
11190   static int touch_dir[4] =
11191   {
11192     MV_LEFT | MV_RIGHT,
11193     MV_UP   | MV_DOWN,
11194     MV_UP   | MV_DOWN,
11195     MV_LEFT | MV_RIGHT
11196   };
11197   static int test_dir[4] =
11198   {
11199     MV_UP,
11200     MV_LEFT,
11201     MV_RIGHT,
11202     MV_DOWN
11203   };
11204
11205   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11206     return;
11207
11208   for (i = 0; i < NUM_DIRECTIONS; i++)
11209   {
11210     int test_x, test_y, test_move_dir, test_element;
11211
11212     test_x = bad_x + test_xy[i][0];
11213     test_y = bad_y + test_xy[i][1];
11214     if (!IN_LEV_FIELD(test_x, test_y))
11215       continue;
11216
11217     test_move_dir =
11218       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11219
11220     test_element = Feld[test_x][test_y];
11221
11222     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11223        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11224     */
11225     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11226         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11227     {
11228       /* good thing is player or penguin that does not move away */
11229       if (IS_PLAYER(test_x, test_y))
11230       {
11231         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11232
11233         if (bad_element == EL_ROBOT && player->is_moving)
11234           continue;     /* robot does not kill player if he is moving */
11235
11236         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11237         {
11238           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11239             continue;           /* center and border element do not touch */
11240         }
11241
11242         kill_x = test_x;
11243         kill_y = test_y;
11244         break;
11245       }
11246       else if (test_element == EL_PENGUIN)
11247       {
11248         kill_x = test_x;
11249         kill_y = test_y;
11250         break;
11251       }
11252     }
11253   }
11254
11255   if (kill_x != -1 || kill_y != -1)
11256   {
11257     if (IS_PLAYER(kill_x, kill_y))
11258     {
11259       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11260
11261       if (player->shield_deadly_time_left > 0 &&
11262           !IS_INDESTRUCTIBLE(bad_element))
11263         Bang(bad_x, bad_y);
11264       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11265         KillPlayer(player);
11266     }
11267     else
11268       Bang(kill_x, kill_y);
11269   }
11270 }
11271
11272 void TestIfPlayerTouchesBadThing(int x, int y)
11273 {
11274   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11275 }
11276
11277 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11278 {
11279   TestIfGoodThingHitsBadThing(x, y, move_dir);
11280 }
11281
11282 void TestIfBadThingTouchesPlayer(int x, int y)
11283 {
11284   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11285 }
11286
11287 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11288 {
11289   TestIfBadThingHitsGoodThing(x, y, move_dir);
11290 }
11291
11292 void TestIfFriendTouchesBadThing(int x, int y)
11293 {
11294   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11295 }
11296
11297 void TestIfBadThingTouchesFriend(int x, int y)
11298 {
11299   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11300 }
11301
11302 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11303 {
11304   int i, kill_x = bad_x, kill_y = bad_y;
11305   static int xy[4][2] =
11306   {
11307     { 0, -1 },
11308     { -1, 0 },
11309     { +1, 0 },
11310     { 0, +1 }
11311   };
11312
11313   for (i = 0; i < NUM_DIRECTIONS; i++)
11314   {
11315     int x, y, element;
11316
11317     x = bad_x + xy[i][0];
11318     y = bad_y + xy[i][1];
11319     if (!IN_LEV_FIELD(x, y))
11320       continue;
11321
11322     element = Feld[x][y];
11323     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11324         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11325     {
11326       kill_x = x;
11327       kill_y = y;
11328       break;
11329     }
11330   }
11331
11332   if (kill_x != bad_x || kill_y != bad_y)
11333     Bang(bad_x, bad_y);
11334 }
11335
11336 void KillPlayer(struct PlayerInfo *player)
11337 {
11338   int jx = player->jx, jy = player->jy;
11339
11340   if (!player->active)
11341     return;
11342
11343   /* remove accessible field at the player's position */
11344   Feld[jx][jy] = EL_EMPTY;
11345
11346   /* deactivate shield (else Bang()/Explode() would not work right) */
11347   player->shield_normal_time_left = 0;
11348   player->shield_deadly_time_left = 0;
11349
11350   Bang(jx, jy);
11351   BuryPlayer(player);
11352 }
11353
11354 static void KillPlayerUnlessEnemyProtected(int x, int y)
11355 {
11356   if (!PLAYER_ENEMY_PROTECTED(x, y))
11357     KillPlayer(PLAYERINFO(x, y));
11358 }
11359
11360 static void KillPlayerUnlessExplosionProtected(int x, int y)
11361 {
11362   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11363     KillPlayer(PLAYERINFO(x, y));
11364 }
11365
11366 void BuryPlayer(struct PlayerInfo *player)
11367 {
11368   int jx = player->jx, jy = player->jy;
11369
11370   if (!player->active)
11371     return;
11372
11373   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11374   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11375
11376   player->GameOver = TRUE;
11377   RemovePlayer(player);
11378 }
11379
11380 void RemovePlayer(struct PlayerInfo *player)
11381 {
11382   int jx = player->jx, jy = player->jy;
11383   int i, found = FALSE;
11384
11385   player->present = FALSE;
11386   player->active = FALSE;
11387
11388   if (!ExplodeField[jx][jy])
11389     StorePlayer[jx][jy] = 0;
11390
11391   if (player->is_moving)
11392     DrawLevelField(player->last_jx, player->last_jy);
11393
11394   for (i = 0; i < MAX_PLAYERS; i++)
11395     if (stored_player[i].active)
11396       found = TRUE;
11397
11398   if (!found)
11399     AllPlayersGone = TRUE;
11400
11401   ExitX = ZX = jx;
11402   ExitY = ZY = jy;
11403 }
11404
11405 #if USE_NEW_SNAP_DELAY
11406 static void setFieldForSnapping(int x, int y, int element, int direction)
11407 {
11408   struct ElementInfo *ei = &element_info[element];
11409   int direction_bit = MV_DIR_TO_BIT(direction);
11410   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11411   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11412                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11413
11414   Feld[x][y] = EL_ELEMENT_SNAPPING;
11415   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11416
11417   ResetGfxAnimation(x, y);
11418
11419   GfxElement[x][y] = element;
11420   GfxAction[x][y] = action;
11421   GfxDir[x][y] = direction;
11422   GfxFrame[x][y] = -1;
11423 }
11424 #endif
11425
11426 /*
11427   =============================================================================
11428   checkDiagonalPushing()
11429   -----------------------------------------------------------------------------
11430   check if diagonal input device direction results in pushing of object
11431   (by checking if the alternative direction is walkable, diggable, ...)
11432   =============================================================================
11433 */
11434
11435 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11436                                     int x, int y, int real_dx, int real_dy)
11437 {
11438   int jx, jy, dx, dy, xx, yy;
11439
11440   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11441     return TRUE;
11442
11443   /* diagonal direction: check alternative direction */
11444   jx = player->jx;
11445   jy = player->jy;
11446   dx = x - jx;
11447   dy = y - jy;
11448   xx = jx + (dx == 0 ? real_dx : 0);
11449   yy = jy + (dy == 0 ? real_dy : 0);
11450
11451   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11452 }
11453
11454 /*
11455   =============================================================================
11456   DigField()
11457   -----------------------------------------------------------------------------
11458   x, y:                 field next to player (non-diagonal) to try to dig to
11459   real_dx, real_dy:     direction as read from input device (can be diagonal)
11460   =============================================================================
11461 */
11462
11463 int DigField(struct PlayerInfo *player,
11464              int oldx, int oldy, int x, int y,
11465              int real_dx, int real_dy, int mode)
11466 {
11467   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11468   boolean player_was_pushing = player->is_pushing;
11469   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11470   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11471   int jx = oldx, jy = oldy;
11472   int dx = x - jx, dy = y - jy;
11473   int nextx = x + dx, nexty = y + dy;
11474   int move_direction = (dx == -1 ? MV_LEFT  :
11475                         dx == +1 ? MV_RIGHT :
11476                         dy == -1 ? MV_UP    :
11477                         dy == +1 ? MV_DOWN  : MV_NONE);
11478   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11479   int dig_side = MV_DIR_OPPOSITE(move_direction);
11480   int old_element = Feld[jx][jy];
11481 #if USE_FIXED_DONT_RUN_INTO
11482   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11483 #else
11484   int element;
11485 #endif
11486   int collect_count;
11487
11488   if (is_player)                /* function can also be called by EL_PENGUIN */
11489   {
11490     if (player->MovPos == 0)
11491     {
11492       player->is_digging = FALSE;
11493       player->is_collecting = FALSE;
11494     }
11495
11496     if (player->MovPos == 0)    /* last pushing move finished */
11497       player->is_pushing = FALSE;
11498
11499     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11500     {
11501       player->is_switching = FALSE;
11502       player->push_delay = -1;
11503
11504       return MP_NO_ACTION;
11505     }
11506   }
11507
11508 #if !USE_FIXED_DONT_RUN_INTO
11509   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11510     return MP_NO_ACTION;
11511 #endif
11512
11513   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11514     old_element = Back[jx][jy];
11515
11516   /* in case of element dropped at player position, check background */
11517   else if (Back[jx][jy] != EL_EMPTY &&
11518            game.engine_version >= VERSION_IDENT(2,2,0,0))
11519     old_element = Back[jx][jy];
11520
11521 #if 0
11522 #if USE_FIXED_DONT_RUN_INTO
11523   if (player_can_move && DONT_RUN_INTO(element))
11524   {
11525     if (element == EL_ACID && dx == 0 && dy == 1)
11526     {
11527       SplashAcid(x, y);
11528       Feld[jx][jy] = EL_PLAYER_1;
11529       InitMovingField(jx, jy, MV_DOWN);
11530       Store[jx][jy] = EL_ACID;
11531       ContinueMoving(jx, jy);
11532       BuryPlayer(player);
11533     }
11534     else
11535       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11536
11537     return MP_DONT_RUN_INTO;
11538   }
11539 #endif
11540 #endif
11541
11542 #if 1
11543 #if USE_FIXED_DONT_RUN_INTO
11544   if (player_can_move && DONT_RUN_INTO(element))
11545   {
11546     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11547
11548     return MP_DONT_RUN_INTO;
11549   }
11550 #endif
11551 #endif
11552
11553   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11554     return MP_NO_ACTION;        /* field has no opening in this direction */
11555
11556   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11557     return MP_NO_ACTION;        /* field has no opening in this direction */
11558
11559 #if 1
11560 #if USE_FIXED_DONT_RUN_INTO
11561   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11562   {
11563     SplashAcid(x, y);
11564     Feld[jx][jy] = EL_PLAYER_1;
11565     InitMovingField(jx, jy, MV_DOWN);
11566     Store[jx][jy] = EL_ACID;
11567     ContinueMoving(jx, jy);
11568     BuryPlayer(player);
11569
11570     return MP_DONT_RUN_INTO;
11571   }
11572 #endif
11573 #endif
11574
11575 #if 0
11576 #if USE_FIXED_DONT_RUN_INTO
11577   if (player_can_move && DONT_RUN_INTO(element))
11578   {
11579     if (element == EL_ACID && dx == 0 && dy == 1)
11580     {
11581       SplashAcid(x, y);
11582       Feld[jx][jy] = EL_PLAYER_1;
11583       InitMovingField(jx, jy, MV_DOWN);
11584       Store[jx][jy] = EL_ACID;
11585       ContinueMoving(jx, jy);
11586       BuryPlayer(player);
11587     }
11588     else
11589       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11590
11591     return MP_DONT_RUN_INTO;
11592   }
11593 #endif
11594 #endif
11595
11596 #if USE_FIXED_DONT_RUN_INTO
11597   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11598     return MP_NO_ACTION;
11599 #endif
11600
11601 #if !USE_FIXED_DONT_RUN_INTO
11602   element = Feld[x][y];
11603 #endif
11604
11605   collect_count = element_info[element].collect_count_initial;
11606
11607   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
11608     return MP_NO_ACTION;
11609
11610   if (game.engine_version < VERSION_IDENT(2,2,0,0))
11611     player_can_move = player_can_move_or_snap;
11612
11613   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11614       game.engine_version >= VERSION_IDENT(2,2,0,0))
11615   {
11616     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11617                                player->index_bit, dig_side);
11618     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11619                                         player->index_bit, dig_side);
11620
11621     if (Feld[x][y] != element)          /* field changed by snapping */
11622       return MP_ACTION;
11623
11624     return MP_NO_ACTION;
11625   }
11626
11627   if (game.gravity && is_player && !player->is_auto_moving &&
11628       canFallDown(player) && move_direction != MV_DOWN &&
11629       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11630     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
11631
11632   if (player_can_move &&
11633       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11634   {
11635     int sound_element = SND_ELEMENT(element);
11636     int sound_action = ACTION_WALKING;
11637
11638     if (IS_RND_GATE(element))
11639     {
11640       if (!player->key[RND_GATE_NR(element)])
11641         return MP_NO_ACTION;
11642     }
11643     else if (IS_RND_GATE_GRAY(element))
11644     {
11645       if (!player->key[RND_GATE_GRAY_NR(element)])
11646         return MP_NO_ACTION;
11647     }
11648     else if (IS_RND_GATE_GRAY_ACTIVE(element))
11649     {
11650       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11651         return MP_NO_ACTION;
11652     }
11653     else if (element == EL_EXIT_OPEN ||
11654              element == EL_SP_EXIT_OPEN ||
11655              element == EL_SP_EXIT_OPENING)
11656     {
11657       sound_action = ACTION_PASSING;    /* player is passing exit */
11658     }
11659     else if (element == EL_EMPTY)
11660     {
11661       sound_action = ACTION_MOVING;             /* nothing to walk on */
11662     }
11663
11664     /* play sound from background or player, whatever is available */
11665     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11666       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11667     else
11668       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11669   }
11670   else if (player_can_move &&
11671            IS_PASSABLE(element) && canPassField(x, y, move_direction))
11672   {
11673     if (!ACCESS_FROM(element, opposite_direction))
11674       return MP_NO_ACTION;      /* field not accessible from this direction */
11675
11676     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
11677       return MP_NO_ACTION;
11678
11679     if (IS_EM_GATE(element))
11680     {
11681       if (!player->key[EM_GATE_NR(element)])
11682         return MP_NO_ACTION;
11683     }
11684     else if (IS_EM_GATE_GRAY(element))
11685     {
11686       if (!player->key[EM_GATE_GRAY_NR(element)])
11687         return MP_NO_ACTION;
11688     }
11689     else if (IS_EM_GATE_GRAY_ACTIVE(element))
11690     {
11691       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11692         return MP_NO_ACTION;
11693     }
11694     else if (IS_SP_PORT(element))
11695     {
11696       if (element == EL_SP_GRAVITY_PORT_LEFT ||
11697           element == EL_SP_GRAVITY_PORT_RIGHT ||
11698           element == EL_SP_GRAVITY_PORT_UP ||
11699           element == EL_SP_GRAVITY_PORT_DOWN)
11700         game.gravity = !game.gravity;
11701       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11702                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11703                element == EL_SP_GRAVITY_ON_PORT_UP ||
11704                element == EL_SP_GRAVITY_ON_PORT_DOWN)
11705         game.gravity = TRUE;
11706       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11707                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11708                element == EL_SP_GRAVITY_OFF_PORT_UP ||
11709                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11710         game.gravity = FALSE;
11711     }
11712
11713     /* automatically move to the next field with double speed */
11714     player->programmed_action = move_direction;
11715
11716     if (player->move_delay_reset_counter == 0)
11717     {
11718       player->move_delay_reset_counter = 2;     /* two double speed steps */
11719
11720       DOUBLE_PLAYER_SPEED(player);
11721     }
11722
11723     PlayLevelSoundAction(x, y, ACTION_PASSING);
11724   }
11725   else if (player_can_move_or_snap && IS_DIGGABLE(element))
11726   {
11727     RemoveField(x, y);
11728
11729     if (mode != DF_SNAP)
11730     {
11731       GfxElement[x][y] = GFX_ELEMENT(element);
11732       player->is_digging = TRUE;
11733     }
11734
11735     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11736
11737     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11738                                         player->index_bit, dig_side);
11739
11740     if (mode == DF_SNAP)
11741     {
11742 #if USE_NEW_SNAP_DELAY
11743       if (level.block_snap_field)
11744         setFieldForSnapping(x, y, element, move_direction);
11745       else
11746         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11747 #else
11748       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
11749 #endif
11750
11751       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11752                                           player->index_bit, dig_side);
11753     }
11754   }
11755   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11756   {
11757     RemoveField(x, y);
11758
11759     if (is_player && mode != DF_SNAP)
11760     {
11761       GfxElement[x][y] = element;
11762       player->is_collecting = TRUE;
11763     }
11764
11765     if (element == EL_SPEED_PILL)
11766     {
11767       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11768     }
11769     else if (element == EL_EXTRA_TIME && level.time > 0)
11770     {
11771       TimeLeft += level.extra_time;
11772       DrawGameValue_Time(TimeLeft);
11773     }
11774     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11775     {
11776       player->shield_normal_time_left += level.shield_normal_time;
11777       if (element == EL_SHIELD_DEADLY)
11778         player->shield_deadly_time_left += level.shield_deadly_time;
11779     }
11780     else if (element == EL_DYNAMITE ||
11781              element == EL_EM_DYNAMITE ||
11782              element == EL_SP_DISK_RED)
11783     {
11784       if (player->inventory_size < MAX_INVENTORY_SIZE)
11785         player->inventory_element[player->inventory_size++] = element;
11786
11787 #if 1
11788       DrawGameDoorValues();
11789 #else
11790       DrawGameValue_Dynamite(local_player->inventory_size);
11791 #endif
11792     }
11793     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11794     {
11795       player->dynabomb_count++;
11796       player->dynabombs_left++;
11797     }
11798     else if (element == EL_DYNABOMB_INCREASE_SIZE)
11799     {
11800       player->dynabomb_size++;
11801     }
11802     else if (element == EL_DYNABOMB_INCREASE_POWER)
11803     {
11804       player->dynabomb_xl = TRUE;
11805     }
11806     else if (IS_KEY(element))
11807     {
11808       player->key[KEY_NR(element)] = TRUE;
11809
11810 #if 1
11811       DrawGameDoorValues();
11812 #else
11813       DrawGameValue_Keys(player->key);
11814 #endif
11815
11816       redraw_mask |= REDRAW_DOOR_1;
11817     }
11818     else if (IS_ENVELOPE(element))
11819     {
11820       player->show_envelope = element;
11821     }
11822     else if (element == EL_EMC_LENSES)
11823     {
11824       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11825
11826       RedrawAllInvisibleElementsForLenses();
11827     }
11828     else if (element == EL_EMC_MAGNIFIER)
11829     {
11830       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11831
11832       RedrawAllInvisibleElementsForMagnifier();
11833     }
11834     else if (IS_DROPPABLE(element) ||
11835              IS_THROWABLE(element))     /* can be collected and dropped */
11836     {
11837       int i;
11838
11839       if (collect_count == 0)
11840         player->inventory_infinite_element = element;
11841       else
11842         for (i = 0; i < collect_count; i++)
11843           if (player->inventory_size < MAX_INVENTORY_SIZE)
11844             player->inventory_element[player->inventory_size++] = element;
11845
11846 #if 1
11847       DrawGameDoorValues();
11848 #else
11849       DrawGameValue_Dynamite(local_player->inventory_size);
11850 #endif
11851     }
11852     else if (collect_count > 0)
11853     {
11854       local_player->gems_still_needed -= collect_count;
11855       if (local_player->gems_still_needed < 0)
11856         local_player->gems_still_needed = 0;
11857
11858       DrawGameValue_Emeralds(local_player->gems_still_needed);
11859     }
11860
11861     RaiseScoreElement(element);
11862     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11863
11864     if (is_player)
11865       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11866                                           player->index_bit, dig_side);
11867
11868     if (mode == DF_SNAP)
11869     {
11870 #if USE_NEW_SNAP_DELAY
11871       if (level.block_snap_field)
11872         setFieldForSnapping(x, y, element, move_direction);
11873       else
11874         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11875 #else
11876       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
11877 #endif
11878
11879       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11880                                           player->index_bit, dig_side);
11881     }
11882   }
11883   else if (player_can_move_or_snap && IS_PUSHABLE(element))
11884   {
11885     if (mode == DF_SNAP && element != EL_BD_ROCK)
11886       return MP_NO_ACTION;
11887
11888     if (CAN_FALL(element) && dy)
11889       return MP_NO_ACTION;
11890
11891     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11892         !(element == EL_SPRING && level.use_spring_bug))
11893       return MP_NO_ACTION;
11894
11895     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11896         ((move_direction & MV_VERTICAL &&
11897           ((element_info[element].move_pattern & MV_LEFT &&
11898             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11899            (element_info[element].move_pattern & MV_RIGHT &&
11900             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11901          (move_direction & MV_HORIZONTAL &&
11902           ((element_info[element].move_pattern & MV_UP &&
11903             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11904            (element_info[element].move_pattern & MV_DOWN &&
11905             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11906       return MP_NO_ACTION;
11907
11908     /* do not push elements already moving away faster than player */
11909     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11910         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11911       return MP_NO_ACTION;
11912
11913     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11914     {
11915       if (player->push_delay_value == -1 || !player_was_pushing)
11916         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11917     }
11918     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11919     {
11920       if (player->push_delay_value == -1)
11921         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11922     }
11923     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11924     {
11925       if (!player->is_pushing)
11926         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11927     }
11928
11929     player->is_pushing = TRUE;
11930
11931     if (!(IN_LEV_FIELD(nextx, nexty) &&
11932           (IS_FREE(nextx, nexty) ||
11933            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11934             IS_SB_ELEMENT(element)))))
11935       return MP_NO_ACTION;
11936
11937     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11938       return MP_NO_ACTION;
11939
11940     if (player->push_delay == -1)       /* new pushing; restart delay */
11941       player->push_delay = 0;
11942
11943     if (player->push_delay < player->push_delay_value &&
11944         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
11945         element != EL_SPRING && element != EL_BALLOON)
11946     {
11947       /* make sure that there is no move delay before next try to push */
11948       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11949         player->move_delay = 0;
11950
11951       return MP_NO_ACTION;
11952     }
11953
11954     if (IS_SB_ELEMENT(element))
11955     {
11956       if (element == EL_SOKOBAN_FIELD_FULL)
11957       {
11958         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
11959         local_player->sokobanfields_still_needed++;
11960       }
11961
11962       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
11963       {
11964         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
11965         local_player->sokobanfields_still_needed--;
11966       }
11967
11968       Feld[x][y] = EL_SOKOBAN_OBJECT;
11969
11970       if (Back[x][y] == Back[nextx][nexty])
11971         PlayLevelSoundAction(x, y, ACTION_PUSHING);
11972       else if (Back[x][y] != 0)
11973         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
11974                                     ACTION_EMPTYING);
11975       else
11976         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
11977                                     ACTION_FILLING);
11978
11979       if (local_player->sokobanfields_still_needed == 0 &&
11980           game.emulation == EMU_SOKOBAN)
11981       {
11982         player->LevelSolved = player->GameOver = TRUE;
11983         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
11984       }
11985     }
11986     else
11987       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
11988
11989     InitMovingField(x, y, move_direction);
11990     GfxAction[x][y] = ACTION_PUSHING;
11991
11992     if (mode == DF_SNAP)
11993       ContinueMoving(x, y);
11994     else
11995       MovPos[x][y] = (dx != 0 ? dx : dy);
11996
11997     Pushed[x][y] = TRUE;
11998     Pushed[nextx][nexty] = TRUE;
11999
12000     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12001       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12002     else
12003       player->push_delay_value = -1;    /* get new value later */
12004
12005     /* check for element change _after_ element has been pushed */
12006     if (game.use_change_when_pushing_bug)
12007     {
12008       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12009                                  player->index_bit, dig_side);
12010       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12011                                           player->index_bit, dig_side);
12012     }
12013   }
12014   else if (IS_SWITCHABLE(element))
12015   {
12016     if (PLAYER_SWITCHING(player, x, y))
12017     {
12018       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12019                                           player->index_bit, dig_side);
12020
12021       return MP_ACTION;
12022     }
12023
12024     player->is_switching = TRUE;
12025     player->switch_x = x;
12026     player->switch_y = y;
12027
12028     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12029
12030     if (element == EL_ROBOT_WHEEL)
12031     {
12032       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12033       ZX = x;
12034       ZY = y;
12035
12036       DrawLevelField(x, y);
12037     }
12038     else if (element == EL_SP_TERMINAL)
12039     {
12040       int xx, yy;
12041
12042 #if 1
12043       SCAN_PLAYFIELD(xx, yy)
12044 #else
12045       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12046 #endif
12047       {
12048         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12049           Bang(xx, yy);
12050         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12051           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12052       }
12053     }
12054     else if (IS_BELT_SWITCH(element))
12055     {
12056       ToggleBeltSwitch(x, y);
12057     }
12058     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12059              element == EL_SWITCHGATE_SWITCH_DOWN)
12060     {
12061       ToggleSwitchgateSwitch(x, y);
12062     }
12063     else if (element == EL_LIGHT_SWITCH ||
12064              element == EL_LIGHT_SWITCH_ACTIVE)
12065     {
12066       ToggleLightSwitch(x, y);
12067     }
12068     else if (element == EL_TIMEGATE_SWITCH)
12069     {
12070       ActivateTimegateSwitch(x, y);
12071     }
12072     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12073              element == EL_BALLOON_SWITCH_RIGHT ||
12074              element == EL_BALLOON_SWITCH_UP    ||
12075              element == EL_BALLOON_SWITCH_DOWN  ||
12076              element == EL_BALLOON_SWITCH_NONE  ||
12077              element == EL_BALLOON_SWITCH_ANY)
12078     {
12079       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12080                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12081                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12082                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12083                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12084                              move_direction);
12085     }
12086     else if (element == EL_LAMP)
12087     {
12088       Feld[x][y] = EL_LAMP_ACTIVE;
12089       local_player->lights_still_needed--;
12090
12091       ResetGfxAnimation(x, y);
12092       DrawLevelField(x, y);
12093     }
12094     else if (element == EL_TIME_ORB_FULL)
12095     {
12096       Feld[x][y] = EL_TIME_ORB_EMPTY;
12097
12098       if (level.time > 0 || level.use_time_orb_bug)
12099       {
12100         TimeLeft += level.time_orb_time;
12101         DrawGameValue_Time(TimeLeft);
12102       }
12103
12104       ResetGfxAnimation(x, y);
12105       DrawLevelField(x, y);
12106     }
12107     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12108              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12109     {
12110       int xx, yy;
12111
12112       game.ball_state = !game.ball_state;
12113
12114 #if 1
12115       SCAN_PLAYFIELD(xx, yy)
12116 #else
12117       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12118 #endif
12119       {
12120         int e = Feld[xx][yy];
12121
12122         if (game.ball_state)
12123         {
12124           if (e == EL_EMC_MAGIC_BALL)
12125             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12126           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12127             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12128         }
12129         else
12130         {
12131           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12132             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12133           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12134             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12135         }
12136       }
12137     }
12138
12139     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12140                                         player->index_bit, dig_side);
12141
12142     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12143                                         player->index_bit, dig_side);
12144
12145     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12146                                         player->index_bit, dig_side);
12147
12148     return MP_ACTION;
12149   }
12150   else
12151   {
12152     if (!PLAYER_SWITCHING(player, x, y))
12153     {
12154       player->is_switching = TRUE;
12155       player->switch_x = x;
12156       player->switch_y = y;
12157
12158       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12159                                  player->index_bit, dig_side);
12160       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12161                                           player->index_bit, dig_side);
12162
12163       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12164                                  player->index_bit, dig_side);
12165       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12166                                           player->index_bit, dig_side);
12167     }
12168
12169     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12170                                player->index_bit, dig_side);
12171     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12172                                         player->index_bit, dig_side);
12173
12174     return MP_NO_ACTION;
12175   }
12176
12177   player->push_delay = -1;
12178
12179   if (is_player)                /* function can also be called by EL_PENGUIN */
12180   {
12181     if (Feld[x][y] != element)          /* really digged/collected something */
12182       player->is_collecting = !player->is_digging;
12183   }
12184
12185   return MP_MOVING;
12186 }
12187
12188 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12189 {
12190   int jx = player->jx, jy = player->jy;
12191   int x = jx + dx, y = jy + dy;
12192   int snap_direction = (dx == -1 ? MV_LEFT  :
12193                         dx == +1 ? MV_RIGHT :
12194                         dy == -1 ? MV_UP    :
12195                         dy == +1 ? MV_DOWN  : MV_NONE);
12196   boolean can_continue_snapping = (level.continuous_snapping &&
12197                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12198
12199   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12200     return FALSE;
12201
12202   if (!player->active || !IN_LEV_FIELD(x, y))
12203     return FALSE;
12204
12205   if (dx && dy)
12206     return FALSE;
12207
12208   if (!dx && !dy)
12209   {
12210     if (player->MovPos == 0)
12211       player->is_pushing = FALSE;
12212
12213     player->is_snapping = FALSE;
12214
12215     if (player->MovPos == 0)
12216     {
12217       player->is_moving = FALSE;
12218       player->is_digging = FALSE;
12219       player->is_collecting = FALSE;
12220     }
12221
12222     return FALSE;
12223   }
12224
12225 #if USE_NEW_CONTINUOUS_SNAPPING
12226   /* prevent snapping with already pressed snap key when not allowed */
12227   if (player->is_snapping && !can_continue_snapping)
12228     return FALSE;
12229 #else
12230   if (player->is_snapping)
12231     return FALSE;
12232 #endif
12233
12234   player->MovDir = snap_direction;
12235
12236   if (player->MovPos == 0)
12237   {
12238     player->is_moving = FALSE;
12239     player->is_digging = FALSE;
12240     player->is_collecting = FALSE;
12241   }
12242
12243   player->is_dropping = FALSE;
12244   player->is_dropping_pressed = FALSE;
12245   player->drop_pressed_delay = 0;
12246
12247   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12248     return FALSE;
12249
12250   player->is_snapping = TRUE;
12251
12252   if (player->MovPos == 0)
12253   {
12254     player->is_moving = FALSE;
12255     player->is_digging = FALSE;
12256     player->is_collecting = FALSE;
12257   }
12258
12259   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12260     DrawLevelField(player->last_jx, player->last_jy);
12261
12262   DrawLevelField(x, y);
12263
12264   return TRUE;
12265 }
12266
12267 boolean DropElement(struct PlayerInfo *player)
12268 {
12269   int old_element, new_element;
12270   int dropx = player->jx, dropy = player->jy;
12271   int drop_direction = player->MovDir;
12272   int drop_side = drop_direction;
12273   int drop_element = (player->inventory_size > 0 ?
12274                       player->inventory_element[player->inventory_size - 1] :
12275                       player->inventory_infinite_element != EL_UNDEFINED ?
12276                       player->inventory_infinite_element :
12277                       player->dynabombs_left > 0 ?
12278                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12279                       EL_UNDEFINED);
12280
12281   player->is_dropping_pressed = TRUE;
12282
12283   /* do not drop an element on top of another element; when holding drop key
12284      pressed without moving, dropped element must move away before the next
12285      element can be dropped (this is especially important if the next element
12286      is dynamite, which can be placed on background for historical reasons) */
12287   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12288     return MP_ACTION;
12289
12290   if (IS_THROWABLE(drop_element))
12291   {
12292     dropx += GET_DX_FROM_DIR(drop_direction);
12293     dropy += GET_DY_FROM_DIR(drop_direction);
12294
12295     if (!IN_LEV_FIELD(dropx, dropy))
12296       return FALSE;
12297   }
12298
12299   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12300   new_element = drop_element;           /* default: no change when dropping */
12301
12302   /* check if player is active, not moving and ready to drop */
12303   if (!player->active || player->MovPos || player->drop_delay > 0)
12304     return FALSE;
12305
12306   /* check if player has anything that can be dropped */
12307   if (new_element == EL_UNDEFINED)
12308     return FALSE;
12309
12310   /* check if drop key was pressed long enough for EM style dynamite */
12311   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12312     return FALSE;
12313
12314   /* check if anything can be dropped at the current position */
12315   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12316     return FALSE;
12317
12318   /* collected custom elements can only be dropped on empty fields */
12319   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12320     return FALSE;
12321
12322   if (old_element != EL_EMPTY)
12323     Back[dropx][dropy] = old_element;   /* store old element on this field */
12324
12325   ResetGfxAnimation(dropx, dropy);
12326   ResetRandomAnimationValue(dropx, dropy);
12327
12328   if (player->inventory_size > 0 ||
12329       player->inventory_infinite_element != EL_UNDEFINED)
12330   {
12331     if (player->inventory_size > 0)
12332     {
12333       player->inventory_size--;
12334
12335 #if 1
12336       DrawGameDoorValues();
12337 #else
12338       DrawGameValue_Dynamite(local_player->inventory_size);
12339 #endif
12340
12341       if (new_element == EL_DYNAMITE)
12342         new_element = EL_DYNAMITE_ACTIVE;
12343       else if (new_element == EL_EM_DYNAMITE)
12344         new_element = EL_EM_DYNAMITE_ACTIVE;
12345       else if (new_element == EL_SP_DISK_RED)
12346         new_element = EL_SP_DISK_RED_ACTIVE;
12347     }
12348
12349     Feld[dropx][dropy] = new_element;
12350
12351     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12352       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12353                           el2img(Feld[dropx][dropy]), 0);
12354
12355     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12356
12357     /* needed if previous element just changed to "empty" in the last frame */
12358     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12359
12360     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12361                                player->index_bit, drop_side);
12362     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12363                                         CE_PLAYER_DROPS_X,
12364                                         player->index_bit, drop_side);
12365
12366     TestIfElementTouchesCustomElement(dropx, dropy);
12367   }
12368   else          /* player is dropping a dyna bomb */
12369   {
12370     player->dynabombs_left--;
12371
12372     Feld[dropx][dropy] = new_element;
12373
12374     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12375       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12376                           el2img(Feld[dropx][dropy]), 0);
12377
12378     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12379   }
12380
12381   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12382     InitField_WithBug1(dropx, dropy, FALSE);
12383
12384   new_element = Feld[dropx][dropy];     /* element might have changed */
12385
12386   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12387       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12388   {
12389     int move_direction, nextx, nexty;
12390
12391     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12392       MovDir[dropx][dropy] = drop_direction;
12393
12394     move_direction = MovDir[dropx][dropy];
12395     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12396     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12397
12398     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12399     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12400   }
12401
12402   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12403   player->is_dropping = TRUE;
12404
12405   player->drop_pressed_delay = 0;
12406   player->is_dropping_pressed = FALSE;
12407
12408   player->drop_x = dropx;
12409   player->drop_y = dropy;
12410
12411   return TRUE;
12412 }
12413
12414 /* ------------------------------------------------------------------------- */
12415 /* game sound playing functions                                              */
12416 /* ------------------------------------------------------------------------- */
12417
12418 static int *loop_sound_frame = NULL;
12419 static int *loop_sound_volume = NULL;
12420
12421 void InitPlayLevelSound()
12422 {
12423   int num_sounds = getSoundListSize();
12424
12425   checked_free(loop_sound_frame);
12426   checked_free(loop_sound_volume);
12427
12428   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12429   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12430 }
12431
12432 static void PlayLevelSound(int x, int y, int nr)
12433 {
12434   int sx = SCREENX(x), sy = SCREENY(y);
12435   int volume, stereo_position;
12436   int max_distance = 8;
12437   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12438
12439   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12440       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12441     return;
12442
12443   if (!IN_LEV_FIELD(x, y) ||
12444       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12445       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12446     return;
12447
12448   volume = SOUND_MAX_VOLUME;
12449
12450   if (!IN_SCR_FIELD(sx, sy))
12451   {
12452     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12453     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12454
12455     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12456   }
12457
12458   stereo_position = (SOUND_MAX_LEFT +
12459                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12460                      (SCR_FIELDX + 2 * max_distance));
12461
12462   if (IS_LOOP_SOUND(nr))
12463   {
12464     /* This assures that quieter loop sounds do not overwrite louder ones,
12465        while restarting sound volume comparison with each new game frame. */
12466
12467     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12468       return;
12469
12470     loop_sound_volume[nr] = volume;
12471     loop_sound_frame[nr] = FrameCounter;
12472   }
12473
12474   PlaySoundExt(nr, volume, stereo_position, type);
12475 }
12476
12477 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12478 {
12479   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12480                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12481                  y < LEVELY(BY1) ? LEVELY(BY1) :
12482                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12483                  sound_action);
12484 }
12485
12486 static void PlayLevelSoundAction(int x, int y, int action)
12487 {
12488   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12489 }
12490
12491 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12492 {
12493   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12494
12495   if (sound_effect != SND_UNDEFINED)
12496     PlayLevelSound(x, y, sound_effect);
12497 }
12498
12499 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12500                                               int action)
12501 {
12502   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12503
12504   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12505     PlayLevelSound(x, y, sound_effect);
12506 }
12507
12508 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12509 {
12510   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12511
12512   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12513     PlayLevelSound(x, y, sound_effect);
12514 }
12515
12516 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12517 {
12518   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12519
12520   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12521     StopSound(sound_effect);
12522 }
12523
12524 static void PlayLevelMusic()
12525 {
12526   if (levelset.music[level_nr] != MUS_UNDEFINED)
12527     PlayMusic(levelset.music[level_nr]);        /* from config file */
12528   else
12529     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
12530 }
12531
12532 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12533 {
12534   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12535
12536   switch (sample)
12537   {
12538     case SAMPLE_blank:
12539       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12540       break;
12541
12542     case SAMPLE_roll:
12543       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12544       break;
12545
12546     case SAMPLE_stone:
12547       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12548       break;
12549
12550     case SAMPLE_nut:
12551       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12552       break;
12553
12554     case SAMPLE_crack:
12555       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12556       break;
12557
12558     case SAMPLE_bug:
12559       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12560       break;
12561
12562     case SAMPLE_tank:
12563       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12564       break;
12565
12566     case SAMPLE_android_clone:
12567       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12568       break;
12569
12570     case SAMPLE_android_move:
12571       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12572       break;
12573
12574     case SAMPLE_spring:
12575       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12576       break;
12577
12578     case SAMPLE_slurp:
12579       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12580       break;
12581
12582     case SAMPLE_eater:
12583       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12584       break;
12585
12586     case SAMPLE_eater_eat:
12587       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12588       break;
12589
12590     case SAMPLE_alien:
12591       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12592       break;
12593
12594     case SAMPLE_collect:
12595       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12596       break;
12597
12598     case SAMPLE_diamond:
12599       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12600       break;
12601
12602     case SAMPLE_squash:
12603       /* !!! CHECK THIS !!! */
12604 #if 1
12605       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12606 #else
12607       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12608 #endif
12609       break;
12610
12611     case SAMPLE_wonderfall:
12612       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12613       break;
12614
12615     case SAMPLE_drip:
12616       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12617       break;
12618
12619     case SAMPLE_push:
12620       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12621       break;
12622
12623     case SAMPLE_dirt:
12624       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12625       break;
12626
12627     case SAMPLE_acid:
12628       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12629       break;
12630
12631     case SAMPLE_ball:
12632       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12633       break;
12634
12635     case SAMPLE_grow:
12636       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12637       break;
12638
12639     case SAMPLE_wonder:
12640       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12641       break;
12642
12643     case SAMPLE_door:
12644       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12645       break;
12646
12647     case SAMPLE_exit_open:
12648       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12649       break;
12650
12651     case SAMPLE_exit_leave:
12652       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12653       break;
12654
12655     case SAMPLE_dynamite:
12656       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12657       break;
12658
12659     case SAMPLE_tick:
12660       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12661       break;
12662
12663     case SAMPLE_press:
12664       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12665       break;
12666
12667     case SAMPLE_wheel:
12668       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12669       break;
12670
12671     case SAMPLE_boom:
12672       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12673       break;
12674
12675     case SAMPLE_die:
12676       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12677       break;
12678
12679     case SAMPLE_time:
12680       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12681       break;
12682
12683     default:
12684       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12685       break;
12686   }
12687 }
12688
12689 void RaiseScore(int value)
12690 {
12691   local_player->score += value;
12692
12693   DrawGameValue_Score(local_player->score);
12694 }
12695
12696 void RaiseScoreElement(int element)
12697 {
12698   switch(element)
12699   {
12700     case EL_EMERALD:
12701     case EL_BD_DIAMOND:
12702     case EL_EMERALD_YELLOW:
12703     case EL_EMERALD_RED:
12704     case EL_EMERALD_PURPLE:
12705     case EL_SP_INFOTRON:
12706       RaiseScore(level.score[SC_EMERALD]);
12707       break;
12708     case EL_DIAMOND:
12709       RaiseScore(level.score[SC_DIAMOND]);
12710       break;
12711     case EL_CRYSTAL:
12712       RaiseScore(level.score[SC_CRYSTAL]);
12713       break;
12714     case EL_PEARL:
12715       RaiseScore(level.score[SC_PEARL]);
12716       break;
12717     case EL_BUG:
12718     case EL_BD_BUTTERFLY:
12719     case EL_SP_ELECTRON:
12720       RaiseScore(level.score[SC_BUG]);
12721       break;
12722     case EL_SPACESHIP:
12723     case EL_BD_FIREFLY:
12724     case EL_SP_SNIKSNAK:
12725       RaiseScore(level.score[SC_SPACESHIP]);
12726       break;
12727     case EL_YAMYAM:
12728     case EL_DARK_YAMYAM:
12729       RaiseScore(level.score[SC_YAMYAM]);
12730       break;
12731     case EL_ROBOT:
12732       RaiseScore(level.score[SC_ROBOT]);
12733       break;
12734     case EL_PACMAN:
12735       RaiseScore(level.score[SC_PACMAN]);
12736       break;
12737     case EL_NUT:
12738       RaiseScore(level.score[SC_NUT]);
12739       break;
12740     case EL_DYNAMITE:
12741     case EL_EM_DYNAMITE:
12742     case EL_SP_DISK_RED:
12743     case EL_DYNABOMB_INCREASE_NUMBER:
12744     case EL_DYNABOMB_INCREASE_SIZE:
12745     case EL_DYNABOMB_INCREASE_POWER:
12746       RaiseScore(level.score[SC_DYNAMITE]);
12747       break;
12748     case EL_SHIELD_NORMAL:
12749     case EL_SHIELD_DEADLY:
12750       RaiseScore(level.score[SC_SHIELD]);
12751       break;
12752     case EL_EXTRA_TIME:
12753       RaiseScore(level.extra_time_score);
12754       break;
12755     case EL_KEY_1:
12756     case EL_KEY_2:
12757     case EL_KEY_3:
12758     case EL_KEY_4:
12759     case EL_EM_KEY_1:
12760     case EL_EM_KEY_2:
12761     case EL_EM_KEY_3:
12762     case EL_EM_KEY_4:
12763     case EL_EMC_KEY_5:
12764     case EL_EMC_KEY_6:
12765     case EL_EMC_KEY_7:
12766     case EL_EMC_KEY_8:
12767       RaiseScore(level.score[SC_KEY]);
12768       break;
12769     default:
12770       RaiseScore(element_info[element].collect_score);
12771       break;
12772   }
12773 }
12774
12775 void RequestQuitGame(boolean ask_if_really_quit)
12776 {
12777   if (AllPlayersGone ||
12778       !ask_if_really_quit ||
12779       level_editor_test_game ||
12780       Request("Do you really want to quit the game ?",
12781               REQ_ASK | REQ_STAY_CLOSED))
12782   {
12783 #if defined(NETWORK_AVALIABLE)
12784     if (options.network)
12785       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12786     else
12787 #endif
12788     {
12789       game_status = GAME_MODE_MAIN;
12790       DrawMainMenu();
12791     }
12792   }
12793   else
12794   {
12795     if (tape.playing && tape.deactivate_display)
12796       TapeDeactivateDisplayOff(TRUE);
12797
12798     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12799
12800     if (tape.playing && tape.deactivate_display)
12801       TapeDeactivateDisplayOn();
12802   }
12803 }
12804
12805
12806 /* ---------- new game button stuff ---------------------------------------- */
12807
12808 /* graphic position values for game buttons */
12809 #define GAME_BUTTON_XSIZE       30
12810 #define GAME_BUTTON_YSIZE       30
12811 #define GAME_BUTTON_XPOS        5
12812 #define GAME_BUTTON_YPOS        215
12813 #define SOUND_BUTTON_XPOS       5
12814 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12815
12816 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12817 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12818 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12819 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12820 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12821 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12822
12823 static struct
12824 {
12825   int x, y;
12826   int gadget_id;
12827   char *infotext;
12828 } gamebutton_info[NUM_GAME_BUTTONS] =
12829 {
12830   {
12831     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
12832     GAME_CTRL_ID_STOP,
12833     "stop game"
12834   },
12835   {
12836     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
12837     GAME_CTRL_ID_PAUSE,
12838     "pause game"
12839   },
12840   {
12841     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
12842     GAME_CTRL_ID_PLAY,
12843     "play game"
12844   },
12845   {
12846     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
12847     SOUND_CTRL_ID_MUSIC,
12848     "background music on/off"
12849   },
12850   {
12851     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
12852     SOUND_CTRL_ID_LOOPS,
12853     "sound loops on/off"
12854   },
12855   {
12856     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
12857     SOUND_CTRL_ID_SIMPLE,
12858     "normal sounds on/off"
12859   }
12860 };
12861
12862 void CreateGameButtons()
12863 {
12864   int i;
12865
12866   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12867   {
12868     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12869     struct GadgetInfo *gi;
12870     int button_type;
12871     boolean checked;
12872     unsigned long event_mask;
12873     int gd_xoffset, gd_yoffset;
12874     int gd_x1, gd_x2, gd_y1, gd_y2;
12875     int id = i;
12876
12877     gd_xoffset = gamebutton_info[i].x;
12878     gd_yoffset = gamebutton_info[i].y;
12879     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12880     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12881
12882     if (id == GAME_CTRL_ID_STOP ||
12883         id == GAME_CTRL_ID_PAUSE ||
12884         id == GAME_CTRL_ID_PLAY)
12885     {
12886       button_type = GD_TYPE_NORMAL_BUTTON;
12887       checked = FALSE;
12888       event_mask = GD_EVENT_RELEASED;
12889       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12890       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12891     }
12892     else
12893     {
12894       button_type = GD_TYPE_CHECK_BUTTON;
12895       checked =
12896         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12897          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12898          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12899       event_mask = GD_EVENT_PRESSED;
12900       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
12901       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12902     }
12903
12904     gi = CreateGadget(GDI_CUSTOM_ID, id,
12905                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
12906                       GDI_X, DX + gd_xoffset,
12907                       GDI_Y, DY + gd_yoffset,
12908                       GDI_WIDTH, GAME_BUTTON_XSIZE,
12909                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
12910                       GDI_TYPE, button_type,
12911                       GDI_STATE, GD_BUTTON_UNPRESSED,
12912                       GDI_CHECKED, checked,
12913                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12914                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12915                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12916                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12917                       GDI_EVENT_MASK, event_mask,
12918                       GDI_CALLBACK_ACTION, HandleGameButtons,
12919                       GDI_END);
12920
12921     if (gi == NULL)
12922       Error(ERR_EXIT, "cannot create gadget");
12923
12924     game_gadget[id] = gi;
12925   }
12926 }
12927
12928 void FreeGameButtons()
12929 {
12930   int i;
12931
12932   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12933     FreeGadget(game_gadget[i]);
12934 }
12935
12936 static void MapGameButtons()
12937 {
12938   int i;
12939
12940   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12941     MapGadget(game_gadget[i]);
12942 }
12943
12944 void UnmapGameButtons()
12945 {
12946   int i;
12947
12948   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12949     UnmapGadget(game_gadget[i]);
12950 }
12951
12952 static void HandleGameButtons(struct GadgetInfo *gi)
12953 {
12954   int id = gi->custom_id;
12955
12956   if (game_status != GAME_MODE_PLAYING)
12957     return;
12958
12959   switch (id)
12960   {
12961     case GAME_CTRL_ID_STOP:
12962       if (tape.playing)
12963         TapeStop();
12964       else
12965         RequestQuitGame(TRUE);
12966       break;
12967
12968     case GAME_CTRL_ID_PAUSE:
12969       if (options.network)
12970       {
12971 #if defined(NETWORK_AVALIABLE)
12972         if (tape.pausing)
12973           SendToServer_ContinuePlaying();
12974         else
12975           SendToServer_PausePlaying();
12976 #endif
12977       }
12978       else
12979         TapeTogglePause(TAPE_TOGGLE_MANUAL);
12980       break;
12981
12982     case GAME_CTRL_ID_PLAY:
12983       if (tape.pausing)
12984       {
12985 #if defined(NETWORK_AVALIABLE)
12986         if (options.network)
12987           SendToServer_ContinuePlaying();
12988         else
12989 #endif
12990         {
12991           tape.pausing = FALSE;
12992           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
12993         }
12994       }
12995       break;
12996
12997     case SOUND_CTRL_ID_MUSIC:
12998       if (setup.sound_music)
12999       { 
13000         setup.sound_music = FALSE;
13001         FadeMusic();
13002       }
13003       else if (audio.music_available)
13004       { 
13005         setup.sound = setup.sound_music = TRUE;
13006
13007         SetAudioMode(setup.sound);
13008
13009         PlayLevelMusic();
13010       }
13011       break;
13012
13013     case SOUND_CTRL_ID_LOOPS:
13014       if (setup.sound_loops)
13015         setup.sound_loops = FALSE;
13016       else if (audio.loops_available)
13017       {
13018         setup.sound = setup.sound_loops = TRUE;
13019         SetAudioMode(setup.sound);
13020       }
13021       break;
13022
13023     case SOUND_CTRL_ID_SIMPLE:
13024       if (setup.sound_simple)
13025         setup.sound_simple = FALSE;
13026       else if (audio.sound_available)
13027       {
13028         setup.sound = setup.sound_simple = TRUE;
13029         SetAudioMode(setup.sound);
13030       }
13031       break;
13032
13033     default:
13034       break;
13035   }
13036 }