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