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