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