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