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