rnd-20060428-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45
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_ce_value = CustomValue[x][y];
8286
8287       CustomValue[x][y] = action_arg_number_new;
8288
8289 #if 0
8290       printf("::: CE value == %d\n", CustomValue[x][y]);
8291 #endif
8292
8293       if (CustomValue[x][y] == 0 && last_ce_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 #if USE_NEW_CUSTOM_VALUE
8314       int last_ce_score = ei->collect_score;
8315
8316       ei->collect_score = action_arg_number_new;
8317
8318 #if 0
8319       printf("::: CE score == %d\n", ei->collect_score);
8320 #endif
8321
8322       if (ei->collect_score == 0 && last_ce_score > 0)
8323       {
8324 #if 0
8325         printf("::: CE_SCORE_GETS_ZERO\n");
8326 #endif
8327
8328         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
8329         CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
8330
8331 #if 0
8332         printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
8333 #endif
8334       }
8335 #endif
8336
8337       break;
8338     }
8339
8340     /* ---------- engine actions  ------------------------------------------ */
8341
8342     case CA_SET_ENGINE_SCAN_MODE:
8343     {
8344       InitPlayfieldScanMode(action_arg);
8345
8346       break;
8347     }
8348
8349     default:
8350       break;
8351   }
8352 }
8353
8354 static void CreateFieldExt(int x, int y, int element, boolean is_change)
8355 {
8356   int old_element = Feld[x][y];
8357   int new_element = get_element_from_group_element(element);
8358   int previous_move_direction = MovDir[x][y];
8359 #if USE_NEW_CUSTOM_VALUE
8360   int last_ce_value = CustomValue[x][y];
8361 #endif
8362   boolean add_player = (ELEM_IS_PLAYER(new_element) &&
8363                         IS_WALKABLE(old_element));
8364
8365 #if 0
8366   /* check if element under player changes from accessible to unaccessible
8367      (needed for special case of dropping element which then changes) */
8368   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8369       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8370   {
8371     Bang(x, y);
8372
8373     return;
8374   }
8375 #endif
8376
8377   if (!add_player)
8378   {
8379     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
8380       RemoveMovingField(x, y);
8381     else
8382       RemoveField(x, y);
8383
8384     Feld[x][y] = new_element;
8385
8386 #if !USE_GFX_RESET_GFX_ANIMATION
8387     ResetGfxAnimation(x, y);
8388     ResetRandomAnimationValue(x, y);
8389 #endif
8390
8391     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
8392       MovDir[x][y] = previous_move_direction;
8393
8394 #if USE_NEW_CUSTOM_VALUE
8395     if (element_info[new_element].use_last_ce_value)
8396       CustomValue[x][y] = last_ce_value;
8397 #endif
8398
8399     InitField_WithBug1(x, y, FALSE);
8400
8401     new_element = Feld[x][y];   /* element may have changed */
8402
8403 #if USE_GFX_RESET_GFX_ANIMATION
8404     ResetGfxAnimation(x, y);
8405     ResetRandomAnimationValue(x, y);
8406 #endif
8407
8408     DrawLevelField(x, y);
8409
8410     if (GFX_CRUMBLED(new_element))
8411       DrawLevelFieldCrumbledSandNeighbours(x, y);
8412   }
8413
8414 #if 1
8415   /* check if element under player changes from accessible to unaccessible
8416      (needed for special case of dropping element which then changes) */
8417   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
8418       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
8419   {
8420     Bang(x, y);
8421
8422     return;
8423   }
8424 #endif
8425
8426   /* "ChangeCount" not set yet to allow "entered by player" change one time */
8427   if (ELEM_IS_PLAYER(new_element))
8428     RelocatePlayer(x, y, new_element);
8429
8430   if (is_change)
8431     ChangeCount[x][y]++;        /* count number of changes in the same frame */
8432
8433   TestIfBadThingTouchesPlayer(x, y);
8434   TestIfPlayerTouchesCustomElement(x, y);
8435   TestIfElementTouchesCustomElement(x, y);
8436 }
8437
8438 static void CreateField(int x, int y, int element)
8439 {
8440   CreateFieldExt(x, y, element, FALSE);
8441 }
8442
8443 static void CreateElementFromChange(int x, int y, int element)
8444 {
8445   element = GET_VALID_RUNTIME_ELEMENT(element);
8446
8447 #if USE_STOP_CHANGED_ELEMENTS
8448   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
8449   {
8450     int old_element = Feld[x][y];
8451
8452     /* prevent changed element from moving in same engine frame
8453        unless both old and new element can either fall or move */
8454     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
8455         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
8456       Stop[x][y] = TRUE;
8457   }
8458 #endif
8459
8460   CreateFieldExt(x, y, element, TRUE);
8461 }
8462
8463 static boolean ChangeElement(int x, int y, int element, int page)
8464 {
8465   struct ElementInfo *ei = &element_info[element];
8466   struct ElementChangeInfo *change = &ei->change_page[page];
8467   int ce_value = CustomValue[x][y];
8468   int ce_score = ei->collect_score;
8469   int target_element;
8470   int old_element = Feld[x][y];
8471
8472   /* always use default change event to prevent running into a loop */
8473   if (ChangeEvent[x][y] == -1)
8474     ChangeEvent[x][y] = CE_DELAY;
8475
8476   if (ChangeEvent[x][y] == CE_DELAY)
8477   {
8478     /* reset actual trigger element, trigger player and action element */
8479     change->actual_trigger_element = EL_EMPTY;
8480     change->actual_trigger_player = EL_PLAYER_1;
8481     change->actual_trigger_side = CH_SIDE_NONE;
8482     change->actual_trigger_ce_value = 0;
8483     change->actual_trigger_ce_score = 0;
8484   }
8485
8486   /* do not change elements more than a specified maximum number of changes */
8487   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
8488     return FALSE;
8489
8490   ChangeCount[x][y]++;          /* count number of changes in the same frame */
8491
8492   if (change->explode)
8493   {
8494     Bang(x, y);
8495
8496     return TRUE;
8497   }
8498
8499   if (change->use_target_content)
8500   {
8501     boolean complete_replace = TRUE;
8502     boolean can_replace[3][3];
8503     int xx, yy;
8504
8505     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8506     {
8507       boolean is_empty;
8508       boolean is_walkable;
8509       boolean is_diggable;
8510       boolean is_collectible;
8511       boolean is_removable;
8512       boolean is_destructible;
8513       int ex = x + xx - 1;
8514       int ey = y + yy - 1;
8515       int content_element = change->target_content.e[xx][yy];
8516       int e;
8517
8518       can_replace[xx][yy] = TRUE;
8519
8520       if (ex == x && ey == y)   /* do not check changing element itself */
8521         continue;
8522
8523       if (content_element == EL_EMPTY_SPACE)
8524       {
8525         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
8526
8527         continue;
8528       }
8529
8530       if (!IN_LEV_FIELD(ex, ey))
8531       {
8532         can_replace[xx][yy] = FALSE;
8533         complete_replace = FALSE;
8534
8535         continue;
8536       }
8537
8538       e = Feld[ex][ey];
8539
8540       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8541         e = MovingOrBlocked2Element(ex, ey);
8542
8543       is_empty = (IS_FREE(ex, ey) ||
8544                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
8545
8546       is_walkable     = (is_empty || IS_WALKABLE(e));
8547       is_diggable     = (is_empty || IS_DIGGABLE(e));
8548       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
8549       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
8550       is_removable    = (is_diggable || is_collectible);
8551
8552       can_replace[xx][yy] =
8553         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
8554           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
8555           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
8556           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
8557           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
8558           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
8559          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
8560
8561       if (!can_replace[xx][yy])
8562         complete_replace = FALSE;
8563     }
8564
8565     if (!change->only_if_complete || complete_replace)
8566     {
8567       boolean something_has_changed = FALSE;
8568
8569       if (change->only_if_complete && change->use_random_replace &&
8570           RND(100) < change->random_percentage)
8571         return FALSE;
8572
8573       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
8574       {
8575         int ex = x + xx - 1;
8576         int ey = y + yy - 1;
8577         int content_element;
8578
8579         if (can_replace[xx][yy] && (!change->use_random_replace ||
8580                                     RND(100) < change->random_percentage))
8581         {
8582           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
8583             RemoveMovingField(ex, ey);
8584
8585           ChangeEvent[ex][ey] = ChangeEvent[x][y];
8586
8587           content_element = change->target_content.e[xx][yy];
8588           target_element = GET_TARGET_ELEMENT(content_element, change,
8589                                               ce_value, ce_score);
8590
8591           CreateElementFromChange(ex, ey, target_element);
8592
8593           something_has_changed = TRUE;
8594
8595           /* for symmetry reasons, freeze newly created border elements */
8596           if (ex != x || ey != y)
8597             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
8598         }
8599       }
8600
8601       if (something_has_changed)
8602       {
8603         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8604         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8605       }
8606     }
8607   }
8608   else
8609   {
8610     target_element = GET_TARGET_ELEMENT(change->target_element, change,
8611                                         ce_value, ce_score);
8612
8613     if (element == EL_DIAGONAL_GROWING ||
8614         element == EL_DIAGONAL_SHRINKING)
8615     {
8616       target_element = Store[x][y];
8617
8618       Store[x][y] = EL_EMPTY;
8619     }
8620
8621     CreateElementFromChange(x, y, target_element);
8622
8623     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
8624     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
8625   }
8626
8627   /* this uses direct change before indirect change */
8628   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
8629
8630   return TRUE;
8631 }
8632
8633 #if USE_NEW_DELAYED_ACTION
8634
8635 static void HandleElementChange(int x, int y, int page)
8636 {
8637   int element = MovingOrBlocked2Element(x, y);
8638   struct ElementInfo *ei = &element_info[element];
8639   struct ElementChangeInfo *change = &ei->change_page[page];
8640
8641 #ifdef DEBUG
8642   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
8643       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
8644   {
8645     printf("\n\n");
8646     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8647            x, y, element, element_info[element].token_name);
8648     printf("HandleElementChange(): This should never happen!\n");
8649     printf("\n\n");
8650   }
8651 #endif
8652
8653   /* this can happen with classic bombs on walkable, changing elements */
8654   if (!CAN_CHANGE_OR_HAS_ACTION(element))
8655   {
8656 #if 0
8657     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
8658       ChangeDelay[x][y] = 0;
8659 #endif
8660
8661     return;
8662   }
8663
8664   if (ChangeDelay[x][y] == 0)           /* initialize element change */
8665   {
8666     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8667
8668     if (change->can_change)
8669     {
8670       ResetGfxAnimation(x, y);
8671       ResetRandomAnimationValue(x, y);
8672
8673       if (change->pre_change_function)
8674         change->pre_change_function(x, y);
8675     }
8676   }
8677
8678   ChangeDelay[x][y]--;
8679
8680   if (ChangeDelay[x][y] != 0)           /* continue element change */
8681   {
8682     if (change->can_change)
8683     {
8684       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8685
8686       if (IS_ANIMATED(graphic))
8687         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8688
8689       if (change->change_function)
8690         change->change_function(x, y);
8691     }
8692   }
8693   else                                  /* finish element change */
8694   {
8695     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
8696     {
8697       page = ChangePage[x][y];
8698       ChangePage[x][y] = -1;
8699
8700       change = &ei->change_page[page];
8701     }
8702
8703     if (IS_MOVING(x, y))                /* never change a running system ;-) */
8704     {
8705       ChangeDelay[x][y] = 1;            /* try change after next move step */
8706       ChangePage[x][y] = page;          /* remember page to use for change */
8707
8708       return;
8709     }
8710
8711     if (change->can_change)
8712     {
8713       if (ChangeElement(x, y, element, page))
8714       {
8715         if (change->post_change_function)
8716           change->post_change_function(x, y);
8717       }
8718     }
8719
8720     if (change->has_action)
8721       ExecuteCustomElementAction(x, y, element, page);
8722   }
8723 }
8724
8725 #else
8726
8727 static void HandleElementChange(int x, int y, int page)
8728 {
8729   int element = MovingOrBlocked2Element(x, y);
8730   struct ElementInfo *ei = &element_info[element];
8731   struct ElementChangeInfo *change = &ei->change_page[page];
8732
8733 #ifdef DEBUG
8734   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
8735   {
8736     printf("\n\n");
8737     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
8738            x, y, element, element_info[element].token_name);
8739     printf("HandleElementChange(): This should never happen!\n");
8740     printf("\n\n");
8741   }
8742 #endif
8743
8744   /* this can happen with classic bombs on walkable, changing elements */
8745   if (!CAN_CHANGE(element))
8746   {
8747 #if 0
8748     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
8749       ChangeDelay[x][y] = 0;
8750 #endif
8751
8752     return;
8753   }
8754
8755   if (ChangeDelay[x][y] == 0)           /* initialize element change */
8756   {
8757     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
8758
8759     ResetGfxAnimation(x, y);
8760     ResetRandomAnimationValue(x, y);
8761
8762     if (change->pre_change_function)
8763       change->pre_change_function(x, y);
8764   }
8765
8766   ChangeDelay[x][y]--;
8767
8768   if (ChangeDelay[x][y] != 0)           /* continue element change */
8769   {
8770     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8771
8772     if (IS_ANIMATED(graphic))
8773       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8774
8775     if (change->change_function)
8776       change->change_function(x, y);
8777   }
8778   else                                  /* finish element change */
8779   {
8780     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
8781     {
8782       page = ChangePage[x][y];
8783       ChangePage[x][y] = -1;
8784
8785       change = &ei->change_page[page];
8786     }
8787
8788     if (IS_MOVING(x, y))                /* never change a running system ;-) */
8789     {
8790       ChangeDelay[x][y] = 1;            /* try change after next move step */
8791       ChangePage[x][y] = page;          /* remember page to use for change */
8792
8793       return;
8794     }
8795
8796     if (ChangeElement(x, y, element, page))
8797     {
8798       if (change->post_change_function)
8799         change->post_change_function(x, y);
8800     }
8801   }
8802 }
8803
8804 #endif
8805
8806 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
8807                                               int trigger_element,
8808                                               int trigger_event,
8809                                               int trigger_player,
8810                                               int trigger_side,
8811                                               int trigger_page)
8812 {
8813   boolean change_done_any = FALSE;
8814   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
8815   int i;
8816
8817   if (!(trigger_events[trigger_element][trigger_event]))
8818     return FALSE;
8819
8820   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8821   {
8822     int element = EL_CUSTOM_START + i;
8823     boolean change_done = FALSE;
8824     int p;
8825
8826     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8827         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8828       continue;
8829
8830     for (p = 0; p < element_info[element].num_change_pages; p++)
8831     {
8832       struct ElementChangeInfo *change = &element_info[element].change_page[p];
8833
8834       if (change->can_change_or_has_action &&
8835           change->has_event[trigger_event] &&
8836           change->trigger_side & trigger_side &&
8837           change->trigger_player & trigger_player &&
8838           change->trigger_page & trigger_page_bits &&
8839           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
8840       {
8841         change->actual_trigger_element = trigger_element;
8842         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8843         change->actual_trigger_side = trigger_side;
8844         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
8845         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8846
8847         if ((change->can_change && !change_done) || change->has_action)
8848         {
8849           int x, y;
8850
8851 #if 1
8852           SCAN_PLAYFIELD(x, y)
8853 #else
8854           for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
8855 #endif
8856           {
8857             if (Feld[x][y] == element)
8858             {
8859               if (change->can_change && !change_done)
8860               {
8861                 ChangeDelay[x][y] = 1;
8862                 ChangeEvent[x][y] = trigger_event;
8863
8864                 HandleElementChange(x, y, p);
8865               }
8866 #if USE_NEW_DELAYED_ACTION
8867               else if (change->has_action)
8868               {
8869                 ExecuteCustomElementAction(x, y, element, p);
8870                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8871               }
8872 #else
8873               if (change->has_action)
8874               {
8875                 ExecuteCustomElementAction(x, y, element, p);
8876                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8877               }
8878 #endif
8879             }
8880           }
8881
8882           if (change->can_change)
8883           {
8884             change_done = TRUE;
8885             change_done_any = TRUE;
8886           }
8887         }
8888       }
8889     }
8890   }
8891
8892   return change_done_any;
8893 }
8894
8895 static boolean CheckElementChangeExt(int x, int y,
8896                                      int element,
8897                                      int trigger_element,
8898                                      int trigger_event,
8899                                      int trigger_player,
8900                                      int trigger_side)
8901 {
8902   boolean change_done = FALSE;
8903   int p;
8904
8905   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
8906       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
8907     return FALSE;
8908
8909   if (Feld[x][y] == EL_BLOCKED)
8910   {
8911     Blocked2Moving(x, y, &x, &y);
8912     element = Feld[x][y];
8913   }
8914
8915 #if 0
8916   /* check if element has already changed */
8917   if (Feld[x][y] != element)
8918     return FALSE;
8919 #else
8920   /* check if element has already changed or is about to change after moving */
8921   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
8922        Feld[x][y] != element) ||
8923
8924       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
8925        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
8926         ChangePage[x][y] != -1)))
8927     return FALSE;
8928 #endif
8929
8930   for (p = 0; p < element_info[element].num_change_pages; p++)
8931   {
8932     struct ElementChangeInfo *change = &element_info[element].change_page[p];
8933
8934     boolean check_trigger_element =
8935       (trigger_event == CE_TOUCHING_X ||
8936        trigger_event == CE_HITTING_X ||
8937        trigger_event == CE_HIT_BY_X);
8938
8939     if (change->can_change_or_has_action &&
8940         change->has_event[trigger_event] &&
8941         change->trigger_side & trigger_side &&
8942         change->trigger_player & trigger_player &&
8943         (!check_trigger_element ||
8944          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
8945     {
8946       change->actual_trigger_element = trigger_element;
8947       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
8948       change->actual_trigger_side = trigger_side;
8949       change->actual_trigger_ce_value = CustomValue[x][y];
8950       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8951
8952       /* special case: trigger element not at (x,y) position for some events */
8953       if (check_trigger_element)
8954       {
8955         static struct
8956         {
8957           int dx, dy;
8958         } move_xy[] =
8959           {
8960             {  0,  0 },
8961             { -1,  0 },
8962             { +1,  0 },
8963             {  0,  0 },
8964             {  0, -1 },
8965             {  0,  0 }, { 0, 0 }, { 0, 0 },
8966             {  0, +1 }
8967           };
8968
8969         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
8970         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
8971
8972         change->actual_trigger_ce_value = CustomValue[xx][yy];
8973         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
8974       }
8975
8976       if (change->can_change && !change_done)
8977       {
8978         ChangeDelay[x][y] = 1;
8979         ChangeEvent[x][y] = trigger_event;
8980
8981         HandleElementChange(x, y, p);
8982
8983         change_done = TRUE;
8984       }
8985 #if USE_NEW_DELAYED_ACTION
8986       else if (change->has_action)
8987       {
8988         ExecuteCustomElementAction(x, y, element, p);
8989         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8990       }
8991 #else
8992       if (change->has_action)
8993       {
8994         ExecuteCustomElementAction(x, y, element, p);
8995         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
8996       }
8997 #endif
8998     }
8999   }
9000
9001   return change_done;
9002 }
9003
9004 static void PlayPlayerSound(struct PlayerInfo *player)
9005 {
9006   int jx = player->jx, jy = player->jy;
9007   int sound_element = player->artwork_element;
9008   int last_action = player->last_action_waiting;
9009   int action = player->action_waiting;
9010
9011   if (player->is_waiting)
9012   {
9013     if (action != last_action)
9014       PlayLevelSoundElementAction(jx, jy, sound_element, action);
9015     else
9016       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
9017   }
9018   else
9019   {
9020     if (action != last_action)
9021       StopSound(element_info[sound_element].sound[last_action]);
9022
9023     if (last_action == ACTION_SLEEPING)
9024       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
9025   }
9026 }
9027
9028 static void PlayAllPlayersSound()
9029 {
9030   int i;
9031
9032   for (i = 0; i < MAX_PLAYERS; i++)
9033     if (stored_player[i].active)
9034       PlayPlayerSound(&stored_player[i]);
9035 }
9036
9037 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
9038 {
9039   boolean last_waiting = player->is_waiting;
9040   int move_dir = player->MovDir;
9041
9042   player->dir_waiting = move_dir;
9043   player->last_action_waiting = player->action_waiting;
9044
9045   if (is_waiting)
9046   {
9047     if (!last_waiting)          /* not waiting -> waiting */
9048     {
9049       player->is_waiting = TRUE;
9050
9051       player->frame_counter_bored =
9052         FrameCounter +
9053         game.player_boring_delay_fixed +
9054         SimpleRND(game.player_boring_delay_random);
9055       player->frame_counter_sleeping =
9056         FrameCounter +
9057         game.player_sleeping_delay_fixed +
9058         SimpleRND(game.player_sleeping_delay_random);
9059
9060 #if 1
9061       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
9062 #else
9063       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
9064 #endif
9065     }
9066
9067     if (game.player_sleeping_delay_fixed +
9068         game.player_sleeping_delay_random > 0 &&
9069         player->anim_delay_counter == 0 &&
9070         player->post_delay_counter == 0 &&
9071         FrameCounter >= player->frame_counter_sleeping)
9072       player->is_sleeping = TRUE;
9073     else if (game.player_boring_delay_fixed +
9074              game.player_boring_delay_random > 0 &&
9075              FrameCounter >= player->frame_counter_bored)
9076       player->is_bored = TRUE;
9077
9078     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
9079                               player->is_bored ? ACTION_BORING :
9080                               ACTION_WAITING);
9081
9082 #if 1
9083     if (player->is_sleeping && player->use_murphy)
9084     {
9085       /* special case for sleeping Murphy when leaning against non-free tile */
9086
9087       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
9088           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
9089            !IS_MOVING(player->jx - 1, player->jy)))
9090         move_dir = MV_LEFT;
9091       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
9092                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
9093                 !IS_MOVING(player->jx + 1, player->jy)))
9094         move_dir = MV_RIGHT;
9095       else
9096         player->is_sleeping = FALSE;
9097
9098       player->dir_waiting = move_dir;
9099     }
9100 #endif
9101
9102     if (player->is_sleeping)
9103     {
9104       if (player->num_special_action_sleeping > 0)
9105       {
9106         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9107         {
9108           int last_special_action = player->special_action_sleeping;
9109           int num_special_action = player->num_special_action_sleeping;
9110           int special_action =
9111             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
9112              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
9113              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
9114              last_special_action + 1 : ACTION_SLEEPING);
9115           int special_graphic =
9116             el_act_dir2img(player->artwork_element, special_action, move_dir);
9117
9118           player->anim_delay_counter =
9119             graphic_info[special_graphic].anim_delay_fixed +
9120             SimpleRND(graphic_info[special_graphic].anim_delay_random);
9121           player->post_delay_counter =
9122             graphic_info[special_graphic].post_delay_fixed +
9123             SimpleRND(graphic_info[special_graphic].post_delay_random);
9124
9125           player->special_action_sleeping = special_action;
9126         }
9127
9128         if (player->anim_delay_counter > 0)
9129         {
9130           player->action_waiting = player->special_action_sleeping;
9131           player->anim_delay_counter--;
9132         }
9133         else if (player->post_delay_counter > 0)
9134         {
9135           player->post_delay_counter--;
9136         }
9137       }
9138     }
9139     else if (player->is_bored)
9140     {
9141       if (player->num_special_action_bored > 0)
9142       {
9143         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
9144         {
9145           int special_action =
9146             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
9147           int special_graphic =
9148             el_act_dir2img(player->artwork_element, special_action, move_dir);
9149
9150           player->anim_delay_counter =
9151             graphic_info[special_graphic].anim_delay_fixed +
9152             SimpleRND(graphic_info[special_graphic].anim_delay_random);
9153           player->post_delay_counter =
9154             graphic_info[special_graphic].post_delay_fixed +
9155             SimpleRND(graphic_info[special_graphic].post_delay_random);
9156
9157           player->special_action_bored = special_action;
9158         }
9159
9160         if (player->anim_delay_counter > 0)
9161         {
9162           player->action_waiting = player->special_action_bored;
9163           player->anim_delay_counter--;
9164         }
9165         else if (player->post_delay_counter > 0)
9166         {
9167           player->post_delay_counter--;
9168         }
9169       }
9170     }
9171   }
9172   else if (last_waiting)        /* waiting -> not waiting */
9173   {
9174     player->is_waiting = FALSE;
9175     player->is_bored = FALSE;
9176     player->is_sleeping = FALSE;
9177
9178     player->frame_counter_bored = -1;
9179     player->frame_counter_sleeping = -1;
9180
9181     player->anim_delay_counter = 0;
9182     player->post_delay_counter = 0;
9183
9184     player->dir_waiting = player->MovDir;
9185     player->action_waiting = ACTION_DEFAULT;
9186
9187     player->special_action_bored = ACTION_DEFAULT;
9188     player->special_action_sleeping = ACTION_DEFAULT;
9189   }
9190 }
9191
9192 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
9193 {
9194   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
9195   int left      = player_action & JOY_LEFT;
9196   int right     = player_action & JOY_RIGHT;
9197   int up        = player_action & JOY_UP;
9198   int down      = player_action & JOY_DOWN;
9199   int button1   = player_action & JOY_BUTTON_1;
9200   int button2   = player_action & JOY_BUTTON_2;
9201   int dx        = (left ? -1 : right ? 1 : 0);
9202   int dy        = (up   ? -1 : down  ? 1 : 0);
9203
9204   if (!player->active || tape.pausing)
9205     return 0;
9206
9207   if (player_action)
9208   {
9209     if (button1)
9210       snapped = SnapField(player, dx, dy);
9211     else
9212     {
9213       if (button2)
9214         dropped = DropElement(player);
9215
9216       moved = MovePlayer(player, dx, dy);
9217     }
9218
9219     if (tape.single_step && tape.recording && !tape.pausing)
9220     {
9221       if (button1 || (dropped && !moved))
9222       {
9223         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9224         SnapField(player, 0, 0);                /* stop snapping */
9225       }
9226     }
9227
9228     SetPlayerWaiting(player, FALSE);
9229
9230     return player_action;
9231   }
9232   else
9233   {
9234     /* no actions for this player (no input at player's configured device) */
9235
9236     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
9237     SnapField(player, 0, 0);
9238     CheckGravityMovementWhenNotMoving(player);
9239
9240     if (player->MovPos == 0)
9241       SetPlayerWaiting(player, TRUE);
9242
9243     if (player->MovPos == 0)    /* needed for tape.playing */
9244       player->is_moving = FALSE;
9245
9246     player->is_dropping = FALSE;
9247     player->is_dropping_pressed = FALSE;
9248     player->drop_pressed_delay = 0;
9249
9250     return 0;
9251   }
9252 }
9253
9254 static void CheckLevelTime()
9255 {
9256   int i;
9257
9258   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9259   {
9260     if (level.native_em_level->lev->home == 0)  /* all players at home */
9261     {
9262       local_player->LevelSolved = TRUE;
9263       AllPlayersGone = TRUE;
9264
9265       level.native_em_level->lev->home = -1;
9266     }
9267
9268     if (level.native_em_level->ply[0]->alive == 0 &&
9269         level.native_em_level->ply[1]->alive == 0 &&
9270         level.native_em_level->ply[2]->alive == 0 &&
9271         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9272       AllPlayersGone = TRUE;
9273   }
9274
9275   if (TimeFrames >= FRAMES_PER_SECOND)
9276   {
9277     TimeFrames = 0;
9278     TapeTime++;
9279
9280     for (i = 0; i < MAX_PLAYERS; i++)
9281     {
9282       struct PlayerInfo *player = &stored_player[i];
9283
9284       if (SHIELD_ON(player))
9285       {
9286         player->shield_normal_time_left--;
9287
9288         if (player->shield_deadly_time_left > 0)
9289           player->shield_deadly_time_left--;
9290       }
9291     }
9292
9293     if (!level.use_step_counter)
9294     {
9295       TimePlayed++;
9296
9297       if (TimeLeft > 0)
9298       {
9299         TimeLeft--;
9300
9301         if (TimeLeft <= 10 && setup.time_limit)
9302           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
9303
9304         DrawGameValue_Time(TimeLeft);
9305
9306         if (!TimeLeft && setup.time_limit)
9307         {
9308           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9309             level.native_em_level->lev->killed_out_of_time = TRUE;
9310           else
9311             for (i = 0; i < MAX_PLAYERS; i++)
9312               KillPlayer(&stored_player[i]);
9313         }
9314       }
9315       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
9316         DrawGameValue_Time(TimePlayed);
9317
9318       level.native_em_level->lev->time =
9319         (level.time == 0 ? TimePlayed : TimeLeft);
9320     }
9321
9322     if (tape.recording || tape.playing)
9323       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
9324   }
9325 }
9326
9327 void AdvanceFrameAndPlayerCounters(int player_nr)
9328 {
9329   int i;
9330
9331 #if 0
9332   Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
9333         FrameCounter, FrameCounter + 1);
9334 #endif
9335
9336   /* advance frame counters (global frame counter and time frame counter) */
9337   FrameCounter++;
9338   TimeFrames++;
9339
9340   /* advance player counters (counters for move delay, move animation etc.) */
9341   for (i = 0; i < MAX_PLAYERS; i++)
9342   {
9343     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
9344     int move_delay_value = stored_player[i].move_delay_value;
9345     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
9346
9347     if (!advance_player_counters)       /* not all players may be affected */
9348       continue;
9349
9350 #if USE_NEW_PLAYER_ANIM
9351     if (move_frames == 0)       /* less than one move per game frame */
9352     {
9353       int stepsize = TILEX / move_delay_value;
9354       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
9355       int count = (stored_player[i].is_moving ?
9356                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
9357
9358       if (count % delay == 0)
9359         move_frames = 1;
9360     }
9361 #endif
9362
9363     stored_player[i].Frame += move_frames;
9364
9365     if (stored_player[i].MovPos != 0)
9366       stored_player[i].StepFrame += move_frames;
9367
9368     if (stored_player[i].move_delay > 0)
9369       stored_player[i].move_delay--;
9370
9371     /* due to bugs in previous versions, counter must count up, not down */
9372     if (stored_player[i].push_delay != -1)
9373       stored_player[i].push_delay++;
9374
9375     if (stored_player[i].drop_delay > 0)
9376       stored_player[i].drop_delay--;
9377
9378     if (stored_player[i].is_dropping_pressed)
9379       stored_player[i].drop_pressed_delay++;
9380   }
9381 }
9382
9383 void StartGameActions(boolean init_network_game, boolean record_tape,
9384                       long random_seed)
9385 {
9386   unsigned long new_random_seed = InitRND(random_seed);
9387
9388   if (record_tape)
9389     TapeStartRecording(new_random_seed);
9390
9391 #if defined(NETWORK_AVALIABLE)
9392   if (init_network_game)
9393   {
9394     SendToServer_StartPlaying();
9395
9396     return;
9397   }
9398 #endif
9399
9400   StopAnimation();
9401
9402   game_status = GAME_MODE_PLAYING;
9403
9404   InitGame();
9405 }
9406
9407 void GameActions()
9408 {
9409   static unsigned long game_frame_delay = 0;
9410   unsigned long game_frame_delay_value;
9411   byte *recorded_player_action;
9412   byte summarized_player_action = 0;
9413   byte tape_action[MAX_PLAYERS];
9414   int i;
9415
9416   if (game.restart_level)
9417     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
9418
9419   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9420   {
9421     if (level.native_em_level->lev->home == 0)  /* all players at home */
9422     {
9423       local_player->LevelSolved = TRUE;
9424       AllPlayersGone = TRUE;
9425
9426       level.native_em_level->lev->home = -1;
9427     }
9428
9429     if (level.native_em_level->ply[0]->alive == 0 &&
9430         level.native_em_level->ply[1]->alive == 0 &&
9431         level.native_em_level->ply[2]->alive == 0 &&
9432         level.native_em_level->ply[3]->alive == 0)      /* all dead */
9433       AllPlayersGone = TRUE;
9434   }
9435
9436   if (local_player->LevelSolved)
9437     GameWon();
9438
9439   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
9440     TapeStop();
9441
9442   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
9443     return;
9444
9445   game_frame_delay_value =
9446     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
9447
9448   if (tape.playing && tape.warp_forward && !tape.pausing)
9449     game_frame_delay_value = 0;
9450
9451   /* ---------- main game synchronization point ---------- */
9452
9453   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
9454
9455   if (network_playing && !network_player_action_received)
9456   {
9457     /* try to get network player actions in time */
9458
9459 #if defined(NETWORK_AVALIABLE)
9460     /* last chance to get network player actions without main loop delay */
9461     HandleNetworking();
9462 #endif
9463
9464     /* game was quit by network peer */
9465     if (game_status != GAME_MODE_PLAYING)
9466       return;
9467
9468     if (!network_player_action_received)
9469       return;           /* failed to get network player actions in time */
9470
9471     /* do not yet reset "network_player_action_received" (for tape.pausing) */
9472   }
9473
9474   if (tape.pausing)
9475     return;
9476
9477   /* at this point we know that we really continue executing the game */
9478
9479 #if 1
9480   network_player_action_received = FALSE;
9481 #endif
9482
9483   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
9484
9485   if (tape.set_centered_player)
9486   {
9487     game.centered_player_nr_next = tape.centered_player_nr_next;
9488     game.set_centered_player = TRUE;
9489   }
9490
9491   for (i = 0; i < MAX_PLAYERS; i++)
9492   {
9493     summarized_player_action |= stored_player[i].action;
9494
9495     if (!network_playing)
9496       stored_player[i].effective_action = stored_player[i].action;
9497   }
9498
9499 #if defined(NETWORK_AVALIABLE)
9500   if (network_playing)
9501     SendToServer_MovePlayer(summarized_player_action);
9502 #endif
9503
9504   if (!options.network && !setup.team_mode)
9505     local_player->effective_action = summarized_player_action;
9506
9507   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
9508   {
9509     for (i = 0; i < MAX_PLAYERS; i++)
9510       stored_player[i].effective_action =
9511         (i == game.centered_player_nr ? summarized_player_action : 0);
9512   }
9513
9514   if (recorded_player_action != NULL)
9515     for (i = 0; i < MAX_PLAYERS; i++)
9516       stored_player[i].effective_action = recorded_player_action[i];
9517
9518   for (i = 0; i < MAX_PLAYERS; i++)
9519   {
9520     tape_action[i] = stored_player[i].effective_action;
9521
9522     /* (this can only happen in the R'n'D game engine) */
9523     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9524       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9525   }
9526
9527   /* only record actions from input devices, but not programmed actions */
9528   if (tape.recording)
9529     TapeRecordAction(tape_action);
9530
9531   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
9532   {
9533     GameActions_EM_Main();
9534   }
9535   else
9536   {
9537     GameActions_RND();
9538   }
9539 }
9540
9541 void GameActions_EM_Main()
9542 {
9543   byte effective_action[MAX_PLAYERS];
9544   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
9545   int i;
9546
9547   for (i = 0; i < MAX_PLAYERS; i++)
9548     effective_action[i] = stored_player[i].effective_action;
9549
9550   GameActions_EM(effective_action, warp_mode);
9551
9552   CheckLevelTime();
9553
9554   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
9555 }
9556
9557 void GameActions_RND()
9558 {
9559   int magic_wall_x = 0, magic_wall_y = 0;
9560   int i, x, y, element, graphic;
9561
9562   InitPlayfieldScanModeVars();
9563
9564 #if USE_ONE_MORE_CHANGE_PER_FRAME
9565   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9566   {
9567     SCAN_PLAYFIELD(x, y)
9568     {
9569       ChangeCount[x][y] = 0;
9570       ChangeEvent[x][y] = -1;
9571     }
9572   }
9573 #endif
9574
9575 #if 1
9576   if (game.set_centered_player)
9577   {
9578     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
9579
9580     /* switching to "all players" only possible if all players fit to screen */
9581     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
9582     {
9583       game.centered_player_nr_next = game.centered_player_nr;
9584       game.set_centered_player = FALSE;
9585     }
9586
9587     /* do not switch focus to non-existing (or non-active) player */
9588     if (game.centered_player_nr_next >= 0 &&
9589         !stored_player[game.centered_player_nr_next].active)
9590     {
9591       game.centered_player_nr_next = game.centered_player_nr;
9592       game.set_centered_player = FALSE;
9593     }
9594   }
9595
9596   if (game.set_centered_player &&
9597       ScreenMovPos == 0)        /* screen currently aligned at tile position */
9598   {
9599     int sx, sy;
9600
9601     if (game.centered_player_nr_next == -1)
9602     {
9603       setScreenCenteredToAllPlayers(&sx, &sy);
9604     }
9605     else
9606     {
9607       sx = stored_player[game.centered_player_nr_next].jx;
9608       sy = stored_player[game.centered_player_nr_next].jy;
9609     }
9610
9611     game.centered_player_nr = game.centered_player_nr_next;
9612     game.set_centered_player = FALSE;
9613
9614     DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
9615     DrawGameDoorValues();
9616   }
9617 #endif
9618
9619   for (i = 0; i < MAX_PLAYERS; i++)
9620   {
9621     int actual_player_action = stored_player[i].effective_action;
9622
9623 #if 1
9624     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
9625        - rnd_equinox_tetrachloride 048
9626        - rnd_equinox_tetrachloride_ii 096
9627        - rnd_emanuel_schmieg 002
9628        - doctor_sloan_ww 001, 020
9629     */
9630     if (stored_player[i].MovPos == 0)
9631       CheckGravityMovement(&stored_player[i]);
9632 #endif
9633
9634     /* overwrite programmed action with tape action */
9635     if (stored_player[i].programmed_action)
9636       actual_player_action = stored_player[i].programmed_action;
9637
9638 #if 1
9639     PlayerActions(&stored_player[i], actual_player_action);
9640 #else
9641     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
9642
9643     if (tape.recording && tape_action[i] && !tape.player_participates[i])
9644       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
9645 #endif
9646
9647     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
9648   }
9649
9650 #if 0
9651   network_player_action_received = FALSE;
9652 #endif
9653
9654   ScrollScreen(NULL, SCROLL_GO_ON);
9655
9656   /* for backwards compatibility, the following code emulates a fixed bug that
9657      occured when pushing elements (causing elements that just made their last
9658      pushing step to already (if possible) make their first falling step in the
9659      same game frame, which is bad); this code is also needed to use the famous
9660      "spring push bug" which is used in older levels and might be wanted to be
9661      used also in newer levels, but in this case the buggy pushing code is only
9662      affecting the "spring" element and no other elements */
9663
9664   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
9665   {
9666     for (i = 0; i < MAX_PLAYERS; i++)
9667     {
9668       struct PlayerInfo *player = &stored_player[i];
9669       int x = player->jx;
9670       int y = player->jy;
9671
9672       if (player->active && player->is_pushing && player->is_moving &&
9673           IS_MOVING(x, y) &&
9674           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
9675            Feld[x][y] == EL_SPRING))
9676       {
9677         ContinueMoving(x, y);
9678
9679         /* continue moving after pushing (this is actually a bug) */
9680         if (!IS_MOVING(x, y))
9681         {
9682           Stop[x][y] = FALSE;
9683         }
9684       }
9685     }
9686   }
9687
9688 #if 1
9689   SCAN_PLAYFIELD(x, y)
9690 #else
9691   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9692 #endif
9693   {
9694     ChangeCount[x][y] = 0;
9695     ChangeEvent[x][y] = -1;
9696
9697     /* this must be handled before main playfield loop */
9698     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
9699     {
9700       MovDelay[x][y]--;
9701       if (MovDelay[x][y] <= 0)
9702         RemoveField(x, y);
9703     }
9704
9705 #if USE_NEW_SNAP_DELAY
9706     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
9707     {
9708       MovDelay[x][y]--;
9709       if (MovDelay[x][y] <= 0)
9710       {
9711         RemoveField(x, y);
9712         DrawLevelField(x, y);
9713
9714         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
9715       }
9716     }
9717 #endif
9718
9719 #if DEBUG
9720     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
9721     {
9722       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
9723       printf("GameActions(): This should never happen!\n");
9724
9725       ChangePage[x][y] = -1;
9726     }
9727 #endif
9728
9729     Stop[x][y] = FALSE;
9730     if (WasJustMoving[x][y] > 0)
9731       WasJustMoving[x][y]--;
9732     if (WasJustFalling[x][y] > 0)
9733       WasJustFalling[x][y]--;
9734     if (CheckCollision[x][y] > 0)
9735       CheckCollision[x][y]--;
9736
9737     GfxFrame[x][y]++;
9738
9739     /* reset finished pushing action (not done in ContinueMoving() to allow
9740        continuous pushing animation for elements with zero push delay) */
9741     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
9742     {
9743       ResetGfxAnimation(x, y);
9744       DrawLevelField(x, y);
9745     }
9746
9747 #if DEBUG
9748     if (IS_BLOCKED(x, y))
9749     {
9750       int oldx, oldy;
9751
9752       Blocked2Moving(x, y, &oldx, &oldy);
9753       if (!IS_MOVING(oldx, oldy))
9754       {
9755         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
9756         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
9757         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
9758         printf("GameActions(): This should never happen!\n");
9759       }
9760     }
9761 #endif
9762   }
9763
9764 #if 1
9765   SCAN_PLAYFIELD(x, y)
9766 #else
9767   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9768 #endif
9769   {
9770     element = Feld[x][y];
9771     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9772
9773 #if 0
9774     printf("::: %d,%d\n", x, y);
9775
9776     if (element == EL_ROCK)
9777       printf("::: Yo man! Rocks can fall!\n");
9778 #endif
9779
9780 #if 1
9781     ResetGfxFrame(x, y, TRUE);
9782 #else
9783     if (graphic_info[graphic].anim_global_sync)
9784       GfxFrame[x][y] = FrameCounter;
9785     else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
9786     {
9787       int old_gfx_frame = GfxFrame[x][y];
9788
9789       GfxFrame[x][y] = CustomValue[x][y];
9790
9791 #if 1
9792       if (GfxFrame[x][y] != old_gfx_frame)
9793 #endif
9794         DrawLevelGraphicAnimation(x, y, graphic);
9795     }
9796     else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
9797     {
9798       int old_gfx_frame = GfxFrame[x][y];
9799
9800       GfxFrame[x][y] = element_info[element].collect_score;
9801
9802 #if 1
9803       if (GfxFrame[x][y] != old_gfx_frame)
9804 #endif
9805         DrawLevelGraphicAnimation(x, y, graphic);
9806     }
9807 #endif
9808
9809     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
9810         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
9811       ResetRandomAnimationValue(x, y);
9812
9813     SetRandomAnimationValue(x, y);
9814
9815     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
9816
9817     if (IS_INACTIVE(element))
9818     {
9819       if (IS_ANIMATED(graphic))
9820         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9821
9822       continue;
9823     }
9824
9825     /* this may take place after moving, so 'element' may have changed */
9826     if (IS_CHANGING(x, y) &&
9827         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
9828     {
9829       int page = element_info[element].event_page_nr[CE_DELAY];
9830 #if 0
9831       HandleElementChange(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
9832 #else
9833
9834 #if 0
9835       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9836 #endif
9837
9838 #if 0
9839       if (element == EL_CUSTOM_255)
9840         printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
9841 #endif
9842
9843 #if 1
9844       HandleElementChange(x, y, page);
9845 #else
9846       if (CAN_CHANGE(element))
9847         HandleElementChange(x, y, page);
9848
9849       if (HAS_ACTION(element))
9850         ExecuteCustomElementAction(x, y, element, page);
9851 #endif
9852
9853 #endif
9854
9855       element = Feld[x][y];
9856       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9857     }
9858
9859     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
9860     {
9861       StartMoving(x, y);
9862
9863       element = Feld[x][y];
9864       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
9865
9866       if (IS_ANIMATED(graphic) &&
9867           !IS_MOVING(x, y) &&
9868           !Stop[x][y])
9869         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9870
9871       if (IS_GEM(element) || element == EL_SP_INFOTRON)
9872         EdelsteinFunkeln(x, y);
9873     }
9874     else if ((element == EL_ACID ||
9875               element == EL_EXIT_OPEN ||
9876               element == EL_SP_EXIT_OPEN ||
9877               element == EL_SP_TERMINAL ||
9878               element == EL_SP_TERMINAL_ACTIVE ||
9879               element == EL_EXTRA_TIME ||
9880               element == EL_SHIELD_NORMAL ||
9881               element == EL_SHIELD_DEADLY) &&
9882              IS_ANIMATED(graphic))
9883       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9884     else if (IS_MOVING(x, y))
9885       ContinueMoving(x, y);
9886     else if (IS_ACTIVE_BOMB(element))
9887       CheckDynamite(x, y);
9888     else if (element == EL_AMOEBA_GROWING)
9889       AmoebeWaechst(x, y);
9890     else if (element == EL_AMOEBA_SHRINKING)
9891       AmoebaDisappearing(x, y);
9892
9893 #if !USE_NEW_AMOEBA_CODE
9894     else if (IS_AMOEBALIVE(element))
9895       AmoebeAbleger(x, y);
9896 #endif
9897
9898     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
9899       Life(x, y);
9900     else if (element == EL_EXIT_CLOSED)
9901       CheckExit(x, y);
9902     else if (element == EL_SP_EXIT_CLOSED)
9903       CheckExitSP(x, y);
9904     else if (element == EL_EXPANDABLE_WALL_GROWING)
9905       MauerWaechst(x, y);
9906     else if (element == EL_EXPANDABLE_WALL ||
9907              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9908              element == EL_EXPANDABLE_WALL_VERTICAL ||
9909              element == EL_EXPANDABLE_WALL_ANY)
9910       MauerAbleger(x, y);
9911     else if (element == EL_FLAMES)
9912       CheckForDragon(x, y);
9913     else if (element == EL_EXPLOSION)
9914       ; /* drawing of correct explosion animation is handled separately */
9915     else if (element == EL_ELEMENT_SNAPPING ||
9916              element == EL_DIAGONAL_SHRINKING ||
9917              element == EL_DIAGONAL_GROWING)
9918     {
9919 #if 1
9920       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
9921
9922       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9923 #endif
9924     }
9925     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
9926       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9927
9928 #if 0
9929     if (element == EL_CUSTOM_255 ||
9930         element == EL_CUSTOM_256)
9931       DrawLevelGraphicAnimation(x, y, graphic);
9932 #endif
9933
9934     if (IS_BELT_ACTIVE(element))
9935       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
9936
9937     if (game.magic_wall_active)
9938     {
9939       int jx = local_player->jx, jy = local_player->jy;
9940
9941       /* play the element sound at the position nearest to the player */
9942       if ((element == EL_MAGIC_WALL_FULL ||
9943            element == EL_MAGIC_WALL_ACTIVE ||
9944            element == EL_MAGIC_WALL_EMPTYING ||
9945            element == EL_BD_MAGIC_WALL_FULL ||
9946            element == EL_BD_MAGIC_WALL_ACTIVE ||
9947            element == EL_BD_MAGIC_WALL_EMPTYING) &&
9948           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
9949       {
9950         magic_wall_x = x;
9951         magic_wall_y = y;
9952       }
9953     }
9954   }
9955
9956 #if USE_NEW_AMOEBA_CODE
9957   /* new experimental amoeba growth stuff */
9958   if (!(FrameCounter % 8))
9959   {
9960     static unsigned long random = 1684108901;
9961
9962     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
9963     {
9964       x = RND(lev_fieldx);
9965       y = RND(lev_fieldy);
9966       element = Feld[x][y];
9967
9968       if (!IS_PLAYER(x,y) &&
9969           (element == EL_EMPTY ||
9970            CAN_GROW_INTO(element) ||
9971            element == EL_QUICKSAND_EMPTY ||
9972            element == EL_ACID_SPLASH_LEFT ||
9973            element == EL_ACID_SPLASH_RIGHT))
9974       {
9975         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
9976             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
9977             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
9978             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
9979           Feld[x][y] = EL_AMOEBA_DROP;
9980       }
9981
9982       random = random * 129 + 1;
9983     }
9984   }
9985 #endif
9986
9987 #if 0
9988   if (game.explosions_delayed)
9989 #endif
9990   {
9991     game.explosions_delayed = FALSE;
9992
9993 #if 1
9994     SCAN_PLAYFIELD(x, y)
9995 #else
9996     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
9997 #endif
9998     {
9999       element = Feld[x][y];
10000
10001       if (ExplodeField[x][y])
10002         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
10003       else if (element == EL_EXPLOSION)
10004         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
10005
10006       ExplodeField[x][y] = EX_TYPE_NONE;
10007     }
10008
10009     game.explosions_delayed = TRUE;
10010   }
10011
10012   if (game.magic_wall_active)
10013   {
10014     if (!(game.magic_wall_time_left % 4))
10015     {
10016       int element = Feld[magic_wall_x][magic_wall_y];
10017
10018       if (element == EL_BD_MAGIC_WALL_FULL ||
10019           element == EL_BD_MAGIC_WALL_ACTIVE ||
10020           element == EL_BD_MAGIC_WALL_EMPTYING)
10021         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
10022       else
10023         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
10024     }
10025
10026     if (game.magic_wall_time_left > 0)
10027     {
10028       game.magic_wall_time_left--;
10029       if (!game.magic_wall_time_left)
10030       {
10031 #if 1
10032         SCAN_PLAYFIELD(x, y)
10033 #else
10034         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
10035 #endif
10036         {
10037           element = Feld[x][y];
10038
10039           if (element == EL_MAGIC_WALL_ACTIVE ||
10040               element == EL_MAGIC_WALL_FULL)
10041           {
10042             Feld[x][y] = EL_MAGIC_WALL_DEAD;
10043             DrawLevelField(x, y);
10044           }
10045           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
10046                    element == EL_BD_MAGIC_WALL_FULL)
10047           {
10048             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
10049             DrawLevelField(x, y);
10050           }
10051         }
10052
10053         game.magic_wall_active = FALSE;
10054       }
10055     }
10056   }
10057
10058   if (game.light_time_left > 0)
10059   {
10060     game.light_time_left--;
10061
10062     if (game.light_time_left == 0)
10063       RedrawAllLightSwitchesAndInvisibleElements();
10064   }
10065
10066   if (game.timegate_time_left > 0)
10067   {
10068     game.timegate_time_left--;
10069
10070     if (game.timegate_time_left == 0)
10071       CloseAllOpenTimegates();
10072   }
10073
10074   if (game.lenses_time_left > 0)
10075   {
10076     game.lenses_time_left--;
10077
10078     if (game.lenses_time_left == 0)
10079       RedrawAllInvisibleElementsForLenses();
10080   }
10081
10082   if (game.magnify_time_left > 0)
10083   {
10084     game.magnify_time_left--;
10085
10086     if (game.magnify_time_left == 0)
10087       RedrawAllInvisibleElementsForMagnifier();
10088   }
10089
10090   for (i = 0; i < MAX_PLAYERS; i++)
10091   {
10092     struct PlayerInfo *player = &stored_player[i];
10093
10094     if (SHIELD_ON(player))
10095     {
10096       if (player->shield_deadly_time_left)
10097         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
10098       else if (player->shield_normal_time_left)
10099         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
10100     }
10101   }
10102
10103   CheckLevelTime();
10104
10105   DrawAllPlayers();
10106   PlayAllPlayersSound();
10107
10108   if (options.debug)                    /* calculate frames per second */
10109   {
10110     static unsigned long fps_counter = 0;
10111     static int fps_frames = 0;
10112     unsigned long fps_delay_ms = Counter() - fps_counter;
10113
10114     fps_frames++;
10115
10116     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
10117     {
10118       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
10119
10120       fps_frames = 0;
10121       fps_counter = Counter();
10122     }
10123
10124     redraw_mask |= REDRAW_FPS;
10125   }
10126
10127   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
10128
10129   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
10130   {
10131     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
10132
10133     local_player->show_envelope = 0;
10134   }
10135
10136   /* use random number generator in every frame to make it less predictable */
10137   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10138     RND(1);
10139 }
10140
10141 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
10142 {
10143   int min_x = x, min_y = y, max_x = x, max_y = y;
10144   int i;
10145
10146   for (i = 0; i < MAX_PLAYERS; i++)
10147   {
10148     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10149
10150     if (!stored_player[i].active || &stored_player[i] == player)
10151       continue;
10152
10153     min_x = MIN(min_x, jx);
10154     min_y = MIN(min_y, jy);
10155     max_x = MAX(max_x, jx);
10156     max_y = MAX(max_y, jy);
10157   }
10158
10159   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
10160 }
10161
10162 static boolean AllPlayersInVisibleScreen()
10163 {
10164   int i;
10165
10166   for (i = 0; i < MAX_PLAYERS; i++)
10167   {
10168     int jx = stored_player[i].jx, jy = stored_player[i].jy;
10169
10170     if (!stored_player[i].active)
10171       continue;
10172
10173     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10174       return FALSE;
10175   }
10176
10177   return TRUE;
10178 }
10179
10180 void ScrollLevel(int dx, int dy)
10181 {
10182   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
10183   int x, y;
10184
10185   BlitBitmap(drawto_field, drawto_field,
10186              FX + TILEX * (dx == -1) - softscroll_offset,
10187              FY + TILEY * (dy == -1) - softscroll_offset,
10188              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
10189              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
10190              FX + TILEX * (dx == 1) - softscroll_offset,
10191              FY + TILEY * (dy == 1) - softscroll_offset);
10192
10193   if (dx)
10194   {
10195     x = (dx == 1 ? BX1 : BX2);
10196     for (y = BY1; y <= BY2; y++)
10197       DrawScreenField(x, y);
10198   }
10199
10200   if (dy)
10201   {
10202     y = (dy == 1 ? BY1 : BY2);
10203     for (x = BX1; x <= BX2; x++)
10204       DrawScreenField(x, y);
10205   }
10206
10207   redraw_mask |= REDRAW_FIELD;
10208 }
10209
10210 static boolean canFallDown(struct PlayerInfo *player)
10211 {
10212   int jx = player->jx, jy = player->jy;
10213
10214   return (IN_LEV_FIELD(jx, jy + 1) &&
10215           (IS_FREE(jx, jy + 1) ||
10216            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
10217           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
10218           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
10219 }
10220
10221 static boolean canPassField(int x, int y, int move_dir)
10222 {
10223   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10224   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10225   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10226   int nextx = x + dx;
10227   int nexty = y + dy;
10228   int element = Feld[x][y];
10229
10230   return (IS_PASSABLE_FROM(element, opposite_dir) &&
10231           !CAN_MOVE(element) &&
10232           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
10233           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
10234           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
10235 }
10236
10237 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
10238 {
10239   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
10240   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
10241   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
10242   int newx = x + dx;
10243   int newy = y + dy;
10244
10245   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
10246           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
10247           (IS_DIGGABLE(Feld[newx][newy]) ||
10248            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
10249            canPassField(newx, newy, move_dir)));
10250 }
10251
10252 static void CheckGravityMovement(struct PlayerInfo *player)
10253 {
10254   if (game.gravity && !player->programmed_action)
10255   {
10256     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
10257     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
10258     boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
10259     int jx = player->jx, jy = player->jy;
10260     boolean player_is_moving_to_valid_field =
10261       (!player_is_snapping &&
10262        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
10263         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
10264     boolean player_can_fall_down = canFallDown(player);
10265
10266     if (player_can_fall_down &&
10267         !player_is_moving_to_valid_field)
10268       player->programmed_action = MV_DOWN;
10269   }
10270 }
10271
10272 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
10273 {
10274   return CheckGravityMovement(player);
10275
10276   if (game.gravity && !player->programmed_action)
10277   {
10278     int jx = player->jx, jy = player->jy;
10279     boolean field_under_player_is_free =
10280       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
10281     boolean player_is_standing_on_valid_field =
10282       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
10283        (IS_WALKABLE(Feld[jx][jy]) &&
10284         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
10285
10286     if (field_under_player_is_free && !player_is_standing_on_valid_field)
10287       player->programmed_action = MV_DOWN;
10288   }
10289 }
10290
10291 /*
10292   MovePlayerOneStep()
10293   -----------------------------------------------------------------------------
10294   dx, dy:               direction (non-diagonal) to try to move the player to
10295   real_dx, real_dy:     direction as read from input device (can be diagonal)
10296 */
10297
10298 boolean MovePlayerOneStep(struct PlayerInfo *player,
10299                           int dx, int dy, int real_dx, int real_dy)
10300 {
10301   int jx = player->jx, jy = player->jy;
10302   int new_jx = jx + dx, new_jy = jy + dy;
10303 #if !USE_FIXED_DONT_RUN_INTO
10304   int element;
10305 #endif
10306   int can_move;
10307   boolean player_can_move = !player->cannot_move;
10308
10309   if (!player->active || (!dx && !dy))
10310     return MP_NO_ACTION;
10311
10312   player->MovDir = (dx < 0 ? MV_LEFT :
10313                     dx > 0 ? MV_RIGHT :
10314                     dy < 0 ? MV_UP :
10315                     dy > 0 ? MV_DOWN :  MV_NONE);
10316
10317   if (!IN_LEV_FIELD(new_jx, new_jy))
10318     return MP_NO_ACTION;
10319
10320   if (!player_can_move)
10321   {
10322 #if 1
10323     if (player->MovPos == 0)
10324     {
10325       player->is_moving = FALSE;
10326       player->is_digging = FALSE;
10327       player->is_collecting = FALSE;
10328       player->is_snapping = FALSE;
10329       player->is_pushing = FALSE;
10330     }
10331 #else
10332     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10333     SnapField(player, 0, 0);
10334 #endif
10335
10336 #if 0
10337     return MP_NO_ACTION;
10338 #endif
10339   }
10340
10341 #if 1
10342   if (!options.network && game.centered_player_nr == -1 &&
10343       !AllPlayersInSight(player, new_jx, new_jy))
10344     return MP_NO_ACTION;
10345 #else
10346   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
10347     return MP_NO_ACTION;
10348 #endif
10349
10350 #if !USE_FIXED_DONT_RUN_INTO
10351   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
10352
10353   /* (moved to DigField()) */
10354   if (player_can_move && DONT_RUN_INTO(element))
10355   {
10356     if (element == EL_ACID && dx == 0 && dy == 1)
10357     {
10358       SplashAcid(new_jx, new_jy);
10359       Feld[jx][jy] = EL_PLAYER_1;
10360       InitMovingField(jx, jy, MV_DOWN);
10361       Store[jx][jy] = EL_ACID;
10362       ContinueMoving(jx, jy);
10363       BuryPlayer(player);
10364     }
10365     else
10366       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
10367
10368     return MP_MOVING;
10369   }
10370 #endif
10371
10372   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
10373 #if 0
10374 #if USE_FIXED_DONT_RUN_INTO
10375   if (can_move == MP_DONT_RUN_INTO)
10376     return MP_MOVING;
10377 #endif
10378 #endif
10379   if (can_move != MP_MOVING)
10380     return can_move;
10381
10382 #if USE_FIXED_DONT_RUN_INTO
10383 #endif
10384
10385   /* check if DigField() has caused relocation of the player */
10386   if (player->jx != jx || player->jy != jy)
10387     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
10388
10389   StorePlayer[jx][jy] = 0;
10390   player->last_jx = jx;
10391   player->last_jy = jy;
10392   player->jx = new_jx;
10393   player->jy = new_jy;
10394   StorePlayer[new_jx][new_jy] = player->element_nr;
10395
10396   if (player->move_delay_value_next != -1)
10397   {
10398     player->move_delay_value = player->move_delay_value_next;
10399     player->move_delay_value_next = -1;
10400   }
10401
10402   player->MovPos =
10403     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
10404
10405   player->step_counter++;
10406
10407   PlayerVisit[jx][jy] = FrameCounter;
10408
10409   ScrollPlayer(player, SCROLL_INIT);
10410
10411   return MP_MOVING;
10412 }
10413
10414 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
10415 {
10416   int jx = player->jx, jy = player->jy;
10417   int old_jx = jx, old_jy = jy;
10418   int moved = MP_NO_ACTION;
10419
10420   if (!player->active)
10421     return FALSE;
10422
10423   if (!dx && !dy)
10424   {
10425     if (player->MovPos == 0)
10426     {
10427       player->is_moving = FALSE;
10428       player->is_digging = FALSE;
10429       player->is_collecting = FALSE;
10430       player->is_snapping = FALSE;
10431       player->is_pushing = FALSE;
10432     }
10433
10434     return FALSE;
10435   }
10436
10437   if (player->move_delay > 0)
10438     return FALSE;
10439
10440   player->move_delay = -1;              /* set to "uninitialized" value */
10441
10442   /* store if player is automatically moved to next field */
10443   player->is_auto_moving = (player->programmed_action != MV_NONE);
10444
10445   /* remove the last programmed player action */
10446   player->programmed_action = 0;
10447
10448   if (player->MovPos)
10449   {
10450     /* should only happen if pre-1.2 tape recordings are played */
10451     /* this is only for backward compatibility */
10452
10453     int original_move_delay_value = player->move_delay_value;
10454
10455 #if DEBUG
10456     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
10457            tape.counter);
10458 #endif
10459
10460     /* scroll remaining steps with finest movement resolution */
10461     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
10462
10463     while (player->MovPos)
10464     {
10465       ScrollPlayer(player, SCROLL_GO_ON);
10466       ScrollScreen(NULL, SCROLL_GO_ON);
10467
10468       AdvanceFrameAndPlayerCounters(player->index_nr);
10469
10470       DrawAllPlayers();
10471       BackToFront();
10472     }
10473
10474     player->move_delay_value = original_move_delay_value;
10475   }
10476
10477   if (player->last_move_dir & MV_HORIZONTAL)
10478   {
10479     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
10480       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
10481   }
10482   else
10483   {
10484     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
10485       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
10486   }
10487
10488   jx = player->jx;
10489   jy = player->jy;
10490
10491 #if 1
10492   if (moved & MP_MOVING && !ScreenMovPos &&
10493       (player->index_nr == game.centered_player_nr ||
10494        game.centered_player_nr == -1))
10495 #else
10496   if (moved & MP_MOVING && !ScreenMovPos &&
10497       (player == local_player || !options.network))
10498 #endif
10499   {
10500     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
10501     int offset = (setup.scroll_delay ? 3 : 0);
10502
10503     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
10504     {
10505       /* actual player has left the screen -- scroll in that direction */
10506       if (jx != old_jx)         /* player has moved horizontally */
10507         scroll_x += (jx - old_jx);
10508       else                      /* player has moved vertically */
10509         scroll_y += (jy - old_jy);
10510     }
10511     else
10512     {
10513       if (jx != old_jx)         /* player has moved horizontally */
10514       {
10515         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
10516             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
10517           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
10518
10519         /* don't scroll over playfield boundaries */
10520         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
10521           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
10522
10523         /* don't scroll more than one field at a time */
10524         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
10525
10526         /* don't scroll against the player's moving direction */
10527         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
10528             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
10529           scroll_x = old_scroll_x;
10530       }
10531       else                      /* player has moved vertically */
10532       {
10533         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
10534             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
10535           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
10536
10537         /* don't scroll over playfield boundaries */
10538         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
10539           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
10540
10541         /* don't scroll more than one field at a time */
10542         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
10543
10544         /* don't scroll against the player's moving direction */
10545         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
10546             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
10547           scroll_y = old_scroll_y;
10548       }
10549     }
10550
10551     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
10552     {
10553 #if 1
10554       if (!options.network && game.centered_player_nr == -1 &&
10555           !AllPlayersInVisibleScreen())
10556       {
10557         scroll_x = old_scroll_x;
10558         scroll_y = old_scroll_y;
10559       }
10560       else
10561 #else
10562       if (!options.network && !AllPlayersInVisibleScreen())
10563       {
10564         scroll_x = old_scroll_x;
10565         scroll_y = old_scroll_y;
10566       }
10567       else
10568 #endif
10569       {
10570         ScrollScreen(player, SCROLL_INIT);
10571         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
10572       }
10573     }
10574   }
10575
10576   player->StepFrame = 0;
10577
10578   if (moved & MP_MOVING)
10579   {
10580     if (old_jx != jx && old_jy == jy)
10581       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
10582     else if (old_jx == jx && old_jy != jy)
10583       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
10584
10585     DrawLevelField(jx, jy);     /* for "crumbled sand" */
10586
10587     player->last_move_dir = player->MovDir;
10588     player->is_moving = TRUE;
10589     player->is_snapping = FALSE;
10590     player->is_switching = FALSE;
10591     player->is_dropping = FALSE;
10592     player->is_dropping_pressed = FALSE;
10593     player->drop_pressed_delay = 0;
10594   }
10595   else
10596   {
10597     CheckGravityMovementWhenNotMoving(player);
10598
10599     player->is_moving = FALSE;
10600
10601     /* at this point, the player is allowed to move, but cannot move right now
10602        (e.g. because of something blocking the way) -- ensure that the player
10603        is also allowed to move in the next frame (in old versions before 3.1.1,
10604        the player was forced to wait again for eight frames before next try) */
10605
10606     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
10607       player->move_delay = 0;   /* allow direct movement in the next frame */
10608   }
10609
10610   if (player->move_delay == -1)         /* not yet initialized by DigField() */
10611     player->move_delay = player->move_delay_value;
10612
10613   if (game.engine_version < VERSION_IDENT(3,0,7,0))
10614   {
10615     TestIfPlayerTouchesBadThing(jx, jy);
10616     TestIfPlayerTouchesCustomElement(jx, jy);
10617   }
10618
10619   if (!player->active)
10620     RemovePlayer(player);
10621
10622   return moved;
10623 }
10624
10625 void ScrollPlayer(struct PlayerInfo *player, int mode)
10626 {
10627   int jx = player->jx, jy = player->jy;
10628   int last_jx = player->last_jx, last_jy = player->last_jy;
10629   int move_stepsize = TILEX / player->move_delay_value;
10630
10631 #if USE_NEW_PLAYER_SPEED
10632   if (!player->active)
10633     return;
10634
10635   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
10636     return;
10637 #else
10638   if (!player->active || player->MovPos == 0)
10639     return;
10640 #endif
10641
10642   if (mode == SCROLL_INIT)
10643   {
10644     player->actual_frame_counter = FrameCounter;
10645     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10646
10647     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
10648         Feld[last_jx][last_jy] == EL_EMPTY)
10649     {
10650       int last_field_block_delay = 0;   /* start with no blocking at all */
10651       int block_delay_adjustment = player->block_delay_adjustment;
10652
10653       /* if player blocks last field, add delay for exactly one move */
10654       if (player->block_last_field)
10655       {
10656         last_field_block_delay += player->move_delay_value;
10657
10658         /* when blocking enabled, prevent moving up despite gravity */
10659         if (game.gravity && player->MovDir == MV_UP)
10660           block_delay_adjustment = -1;
10661       }
10662
10663       /* add block delay adjustment (also possible when not blocking) */
10664       last_field_block_delay += block_delay_adjustment;
10665
10666       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
10667       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
10668     }
10669
10670 #if USE_NEW_PLAYER_SPEED
10671     if (player->MovPos != 0)    /* player has not yet reached destination */
10672       return;
10673 #else
10674     return;
10675 #endif
10676   }
10677   else if (!FrameReached(&player->actual_frame_counter, 1))
10678     return;
10679
10680 #if 0
10681   printf("::: player->MovPos: %d -> %d\n",
10682          player->MovPos,
10683          player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
10684 #endif
10685
10686 #if USE_NEW_PLAYER_SPEED
10687   if (player->MovPos != 0)
10688   {
10689     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10690     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10691
10692     /* before DrawPlayer() to draw correct player graphic for this case */
10693     if (player->MovPos == 0)
10694       CheckGravityMovement(player);
10695   }
10696 #else
10697   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
10698   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
10699
10700   /* before DrawPlayer() to draw correct player graphic for this case */
10701   if (player->MovPos == 0)
10702     CheckGravityMovement(player);
10703 #endif
10704
10705   if (player->MovPos == 0)      /* player reached destination field */
10706   {
10707 #if 0
10708     printf("::: player reached destination field\n");
10709 #endif
10710
10711     if (player->move_delay_reset_counter > 0)
10712     {
10713       player->move_delay_reset_counter--;
10714
10715       if (player->move_delay_reset_counter == 0)
10716       {
10717         /* continue with normal speed after quickly moving through gate */
10718         HALVE_PLAYER_SPEED(player);
10719
10720         /* be able to make the next move without delay */
10721         player->move_delay = 0;
10722       }
10723     }
10724
10725     player->last_jx = jx;
10726     player->last_jy = jy;
10727
10728     if (Feld[jx][jy] == EL_EXIT_OPEN ||
10729         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
10730         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
10731     {
10732       DrawPlayer(player);       /* needed here only to cleanup last field */
10733       RemovePlayer(player);
10734
10735       if (local_player->friends_still_needed == 0 ||
10736           IS_SP_ELEMENT(Feld[jx][jy]))
10737         player->LevelSolved = player->GameOver = TRUE;
10738     }
10739
10740     /* this breaks one level: "machine", level 000 */
10741     {
10742       int move_direction = player->MovDir;
10743       int enter_side = MV_DIR_OPPOSITE(move_direction);
10744       int leave_side = move_direction;
10745       int old_jx = last_jx;
10746       int old_jy = last_jy;
10747       int old_element = Feld[old_jx][old_jy];
10748       int new_element = Feld[jx][jy];
10749
10750       if (IS_CUSTOM_ELEMENT(old_element))
10751         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
10752                                    CE_LEFT_BY_PLAYER,
10753                                    player->index_bit, leave_side);
10754
10755       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
10756                                           CE_PLAYER_LEAVES_X,
10757                                           player->index_bit, leave_side);
10758
10759       if (IS_CUSTOM_ELEMENT(new_element))
10760         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
10761                                    player->index_bit, enter_side);
10762
10763       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
10764                                           CE_PLAYER_ENTERS_X,
10765                                           player->index_bit, enter_side);
10766
10767       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
10768                                         CE_MOVE_OF_X, move_direction);
10769     }
10770
10771     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10772     {
10773       TestIfPlayerTouchesBadThing(jx, jy);
10774       TestIfPlayerTouchesCustomElement(jx, jy);
10775
10776       /* needed because pushed element has not yet reached its destination,
10777          so it would trigger a change event at its previous field location */
10778       if (!player->is_pushing)
10779         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
10780
10781       if (!player->active)
10782         RemovePlayer(player);
10783     }
10784
10785     if (level.use_step_counter)
10786     {
10787       int i;
10788
10789       TimePlayed++;
10790
10791       if (TimeLeft > 0)
10792       {
10793         TimeLeft--;
10794
10795         if (TimeLeft <= 10 && setup.time_limit)
10796           PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
10797
10798         DrawGameValue_Time(TimeLeft);
10799
10800         if (!TimeLeft && setup.time_limit)
10801           for (i = 0; i < MAX_PLAYERS; i++)
10802             KillPlayer(&stored_player[i]);
10803       }
10804       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10805         DrawGameValue_Time(TimePlayed);
10806     }
10807
10808     if (tape.single_step && tape.recording && !tape.pausing &&
10809         !player->programmed_action)
10810       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10811   }
10812 }
10813
10814 void ScrollScreen(struct PlayerInfo *player, int mode)
10815 {
10816   static unsigned long screen_frame_counter = 0;
10817
10818   if (mode == SCROLL_INIT)
10819   {
10820     /* set scrolling step size according to actual player's moving speed */
10821     ScrollStepSize = TILEX / player->move_delay_value;
10822
10823     screen_frame_counter = FrameCounter;
10824     ScreenMovDir = player->MovDir;
10825     ScreenMovPos = player->MovPos;
10826     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10827     return;
10828   }
10829   else if (!FrameReached(&screen_frame_counter, 1))
10830     return;
10831
10832   if (ScreenMovPos)
10833   {
10834     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
10835     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
10836     redraw_mask |= REDRAW_FIELD;
10837   }
10838   else
10839     ScreenMovDir = MV_NONE;
10840 }
10841
10842 void TestIfPlayerTouchesCustomElement(int x, int y)
10843 {
10844   static int xy[4][2] =
10845   {
10846     { 0, -1 },
10847     { -1, 0 },
10848     { +1, 0 },
10849     { 0, +1 }
10850   };
10851   static int trigger_sides[4][2] =
10852   {
10853     /* center side       border side */
10854     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10855     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10856     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10857     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10858   };
10859   static int touch_dir[4] =
10860   {
10861     MV_LEFT | MV_RIGHT,
10862     MV_UP   | MV_DOWN,
10863     MV_UP   | MV_DOWN,
10864     MV_LEFT | MV_RIGHT
10865   };
10866   int center_element = Feld[x][y];      /* should always be non-moving! */
10867   int i;
10868
10869   for (i = 0; i < NUM_DIRECTIONS; i++)
10870   {
10871     int xx = x + xy[i][0];
10872     int yy = y + xy[i][1];
10873     int center_side = trigger_sides[i][0];
10874     int border_side = trigger_sides[i][1];
10875     int border_element;
10876
10877     if (!IN_LEV_FIELD(xx, yy))
10878       continue;
10879
10880     if (IS_PLAYER(x, y))
10881     {
10882       struct PlayerInfo *player = PLAYERINFO(x, y);
10883
10884       if (game.engine_version < VERSION_IDENT(3,0,7,0))
10885         border_element = Feld[xx][yy];          /* may be moving! */
10886       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10887         border_element = Feld[xx][yy];
10888       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
10889         border_element = MovingOrBlocked2Element(xx, yy);
10890       else
10891         continue;               /* center and border element do not touch */
10892
10893       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
10894                                  player->index_bit, border_side);
10895       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
10896                                           CE_PLAYER_TOUCHES_X,
10897                                           player->index_bit, border_side);
10898     }
10899     else if (IS_PLAYER(xx, yy))
10900     {
10901       struct PlayerInfo *player = PLAYERINFO(xx, yy);
10902
10903       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
10904       {
10905         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
10906           continue;             /* center and border element do not touch */
10907       }
10908
10909       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
10910                                  player->index_bit, center_side);
10911       CheckTriggeredElementChangeByPlayer(x, y, center_element,
10912                                           CE_PLAYER_TOUCHES_X,
10913                                           player->index_bit, center_side);
10914       break;
10915     }
10916   }
10917 }
10918
10919 #if USE_ELEMENT_TOUCHING_BUGFIX
10920
10921 void TestIfElementTouchesCustomElement(int x, int y)
10922 {
10923   static int xy[4][2] =
10924   {
10925     { 0, -1 },
10926     { -1, 0 },
10927     { +1, 0 },
10928     { 0, +1 }
10929   };
10930   static int trigger_sides[4][2] =
10931   {
10932     /* center side      border side */
10933     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
10934     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
10935     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
10936     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
10937   };
10938   static int touch_dir[4] =
10939   {
10940     MV_LEFT | MV_RIGHT,
10941     MV_UP   | MV_DOWN,
10942     MV_UP   | MV_DOWN,
10943     MV_LEFT | MV_RIGHT
10944   };
10945   boolean change_center_element = FALSE;
10946   int center_element = Feld[x][y];      /* should always be non-moving! */
10947   int border_element_old[NUM_DIRECTIONS];
10948   int i;
10949
10950   for (i = 0; i < NUM_DIRECTIONS; i++)
10951   {
10952     int xx = x + xy[i][0];
10953     int yy = y + xy[i][1];
10954     int border_element;
10955
10956     border_element_old[i] = -1;
10957
10958     if (!IN_LEV_FIELD(xx, yy))
10959       continue;
10960
10961     if (game.engine_version < VERSION_IDENT(3,0,7,0))
10962       border_element = Feld[xx][yy];    /* may be moving! */
10963     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
10964       border_element = Feld[xx][yy];
10965     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
10966       border_element = MovingOrBlocked2Element(xx, yy);
10967     else
10968       continue;                 /* center and border element do not touch */
10969
10970     border_element_old[i] = border_element;
10971   }
10972
10973   for (i = 0; i < NUM_DIRECTIONS; i++)
10974   {
10975     int xx = x + xy[i][0];
10976     int yy = y + xy[i][1];
10977     int center_side = trigger_sides[i][0];
10978     int border_element = border_element_old[i];
10979
10980     if (border_element == -1)
10981       continue;
10982
10983     /* check for change of border element */
10984     CheckElementChangeBySide(xx, yy, border_element, center_element,
10985                              CE_TOUCHING_X, center_side);
10986   }
10987
10988   for (i = 0; i < NUM_DIRECTIONS; i++)
10989   {
10990     int border_side = trigger_sides[i][1];
10991     int border_element = border_element_old[i];
10992
10993     if (border_element == -1)
10994       continue;
10995
10996     /* check for change of center element (but change it only once) */
10997     if (!change_center_element)
10998       change_center_element =
10999         CheckElementChangeBySide(x, y, center_element, border_element,
11000                                  CE_TOUCHING_X, border_side);
11001   }
11002 }
11003
11004 #else
11005
11006 void TestIfElementTouchesCustomElement_OLD(int x, int y)
11007 {
11008   static int xy[4][2] =
11009   {
11010     { 0, -1 },
11011     { -1, 0 },
11012     { +1, 0 },
11013     { 0, +1 }
11014   };
11015   static int trigger_sides[4][2] =
11016   {
11017     /* center side      border side */
11018     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
11019     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
11020     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
11021     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
11022   };
11023   static int touch_dir[4] =
11024   {
11025     MV_LEFT | MV_RIGHT,
11026     MV_UP   | MV_DOWN,
11027     MV_UP   | MV_DOWN,
11028     MV_LEFT | MV_RIGHT
11029   };
11030   boolean change_center_element = FALSE;
11031   int center_element = Feld[x][y];      /* should always be non-moving! */
11032   int i;
11033
11034   for (i = 0; i < NUM_DIRECTIONS; i++)
11035   {
11036     int xx = x + xy[i][0];
11037     int yy = y + xy[i][1];
11038     int center_side = trigger_sides[i][0];
11039     int border_side = trigger_sides[i][1];
11040     int border_element;
11041
11042     if (!IN_LEV_FIELD(xx, yy))
11043       continue;
11044
11045     if (game.engine_version < VERSION_IDENT(3,0,7,0))
11046       border_element = Feld[xx][yy];    /* may be moving! */
11047     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
11048       border_element = Feld[xx][yy];
11049     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
11050       border_element = MovingOrBlocked2Element(xx, yy);
11051     else
11052       continue;                 /* center and border element do not touch */
11053
11054     /* check for change of center element (but change it only once) */
11055     if (!change_center_element)
11056       change_center_element =
11057         CheckElementChangeBySide(x, y, center_element, border_element,
11058                                  CE_TOUCHING_X, border_side);
11059
11060     /* check for change of border element */
11061     CheckElementChangeBySide(xx, yy, border_element, center_element,
11062                              CE_TOUCHING_X, center_side);
11063   }
11064 }
11065
11066 #endif
11067
11068 void TestIfElementHitsCustomElement(int x, int y, int direction)
11069 {
11070   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11071   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11072   int hitx = x + dx, hity = y + dy;
11073   int hitting_element = Feld[x][y];
11074   int touched_element;
11075
11076   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11077     return;
11078
11079   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11080                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11081
11082   if (IN_LEV_FIELD(hitx, hity))
11083   {
11084     int opposite_direction = MV_DIR_OPPOSITE(direction);
11085     int hitting_side = direction;
11086     int touched_side = opposite_direction;
11087     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11088                           MovDir[hitx][hity] != direction ||
11089                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11090
11091     object_hit = TRUE;
11092
11093     if (object_hit)
11094     {
11095       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11096                                CE_HITTING_X, touched_side);
11097
11098       CheckElementChangeBySide(hitx, hity, touched_element,
11099                                hitting_element, CE_HIT_BY_X, hitting_side);
11100
11101       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11102                                CE_HIT_BY_SOMETHING, opposite_direction);
11103     }
11104   }
11105
11106   /* "hitting something" is also true when hitting the playfield border */
11107   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11108                            CE_HITTING_SOMETHING, direction);
11109 }
11110
11111 #if 0
11112 void TestIfElementSmashesCustomElement(int x, int y, int direction)
11113 {
11114   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
11115   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
11116   int hitx = x + dx, hity = y + dy;
11117   int hitting_element = Feld[x][y];
11118   int touched_element;
11119 #if 0
11120   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
11121                         !IS_FREE(hitx, hity) &&
11122                         (!IS_MOVING(hitx, hity) ||
11123                          MovDir[hitx][hity] != direction ||
11124                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
11125 #endif
11126
11127   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
11128     return;
11129
11130 #if 0
11131   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
11132     return;
11133 #endif
11134
11135   touched_element = (IN_LEV_FIELD(hitx, hity) ?
11136                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
11137
11138   CheckElementChangeBySide(x, y, hitting_element, touched_element,
11139                            EP_CAN_SMASH_EVERYTHING, direction);
11140
11141   if (IN_LEV_FIELD(hitx, hity))
11142   {
11143     int opposite_direction = MV_DIR_OPPOSITE(direction);
11144     int hitting_side = direction;
11145     int touched_side = opposite_direction;
11146 #if 0
11147     int touched_element = MovingOrBlocked2Element(hitx, hity);
11148 #endif
11149 #if 1
11150     boolean object_hit = (!IS_MOVING(hitx, hity) ||
11151                           MovDir[hitx][hity] != direction ||
11152                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
11153
11154     object_hit = TRUE;
11155 #endif
11156
11157     if (object_hit)
11158     {
11159       int i;
11160
11161       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11162                                CE_SMASHED_BY_SOMETHING, opposite_direction);
11163
11164       CheckElementChangeBySide(x, y, hitting_element, touched_element,
11165                                CE_OTHER_IS_SMASHING, touched_side);
11166
11167       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
11168                                CE_OTHER_GETS_SMASHED, hitting_side);
11169     }
11170   }
11171 }
11172 #endif
11173
11174 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
11175 {
11176   int i, kill_x = -1, kill_y = -1;
11177
11178   int bad_element = -1;
11179   static int test_xy[4][2] =
11180   {
11181     { 0, -1 },
11182     { -1, 0 },
11183     { +1, 0 },
11184     { 0, +1 }
11185   };
11186   static int test_dir[4] =
11187   {
11188     MV_UP,
11189     MV_LEFT,
11190     MV_RIGHT,
11191     MV_DOWN
11192   };
11193
11194   for (i = 0; i < NUM_DIRECTIONS; i++)
11195   {
11196     int test_x, test_y, test_move_dir, test_element;
11197
11198     test_x = good_x + test_xy[i][0];
11199     test_y = good_y + test_xy[i][1];
11200
11201     if (!IN_LEV_FIELD(test_x, test_y))
11202       continue;
11203
11204     test_move_dir =
11205       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11206
11207     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
11208
11209     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11210        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11211     */
11212     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
11213         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
11214     {
11215       kill_x = test_x;
11216       kill_y = test_y;
11217       bad_element = test_element;
11218
11219       break;
11220     }
11221   }
11222
11223   if (kill_x != -1 || kill_y != -1)
11224   {
11225     if (IS_PLAYER(good_x, good_y))
11226     {
11227       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
11228
11229       if (player->shield_deadly_time_left > 0 &&
11230           !IS_INDESTRUCTIBLE(bad_element))
11231         Bang(kill_x, kill_y);
11232       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
11233         KillPlayer(player);
11234     }
11235     else
11236       Bang(good_x, good_y);
11237   }
11238 }
11239
11240 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
11241 {
11242   int i, kill_x = -1, kill_y = -1;
11243   int bad_element = Feld[bad_x][bad_y];
11244   static int test_xy[4][2] =
11245   {
11246     { 0, -1 },
11247     { -1, 0 },
11248     { +1, 0 },
11249     { 0, +1 }
11250   };
11251   static int touch_dir[4] =
11252   {
11253     MV_LEFT | MV_RIGHT,
11254     MV_UP   | MV_DOWN,
11255     MV_UP   | MV_DOWN,
11256     MV_LEFT | MV_RIGHT
11257   };
11258   static int test_dir[4] =
11259   {
11260     MV_UP,
11261     MV_LEFT,
11262     MV_RIGHT,
11263     MV_DOWN
11264   };
11265
11266   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
11267     return;
11268
11269   for (i = 0; i < NUM_DIRECTIONS; i++)
11270   {
11271     int test_x, test_y, test_move_dir, test_element;
11272
11273     test_x = bad_x + test_xy[i][0];
11274     test_y = bad_y + test_xy[i][1];
11275     if (!IN_LEV_FIELD(test_x, test_y))
11276       continue;
11277
11278     test_move_dir =
11279       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
11280
11281     test_element = Feld[test_x][test_y];
11282
11283     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
11284        2nd case: DONT_TOUCH style bad thing does not move away from good thing
11285     */
11286     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
11287         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
11288     {
11289       /* good thing is player or penguin that does not move away */
11290       if (IS_PLAYER(test_x, test_y))
11291       {
11292         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
11293
11294         if (bad_element == EL_ROBOT && player->is_moving)
11295           continue;     /* robot does not kill player if he is moving */
11296
11297         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
11298         {
11299           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
11300             continue;           /* center and border element do not touch */
11301         }
11302
11303         kill_x = test_x;
11304         kill_y = test_y;
11305         break;
11306       }
11307       else if (test_element == EL_PENGUIN)
11308       {
11309         kill_x = test_x;
11310         kill_y = test_y;
11311         break;
11312       }
11313     }
11314   }
11315
11316   if (kill_x != -1 || kill_y != -1)
11317   {
11318     if (IS_PLAYER(kill_x, kill_y))
11319     {
11320       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
11321
11322       if (player->shield_deadly_time_left > 0 &&
11323           !IS_INDESTRUCTIBLE(bad_element))
11324         Bang(bad_x, bad_y);
11325       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
11326         KillPlayer(player);
11327     }
11328     else
11329       Bang(kill_x, kill_y);
11330   }
11331 }
11332
11333 void TestIfPlayerTouchesBadThing(int x, int y)
11334 {
11335   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11336 }
11337
11338 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
11339 {
11340   TestIfGoodThingHitsBadThing(x, y, move_dir);
11341 }
11342
11343 void TestIfBadThingTouchesPlayer(int x, int y)
11344 {
11345   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11346 }
11347
11348 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
11349 {
11350   TestIfBadThingHitsGoodThing(x, y, move_dir);
11351 }
11352
11353 void TestIfFriendTouchesBadThing(int x, int y)
11354 {
11355   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
11356 }
11357
11358 void TestIfBadThingTouchesFriend(int x, int y)
11359 {
11360   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
11361 }
11362
11363 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
11364 {
11365   int i, kill_x = bad_x, kill_y = bad_y;
11366   static int xy[4][2] =
11367   {
11368     { 0, -1 },
11369     { -1, 0 },
11370     { +1, 0 },
11371     { 0, +1 }
11372   };
11373
11374   for (i = 0; i < NUM_DIRECTIONS; i++)
11375   {
11376     int x, y, element;
11377
11378     x = bad_x + xy[i][0];
11379     y = bad_y + xy[i][1];
11380     if (!IN_LEV_FIELD(x, y))
11381       continue;
11382
11383     element = Feld[x][y];
11384     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
11385         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
11386     {
11387       kill_x = x;
11388       kill_y = y;
11389       break;
11390     }
11391   }
11392
11393   if (kill_x != bad_x || kill_y != bad_y)
11394     Bang(bad_x, bad_y);
11395 }
11396
11397 void KillPlayer(struct PlayerInfo *player)
11398 {
11399   int jx = player->jx, jy = player->jy;
11400
11401   if (!player->active)
11402     return;
11403
11404   /* remove accessible field at the player's position */
11405   Feld[jx][jy] = EL_EMPTY;
11406
11407   /* deactivate shield (else Bang()/Explode() would not work right) */
11408   player->shield_normal_time_left = 0;
11409   player->shield_deadly_time_left = 0;
11410
11411   Bang(jx, jy);
11412   BuryPlayer(player);
11413 }
11414
11415 static void KillPlayerUnlessEnemyProtected(int x, int y)
11416 {
11417   if (!PLAYER_ENEMY_PROTECTED(x, y))
11418     KillPlayer(PLAYERINFO(x, y));
11419 }
11420
11421 static void KillPlayerUnlessExplosionProtected(int x, int y)
11422 {
11423   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
11424     KillPlayer(PLAYERINFO(x, y));
11425 }
11426
11427 void BuryPlayer(struct PlayerInfo *player)
11428 {
11429   int jx = player->jx, jy = player->jy;
11430
11431   if (!player->active)
11432     return;
11433
11434   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
11435   PlayLevelSound(jx, jy, SND_GAME_LOSING);
11436
11437   player->GameOver = TRUE;
11438   RemovePlayer(player);
11439 }
11440
11441 void RemovePlayer(struct PlayerInfo *player)
11442 {
11443   int jx = player->jx, jy = player->jy;
11444   int i, found = FALSE;
11445
11446   player->present = FALSE;
11447   player->active = FALSE;
11448
11449   if (!ExplodeField[jx][jy])
11450     StorePlayer[jx][jy] = 0;
11451
11452   if (player->is_moving)
11453     DrawLevelField(player->last_jx, player->last_jy);
11454
11455   for (i = 0; i < MAX_PLAYERS; i++)
11456     if (stored_player[i].active)
11457       found = TRUE;
11458
11459   if (!found)
11460     AllPlayersGone = TRUE;
11461
11462   ExitX = ZX = jx;
11463   ExitY = ZY = jy;
11464 }
11465
11466 #if USE_NEW_SNAP_DELAY
11467 static void setFieldForSnapping(int x, int y, int element, int direction)
11468 {
11469   struct ElementInfo *ei = &element_info[element];
11470   int direction_bit = MV_DIR_TO_BIT(direction);
11471   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
11472   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
11473                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
11474
11475   Feld[x][y] = EL_ELEMENT_SNAPPING;
11476   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
11477
11478   ResetGfxAnimation(x, y);
11479
11480   GfxElement[x][y] = element;
11481   GfxAction[x][y] = action;
11482   GfxDir[x][y] = direction;
11483   GfxFrame[x][y] = -1;
11484 }
11485 #endif
11486
11487 /*
11488   =============================================================================
11489   checkDiagonalPushing()
11490   -----------------------------------------------------------------------------
11491   check if diagonal input device direction results in pushing of object
11492   (by checking if the alternative direction is walkable, diggable, ...)
11493   =============================================================================
11494 */
11495
11496 static boolean checkDiagonalPushing(struct PlayerInfo *player,
11497                                     int x, int y, int real_dx, int real_dy)
11498 {
11499   int jx, jy, dx, dy, xx, yy;
11500
11501   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
11502     return TRUE;
11503
11504   /* diagonal direction: check alternative direction */
11505   jx = player->jx;
11506   jy = player->jy;
11507   dx = x - jx;
11508   dy = y - jy;
11509   xx = jx + (dx == 0 ? real_dx : 0);
11510   yy = jy + (dy == 0 ? real_dy : 0);
11511
11512   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
11513 }
11514
11515 /*
11516   =============================================================================
11517   DigField()
11518   -----------------------------------------------------------------------------
11519   x, y:                 field next to player (non-diagonal) to try to dig to
11520   real_dx, real_dy:     direction as read from input device (can be diagonal)
11521   =============================================================================
11522 */
11523
11524 int DigField(struct PlayerInfo *player,
11525              int oldx, int oldy, int x, int y,
11526              int real_dx, int real_dy, int mode)
11527 {
11528   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
11529   boolean player_was_pushing = player->is_pushing;
11530   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
11531   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
11532   int jx = oldx, jy = oldy;
11533   int dx = x - jx, dy = y - jy;
11534   int nextx = x + dx, nexty = y + dy;
11535   int move_direction = (dx == -1 ? MV_LEFT  :
11536                         dx == +1 ? MV_RIGHT :
11537                         dy == -1 ? MV_UP    :
11538                         dy == +1 ? MV_DOWN  : MV_NONE);
11539   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
11540   int dig_side = MV_DIR_OPPOSITE(move_direction);
11541   int old_element = Feld[jx][jy];
11542 #if USE_FIXED_DONT_RUN_INTO
11543   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
11544 #else
11545   int element;
11546 #endif
11547   int collect_count;
11548
11549   if (is_player)                /* function can also be called by EL_PENGUIN */
11550   {
11551     if (player->MovPos == 0)
11552     {
11553       player->is_digging = FALSE;
11554       player->is_collecting = FALSE;
11555     }
11556
11557     if (player->MovPos == 0)    /* last pushing move finished */
11558       player->is_pushing = FALSE;
11559
11560     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
11561     {
11562       player->is_switching = FALSE;
11563       player->push_delay = -1;
11564
11565       return MP_NO_ACTION;
11566     }
11567   }
11568
11569 #if !USE_FIXED_DONT_RUN_INTO
11570   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11571     return MP_NO_ACTION;
11572 #endif
11573
11574   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
11575     old_element = Back[jx][jy];
11576
11577   /* in case of element dropped at player position, check background */
11578   else if (Back[jx][jy] != EL_EMPTY &&
11579            game.engine_version >= VERSION_IDENT(2,2,0,0))
11580     old_element = Back[jx][jy];
11581
11582 #if 0
11583 #if USE_FIXED_DONT_RUN_INTO
11584   if (player_can_move && DONT_RUN_INTO(element))
11585   {
11586     if (element == EL_ACID && dx == 0 && dy == 1)
11587     {
11588       SplashAcid(x, y);
11589       Feld[jx][jy] = EL_PLAYER_1;
11590       InitMovingField(jx, jy, MV_DOWN);
11591       Store[jx][jy] = EL_ACID;
11592       ContinueMoving(jx, jy);
11593       BuryPlayer(player);
11594     }
11595     else
11596       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11597
11598     return MP_DONT_RUN_INTO;
11599   }
11600 #endif
11601 #endif
11602
11603 #if 1
11604 #if USE_FIXED_DONT_RUN_INTO
11605   if (player_can_move && DONT_RUN_INTO(element))
11606   {
11607     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11608
11609     return MP_DONT_RUN_INTO;
11610   }
11611 #endif
11612 #endif
11613
11614   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
11615     return MP_NO_ACTION;        /* field has no opening in this direction */
11616
11617   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
11618     return MP_NO_ACTION;        /* field has no opening in this direction */
11619
11620 #if 1
11621 #if USE_FIXED_DONT_RUN_INTO
11622   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
11623   {
11624     SplashAcid(x, y);
11625     Feld[jx][jy] = EL_PLAYER_1;
11626     InitMovingField(jx, jy, MV_DOWN);
11627     Store[jx][jy] = EL_ACID;
11628     ContinueMoving(jx, jy);
11629     BuryPlayer(player);
11630
11631     return MP_DONT_RUN_INTO;
11632   }
11633 #endif
11634 #endif
11635
11636 #if 0
11637 #if USE_FIXED_DONT_RUN_INTO
11638   if (player_can_move && DONT_RUN_INTO(element))
11639   {
11640     if (element == EL_ACID && dx == 0 && dy == 1)
11641     {
11642       SplashAcid(x, y);
11643       Feld[jx][jy] = EL_PLAYER_1;
11644       InitMovingField(jx, jy, MV_DOWN);
11645       Store[jx][jy] = EL_ACID;
11646       ContinueMoving(jx, jy);
11647       BuryPlayer(player);
11648     }
11649     else
11650       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
11651
11652     return MP_DONT_RUN_INTO;
11653   }
11654 #endif
11655 #endif
11656
11657 #if USE_FIXED_DONT_RUN_INTO
11658   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
11659     return MP_NO_ACTION;
11660 #endif
11661
11662 #if !USE_FIXED_DONT_RUN_INTO
11663   element = Feld[x][y];
11664 #endif
11665
11666   collect_count = element_info[element].collect_count_initial;
11667
11668   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
11669     return MP_NO_ACTION;
11670
11671   if (game.engine_version < VERSION_IDENT(2,2,0,0))
11672     player_can_move = player_can_move_or_snap;
11673
11674   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
11675       game.engine_version >= VERSION_IDENT(2,2,0,0))
11676   {
11677     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
11678                                player->index_bit, dig_side);
11679     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11680                                         player->index_bit, dig_side);
11681
11682     if (Feld[x][y] != element)          /* field changed by snapping */
11683       return MP_ACTION;
11684
11685     return MP_NO_ACTION;
11686   }
11687
11688   if (game.gravity && is_player && !player->is_auto_moving &&
11689       canFallDown(player) && move_direction != MV_DOWN &&
11690       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
11691     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
11692
11693   if (player_can_move &&
11694       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
11695   {
11696     int sound_element = SND_ELEMENT(element);
11697     int sound_action = ACTION_WALKING;
11698
11699     if (IS_RND_GATE(element))
11700     {
11701       if (!player->key[RND_GATE_NR(element)])
11702         return MP_NO_ACTION;
11703     }
11704     else if (IS_RND_GATE_GRAY(element))
11705     {
11706       if (!player->key[RND_GATE_GRAY_NR(element)])
11707         return MP_NO_ACTION;
11708     }
11709     else if (IS_RND_GATE_GRAY_ACTIVE(element))
11710     {
11711       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
11712         return MP_NO_ACTION;
11713     }
11714     else if (element == EL_EXIT_OPEN ||
11715              element == EL_SP_EXIT_OPEN ||
11716              element == EL_SP_EXIT_OPENING)
11717     {
11718       sound_action = ACTION_PASSING;    /* player is passing exit */
11719     }
11720     else if (element == EL_EMPTY)
11721     {
11722       sound_action = ACTION_MOVING;             /* nothing to walk on */
11723     }
11724
11725     /* play sound from background or player, whatever is available */
11726     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
11727       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
11728     else
11729       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
11730   }
11731   else if (player_can_move &&
11732            IS_PASSABLE(element) && canPassField(x, y, move_direction))
11733   {
11734     if (!ACCESS_FROM(element, opposite_direction))
11735       return MP_NO_ACTION;      /* field not accessible from this direction */
11736
11737     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
11738       return MP_NO_ACTION;
11739
11740     if (IS_EM_GATE(element))
11741     {
11742       if (!player->key[EM_GATE_NR(element)])
11743         return MP_NO_ACTION;
11744     }
11745     else if (IS_EM_GATE_GRAY(element))
11746     {
11747       if (!player->key[EM_GATE_GRAY_NR(element)])
11748         return MP_NO_ACTION;
11749     }
11750     else if (IS_EM_GATE_GRAY_ACTIVE(element))
11751     {
11752       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
11753         return MP_NO_ACTION;
11754     }
11755     else if (IS_SP_PORT(element))
11756     {
11757       if (element == EL_SP_GRAVITY_PORT_LEFT ||
11758           element == EL_SP_GRAVITY_PORT_RIGHT ||
11759           element == EL_SP_GRAVITY_PORT_UP ||
11760           element == EL_SP_GRAVITY_PORT_DOWN)
11761         game.gravity = !game.gravity;
11762       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
11763                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
11764                element == EL_SP_GRAVITY_ON_PORT_UP ||
11765                element == EL_SP_GRAVITY_ON_PORT_DOWN)
11766         game.gravity = TRUE;
11767       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
11768                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
11769                element == EL_SP_GRAVITY_OFF_PORT_UP ||
11770                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
11771         game.gravity = FALSE;
11772     }
11773
11774     /* automatically move to the next field with double speed */
11775     player->programmed_action = move_direction;
11776
11777     if (player->move_delay_reset_counter == 0)
11778     {
11779       player->move_delay_reset_counter = 2;     /* two double speed steps */
11780
11781       DOUBLE_PLAYER_SPEED(player);
11782     }
11783
11784     PlayLevelSoundAction(x, y, ACTION_PASSING);
11785   }
11786   else if (player_can_move_or_snap && IS_DIGGABLE(element))
11787   {
11788     RemoveField(x, y);
11789
11790     if (mode != DF_SNAP)
11791     {
11792       GfxElement[x][y] = GFX_ELEMENT(element);
11793       player->is_digging = TRUE;
11794     }
11795
11796     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
11797
11798     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
11799                                         player->index_bit, dig_side);
11800
11801     if (mode == DF_SNAP)
11802     {
11803 #if USE_NEW_SNAP_DELAY
11804       if (level.block_snap_field)
11805         setFieldForSnapping(x, y, element, move_direction);
11806       else
11807         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11808 #else
11809       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
11810 #endif
11811
11812       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11813                                           player->index_bit, dig_side);
11814     }
11815   }
11816   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
11817   {
11818     RemoveField(x, y);
11819
11820     if (is_player && mode != DF_SNAP)
11821     {
11822       GfxElement[x][y] = element;
11823       player->is_collecting = TRUE;
11824     }
11825
11826     if (element == EL_SPEED_PILL)
11827     {
11828       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
11829     }
11830     else if (element == EL_EXTRA_TIME && level.time > 0)
11831     {
11832       TimeLeft += level.extra_time;
11833       DrawGameValue_Time(TimeLeft);
11834     }
11835     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
11836     {
11837       player->shield_normal_time_left += level.shield_normal_time;
11838       if (element == EL_SHIELD_DEADLY)
11839         player->shield_deadly_time_left += level.shield_deadly_time;
11840     }
11841     else if (element == EL_DYNAMITE ||
11842              element == EL_EM_DYNAMITE ||
11843              element == EL_SP_DISK_RED)
11844     {
11845       if (player->inventory_size < MAX_INVENTORY_SIZE)
11846         player->inventory_element[player->inventory_size++] = element;
11847
11848 #if 1
11849       DrawGameDoorValues();
11850 #else
11851       DrawGameValue_Dynamite(local_player->inventory_size);
11852 #endif
11853     }
11854     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
11855     {
11856       player->dynabomb_count++;
11857       player->dynabombs_left++;
11858     }
11859     else if (element == EL_DYNABOMB_INCREASE_SIZE)
11860     {
11861       player->dynabomb_size++;
11862     }
11863     else if (element == EL_DYNABOMB_INCREASE_POWER)
11864     {
11865       player->dynabomb_xl = TRUE;
11866     }
11867     else if (IS_KEY(element))
11868     {
11869       player->key[KEY_NR(element)] = TRUE;
11870
11871 #if 1
11872       DrawGameDoorValues();
11873 #else
11874       DrawGameValue_Keys(player->key);
11875 #endif
11876
11877       redraw_mask |= REDRAW_DOOR_1;
11878     }
11879     else if (IS_ENVELOPE(element))
11880     {
11881       player->show_envelope = element;
11882     }
11883     else if (element == EL_EMC_LENSES)
11884     {
11885       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
11886
11887       RedrawAllInvisibleElementsForLenses();
11888     }
11889     else if (element == EL_EMC_MAGNIFIER)
11890     {
11891       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
11892
11893       RedrawAllInvisibleElementsForMagnifier();
11894     }
11895     else if (IS_DROPPABLE(element) ||
11896              IS_THROWABLE(element))     /* can be collected and dropped */
11897     {
11898       int i;
11899
11900       if (collect_count == 0)
11901         player->inventory_infinite_element = element;
11902       else
11903         for (i = 0; i < collect_count; i++)
11904           if (player->inventory_size < MAX_INVENTORY_SIZE)
11905             player->inventory_element[player->inventory_size++] = element;
11906
11907 #if 1
11908       DrawGameDoorValues();
11909 #else
11910       DrawGameValue_Dynamite(local_player->inventory_size);
11911 #endif
11912     }
11913     else if (collect_count > 0)
11914     {
11915       local_player->gems_still_needed -= collect_count;
11916       if (local_player->gems_still_needed < 0)
11917         local_player->gems_still_needed = 0;
11918
11919       DrawGameValue_Emeralds(local_player->gems_still_needed);
11920     }
11921
11922     RaiseScoreElement(element);
11923     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
11924
11925     if (is_player)
11926       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
11927                                           player->index_bit, dig_side);
11928
11929     if (mode == DF_SNAP)
11930     {
11931 #if USE_NEW_SNAP_DELAY
11932       if (level.block_snap_field)
11933         setFieldForSnapping(x, y, element, move_direction);
11934       else
11935         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11936 #else
11937       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
11938 #endif
11939
11940       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
11941                                           player->index_bit, dig_side);
11942     }
11943   }
11944   else if (player_can_move_or_snap && IS_PUSHABLE(element))
11945   {
11946     if (mode == DF_SNAP && element != EL_BD_ROCK)
11947       return MP_NO_ACTION;
11948
11949     if (CAN_FALL(element) && dy)
11950       return MP_NO_ACTION;
11951
11952     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
11953         !(element == EL_SPRING && level.use_spring_bug))
11954       return MP_NO_ACTION;
11955
11956     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
11957         ((move_direction & MV_VERTICAL &&
11958           ((element_info[element].move_pattern & MV_LEFT &&
11959             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
11960            (element_info[element].move_pattern & MV_RIGHT &&
11961             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
11962          (move_direction & MV_HORIZONTAL &&
11963           ((element_info[element].move_pattern & MV_UP &&
11964             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
11965            (element_info[element].move_pattern & MV_DOWN &&
11966             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
11967       return MP_NO_ACTION;
11968
11969     /* do not push elements already moving away faster than player */
11970     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
11971         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
11972       return MP_NO_ACTION;
11973
11974     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
11975     {
11976       if (player->push_delay_value == -1 || !player_was_pushing)
11977         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11978     }
11979     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
11980     {
11981       if (player->push_delay_value == -1)
11982         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11983     }
11984     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
11985     {
11986       if (!player->is_pushing)
11987         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
11988     }
11989
11990     player->is_pushing = TRUE;
11991
11992     if (!(IN_LEV_FIELD(nextx, nexty) &&
11993           (IS_FREE(nextx, nexty) ||
11994            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
11995             IS_SB_ELEMENT(element)))))
11996       return MP_NO_ACTION;
11997
11998     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
11999       return MP_NO_ACTION;
12000
12001     if (player->push_delay == -1)       /* new pushing; restart delay */
12002       player->push_delay = 0;
12003
12004     if (player->push_delay < player->push_delay_value &&
12005         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
12006         element != EL_SPRING && element != EL_BALLOON)
12007     {
12008       /* make sure that there is no move delay before next try to push */
12009       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
12010         player->move_delay = 0;
12011
12012       return MP_NO_ACTION;
12013     }
12014
12015     if (IS_SB_ELEMENT(element))
12016     {
12017       if (element == EL_SOKOBAN_FIELD_FULL)
12018       {
12019         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
12020         local_player->sokobanfields_still_needed++;
12021       }
12022
12023       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
12024       {
12025         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
12026         local_player->sokobanfields_still_needed--;
12027       }
12028
12029       Feld[x][y] = EL_SOKOBAN_OBJECT;
12030
12031       if (Back[x][y] == Back[nextx][nexty])
12032         PlayLevelSoundAction(x, y, ACTION_PUSHING);
12033       else if (Back[x][y] != 0)
12034         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
12035                                     ACTION_EMPTYING);
12036       else
12037         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
12038                                     ACTION_FILLING);
12039
12040       if (local_player->sokobanfields_still_needed == 0 &&
12041           game.emulation == EMU_SOKOBAN)
12042       {
12043         player->LevelSolved = player->GameOver = TRUE;
12044         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
12045       }
12046     }
12047     else
12048       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12049
12050     InitMovingField(x, y, move_direction);
12051     GfxAction[x][y] = ACTION_PUSHING;
12052
12053     if (mode == DF_SNAP)
12054       ContinueMoving(x, y);
12055     else
12056       MovPos[x][y] = (dx != 0 ? dx : dy);
12057
12058     Pushed[x][y] = TRUE;
12059     Pushed[nextx][nexty] = TRUE;
12060
12061     if (game.engine_version < VERSION_IDENT(2,2,0,7))
12062       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
12063     else
12064       player->push_delay_value = -1;    /* get new value later */
12065
12066     /* check for element change _after_ element has been pushed */
12067     if (game.use_change_when_pushing_bug)
12068     {
12069       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
12070                                  player->index_bit, dig_side);
12071       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
12072                                           player->index_bit, dig_side);
12073     }
12074   }
12075   else if (IS_SWITCHABLE(element))
12076   {
12077     if (PLAYER_SWITCHING(player, x, y))
12078     {
12079       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12080                                           player->index_bit, dig_side);
12081
12082       return MP_ACTION;
12083     }
12084
12085     player->is_switching = TRUE;
12086     player->switch_x = x;
12087     player->switch_y = y;
12088
12089     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12090
12091     if (element == EL_ROBOT_WHEEL)
12092     {
12093       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
12094       ZX = x;
12095       ZY = y;
12096
12097       DrawLevelField(x, y);
12098     }
12099     else if (element == EL_SP_TERMINAL)
12100     {
12101       int xx, yy;
12102
12103 #if 1
12104       SCAN_PLAYFIELD(xx, yy)
12105 #else
12106       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12107 #endif
12108       {
12109         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
12110           Bang(xx, yy);
12111         else if (Feld[xx][yy] == EL_SP_TERMINAL)
12112           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
12113       }
12114     }
12115     else if (IS_BELT_SWITCH(element))
12116     {
12117       ToggleBeltSwitch(x, y);
12118     }
12119     else if (element == EL_SWITCHGATE_SWITCH_UP ||
12120              element == EL_SWITCHGATE_SWITCH_DOWN)
12121     {
12122       ToggleSwitchgateSwitch(x, y);
12123     }
12124     else if (element == EL_LIGHT_SWITCH ||
12125              element == EL_LIGHT_SWITCH_ACTIVE)
12126     {
12127       ToggleLightSwitch(x, y);
12128     }
12129     else if (element == EL_TIMEGATE_SWITCH)
12130     {
12131       ActivateTimegateSwitch(x, y);
12132     }
12133     else if (element == EL_BALLOON_SWITCH_LEFT  ||
12134              element == EL_BALLOON_SWITCH_RIGHT ||
12135              element == EL_BALLOON_SWITCH_UP    ||
12136              element == EL_BALLOON_SWITCH_DOWN  ||
12137              element == EL_BALLOON_SWITCH_NONE  ||
12138              element == EL_BALLOON_SWITCH_ANY)
12139     {
12140       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
12141                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
12142                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
12143                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
12144                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
12145                              move_direction);
12146     }
12147     else if (element == EL_LAMP)
12148     {
12149       Feld[x][y] = EL_LAMP_ACTIVE;
12150       local_player->lights_still_needed--;
12151
12152       ResetGfxAnimation(x, y);
12153       DrawLevelField(x, y);
12154     }
12155     else if (element == EL_TIME_ORB_FULL)
12156     {
12157       Feld[x][y] = EL_TIME_ORB_EMPTY;
12158
12159       if (level.time > 0 || level.use_time_orb_bug)
12160       {
12161         TimeLeft += level.time_orb_time;
12162         DrawGameValue_Time(TimeLeft);
12163       }
12164
12165       ResetGfxAnimation(x, y);
12166       DrawLevelField(x, y);
12167     }
12168     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
12169              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12170     {
12171       int xx, yy;
12172
12173       game.ball_state = !game.ball_state;
12174
12175 #if 1
12176       SCAN_PLAYFIELD(xx, yy)
12177 #else
12178       for (yy = 0; yy < lev_fieldy; yy++) for (xx = 0; xx < lev_fieldx; xx++)
12179 #endif
12180       {
12181         int e = Feld[xx][yy];
12182
12183         if (game.ball_state)
12184         {
12185           if (e == EL_EMC_MAGIC_BALL)
12186             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
12187           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
12188             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
12189         }
12190         else
12191         {
12192           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
12193             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
12194           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
12195             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
12196         }
12197       }
12198     }
12199
12200     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12201                                         player->index_bit, dig_side);
12202
12203     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12204                                         player->index_bit, dig_side);
12205
12206     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12207                                         player->index_bit, dig_side);
12208
12209     return MP_ACTION;
12210   }
12211   else
12212   {
12213     if (!PLAYER_SWITCHING(player, x, y))
12214     {
12215       player->is_switching = TRUE;
12216       player->switch_x = x;
12217       player->switch_y = y;
12218
12219       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
12220                                  player->index_bit, dig_side);
12221       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
12222                                           player->index_bit, dig_side);
12223
12224       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
12225                                  player->index_bit, dig_side);
12226       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
12227                                           player->index_bit, dig_side);
12228     }
12229
12230     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
12231                                player->index_bit, dig_side);
12232     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
12233                                         player->index_bit, dig_side);
12234
12235     return MP_NO_ACTION;
12236   }
12237
12238   player->push_delay = -1;
12239
12240   if (is_player)                /* function can also be called by EL_PENGUIN */
12241   {
12242     if (Feld[x][y] != element)          /* really digged/collected something */
12243       player->is_collecting = !player->is_digging;
12244   }
12245
12246   return MP_MOVING;
12247 }
12248
12249 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
12250 {
12251   int jx = player->jx, jy = player->jy;
12252   int x = jx + dx, y = jy + dy;
12253   int snap_direction = (dx == -1 ? MV_LEFT  :
12254                         dx == +1 ? MV_RIGHT :
12255                         dy == -1 ? MV_UP    :
12256                         dy == +1 ? MV_DOWN  : MV_NONE);
12257   boolean can_continue_snapping = (level.continuous_snapping &&
12258                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
12259
12260   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
12261     return FALSE;
12262
12263   if (!player->active || !IN_LEV_FIELD(x, y))
12264     return FALSE;
12265
12266   if (dx && dy)
12267     return FALSE;
12268
12269   if (!dx && !dy)
12270   {
12271     if (player->MovPos == 0)
12272       player->is_pushing = FALSE;
12273
12274     player->is_snapping = FALSE;
12275
12276     if (player->MovPos == 0)
12277     {
12278       player->is_moving = FALSE;
12279       player->is_digging = FALSE;
12280       player->is_collecting = FALSE;
12281     }
12282
12283     return FALSE;
12284   }
12285
12286 #if USE_NEW_CONTINUOUS_SNAPPING
12287   /* prevent snapping with already pressed snap key when not allowed */
12288   if (player->is_snapping && !can_continue_snapping)
12289     return FALSE;
12290 #else
12291   if (player->is_snapping)
12292     return FALSE;
12293 #endif
12294
12295   player->MovDir = snap_direction;
12296
12297   if (player->MovPos == 0)
12298   {
12299     player->is_moving = FALSE;
12300     player->is_digging = FALSE;
12301     player->is_collecting = FALSE;
12302   }
12303
12304   player->is_dropping = FALSE;
12305   player->is_dropping_pressed = FALSE;
12306   player->drop_pressed_delay = 0;
12307
12308   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
12309     return FALSE;
12310
12311   player->is_snapping = TRUE;
12312
12313   if (player->MovPos == 0)
12314   {
12315     player->is_moving = FALSE;
12316     player->is_digging = FALSE;
12317     player->is_collecting = FALSE;
12318   }
12319
12320   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
12321     DrawLevelField(player->last_jx, player->last_jy);
12322
12323   DrawLevelField(x, y);
12324
12325   return TRUE;
12326 }
12327
12328 boolean DropElement(struct PlayerInfo *player)
12329 {
12330   int old_element, new_element;
12331   int dropx = player->jx, dropy = player->jy;
12332   int drop_direction = player->MovDir;
12333   int drop_side = drop_direction;
12334   int drop_element = (player->inventory_size > 0 ?
12335                       player->inventory_element[player->inventory_size - 1] :
12336                       player->inventory_infinite_element != EL_UNDEFINED ?
12337                       player->inventory_infinite_element :
12338                       player->dynabombs_left > 0 ?
12339                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
12340                       EL_UNDEFINED);
12341
12342   player->is_dropping_pressed = TRUE;
12343
12344   /* do not drop an element on top of another element; when holding drop key
12345      pressed without moving, dropped element must move away before the next
12346      element can be dropped (this is especially important if the next element
12347      is dynamite, which can be placed on background for historical reasons) */
12348   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
12349     return MP_ACTION;
12350
12351   if (IS_THROWABLE(drop_element))
12352   {
12353     dropx += GET_DX_FROM_DIR(drop_direction);
12354     dropy += GET_DY_FROM_DIR(drop_direction);
12355
12356     if (!IN_LEV_FIELD(dropx, dropy))
12357       return FALSE;
12358   }
12359
12360   old_element = Feld[dropx][dropy];     /* old element at dropping position */
12361   new_element = drop_element;           /* default: no change when dropping */
12362
12363   /* check if player is active, not moving and ready to drop */
12364   if (!player->active || player->MovPos || player->drop_delay > 0)
12365     return FALSE;
12366
12367   /* check if player has anything that can be dropped */
12368   if (new_element == EL_UNDEFINED)
12369     return FALSE;
12370
12371   /* check if drop key was pressed long enough for EM style dynamite */
12372   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
12373     return FALSE;
12374
12375   /* check if anything can be dropped at the current position */
12376   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
12377     return FALSE;
12378
12379   /* collected custom elements can only be dropped on empty fields */
12380   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
12381     return FALSE;
12382
12383   if (old_element != EL_EMPTY)
12384     Back[dropx][dropy] = old_element;   /* store old element on this field */
12385
12386   ResetGfxAnimation(dropx, dropy);
12387   ResetRandomAnimationValue(dropx, dropy);
12388
12389   if (player->inventory_size > 0 ||
12390       player->inventory_infinite_element != EL_UNDEFINED)
12391   {
12392     if (player->inventory_size > 0)
12393     {
12394       player->inventory_size--;
12395
12396 #if 1
12397       DrawGameDoorValues();
12398 #else
12399       DrawGameValue_Dynamite(local_player->inventory_size);
12400 #endif
12401
12402       if (new_element == EL_DYNAMITE)
12403         new_element = EL_DYNAMITE_ACTIVE;
12404       else if (new_element == EL_EM_DYNAMITE)
12405         new_element = EL_EM_DYNAMITE_ACTIVE;
12406       else if (new_element == EL_SP_DISK_RED)
12407         new_element = EL_SP_DISK_RED_ACTIVE;
12408     }
12409
12410     Feld[dropx][dropy] = new_element;
12411
12412     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12413       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12414                           el2img(Feld[dropx][dropy]), 0);
12415
12416     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12417
12418     /* needed if previous element just changed to "empty" in the last frame */
12419     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12420
12421     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
12422                                player->index_bit, drop_side);
12423     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
12424                                         CE_PLAYER_DROPS_X,
12425                                         player->index_bit, drop_side);
12426
12427     TestIfElementTouchesCustomElement(dropx, dropy);
12428   }
12429   else          /* player is dropping a dyna bomb */
12430   {
12431     player->dynabombs_left--;
12432
12433     Feld[dropx][dropy] = new_element;
12434
12435     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
12436       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
12437                           el2img(Feld[dropx][dropy]), 0);
12438
12439     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
12440   }
12441
12442   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
12443     InitField_WithBug1(dropx, dropy, FALSE);
12444
12445   new_element = Feld[dropx][dropy];     /* element might have changed */
12446
12447   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
12448       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
12449   {
12450     int move_direction, nextx, nexty;
12451
12452     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
12453       MovDir[dropx][dropy] = drop_direction;
12454
12455     move_direction = MovDir[dropx][dropy];
12456     nextx = dropx + GET_DX_FROM_DIR(move_direction);
12457     nexty = dropy + GET_DY_FROM_DIR(move_direction);
12458
12459     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
12460     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
12461   }
12462
12463   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
12464   player->is_dropping = TRUE;
12465
12466   player->drop_pressed_delay = 0;
12467   player->is_dropping_pressed = FALSE;
12468
12469   player->drop_x = dropx;
12470   player->drop_y = dropy;
12471
12472   return TRUE;
12473 }
12474
12475 /* ------------------------------------------------------------------------- */
12476 /* game sound playing functions                                              */
12477 /* ------------------------------------------------------------------------- */
12478
12479 static int *loop_sound_frame = NULL;
12480 static int *loop_sound_volume = NULL;
12481
12482 void InitPlayLevelSound()
12483 {
12484   int num_sounds = getSoundListSize();
12485
12486   checked_free(loop_sound_frame);
12487   checked_free(loop_sound_volume);
12488
12489   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
12490   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
12491 }
12492
12493 static void PlayLevelSound(int x, int y, int nr)
12494 {
12495   int sx = SCREENX(x), sy = SCREENY(y);
12496   int volume, stereo_position;
12497   int max_distance = 8;
12498   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
12499
12500   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
12501       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
12502     return;
12503
12504   if (!IN_LEV_FIELD(x, y) ||
12505       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
12506       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
12507     return;
12508
12509   volume = SOUND_MAX_VOLUME;
12510
12511   if (!IN_SCR_FIELD(sx, sy))
12512   {
12513     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
12514     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
12515
12516     volume -= volume * (dx > dy ? dx : dy) / max_distance;
12517   }
12518
12519   stereo_position = (SOUND_MAX_LEFT +
12520                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
12521                      (SCR_FIELDX + 2 * max_distance));
12522
12523   if (IS_LOOP_SOUND(nr))
12524   {
12525     /* This assures that quieter loop sounds do not overwrite louder ones,
12526        while restarting sound volume comparison with each new game frame. */
12527
12528     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
12529       return;
12530
12531     loop_sound_volume[nr] = volume;
12532     loop_sound_frame[nr] = FrameCounter;
12533   }
12534
12535   PlaySoundExt(nr, volume, stereo_position, type);
12536 }
12537
12538 static void PlayLevelSoundNearest(int x, int y, int sound_action)
12539 {
12540   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
12541                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
12542                  y < LEVELY(BY1) ? LEVELY(BY1) :
12543                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
12544                  sound_action);
12545 }
12546
12547 static void PlayLevelSoundAction(int x, int y, int action)
12548 {
12549   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
12550 }
12551
12552 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
12553 {
12554   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12555
12556   if (sound_effect != SND_UNDEFINED)
12557     PlayLevelSound(x, y, sound_effect);
12558 }
12559
12560 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
12561                                               int action)
12562 {
12563   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
12564
12565   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12566     PlayLevelSound(x, y, sound_effect);
12567 }
12568
12569 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
12570 {
12571   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12572
12573   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12574     PlayLevelSound(x, y, sound_effect);
12575 }
12576
12577 static void StopLevelSoundActionIfLoop(int x, int y, int action)
12578 {
12579   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
12580
12581   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
12582     StopSound(sound_effect);
12583 }
12584
12585 static void PlayLevelMusic()
12586 {
12587   if (levelset.music[level_nr] != MUS_UNDEFINED)
12588     PlayMusic(levelset.music[level_nr]);        /* from config file */
12589   else
12590     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
12591 }
12592
12593 void PlayLevelSound_EM(int x, int y, int element_em, int sample)
12594 {
12595   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
12596
12597   switch (sample)
12598   {
12599     case SAMPLE_blank:
12600       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
12601       break;
12602
12603     case SAMPLE_roll:
12604       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12605       break;
12606
12607     case SAMPLE_stone:
12608       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12609       break;
12610
12611     case SAMPLE_nut:
12612       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12613       break;
12614
12615     case SAMPLE_crack:
12616       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12617       break;
12618
12619     case SAMPLE_bug:
12620       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12621       break;
12622
12623     case SAMPLE_tank:
12624       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12625       break;
12626
12627     case SAMPLE_android_clone:
12628       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12629       break;
12630
12631     case SAMPLE_android_move:
12632       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12633       break;
12634
12635     case SAMPLE_spring:
12636       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12637       break;
12638
12639     case SAMPLE_slurp:
12640       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
12641       break;
12642
12643     case SAMPLE_eater:
12644       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
12645       break;
12646
12647     case SAMPLE_eater_eat:
12648       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12649       break;
12650
12651     case SAMPLE_alien:
12652       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
12653       break;
12654
12655     case SAMPLE_collect:
12656       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
12657       break;
12658
12659     case SAMPLE_diamond:
12660       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12661       break;
12662
12663     case SAMPLE_squash:
12664       /* !!! CHECK THIS !!! */
12665 #if 1
12666       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
12667 #else
12668       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
12669 #endif
12670       break;
12671
12672     case SAMPLE_wonderfall:
12673       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
12674       break;
12675
12676     case SAMPLE_drip:
12677       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
12678       break;
12679
12680     case SAMPLE_push:
12681       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
12682       break;
12683
12684     case SAMPLE_dirt:
12685       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
12686       break;
12687
12688     case SAMPLE_acid:
12689       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
12690       break;
12691
12692     case SAMPLE_ball:
12693       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12694       break;
12695
12696     case SAMPLE_grow:
12697       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
12698       break;
12699
12700     case SAMPLE_wonder:
12701       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12702       break;
12703
12704     case SAMPLE_door:
12705       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12706       break;
12707
12708     case SAMPLE_exit_open:
12709       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
12710       break;
12711
12712     case SAMPLE_exit_leave:
12713       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
12714       break;
12715
12716     case SAMPLE_dynamite:
12717       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
12718       break;
12719
12720     case SAMPLE_tick:
12721       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12722       break;
12723
12724     case SAMPLE_press:
12725       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
12726       break;
12727
12728     case SAMPLE_wheel:
12729       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
12730       break;
12731
12732     case SAMPLE_boom:
12733       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
12734       break;
12735
12736     case SAMPLE_die:
12737       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
12738       break;
12739
12740     case SAMPLE_time:
12741       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
12742       break;
12743
12744     default:
12745       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
12746       break;
12747   }
12748 }
12749
12750 void RaiseScore(int value)
12751 {
12752   local_player->score += value;
12753
12754   DrawGameValue_Score(local_player->score);
12755 }
12756
12757 void RaiseScoreElement(int element)
12758 {
12759   switch(element)
12760   {
12761     case EL_EMERALD:
12762     case EL_BD_DIAMOND:
12763     case EL_EMERALD_YELLOW:
12764     case EL_EMERALD_RED:
12765     case EL_EMERALD_PURPLE:
12766     case EL_SP_INFOTRON:
12767       RaiseScore(level.score[SC_EMERALD]);
12768       break;
12769     case EL_DIAMOND:
12770       RaiseScore(level.score[SC_DIAMOND]);
12771       break;
12772     case EL_CRYSTAL:
12773       RaiseScore(level.score[SC_CRYSTAL]);
12774       break;
12775     case EL_PEARL:
12776       RaiseScore(level.score[SC_PEARL]);
12777       break;
12778     case EL_BUG:
12779     case EL_BD_BUTTERFLY:
12780     case EL_SP_ELECTRON:
12781       RaiseScore(level.score[SC_BUG]);
12782       break;
12783     case EL_SPACESHIP:
12784     case EL_BD_FIREFLY:
12785     case EL_SP_SNIKSNAK:
12786       RaiseScore(level.score[SC_SPACESHIP]);
12787       break;
12788     case EL_YAMYAM:
12789     case EL_DARK_YAMYAM:
12790       RaiseScore(level.score[SC_YAMYAM]);
12791       break;
12792     case EL_ROBOT:
12793       RaiseScore(level.score[SC_ROBOT]);
12794       break;
12795     case EL_PACMAN:
12796       RaiseScore(level.score[SC_PACMAN]);
12797       break;
12798     case EL_NUT:
12799       RaiseScore(level.score[SC_NUT]);
12800       break;
12801     case EL_DYNAMITE:
12802     case EL_EM_DYNAMITE:
12803     case EL_SP_DISK_RED:
12804     case EL_DYNABOMB_INCREASE_NUMBER:
12805     case EL_DYNABOMB_INCREASE_SIZE:
12806     case EL_DYNABOMB_INCREASE_POWER:
12807       RaiseScore(level.score[SC_DYNAMITE]);
12808       break;
12809     case EL_SHIELD_NORMAL:
12810     case EL_SHIELD_DEADLY:
12811       RaiseScore(level.score[SC_SHIELD]);
12812       break;
12813     case EL_EXTRA_TIME:
12814       RaiseScore(level.extra_time_score);
12815       break;
12816     case EL_KEY_1:
12817     case EL_KEY_2:
12818     case EL_KEY_3:
12819     case EL_KEY_4:
12820     case EL_EM_KEY_1:
12821     case EL_EM_KEY_2:
12822     case EL_EM_KEY_3:
12823     case EL_EM_KEY_4:
12824     case EL_EMC_KEY_5:
12825     case EL_EMC_KEY_6:
12826     case EL_EMC_KEY_7:
12827     case EL_EMC_KEY_8:
12828       RaiseScore(level.score[SC_KEY]);
12829       break;
12830     default:
12831       RaiseScore(element_info[element].collect_score);
12832       break;
12833   }
12834 }
12835
12836 void RequestQuitGame(boolean ask_if_really_quit)
12837 {
12838   if (AllPlayersGone ||
12839       !ask_if_really_quit ||
12840       level_editor_test_game ||
12841       Request("Do you really want to quit the game ?",
12842               REQ_ASK | REQ_STAY_CLOSED))
12843   {
12844 #if defined(NETWORK_AVALIABLE)
12845     if (options.network)
12846       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
12847     else
12848 #endif
12849     {
12850       game_status = GAME_MODE_MAIN;
12851       DrawMainMenu();
12852     }
12853   }
12854   else
12855   {
12856     if (tape.playing && tape.deactivate_display)
12857       TapeDeactivateDisplayOff(TRUE);
12858
12859     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
12860
12861     if (tape.playing && tape.deactivate_display)
12862       TapeDeactivateDisplayOn();
12863   }
12864 }
12865
12866
12867 /* ---------- new game button stuff ---------------------------------------- */
12868
12869 /* graphic position values for game buttons */
12870 #define GAME_BUTTON_XSIZE       30
12871 #define GAME_BUTTON_YSIZE       30
12872 #define GAME_BUTTON_XPOS        5
12873 #define GAME_BUTTON_YPOS        215
12874 #define SOUND_BUTTON_XPOS       5
12875 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
12876
12877 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12878 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12879 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12880 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
12881 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
12882 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
12883
12884 static struct
12885 {
12886   int x, y;
12887   int gadget_id;
12888   char *infotext;
12889 } gamebutton_info[NUM_GAME_BUTTONS] =
12890 {
12891   {
12892     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
12893     GAME_CTRL_ID_STOP,
12894     "stop game"
12895   },
12896   {
12897     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
12898     GAME_CTRL_ID_PAUSE,
12899     "pause game"
12900   },
12901   {
12902     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
12903     GAME_CTRL_ID_PLAY,
12904     "play game"
12905   },
12906   {
12907     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
12908     SOUND_CTRL_ID_MUSIC,
12909     "background music on/off"
12910   },
12911   {
12912     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
12913     SOUND_CTRL_ID_LOOPS,
12914     "sound loops on/off"
12915   },
12916   {
12917     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
12918     SOUND_CTRL_ID_SIMPLE,
12919     "normal sounds on/off"
12920   }
12921 };
12922
12923 void CreateGameButtons()
12924 {
12925   int i;
12926
12927   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12928   {
12929     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
12930     struct GadgetInfo *gi;
12931     int button_type;
12932     boolean checked;
12933     unsigned long event_mask;
12934     int gd_xoffset, gd_yoffset;
12935     int gd_x1, gd_x2, gd_y1, gd_y2;
12936     int id = i;
12937
12938     gd_xoffset = gamebutton_info[i].x;
12939     gd_yoffset = gamebutton_info[i].y;
12940     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
12941     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
12942
12943     if (id == GAME_CTRL_ID_STOP ||
12944         id == GAME_CTRL_ID_PAUSE ||
12945         id == GAME_CTRL_ID_PLAY)
12946     {
12947       button_type = GD_TYPE_NORMAL_BUTTON;
12948       checked = FALSE;
12949       event_mask = GD_EVENT_RELEASED;
12950       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12951       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12952     }
12953     else
12954     {
12955       button_type = GD_TYPE_CHECK_BUTTON;
12956       checked =
12957         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
12958          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
12959          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
12960       event_mask = GD_EVENT_PRESSED;
12961       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
12962       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
12963     }
12964
12965     gi = CreateGadget(GDI_CUSTOM_ID, id,
12966                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
12967                       GDI_X, DX + gd_xoffset,
12968                       GDI_Y, DY + gd_yoffset,
12969                       GDI_WIDTH, GAME_BUTTON_XSIZE,
12970                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
12971                       GDI_TYPE, button_type,
12972                       GDI_STATE, GD_BUTTON_UNPRESSED,
12973                       GDI_CHECKED, checked,
12974                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
12975                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
12976                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
12977                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
12978                       GDI_EVENT_MASK, event_mask,
12979                       GDI_CALLBACK_ACTION, HandleGameButtons,
12980                       GDI_END);
12981
12982     if (gi == NULL)
12983       Error(ERR_EXIT, "cannot create gadget");
12984
12985     game_gadget[id] = gi;
12986   }
12987 }
12988
12989 void FreeGameButtons()
12990 {
12991   int i;
12992
12993   for (i = 0; i < NUM_GAME_BUTTONS; i++)
12994     FreeGadget(game_gadget[i]);
12995 }
12996
12997 static void MapGameButtons()
12998 {
12999   int i;
13000
13001   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13002     MapGadget(game_gadget[i]);
13003 }
13004
13005 void UnmapGameButtons()
13006 {
13007   int i;
13008
13009   for (i = 0; i < NUM_GAME_BUTTONS; i++)
13010     UnmapGadget(game_gadget[i]);
13011 }
13012
13013 static void HandleGameButtons(struct GadgetInfo *gi)
13014 {
13015   int id = gi->custom_id;
13016
13017   if (game_status != GAME_MODE_PLAYING)
13018     return;
13019
13020   switch (id)
13021   {
13022     case GAME_CTRL_ID_STOP:
13023       if (tape.playing)
13024         TapeStop();
13025       else
13026         RequestQuitGame(TRUE);
13027       break;
13028
13029     case GAME_CTRL_ID_PAUSE:
13030       if (options.network)
13031       {
13032 #if defined(NETWORK_AVALIABLE)
13033         if (tape.pausing)
13034           SendToServer_ContinuePlaying();
13035         else
13036           SendToServer_PausePlaying();
13037 #endif
13038       }
13039       else
13040         TapeTogglePause(TAPE_TOGGLE_MANUAL);
13041       break;
13042
13043     case GAME_CTRL_ID_PLAY:
13044       if (tape.pausing)
13045       {
13046 #if defined(NETWORK_AVALIABLE)
13047         if (options.network)
13048           SendToServer_ContinuePlaying();
13049         else
13050 #endif
13051         {
13052           tape.pausing = FALSE;
13053           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
13054         }
13055       }
13056       break;
13057
13058     case SOUND_CTRL_ID_MUSIC:
13059       if (setup.sound_music)
13060       { 
13061         setup.sound_music = FALSE;
13062         FadeMusic();
13063       }
13064       else if (audio.music_available)
13065       { 
13066         setup.sound = setup.sound_music = TRUE;
13067
13068         SetAudioMode(setup.sound);
13069
13070         PlayLevelMusic();
13071       }
13072       break;
13073
13074     case SOUND_CTRL_ID_LOOPS:
13075       if (setup.sound_loops)
13076         setup.sound_loops = FALSE;
13077       else if (audio.loops_available)
13078       {
13079         setup.sound = setup.sound_loops = TRUE;
13080         SetAudioMode(setup.sound);
13081       }
13082       break;
13083
13084     case SOUND_CTRL_ID_SIMPLE:
13085       if (setup.sound_simple)
13086         setup.sound_simple = FALSE;
13087       else if (audio.sound_available)
13088       {
13089         setup.sound = setup.sound_simple = TRUE;
13090         SetAudioMode(setup.sound);
13091       }
13092       break;
13093
13094     default:
13095       break;
13096   }
13097 }