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