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