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