rnd-20040207-2-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* this switch controls how rocks move horizontally */
25 #define OLD_GAME_BEHAVIOUR      FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_AMOEBA_CODE     FALSE
29
30 /* for DigField() */
31 #define DF_NO_PUSH              0
32 #define DF_DIG                  1
33 #define DF_SNAP                 2
34
35 /* for MovePlayer() */
36 #define MF_NO_ACTION            0
37 #define MF_MOVING               1
38 #define MF_ACTION               2
39
40 /* for ScrollPlayer() */
41 #define SCROLL_INIT             0
42 #define SCROLL_GO_ON            1
43
44 /* for Explode() */
45 #define EX_PHASE_START          0
46 #define EX_NO_EXPLOSION         0
47 #define EX_NORMAL               1
48 #define EX_CENTER               2
49 #define EX_BORDER               3
50
51 /* special positions in the game control window (relative to control window) */
52 #define XX_LEVEL                37
53 #define YY_LEVEL                20
54 #define XX_EMERALDS             29
55 #define YY_EMERALDS             54
56 #define XX_DYNAMITE             29
57 #define YY_DYNAMITE             89
58 #define XX_KEYS                 18
59 #define YY_KEYS                 123
60 #define XX_SCORE                15
61 #define YY_SCORE                159
62 #define XX_TIME                 29
63 #define YY_TIME                 194
64
65 /* special positions in the game control window (relative to main window) */
66 #define DX_LEVEL                (DX + XX_LEVEL)
67 #define DY_LEVEL                (DY + YY_LEVEL)
68 #define DX_EMERALDS             (DX + XX_EMERALDS)
69 #define DY_EMERALDS             (DY + YY_EMERALDS)
70 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
71 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
72 #define DX_KEYS                 (DX + XX_KEYS)
73 #define DY_KEYS                 (DY + YY_KEYS)
74 #define DX_SCORE                (DX + XX_SCORE)
75 #define DY_SCORE                (DY + YY_SCORE)
76 #define DX_TIME                 (DX + XX_TIME)
77 #define DY_TIME                 (DY + YY_TIME)
78
79 /* values for initial player move delay (initial delay counter value) */
80 #define INITIAL_MOVE_DELAY_OFF  -1
81 #define INITIAL_MOVE_DELAY_ON   0
82
83 /* values for player movement speed (which is in fact a delay value) */
84 #define MOVE_DELAY_NORMAL_SPEED 8
85 #define MOVE_DELAY_HIGH_SPEED   4
86
87 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
88 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
89 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
90 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
91
92 /* values for other actions */
93 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
94
95 #define INIT_GFX_RANDOM()       (SimpleRND(1000000))
96
97 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
98                                  RND(element_info[e].push_delay_random))
99 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
100                                  RND(element_info[e].move_delay_random))
101 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
102                                     (element_info[e].move_delay_random))
103
104 #if 1
105 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
106                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
107                                         (condition) ||                  \
108                                         (DONT_COLLIDE_WITH(e) &&        \
109                                          IS_PLAYER(x, y) &&             \
110                                          !PLAYER_ENEMY_PROTECTED(x, y))))
111 #else
112 #define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)             \
113                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
114                                         (condition) ||                  \
115                                         (DONT_COLLIDE_WITH(e) &&        \
116                                          IS_FREE_OR_PLAYER(x, y))))
117 #endif
118
119 #define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition)              \
120                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
121                                         (condition)))
122
123 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
124         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
125
126 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y)                        \
127         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
128
129 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y)                         \
130         ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
131
132 #define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
133
134 #define YAMYAM_CAN_ENTER_FIELD(x, y)                                    \
135                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
136                                         Feld[x][y] == EL_DIAMOND))
137
138 #define DARK_YAMYAM_CAN_ENTER_FIELD(x, y)                               \
139                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
140                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
141
142 #define PACMAN_CAN_ENTER_FIELD(x, y)                                    \
143                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
144                                         IS_AMOEBOID(Feld[x][y])))
145
146 #define PIG_CAN_ENTER_FIELD(x, y)                                       \
147                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
148                                         IS_FOOD_PIG(Feld[x][y])))
149
150 #define PENGUIN_CAN_ENTER_FIELD(x, y)                                   \
151                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
152                                         IS_FOOD_PENGUIN(Feld[x][y]) ||  \
153                                         Feld[x][y] == EL_EXIT_OPEN ||   \
154                                         Feld[x][y] == EL_ACID))
155
156 #if 0
157 #if 1
158 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y)                               \
159                 (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
160 #else
161 #define MAZE_RUNNER_CAN_ENTER_FIELD(x, y)                               \
162                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
163                                         IS_FOOD_DARK_YAMYAM(Feld[x][y])))
164 #endif
165 #endif
166
167 #define GROUP_NR(e)             ((e) - EL_GROUP_START)
168 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
169 #define IS_IN_GROUP(e, nr)      (element_info[e].in_group[nr] == TRUE)
170 #define IS_IN_GROUP_EL(e, ge)   (IS_IN_GROUP(e, (ge) - EL_GROUP_START))
171
172 #define IS_EQUAL_OR_IN_GROUP(e, ge)                                     \
173         (IS_GROUP_ELEMENT(ge) ? IS_IN_GROUP(e, GROUP_NR(ge)) : (e) == (ge))
174
175 #if 1
176 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
177                 (!IS_PLAYER(x, y) &&                                    \
178                  (Feld[x][y] == EL_ACID ||                              \
179                   IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e))))
180 #else
181 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
182                 (!IS_PLAYER(x, y) &&                                    \
183                  (Feld[x][y] == EL_ACID ||                              \
184                   Feld[x][y] == MOVE_ENTER_EL(e) ||                     \
185                   (IS_GROUP_ELEMENT(MOVE_ENTER_EL(e)) &&                \
186                    IS_IN_GROUP_EL(Feld[x][y], MOVE_ENTER_EL(e)))))
187 #endif
188
189 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
190         ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
191
192 #define MOLE_CAN_ENTER_FIELD(x, y, condition)                           \
193                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
194
195 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
196 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
197
198 /* game button identifiers */
199 #define GAME_CTRL_ID_STOP               0
200 #define GAME_CTRL_ID_PAUSE              1
201 #define GAME_CTRL_ID_PLAY               2
202 #define SOUND_CTRL_ID_MUSIC             3
203 #define SOUND_CTRL_ID_LOOPS             4
204 #define SOUND_CTRL_ID_SIMPLE            5
205
206 #define NUM_GAME_BUTTONS                6
207
208
209 /* forward declaration for internal use */
210
211 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
212 static boolean MovePlayer(struct PlayerInfo *, int, int);
213 static void ScrollPlayer(struct PlayerInfo *, int);
214 static void ScrollScreen(struct PlayerInfo *, int);
215
216 static void InitBeltMovement(void);
217 static void CloseAllOpenTimegates(void);
218 static void CheckGravityMovement(struct PlayerInfo *);
219 static void KillHeroUnlessEnemyProtected(int, int);
220 static void KillHeroUnlessExplosionProtected(int, int);
221
222 static void TestIfPlayerTouchesCustomElement(int, int);
223 static void TestIfElementTouchesCustomElement(int, int);
224 static void TestIfElementHitsCustomElement(int, int, int);
225
226 static void ChangeElement(int, int, int);
227 static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
228 static boolean CheckTriggeredElementChange(int, int, int, int);
229 static boolean CheckElementSideChange(int, int, int, int, int, int);
230 static boolean CheckElementChange(int, int, int, int);
231
232 static void PlayLevelSound(int, int, int);
233 static void PlayLevelSoundNearest(int, int, int);
234 static void PlayLevelSoundAction(int, int, int);
235 static void PlayLevelSoundElementAction(int, int, int, int);
236 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
237 static void PlayLevelSoundActionIfLoop(int, int, int);
238 static void StopLevelSoundActionIfLoop(int, int, int);
239 static void PlayLevelMusic();
240
241 static void MapGameButtons();
242 static void HandleGameButtons(struct GadgetInfo *);
243
244 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
245
246
247 /* ------------------------------------------------------------------------- */
248 /* definition of elements that automatically change to other elements after  */
249 /* a specified time, eventually calling a function when changing             */
250 /* ------------------------------------------------------------------------- */
251
252 /* forward declaration for changer functions */
253 static void InitBuggyBase(int x, int y);
254 static void WarnBuggyBase(int x, int y);
255
256 static void InitTrap(int x, int y);
257 static void ActivateTrap(int x, int y);
258 static void ChangeActiveTrap(int x, int y);
259
260 static void InitRobotWheel(int x, int y);
261 static void RunRobotWheel(int x, int y);
262 static void StopRobotWheel(int x, int y);
263
264 static void InitTimegateWheel(int x, int y);
265 static void RunTimegateWheel(int x, int y);
266
267 struct ChangingElementInfo
268 {
269   int element;
270   int target_element;
271   int change_delay;
272   void (*pre_change_function)(int x, int y);
273   void (*change_function)(int x, int y);
274   void (*post_change_function)(int x, int y);
275 };
276
277 static struct ChangingElementInfo change_delay_list[] =
278 {
279   {
280     EL_NUT_BREAKING,
281     EL_EMERALD,
282     6,
283     NULL,
284     NULL,
285     NULL
286   },
287   {
288     EL_PEARL_BREAKING,
289     EL_EMPTY,
290     8,
291     NULL,
292     NULL,
293     NULL
294   },
295   {
296     EL_EXIT_OPENING,
297     EL_EXIT_OPEN,
298     29,
299     NULL,
300     NULL,
301     NULL
302   },
303   {
304     EL_EXIT_CLOSING,
305     EL_EXIT_CLOSED,
306     29,
307     NULL,
308     NULL,
309     NULL
310   },
311   {
312     EL_SP_EXIT_OPENING,
313     EL_SP_EXIT_OPEN,
314     29,
315     NULL,
316     NULL,
317     NULL
318   },
319   {
320     EL_SP_EXIT_CLOSING,
321     EL_SP_EXIT_CLOSED,
322     29,
323     NULL,
324     NULL,
325     NULL
326   },
327   {
328     EL_SWITCHGATE_OPENING,
329     EL_SWITCHGATE_OPEN,
330     29,
331     NULL,
332     NULL,
333     NULL
334   },
335   {
336     EL_SWITCHGATE_CLOSING,
337     EL_SWITCHGATE_CLOSED,
338     29,
339     NULL,
340     NULL,
341     NULL
342   },
343   {
344     EL_TIMEGATE_OPENING,
345     EL_TIMEGATE_OPEN,
346     29,
347     NULL,
348     NULL,
349     NULL
350   },
351   {
352     EL_TIMEGATE_CLOSING,
353     EL_TIMEGATE_CLOSED,
354     29,
355     NULL,
356     NULL,
357     NULL
358   },
359
360   {
361     EL_ACID_SPLASH_LEFT,
362     EL_EMPTY,
363     8,
364     NULL,
365     NULL,
366     NULL
367   },
368   {
369     EL_ACID_SPLASH_RIGHT,
370     EL_EMPTY,
371     8,
372     NULL,
373     NULL,
374     NULL
375   },
376   {
377     EL_SP_BUGGY_BASE,
378     EL_SP_BUGGY_BASE_ACTIVATING,
379     0,
380     InitBuggyBase,
381     NULL,
382     NULL
383   },
384   {
385     EL_SP_BUGGY_BASE_ACTIVATING,
386     EL_SP_BUGGY_BASE_ACTIVE,
387     0,
388     InitBuggyBase,
389     NULL,
390     NULL
391   },
392   {
393     EL_SP_BUGGY_BASE_ACTIVE,
394     EL_SP_BUGGY_BASE,
395     0,
396     InitBuggyBase,
397     WarnBuggyBase,
398     NULL
399   },
400   {
401     EL_TRAP,
402     EL_TRAP_ACTIVE,
403     0,
404     InitTrap,
405     NULL,
406     ActivateTrap
407   },
408   {
409     EL_TRAP_ACTIVE,
410     EL_TRAP,
411     31,
412     NULL,
413     ChangeActiveTrap,
414     NULL
415   },
416   {
417     EL_ROBOT_WHEEL_ACTIVE,
418     EL_ROBOT_WHEEL,
419     0,
420     InitRobotWheel,
421     RunRobotWheel,
422     StopRobotWheel
423   },
424   {
425     EL_TIMEGATE_SWITCH_ACTIVE,
426     EL_TIMEGATE_SWITCH,
427     0,
428     InitTimegateWheel,
429     RunTimegateWheel,
430     NULL
431   },
432
433   {
434     EL_UNDEFINED,
435     EL_UNDEFINED,
436     -1,
437     NULL,
438     NULL,
439     NULL
440   }
441 };
442
443 struct
444 {
445   int element;
446   int push_delay_fixed, push_delay_random;
447 }
448 push_delay_list[] =
449 {
450   { EL_SPRING,                  0, 0 },
451   { EL_BALLOON,                 0, 0 },
452
453   { EL_SOKOBAN_OBJECT,          2, 0 },
454   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
455   { EL_SATELLITE,               2, 0 },
456   { EL_SP_DISK_YELLOW,          2, 0 },
457
458   { EL_UNDEFINED,               0, 0 },
459 };
460
461 struct
462 {
463   int element;
464   int move_stepsize;
465 }
466 move_stepsize_list[] =
467 {
468   { EL_AMOEBA_DROP,             2 },
469   { EL_AMOEBA_DROPPING,         2 },
470   { EL_QUICKSAND_FILLING,       1 },
471   { EL_QUICKSAND_EMPTYING,      1 },
472   { EL_MAGIC_WALL_FILLING,      2 },
473   { EL_BD_MAGIC_WALL_FILLING,   2 },
474   { EL_MAGIC_WALL_EMPTYING,     2 },
475   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
476
477   { EL_UNDEFINED,               0 },
478 };
479
480 struct
481 {
482   int element;
483   int count;
484 }
485 collect_count_list[] =
486 {
487   { EL_EMERALD,                 1 },
488   { EL_BD_DIAMOND,              1 },
489   { EL_EMERALD_YELLOW,          1 },
490   { EL_EMERALD_RED,             1 },
491   { EL_EMERALD_PURPLE,          1 },
492   { EL_DIAMOND,                 3 },
493   { EL_SP_INFOTRON,             1 },
494   { EL_PEARL,                   5 },
495   { EL_CRYSTAL,                 8 },
496
497   { EL_UNDEFINED,               0 },
498 };
499
500 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
501
502 #define IS_AUTO_CHANGING(e)     (element_info[e].change_events & \
503                                  CH_EVENT_BIT(CE_DELAY))
504 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
505 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
506                                  IS_JUST_CHANGING(x, y))
507
508 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
509
510
511 void GetPlayerConfig()
512 {
513   if (!audio.sound_available)
514     setup.sound_simple = FALSE;
515
516   if (!audio.loops_available)
517     setup.sound_loops = FALSE;
518
519   if (!audio.music_available)
520     setup.sound_music = FALSE;
521
522   if (!video.fullscreen_available)
523     setup.fullscreen = FALSE;
524
525   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
526
527   SetAudioMode(setup.sound);
528   InitJoysticks();
529 }
530
531 static int getBeltNrFromBeltElement(int element)
532 {
533   return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
534           element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
535           element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
536 }
537
538 static int getBeltNrFromBeltActiveElement(int element)
539 {
540   return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
541           element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
542           element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
543 }
544
545 static int getBeltNrFromBeltSwitchElement(int element)
546 {
547   return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
548           element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
549           element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
550 }
551
552 static int getBeltDirNrFromBeltSwitchElement(int element)
553 {
554   static int belt_base_element[4] =
555   {
556     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
557     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
558     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
559     EL_CONVEYOR_BELT_4_SWITCH_LEFT
560   };
561
562   int belt_nr = getBeltNrFromBeltSwitchElement(element);
563   int belt_dir_nr = element - belt_base_element[belt_nr];
564
565   return (belt_dir_nr % 3);
566 }
567
568 static int getBeltDirFromBeltSwitchElement(int element)
569 {
570   static int belt_move_dir[3] =
571   {
572     MV_LEFT,
573     MV_NO_MOVING,
574     MV_RIGHT
575   };
576
577   int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
578
579   return belt_move_dir[belt_dir_nr];
580 }
581
582 static void InitPlayerField(int x, int y, int element, boolean init_game)
583 {
584   if (element == EL_SP_MURPHY)
585   {
586     if (init_game)
587     {
588       if (stored_player[0].present)
589       {
590         Feld[x][y] = EL_SP_MURPHY_CLONE;
591
592         return;
593       }
594       else
595       {
596         stored_player[0].use_murphy_graphic = TRUE;
597       }
598
599       Feld[x][y] = EL_PLAYER_1;
600     }
601   }
602
603   if (init_game)
604   {
605     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
606     int jx = player->jx, jy = player->jy;
607
608     player->present = TRUE;
609
610     player->block_last_field = (element == EL_SP_MURPHY ?
611                                 level.sp_block_last_field :
612                                 level.block_last_field);
613
614     if (!options.network || player->connected)
615     {
616       player->active = TRUE;
617
618       /* remove potentially duplicate players */
619       if (StorePlayer[jx][jy] == Feld[x][y])
620         StorePlayer[jx][jy] = 0;
621
622       StorePlayer[x][y] = Feld[x][y];
623
624       if (options.debug)
625       {
626         printf("Player %d activated.\n", player->element_nr);
627         printf("[Local player is %d and currently %s.]\n",
628                local_player->element_nr,
629                local_player->active ? "active" : "not active");
630       }
631     }
632
633     Feld[x][y] = EL_EMPTY;
634     player->jx = player->last_jx = x;
635     player->jy = player->last_jy = y;
636   }
637 }
638
639 static void InitField(int x, int y, boolean init_game)
640 {
641   int element = Feld[x][y];
642
643   switch (element)
644   {
645     case EL_SP_MURPHY:
646     case EL_PLAYER_1:
647     case EL_PLAYER_2:
648     case EL_PLAYER_3:
649     case EL_PLAYER_4:
650       InitPlayerField(x, y, element, init_game);
651       break;
652
653     case EL_SOKOBAN_FIELD_PLAYER:
654       element = Feld[x][y] = EL_PLAYER_1;
655       InitField(x, y, init_game);
656
657       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
658       InitField(x, y, init_game);
659       break;
660
661     case EL_SOKOBAN_FIELD_EMPTY:
662       local_player->sokobanfields_still_needed++;
663       break;
664
665     case EL_STONEBLOCK:
666       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
667         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
668       else if (x > 0 && Feld[x-1][y] == EL_ACID)
669         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
670       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
671         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
672       else if (y > 0 && Feld[x][y-1] == EL_ACID)
673         Feld[x][y] = EL_ACID_POOL_BOTTOM;
674       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
675         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
676       break;
677
678     case EL_BUG_RIGHT:
679     case EL_BUG_UP:
680     case EL_BUG_LEFT:
681     case EL_BUG_DOWN:
682     case EL_BUG:
683     case EL_SPACESHIP_RIGHT:
684     case EL_SPACESHIP_UP:
685     case EL_SPACESHIP_LEFT:
686     case EL_SPACESHIP_DOWN:
687     case EL_SPACESHIP:
688     case EL_BD_BUTTERFLY_RIGHT:
689     case EL_BD_BUTTERFLY_UP:
690     case EL_BD_BUTTERFLY_LEFT:
691     case EL_BD_BUTTERFLY_DOWN:
692     case EL_BD_BUTTERFLY:
693     case EL_BD_FIREFLY_RIGHT:
694     case EL_BD_FIREFLY_UP:
695     case EL_BD_FIREFLY_LEFT:
696     case EL_BD_FIREFLY_DOWN:
697     case EL_BD_FIREFLY:
698     case EL_PACMAN_RIGHT:
699     case EL_PACMAN_UP:
700     case EL_PACMAN_LEFT:
701     case EL_PACMAN_DOWN:
702     case EL_YAMYAM:
703     case EL_DARK_YAMYAM:
704     case EL_ROBOT:
705     case EL_PACMAN:
706     case EL_SP_SNIKSNAK:
707     case EL_SP_ELECTRON:
708     case EL_MOLE_LEFT:
709     case EL_MOLE_RIGHT:
710     case EL_MOLE_UP:
711     case EL_MOLE_DOWN:
712     case EL_MOLE:
713       InitMovDir(x, y);
714       break;
715
716     case EL_AMOEBA_FULL:
717     case EL_BD_AMOEBA:
718       InitAmoebaNr(x, y);
719       break;
720
721     case EL_AMOEBA_DROP:
722       if (y == lev_fieldy - 1)
723       {
724         Feld[x][y] = EL_AMOEBA_GROWING;
725         Store[x][y] = EL_AMOEBA_WET;
726       }
727       break;
728
729     case EL_DYNAMITE_ACTIVE:
730     case EL_SP_DISK_RED_ACTIVE:
731     case EL_DYNABOMB_PLAYER_1_ACTIVE:
732     case EL_DYNABOMB_PLAYER_2_ACTIVE:
733     case EL_DYNABOMB_PLAYER_3_ACTIVE:
734     case EL_DYNABOMB_PLAYER_4_ACTIVE:
735       MovDelay[x][y] = 96;
736       break;
737
738     case EL_LAMP:
739       local_player->lights_still_needed++;
740       break;
741
742     case EL_PENGUIN:
743       local_player->friends_still_needed++;
744       break;
745
746     case EL_PIG:
747     case EL_DRAGON:
748       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
749       break;
750
751 #if 0
752     case EL_SP_EMPTY:
753       Feld[x][y] = EL_EMPTY;
754       break;
755 #endif
756
757 #if 0
758     case EL_EM_KEY_1_FILE:
759       Feld[x][y] = EL_EM_KEY_1;
760       break;
761     case EL_EM_KEY_2_FILE:
762       Feld[x][y] = EL_EM_KEY_2;
763       break;
764     case EL_EM_KEY_3_FILE:
765       Feld[x][y] = EL_EM_KEY_3;
766       break;
767     case EL_EM_KEY_4_FILE:
768       Feld[x][y] = EL_EM_KEY_4;
769       break;
770 #endif
771
772     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
773     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
774     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
775     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
776     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
777     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
778     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
779     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
780     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
781     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
782     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
783     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
784       if (init_game)
785       {
786         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
787         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
788         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
789
790         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
791         {
792           game.belt_dir[belt_nr] = belt_dir;
793           game.belt_dir_nr[belt_nr] = belt_dir_nr;
794         }
795         else    /* more than one switch -- set it like the first switch */
796         {
797           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
798         }
799       }
800       break;
801
802     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
803       if (init_game)
804         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
805       break;
806
807     case EL_LIGHT_SWITCH_ACTIVE:
808       if (init_game)
809         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
810       break;
811
812     default:
813       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
814         InitMovDir(x, y);
815       else if (IS_GROUP_ELEMENT(element))
816       {
817         struct ElementGroupInfo *group = element_info[element].group;
818         int last_anim_random_frame = gfx.anim_random_frame;
819         int element_pos;
820
821         if (group->choice_mode == ANIM_RANDOM)
822           gfx.anim_random_frame = RND(group->num_elements_resolved);
823
824         element_pos = getAnimationFrame(group->num_elements_resolved, 1,
825                                         group->choice_mode, 0,
826                                         group->choice_pos);
827
828         if (group->choice_mode == ANIM_RANDOM)
829           gfx.anim_random_frame = last_anim_random_frame;
830
831         group->choice_pos++;
832
833         Feld[x][y] = group->element_resolved[element_pos];
834
835         InitField(x, y, init_game);
836       }
837       break;
838   }
839 }
840
841 void DrawGameDoorValues()
842 {
843   int i, j;
844
845   for (i = 0; i < MAX_PLAYERS; i++)
846     for (j = 0; j < 4; j++)
847       if (stored_player[i].key[j])
848         DrawMiniGraphicExt(drawto, DX_KEYS + j * MINI_TILEX, DY_KEYS,
849                            el2edimg(EL_KEY_1 + j));
850
851   DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
852            int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
853   DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
854            int2str(local_player->inventory_size, 3), FONT_TEXT_2);
855   DrawText(DX + XX_SCORE, DY + YY_SCORE,
856            int2str(local_player->score, 5), FONT_TEXT_2);
857   DrawText(DX + XX_TIME, DY + YY_TIME,
858            int2str(TimeLeft, 3), FONT_TEXT_2);
859 }
860
861 static void resolve_group_element(int group_element, int recursion_depth)
862 {
863   static int group_nr;
864   static struct ElementGroupInfo *group;
865   struct ElementGroupInfo *actual_group = element_info[group_element].group;
866   int i;
867
868   if (recursion_depth > NUM_GROUP_ELEMENTS)     /* recursion too deep */
869   {
870     Error(ERR_WARN, "recursion too deep when resolving group element %d",
871           group_element - EL_GROUP_START + 1);
872
873     /* replace element which caused too deep recursion by question mark */
874     group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
875
876     return;
877   }
878
879   if (recursion_depth == 0)                     /* initialization */
880   {
881     group = element_info[group_element].group;
882     group_nr = group_element - EL_GROUP_START;
883
884     group->num_elements_resolved = 0;
885     group->choice_pos = 0;
886   }
887
888   for (i = 0; i < actual_group->num_elements; i++)
889   {
890     int element = actual_group->element[i];
891
892     if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
893       break;
894
895     if (IS_GROUP_ELEMENT(element))
896       resolve_group_element(element, recursion_depth + 1);
897     else
898     {
899       group->element_resolved[group->num_elements_resolved++] = element;
900       element_info[element].in_group[group_nr] = TRUE;
901     }
902   }
903
904 #if 0
905   if (recursion_depth == 0 && group_element <= EL_GROUP_4)
906   {
907     printf("::: group %d: %d resolved elements\n",
908            group_element - EL_GROUP_START, group->num_elements_resolved);
909     for (i = 0; i < group->num_elements_resolved; i++)
910       printf("::: - %d ['%s']\n", group->element_resolved[i],
911              element_info[group->element_resolved[i]].token_name);
912   }
913 #endif
914 }
915
916
917 /*
918   =============================================================================
919   InitGameEngine()
920   -----------------------------------------------------------------------------
921   initialize game engine due to level / tape version number
922   =============================================================================
923 */
924
925 static void InitGameEngine()
926 {
927   int i, j, k;
928
929   /* set game engine from tape file when re-playing, else from level file */
930   game.engine_version = (tape.playing ? tape.engine_version :
931                          level.game_version);
932
933   /* dynamically adjust element properties according to game engine version */
934   InitElementPropertiesEngine(game.engine_version);
935
936 #if 0
937   printf("level %d: level version == %06d\n", level_nr, level.game_version);
938   printf("          tape version == %06d [%s] [file: %06d]\n",
939          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
940          tape.file_version);
941   printf("       => game.engine_version == %06d\n", game.engine_version);
942 #endif
943
944   /* ---------- recursively resolve group elements ------------------------- */
945
946   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
947     for (j = 0; j < NUM_GROUP_ELEMENTS; j++)
948       element_info[i].in_group[j] = FALSE;
949
950   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
951     resolve_group_element(EL_GROUP_START + i, 0);
952
953   /* ---------- initialize player's initial move delay --------------------- */
954
955   /* dynamically adjust player properties according to game engine version */
956   game.initial_move_delay =
957     (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
958      INITIAL_MOVE_DELAY_OFF);
959
960   /* dynamically adjust player properties according to level information */
961   game.initial_move_delay_value =
962     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
963
964   /* ---------- initialize player's initial push delay --------------------- */
965
966   /* dynamically adjust player properties according to game engine version */
967   game.initial_push_delay_value =
968     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
969
970   /* ---------- initialize changing elements ------------------------------- */
971
972   /* initialize changing elements information */
973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
974   {
975     struct ElementInfo *ei = &element_info[i];
976
977     /* this pointer might have been changed in the level editor */
978     ei->change = &ei->change_page[0];
979
980     if (!IS_CUSTOM_ELEMENT(i))
981     {
982       ei->change->target_element = EL_EMPTY_SPACE;
983       ei->change->delay_fixed = 0;
984       ei->change->delay_random = 0;
985       ei->change->delay_frames = 1;
986     }
987
988     ei->change_events = CE_BITMASK_DEFAULT;
989     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
990     {
991       ei->event_page_nr[j] = 0;
992       ei->event_page[j] = &ei->change_page[0];
993     }
994   }
995
996   /* add changing elements from pre-defined list */
997   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
998   {
999     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
1000     struct ElementInfo *ei = &element_info[ch_delay->element];
1001
1002     ei->change->target_element       = ch_delay->target_element;
1003     ei->change->delay_fixed          = ch_delay->change_delay;
1004
1005     ei->change->pre_change_function  = ch_delay->pre_change_function;
1006     ei->change->change_function      = ch_delay->change_function;
1007     ei->change->post_change_function = ch_delay->post_change_function;
1008
1009     ei->change_events |= CH_EVENT_BIT(CE_DELAY);
1010   }
1011
1012 #if 1
1013   /* add change events from custom element configuration */
1014   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1015   {
1016     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
1017
1018     for (j = 0; j < ei->num_change_pages; j++)
1019     {
1020       if (!ei->change_page[j].can_change)
1021         continue;
1022
1023       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
1024       {
1025         /* only add event page for the first page found with this event */
1026         if (ei->change_page[j].events & CH_EVENT_BIT(k) &&
1027             !(ei->change_events & CH_EVENT_BIT(k)))
1028         {
1029           ei->change_events |= CH_EVENT_BIT(k);
1030           ei->event_page_nr[k] = j;
1031           ei->event_page[k] = &ei->change_page[j];
1032         }
1033       }
1034     }
1035   }
1036
1037 #else
1038
1039   /* add change events from custom element configuration */
1040   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
1041   {
1042     int element = EL_CUSTOM_START + i;
1043
1044     /* only add custom elements that change after fixed/random frame delay */
1045     if (CAN_CHANGE(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
1046       element_info[element].change_events |= CH_EVENT_BIT(CE_DELAY);
1047   }
1048 #endif
1049
1050   /* ---------- initialize trigger events ---------------------------------- */
1051
1052   /* initialize trigger events information */
1053   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1054     trigger_events[i] = EP_BITMASK_DEFAULT;
1055
1056 #if 1
1057   /* add trigger events from element change event properties */
1058   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1059   {
1060     struct ElementInfo *ei = &element_info[i];
1061
1062     for (j = 0; j < ei->num_change_pages; j++)
1063     {
1064       if (!ei->change_page[j].can_change)
1065         continue;
1066
1067       if (ei->change_page[j].events & CH_EVENT_BIT(CE_BY_OTHER_ACTION))
1068       {
1069         int trigger_element = ei->change_page[j].trigger_element;
1070
1071         if (IS_GROUP_ELEMENT(trigger_element))
1072         {
1073           struct ElementGroupInfo *group = element_info[trigger_element].group;
1074
1075           for (k = 0; k < group->num_elements_resolved; k++)
1076             trigger_events[group->element_resolved[k]]
1077               |= ei->change_page[j].events;
1078         }
1079         else
1080           trigger_events[trigger_element] |= ei->change_page[j].events;
1081       }
1082     }
1083   }
1084 #else
1085   /* add trigger events from element change event properties */
1086   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1087     if (HAS_CHANGE_EVENT(i, CE_BY_OTHER_ACTION))
1088       trigger_events[element_info[i].change->trigger_element] |=
1089         element_info[i].change->events;
1090 #endif
1091
1092   /* ---------- initialize push delay -------------------------------------- */
1093
1094   /* initialize push delay values to default */
1095   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1096   {
1097     if (!IS_CUSTOM_ELEMENT(i))
1098     {
1099       element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
1100       element_info[i].push_delay_random = game.default_push_delay_random;
1101     }
1102   }
1103
1104   /* set push delay value for certain elements from pre-defined list */
1105   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
1106   {
1107     int e = push_delay_list[i].element;
1108
1109     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
1110     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
1111   }
1112
1113   /* set push delay value for Supaplex elements for newer engine versions */
1114   if (game.engine_version >= VERSION_IDENT(3,0,9,0))
1115   {
1116     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1117     {
1118       if (IS_SP_ELEMENT(i))
1119       {
1120         element_info[i].push_delay_fixed  = 6;
1121         element_info[i].push_delay_random = 0;
1122       }
1123     }
1124   }
1125
1126   /* ---------- initialize move stepsize ----------------------------------- */
1127
1128   /* initialize move stepsize values to default */
1129   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1130     if (!IS_CUSTOM_ELEMENT(i))
1131       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
1132
1133   /* set move stepsize value for certain elements from pre-defined list */
1134   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
1135   {
1136     int e = move_stepsize_list[i].element;
1137
1138     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
1139   }
1140
1141   /* ---------- initialize move dig/leave ---------------------------------- */
1142
1143   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1144   {
1145     element_info[i].can_leave_element = FALSE;
1146     element_info[i].can_leave_element_last = FALSE;
1147   }
1148
1149   /* ---------- initialize gem count --------------------------------------- */
1150
1151   /* initialize gem count values for each element */
1152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1153     if (!IS_CUSTOM_ELEMENT(i))
1154       element_info[i].collect_count = 0;
1155
1156   /* add gem count values for all elements from pre-defined list */
1157   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
1158     element_info[collect_count_list[i].element].collect_count =
1159       collect_count_list[i].count;
1160 }
1161
1162
1163 /*
1164   =============================================================================
1165   InitGame()
1166   -----------------------------------------------------------------------------
1167   initialize and start new game
1168   =============================================================================
1169 */
1170
1171 void InitGame()
1172 {
1173   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
1174   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
1175   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
1176   int i, j, k, x, y;
1177
1178   InitGameEngine();
1179
1180 #if 0
1181 #if DEBUG
1182 #if USE_NEW_AMOEBA_CODE
1183   printf("Using new amoeba code.\n");
1184 #else
1185   printf("Using old amoeba code.\n");
1186 #endif
1187 #endif
1188 #endif
1189
1190   /* don't play tapes over network */
1191   network_playing = (options.network && !tape.playing);
1192
1193   for (i = 0; i < MAX_PLAYERS; i++)
1194   {
1195     struct PlayerInfo *player = &stored_player[i];
1196
1197     player->index_nr = i;
1198     player->element_nr = EL_PLAYER_1 + i;
1199
1200     player->present = FALSE;
1201     player->active = FALSE;
1202
1203     player->action = 0;
1204     player->effective_action = 0;
1205     player->programmed_action = 0;
1206
1207     player->score = 0;
1208     player->gems_still_needed = level.gems_needed;
1209     player->sokobanfields_still_needed = 0;
1210     player->lights_still_needed = 0;
1211     player->friends_still_needed = 0;
1212
1213     for (j = 0; j < 4; j++)
1214       player->key[j] = FALSE;
1215
1216     player->dynabomb_count = 0;
1217     player->dynabomb_size = 1;
1218     player->dynabombs_left = 0;
1219     player->dynabomb_xl = FALSE;
1220
1221     player->MovDir = MV_NO_MOVING;
1222     player->MovPos = 0;
1223     player->GfxPos = 0;
1224     player->GfxDir = MV_NO_MOVING;
1225     player->GfxAction = ACTION_DEFAULT;
1226     player->Frame = 0;
1227     player->StepFrame = 0;
1228
1229     player->use_murphy_graphic = FALSE;
1230
1231     player->block_last_field = FALSE;
1232
1233     player->actual_frame_counter = 0;
1234
1235     player->step_counter = 0;
1236
1237     player->last_move_dir = MV_NO_MOVING;
1238
1239     player->is_waiting = FALSE;
1240     player->is_moving = FALSE;
1241     player->is_digging = FALSE;
1242     player->is_snapping = FALSE;
1243     player->is_collecting = FALSE;
1244     player->is_pushing = FALSE;
1245     player->is_switching = FALSE;
1246     player->is_dropping = FALSE;
1247
1248     player->is_bored = FALSE;
1249     player->is_sleeping = FALSE;
1250
1251     player->frame_counter_bored = -1;
1252     player->frame_counter_sleeping = -1;
1253
1254     player->anim_delay_counter = 0;
1255     player->post_delay_counter = 0;
1256
1257     player->action_waiting = ACTION_DEFAULT;
1258     player->last_action_waiting = ACTION_DEFAULT;
1259     player->special_action_bored = ACTION_DEFAULT;
1260     player->special_action_sleeping = ACTION_DEFAULT;
1261
1262     player->num_special_action_bored = 0;
1263     player->num_special_action_sleeping = 0;
1264
1265     /* determine number of special actions for bored and sleeping animation */
1266     for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
1267     {
1268       boolean found = FALSE;
1269
1270       for (k = 0; k < NUM_DIRECTIONS; k++)
1271         if (el_act_dir2img(player->element_nr, j, k) !=
1272             el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1273           found = TRUE;
1274
1275       if (found)
1276         player->num_special_action_bored++;
1277       else
1278         break;
1279     }
1280     for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
1281     {
1282       boolean found = FALSE;
1283
1284       for (k = 0; k < NUM_DIRECTIONS; k++)
1285         if (el_act_dir2img(player->element_nr, j, k) !=
1286             el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
1287           found = TRUE;
1288
1289       if (found)
1290         player->num_special_action_sleeping++;
1291       else
1292         break;
1293     }
1294
1295     player->switch_x = -1;
1296     player->switch_y = -1;
1297
1298     player->show_envelope = 0;
1299
1300     player->move_delay       = game.initial_move_delay;
1301     player->move_delay_value = game.initial_move_delay_value;
1302
1303     player->move_delay_reset_counter = 0;
1304
1305     player->push_delay = 0;
1306     player->push_delay_value = game.initial_push_delay_value;
1307
1308     player->drop_delay = 0;
1309
1310     player->last_jx = player->last_jy = 0;
1311     player->jx = player->jy = 0;
1312
1313     player->shield_normal_time_left = 0;
1314     player->shield_deadly_time_left = 0;
1315
1316     player->inventory_size = 0;
1317
1318     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
1319     SnapField(player, 0, 0);
1320
1321     player->LevelSolved = FALSE;
1322     player->GameOver = FALSE;
1323   }
1324
1325   network_player_action_received = FALSE;
1326
1327 #if defined(PLATFORM_UNIX)
1328   /* initial null action */
1329   if (network_playing)
1330     SendToServer_MovePlayer(MV_NO_MOVING);
1331 #endif
1332
1333   ZX = ZY = -1;
1334
1335   FrameCounter = 0;
1336   TimeFrames = 0;
1337   TimePlayed = 0;
1338   TimeLeft = level.time;
1339
1340   ScreenMovDir = MV_NO_MOVING;
1341   ScreenMovPos = 0;
1342   ScreenGfxPos = 0;
1343
1344   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
1345
1346   AllPlayersGone = FALSE;
1347
1348   game.yamyam_content_nr = 0;
1349   game.magic_wall_active = FALSE;
1350   game.magic_wall_time_left = 0;
1351   game.light_time_left = 0;
1352   game.timegate_time_left = 0;
1353   game.switchgate_pos = 0;
1354   game.balloon_dir = MV_NO_MOVING;
1355   game.gravity = level.initial_gravity;
1356   game.explosions_delayed = TRUE;
1357
1358   game.envelope_active = FALSE;
1359
1360   for (i = 0; i < 4; i++)
1361   {
1362     game.belt_dir[i] = MV_NO_MOVING;
1363     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
1364   }
1365
1366   for (i = 0; i < MAX_NUM_AMOEBA; i++)
1367     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
1368
1369   for (x = 0; x < lev_fieldx; x++)
1370   {
1371     for (y = 0; y < lev_fieldy; y++)
1372     {
1373       Feld[x][y] = level.field[x][y];
1374       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
1375       ChangeDelay[x][y] = 0;
1376       ChangePage[x][y] = -1;
1377       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
1378       AmoebaNr[x][y] = 0;
1379       WasJustMoving[x][y] = 0;
1380       WasJustFalling[x][y] = 0;
1381       Stop[x][y] = FALSE;
1382       Pushed[x][y] = FALSE;
1383
1384       Changed[x][y] = CE_BITMASK_DEFAULT;
1385       ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
1386
1387       ExplodePhase[x][y] = 0;
1388       ExplodeDelay[x][y] = 0;
1389       ExplodeField[x][y] = EX_NO_EXPLOSION;
1390
1391       RunnerVisit[x][y] = 0;
1392       PlayerVisit[x][y] = 0;
1393
1394       GfxFrame[x][y] = 0;
1395       GfxRandom[x][y] = INIT_GFX_RANDOM();
1396       GfxElement[x][y] = EL_UNDEFINED;
1397       GfxAction[x][y] = ACTION_DEFAULT;
1398       GfxDir[x][y] = MV_NO_MOVING;
1399     }
1400   }
1401
1402   for (y = 0; y < lev_fieldy; y++)
1403   {
1404     for (x = 0; x < lev_fieldx; x++)
1405     {
1406       if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
1407         emulate_bd = FALSE;
1408       if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
1409         emulate_sb = FALSE;
1410       if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
1411         emulate_sp = FALSE;
1412
1413       InitField(x, y, TRUE);
1414     }
1415   }
1416
1417   InitBeltMovement();
1418
1419   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
1420                     emulate_sb ? EMU_SOKOBAN :
1421                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
1422
1423   /* initialize explosion and ignition delay */
1424   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1425   {
1426     if (!IS_CUSTOM_ELEMENT(i))
1427     {
1428       int num_phase = 9;
1429       int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
1430       int last_phase = num_phase * delay;
1431       int half_phase = (num_phase / 2) * delay;
1432
1433       element_info[i].explosion_delay = last_phase;
1434       element_info[i].ignition_delay = half_phase;
1435
1436       if (i == EL_BLACK_ORB)
1437         element_info[i].ignition_delay = 1;
1438     }
1439
1440     if (element_info[i].explosion_delay < 2)    /* !!! check again !!! */
1441       element_info[i].explosion_delay = 2;
1442
1443     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
1444       element_info[i].ignition_delay = 1;
1445   }
1446
1447   /* correct non-moving belts to start moving left */
1448   for (i = 0; i < 4; i++)
1449     if (game.belt_dir[i] == MV_NO_MOVING)
1450       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
1451
1452   /* check if any connected player was not found in playfield */
1453   for (i = 0; i < MAX_PLAYERS; i++)
1454   {
1455     struct PlayerInfo *player = &stored_player[i];
1456
1457     if (player->connected && !player->present)
1458     {
1459       for (j = 0; j < MAX_PLAYERS; j++)
1460       {
1461         struct PlayerInfo *some_player = &stored_player[j];
1462         int jx = some_player->jx, jy = some_player->jy;
1463
1464         /* assign first free player found that is present in the playfield */
1465         if (some_player->present && !some_player->connected)
1466         {
1467           player->present = TRUE;
1468           player->active = TRUE;
1469
1470           some_player->present = FALSE;
1471           some_player->active = FALSE;
1472
1473           StorePlayer[jx][jy] = player->element_nr;
1474           player->jx = player->last_jx = jx;
1475           player->jy = player->last_jy = jy;
1476
1477           break;
1478         }
1479       }
1480     }
1481   }
1482
1483   if (tape.playing)
1484   {
1485     /* when playing a tape, eliminate all players which do not participate */
1486
1487     for (i = 0; i < MAX_PLAYERS; i++)
1488     {
1489       if (stored_player[i].active && !tape.player_participates[i])
1490       {
1491         struct PlayerInfo *player = &stored_player[i];
1492         int jx = player->jx, jy = player->jy;
1493
1494         player->active = FALSE;
1495         StorePlayer[jx][jy] = 0;
1496         Feld[jx][jy] = EL_EMPTY;
1497       }
1498     }
1499   }
1500   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
1501   {
1502     /* when in single player mode, eliminate all but the first active player */
1503
1504     for (i = 0; i < MAX_PLAYERS; i++)
1505     {
1506       if (stored_player[i].active)
1507       {
1508         for (j = i + 1; j < MAX_PLAYERS; j++)
1509         {
1510           if (stored_player[j].active)
1511           {
1512             struct PlayerInfo *player = &stored_player[j];
1513             int jx = player->jx, jy = player->jy;
1514
1515             player->active = FALSE;
1516             player->present = FALSE;
1517
1518             StorePlayer[jx][jy] = 0;
1519             Feld[jx][jy] = EL_EMPTY;
1520           }
1521         }
1522       }
1523     }
1524   }
1525
1526   /* when recording the game, store which players take part in the game */
1527   if (tape.recording)
1528   {
1529     for (i = 0; i < MAX_PLAYERS; i++)
1530       if (stored_player[i].active)
1531         tape.player_participates[i] = TRUE;
1532   }
1533
1534   if (options.debug)
1535   {
1536     for (i = 0; i < MAX_PLAYERS; i++)
1537     {
1538       struct PlayerInfo *player = &stored_player[i];
1539
1540       printf("Player %d: present == %d, connected == %d, active == %d.\n",
1541              i+1,
1542              player->present,
1543              player->connected,
1544              player->active);
1545       if (local_player == player)
1546         printf("Player  %d is local player.\n", i+1);
1547     }
1548   }
1549
1550   if (BorderElement == EL_EMPTY)
1551   {
1552     SBX_Left = 0;
1553     SBX_Right = lev_fieldx - SCR_FIELDX;
1554     SBY_Upper = 0;
1555     SBY_Lower = lev_fieldy - SCR_FIELDY;
1556   }
1557   else
1558   {
1559     SBX_Left = -1;
1560     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
1561     SBY_Upper = -1;
1562     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
1563   }
1564
1565   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
1566     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
1567
1568   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
1569     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
1570
1571   /* if local player not found, look for custom element that might create
1572      the player (make some assumptions about the right custom element) */
1573   if (!local_player->present)
1574   {
1575     int start_x = 0, start_y = 0;
1576     int found_rating = 0;
1577     int found_element = EL_UNDEFINED;
1578
1579     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
1580     {
1581       int element = Feld[x][y];
1582       int content;
1583       int xx, yy;
1584       boolean is_player;
1585
1586       if (!IS_CUSTOM_ELEMENT(element))
1587         continue;
1588
1589       if (CAN_CHANGE(element))
1590       {
1591         for (i = 0; i < element_info[element].num_change_pages; i++)
1592         {
1593           content = element_info[element].change_page[i].target_element;
1594           is_player = ELEM_IS_PLAYER(content);
1595
1596           if (is_player && (found_rating < 3 || element < found_element))
1597           {
1598             start_x = x;
1599             start_y = y;
1600
1601             found_rating = 3;
1602             found_element = element;
1603           }
1604         }
1605       }
1606
1607       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
1608       {
1609         content = element_info[element].content[xx][yy];
1610         is_player = ELEM_IS_PLAYER(content);
1611
1612         if (is_player && (found_rating < 2 || element < found_element))
1613         {
1614           start_x = x + xx - 1;
1615           start_y = y + yy - 1;
1616
1617           found_rating = 2;
1618           found_element = element;
1619         }
1620
1621         if (!CAN_CHANGE(element))
1622           continue;
1623
1624         for (i = 0; i < element_info[element].num_change_pages; i++)
1625         {
1626           content = element_info[element].change_page[i].content[xx][yy];
1627           is_player = ELEM_IS_PLAYER(content);
1628
1629           if (is_player && (found_rating < 1 || element < found_element))
1630           {
1631             start_x = x + xx - 1;
1632             start_y = y + yy - 1;
1633
1634             found_rating = 1;
1635             found_element = element;
1636           }
1637         }
1638       }
1639     }
1640
1641     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
1642                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
1643                 start_x - MIDPOSX);
1644
1645     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
1646                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
1647                 start_y - MIDPOSY);
1648   }
1649   else
1650   {
1651 #if 1
1652     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
1653                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
1654                 local_player->jx - MIDPOSX);
1655
1656     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
1657                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
1658                 local_player->jy - MIDPOSY);
1659 #else
1660     scroll_x = SBX_Left;
1661     scroll_y = SBY_Upper;
1662     if (local_player->jx >= SBX_Left + MIDPOSX)
1663       scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
1664                   local_player->jx - MIDPOSX :
1665                   SBX_Right);
1666     if (local_player->jy >= SBY_Upper + MIDPOSY)
1667       scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
1668                   local_player->jy - MIDPOSY :
1669                   SBY_Lower);
1670 #endif
1671   }
1672
1673   CloseDoor(DOOR_CLOSE_1);
1674
1675   DrawLevel();
1676   DrawAllPlayers();
1677
1678   /* after drawing the level, correct some elements */
1679   if (game.timegate_time_left == 0)
1680     CloseAllOpenTimegates();
1681
1682   if (setup.soft_scrolling)
1683     BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
1684
1685   redraw_mask |= REDRAW_FROM_BACKBUFFER;
1686   FadeToFront();
1687
1688   /* copy default game door content to main double buffer */
1689   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
1690              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
1691
1692   if (level_nr < 100)
1693     DrawText(DX + XX_LEVEL, DY + YY_LEVEL, int2str(level_nr, 2), FONT_TEXT_2);
1694   else
1695   {
1696     DrawTextExt(drawto, DX + XX_EMERALDS, DY + YY_EMERALDS,
1697                 int2str(level_nr, 3), FONT_LEVEL_NUMBER, BLIT_OPAQUE);
1698     BlitBitmap(drawto, drawto,
1699                DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
1700                getFontWidth(FONT_LEVEL_NUMBER) * 3,
1701                getFontHeight(FONT_LEVEL_NUMBER) - 1,
1702                DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
1703   }
1704
1705   DrawGameDoorValues();
1706
1707   UnmapGameButtons();
1708   UnmapTapeButtons();
1709   game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
1710   game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
1711   game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
1712   MapGameButtons();
1713   MapTapeButtons();
1714
1715   /* copy actual game door content to door double buffer for OpenDoor() */
1716   BlitBitmap(drawto, bitmap_db_door,
1717              DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
1718
1719   OpenDoor(DOOR_OPEN_ALL);
1720
1721   PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
1722
1723   if (setup.sound_music)
1724     PlayLevelMusic();
1725
1726   KeyboardAutoRepeatOffUnlessAutoplay();
1727
1728   if (options.debug)
1729   {
1730     for (i = 0; i < 4; i++)
1731       printf("Player %d %sactive.\n",
1732              i + 1, (stored_player[i].active ? "" : "not "));
1733   }
1734
1735 #if 0
1736   printf("::: starting game [%d]\n", FrameCounter);
1737 #endif
1738 }
1739
1740 void InitMovDir(int x, int y)
1741 {
1742   int i, element = Feld[x][y];
1743   static int xy[4][2] =
1744   {
1745     {  0, +1 },
1746     { +1,  0 },
1747     {  0, -1 },
1748     { -1,  0 }
1749   };
1750   static int direction[3][4] =
1751   {
1752     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
1753     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
1754     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
1755   };
1756
1757   switch(element)
1758   {
1759     case EL_BUG_RIGHT:
1760     case EL_BUG_UP:
1761     case EL_BUG_LEFT:
1762     case EL_BUG_DOWN:
1763       Feld[x][y] = EL_BUG;
1764       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
1765       break;
1766
1767     case EL_SPACESHIP_RIGHT:
1768     case EL_SPACESHIP_UP:
1769     case EL_SPACESHIP_LEFT:
1770     case EL_SPACESHIP_DOWN:
1771       Feld[x][y] = EL_SPACESHIP;
1772       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
1773       break;
1774
1775     case EL_BD_BUTTERFLY_RIGHT:
1776     case EL_BD_BUTTERFLY_UP:
1777     case EL_BD_BUTTERFLY_LEFT:
1778     case EL_BD_BUTTERFLY_DOWN:
1779       Feld[x][y] = EL_BD_BUTTERFLY;
1780       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
1781       break;
1782
1783     case EL_BD_FIREFLY_RIGHT:
1784     case EL_BD_FIREFLY_UP:
1785     case EL_BD_FIREFLY_LEFT:
1786     case EL_BD_FIREFLY_DOWN:
1787       Feld[x][y] = EL_BD_FIREFLY;
1788       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
1789       break;
1790
1791     case EL_PACMAN_RIGHT:
1792     case EL_PACMAN_UP:
1793     case EL_PACMAN_LEFT:
1794     case EL_PACMAN_DOWN:
1795       Feld[x][y] = EL_PACMAN;
1796       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
1797       break;
1798
1799     case EL_SP_SNIKSNAK:
1800       MovDir[x][y] = MV_UP;
1801       break;
1802
1803     case EL_SP_ELECTRON:
1804       MovDir[x][y] = MV_LEFT;
1805       break;
1806
1807     case EL_MOLE_LEFT:
1808     case EL_MOLE_RIGHT:
1809     case EL_MOLE_UP:
1810     case EL_MOLE_DOWN:
1811       Feld[x][y] = EL_MOLE;
1812       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
1813       break;
1814
1815     default:
1816       if (IS_CUSTOM_ELEMENT(element))
1817       {
1818         struct ElementInfo *ei = &element_info[element];
1819         int move_direction_initial = ei->move_direction_initial;
1820         int move_pattern = ei->move_pattern;
1821
1822         if (move_direction_initial == MV_START_PREVIOUS)
1823         {
1824           if (MovDir[x][y] != MV_NO_MOVING)
1825             return;
1826
1827           move_direction_initial = MV_START_AUTOMATIC;
1828         }
1829
1830         if (move_direction_initial == MV_START_RANDOM)
1831           MovDir[x][y] = 1 << RND(4);
1832         else if (move_direction_initial & MV_ANY_DIRECTION)
1833           MovDir[x][y] = move_direction_initial;
1834         else if (move_pattern == MV_ALL_DIRECTIONS ||
1835                  move_pattern == MV_TURNING_LEFT ||
1836                  move_pattern == MV_TURNING_RIGHT ||
1837                  move_pattern == MV_TURNING_LEFT_RIGHT ||
1838                  move_pattern == MV_TURNING_RIGHT_LEFT ||
1839                  move_pattern == MV_TURNING_RANDOM)
1840           MovDir[x][y] = 1 << RND(4);
1841         else if (move_pattern == MV_HORIZONTAL)
1842           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
1843         else if (move_pattern == MV_VERTICAL)
1844           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
1845         else if (move_pattern & MV_ANY_DIRECTION)
1846           MovDir[x][y] = element_info[element].move_pattern;
1847         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
1848                  move_pattern == MV_ALONG_RIGHT_SIDE)
1849         {
1850           for (i = 0; i < 4; i++)
1851           {
1852             int x1 = x + xy[i][0];
1853             int y1 = y + xy[i][1];
1854
1855             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1856             {
1857               if (move_pattern == MV_ALONG_RIGHT_SIDE)
1858                 MovDir[x][y] = direction[0][i];
1859               else
1860                 MovDir[x][y] = direction[1][i];
1861
1862               break;
1863             }
1864           }
1865         }                
1866       }
1867       else
1868       {
1869         MovDir[x][y] = 1 << RND(4);
1870
1871         if (element != EL_BUG &&
1872             element != EL_SPACESHIP &&
1873             element != EL_BD_BUTTERFLY &&
1874             element != EL_BD_FIREFLY)
1875           break;
1876
1877         for (i = 0; i < 4; i++)
1878         {
1879           int x1 = x + xy[i][0];
1880           int y1 = y + xy[i][1];
1881
1882           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
1883           {
1884             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
1885             {
1886               MovDir[x][y] = direction[0][i];
1887               break;
1888             }
1889             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
1890                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1891             {
1892               MovDir[x][y] = direction[1][i];
1893               break;
1894             }
1895           }
1896         }
1897       }
1898       break;
1899   }
1900
1901   GfxDir[x][y] = MovDir[x][y];
1902 }
1903
1904 void InitAmoebaNr(int x, int y)
1905 {
1906   int i;
1907   int group_nr = AmoebeNachbarNr(x, y);
1908
1909   if (group_nr == 0)
1910   {
1911     for (i = 1; i < MAX_NUM_AMOEBA; i++)
1912     {
1913       if (AmoebaCnt[i] == 0)
1914       {
1915         group_nr = i;
1916         break;
1917       }
1918     }
1919   }
1920
1921   AmoebaNr[x][y] = group_nr;
1922   AmoebaCnt[group_nr]++;
1923   AmoebaCnt2[group_nr]++;
1924 }
1925
1926 void GameWon()
1927 {
1928   int hi_pos;
1929   boolean raise_level = FALSE;
1930
1931   if (local_player->MovPos)
1932     return;
1933
1934 #if 1
1935   if (tape.auto_play)           /* tape might already be stopped here */
1936     tape.auto_play_level_solved = TRUE;
1937 #else
1938   if (tape.playing && tape.auto_play)
1939     tape.auto_play_level_solved = TRUE;
1940 #endif
1941
1942   local_player->LevelSolved = FALSE;
1943
1944   PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
1945
1946   if (TimeLeft)
1947   {
1948     if (!tape.playing && setup.sound_loops)
1949       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1950                    SND_CTRL_PLAY_LOOP);
1951
1952     while (TimeLeft > 0)
1953     {
1954       if (!tape.playing && !setup.sound_loops)
1955         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1956       if (TimeLeft > 0 && !(TimeLeft % 10))
1957         RaiseScore(level.score[SC_TIME_BONUS]);
1958       if (TimeLeft > 100 && !(TimeLeft % 10))
1959         TimeLeft -= 10;
1960       else
1961         TimeLeft--;
1962       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
1963       BackToFront();
1964
1965       if (!tape.playing)
1966         Delay(10);
1967     }
1968
1969     if (!tape.playing && setup.sound_loops)
1970       StopSound(SND_GAME_LEVELTIME_BONUS);
1971   }
1972   else if (level.time == 0)             /* level without time limit */
1973   {
1974     if (!tape.playing && setup.sound_loops)
1975       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
1976                    SND_CTRL_PLAY_LOOP);
1977
1978     while (TimePlayed < 999)
1979     {
1980       if (!tape.playing && !setup.sound_loops)
1981         PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
1982       if (TimePlayed < 999 && !(TimePlayed % 10))
1983         RaiseScore(level.score[SC_TIME_BONUS]);
1984       if (TimePlayed < 900 && !(TimePlayed % 10))
1985         TimePlayed += 10;
1986       else
1987         TimePlayed++;
1988       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
1989       BackToFront();
1990
1991       if (!tape.playing)
1992         Delay(10);
1993     }
1994
1995     if (!tape.playing && setup.sound_loops)
1996       StopSound(SND_GAME_LEVELTIME_BONUS);
1997   }
1998
1999   /* close exit door after last player */
2000   if ((Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
2001        Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN) && AllPlayersGone)
2002   {
2003     int element = Feld[ExitX][ExitY];
2004
2005     Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
2006                           EL_SP_EXIT_CLOSING);
2007
2008     PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
2009   }
2010
2011   /* Hero disappears */
2012   DrawLevelField(ExitX, ExitY);
2013   BackToFront();
2014
2015   if (tape.playing)
2016     return;
2017
2018   CloseDoor(DOOR_CLOSE_1);
2019
2020   if (tape.recording)
2021   {
2022     TapeStop();
2023     SaveTape(tape.level_nr);            /* Ask to save tape */
2024   }
2025
2026   if (level_nr == leveldir_current->handicap_level)
2027   {
2028     leveldir_current->handicap_level++;
2029     SaveLevelSetup_SeriesInfo();
2030   }
2031
2032   if (level_editor_test_game)
2033     local_player->score = -1;   /* no highscore when playing from editor */
2034   else if (level_nr < leveldir_current->last_level)
2035     raise_level = TRUE;         /* advance to next level */
2036
2037   if ((hi_pos = NewHiScore()) >= 0) 
2038   {
2039     game_status = GAME_MODE_SCORES;
2040     DrawHallOfFame(hi_pos);
2041     if (raise_level)
2042     {
2043       level_nr++;
2044       TapeErase();
2045     }
2046   }
2047   else
2048   {
2049     game_status = GAME_MODE_MAIN;
2050     if (raise_level)
2051     {
2052       level_nr++;
2053       TapeErase();
2054     }
2055     DrawMainMenu();
2056   }
2057
2058   BackToFront();
2059 }
2060
2061 int NewHiScore()
2062 {
2063   int k, l;
2064   int position = -1;
2065
2066   LoadScore(level_nr);
2067
2068   if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
2069       local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
2070     return -1;
2071
2072   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
2073   {
2074     if (local_player->score > highscore[k].Score)
2075     {
2076       /* player has made it to the hall of fame */
2077
2078       if (k < MAX_SCORE_ENTRIES - 1)
2079       {
2080         int m = MAX_SCORE_ENTRIES - 1;
2081
2082 #ifdef ONE_PER_NAME
2083         for (l = k; l < MAX_SCORE_ENTRIES; l++)
2084           if (!strcmp(setup.player_name, highscore[l].Name))
2085             m = l;
2086         if (m == k)     /* player's new highscore overwrites his old one */
2087           goto put_into_list;
2088 #endif
2089
2090         for (l = m; l > k; l--)
2091         {
2092           strcpy(highscore[l].Name, highscore[l - 1].Name);
2093           highscore[l].Score = highscore[l - 1].Score;
2094         }
2095       }
2096
2097 #ifdef ONE_PER_NAME
2098       put_into_list:
2099 #endif
2100       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
2101       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
2102       highscore[k].Score = local_player->score; 
2103       position = k;
2104       break;
2105     }
2106
2107 #ifdef ONE_PER_NAME
2108     else if (!strncmp(setup.player_name, highscore[k].Name,
2109                       MAX_PLAYER_NAME_LEN))
2110       break;    /* player already there with a higher score */
2111 #endif
2112
2113   }
2114
2115   if (position >= 0) 
2116     SaveScore(level_nr);
2117
2118   return position;
2119 }
2120
2121 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
2122 {
2123   if (player->GfxAction != action || player->GfxDir != dir)
2124   {
2125 #if 0
2126     printf("Player frame reset! (%d => %d, %d => %d)\n",
2127            player->GfxAction, action, player->GfxDir, dir);
2128 #endif
2129
2130     player->GfxAction = action;
2131     player->GfxDir = dir;
2132     player->Frame = 0;
2133     player->StepFrame = 0;
2134   }
2135 }
2136
2137 static void ResetRandomAnimationValue(int x, int y)
2138 {
2139   GfxRandom[x][y] = INIT_GFX_RANDOM();
2140 }
2141
2142 static void ResetGfxAnimation(int x, int y)
2143 {
2144   GfxFrame[x][y] = 0;
2145   GfxAction[x][y] = ACTION_DEFAULT;
2146   GfxDir[x][y] = MovDir[x][y];
2147 }
2148
2149 void InitMovingField(int x, int y, int direction)
2150 {
2151   int element = Feld[x][y];
2152   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2153   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2154   int newx = x + dx;
2155   int newy = y + dy;
2156
2157   if (!WasJustMoving[x][y] || direction != MovDir[x][y])
2158     ResetGfxAnimation(x, y);
2159
2160   MovDir[newx][newy] = MovDir[x][y] = direction;
2161   GfxDir[x][y] = direction;
2162
2163   if (Feld[newx][newy] == EL_EMPTY)
2164     Feld[newx][newy] = EL_BLOCKED;
2165
2166   if (direction == MV_DOWN && CAN_FALL(element))
2167     GfxAction[x][y] = ACTION_FALLING;
2168   else
2169     GfxAction[x][y] = ACTION_MOVING;
2170
2171   GfxFrame[newx][newy] = GfxFrame[x][y];
2172   GfxRandom[newx][newy] = GfxRandom[x][y];
2173   GfxAction[newx][newy] = GfxAction[x][y];
2174   GfxDir[newx][newy] = GfxDir[x][y];
2175 }
2176
2177 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
2178 {
2179   int direction = MovDir[x][y];
2180   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2181   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2182
2183   *goes_to_x = newx;
2184   *goes_to_y = newy;
2185 }
2186
2187 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
2188 {
2189   int oldx = x, oldy = y;
2190   int direction = MovDir[x][y];
2191
2192   if (direction == MV_LEFT)
2193     oldx++;
2194   else if (direction == MV_RIGHT)
2195     oldx--;
2196   else if (direction == MV_UP)
2197     oldy++;
2198   else if (direction == MV_DOWN)
2199     oldy--;
2200
2201   *comes_from_x = oldx;
2202   *comes_from_y = oldy;
2203 }
2204
2205 int MovingOrBlocked2Element(int x, int y)
2206 {
2207   int element = Feld[x][y];
2208
2209   if (element == EL_BLOCKED)
2210   {
2211     int oldx, oldy;
2212
2213     Blocked2Moving(x, y, &oldx, &oldy);
2214     return Feld[oldx][oldy];
2215   }
2216   else
2217     return element;
2218 }
2219
2220 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
2221 {
2222   /* like MovingOrBlocked2Element(), but if element is moving
2223      and (x,y) is the field the moving element is just leaving,
2224      return EL_BLOCKED instead of the element value */
2225   int element = Feld[x][y];
2226
2227   if (IS_MOVING(x, y))
2228   {
2229     if (element == EL_BLOCKED)
2230     {
2231       int oldx, oldy;
2232
2233       Blocked2Moving(x, y, &oldx, &oldy);
2234       return Feld[oldx][oldy];
2235     }
2236     else
2237       return EL_BLOCKED;
2238   }
2239   else
2240     return element;
2241 }
2242
2243 static void RemoveField(int x, int y)
2244 {
2245   Feld[x][y] = EL_EMPTY;
2246
2247   MovPos[x][y] = 0;
2248   MovDir[x][y] = 0;
2249   MovDelay[x][y] = 0;
2250
2251   AmoebaNr[x][y] = 0;
2252   ChangeDelay[x][y] = 0;
2253   ChangePage[x][y] = -1;
2254   Pushed[x][y] = FALSE;
2255
2256   GfxElement[x][y] = EL_UNDEFINED;
2257   GfxAction[x][y] = ACTION_DEFAULT;
2258   GfxDir[x][y] = MV_NO_MOVING;
2259 }
2260
2261 void RemoveMovingField(int x, int y)
2262 {
2263   int oldx = x, oldy = y, newx = x, newy = y;
2264   int element = Feld[x][y];
2265   int next_element = EL_UNDEFINED;
2266
2267   if (element != EL_BLOCKED && !IS_MOVING(x, y))
2268     return;
2269
2270   if (IS_MOVING(x, y))
2271   {
2272     Moving2Blocked(x, y, &newx, &newy);
2273     if (Feld[newx][newy] != EL_BLOCKED)
2274       return;
2275   }
2276   else if (element == EL_BLOCKED)
2277   {
2278     Blocked2Moving(x, y, &oldx, &oldy);
2279     if (!IS_MOVING(oldx, oldy))
2280       return;
2281   }
2282
2283   if (element == EL_BLOCKED &&
2284       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
2285        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
2286        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
2287        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
2288     next_element = get_next_element(Feld[oldx][oldy]);
2289
2290   RemoveField(oldx, oldy);
2291   RemoveField(newx, newy);
2292
2293   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
2294
2295   if (next_element != EL_UNDEFINED)
2296     Feld[oldx][oldy] = next_element;
2297
2298   DrawLevelField(oldx, oldy);
2299   DrawLevelField(newx, newy);
2300 }
2301
2302 void DrawDynamite(int x, int y)
2303 {
2304   int sx = SCREENX(x), sy = SCREENY(y);
2305   int graphic = el2img(Feld[x][y]);
2306   int frame;
2307
2308   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
2309     return;
2310
2311   if (IS_WALKABLE_INSIDE(Back[x][y]))
2312     return;
2313
2314   if (Back[x][y])
2315     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
2316   else if (Store[x][y])
2317     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
2318
2319   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
2320
2321 #if 1
2322   if (Back[x][y] || Store[x][y])
2323     DrawGraphicThruMask(sx, sy, graphic, frame);
2324   else
2325     DrawGraphic(sx, sy, graphic, frame);
2326 #else
2327   if (game.emulation == EMU_SUPAPLEX)
2328     DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
2329   else if (Store[x][y])
2330     DrawGraphicThruMask(sx, sy, graphic, frame);
2331   else
2332     DrawGraphic(sx, sy, graphic, frame);
2333 #endif
2334 }
2335
2336 void CheckDynamite(int x, int y)
2337 {
2338   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
2339   {
2340     MovDelay[x][y]--;
2341
2342     if (MovDelay[x][y] != 0)
2343     {
2344       DrawDynamite(x, y);
2345       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2346
2347       return;
2348     }
2349   }
2350
2351 #if 1
2352   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
2353 #else
2354   if (Feld[x][y] == EL_DYNAMITE_ACTIVE ||
2355       Feld[x][y] == EL_SP_DISK_RED_ACTIVE)
2356     StopSound(SND_DYNAMITE_ACTIVE);
2357   else
2358     StopSound(SND_DYNABOMB_ACTIVE);
2359 #endif
2360
2361   Bang(x, y);
2362 }
2363
2364 void RelocatePlayer(int x, int y, int element)
2365 {
2366   struct PlayerInfo *player = &stored_player[element - EL_PLAYER_1];
2367   boolean ffwd_delay = (tape.playing && tape.fast_forward);
2368   boolean no_delay = (tape.index_search);
2369   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2370   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
2371
2372   if (player->GameOver)         /* do not reanimate dead player */
2373     return;
2374
2375 #if 1
2376   RemoveField(x, y);            /* temporarily remove newly placed player */
2377   DrawLevelField(x, y);
2378 #endif
2379
2380   if (player->present)
2381   {
2382     while (player->MovPos)
2383     {
2384       ScrollPlayer(player, SCROLL_GO_ON);
2385       ScrollScreen(NULL, SCROLL_GO_ON);
2386       FrameCounter++;
2387
2388       DrawPlayer(player);
2389
2390       BackToFront();
2391       Delay(wait_delay_value);
2392     }
2393
2394     DrawPlayer(player);         /* needed here only to cleanup last field */
2395     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
2396
2397     player->is_moving = FALSE;
2398   }
2399
2400   Feld[x][y] = element;
2401   InitPlayerField(x, y, element, TRUE);
2402
2403   if (player == local_player)
2404   {
2405     int scroll_xx = -999, scroll_yy = -999;
2406
2407     while (scroll_xx != scroll_x || scroll_yy != scroll_y)
2408     {
2409       int dx = 0, dy = 0;
2410       int fx = FX, fy = FY;
2411
2412       scroll_xx = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
2413                    local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
2414                    local_player->jx - MIDPOSX);
2415
2416       scroll_yy = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
2417                    local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
2418                    local_player->jy - MIDPOSY);
2419
2420       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
2421       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
2422
2423       scroll_x -= dx;
2424       scroll_y -= dy;
2425
2426       fx += dx * TILEX / 2;
2427       fy += dy * TILEY / 2;
2428
2429       ScrollLevel(dx, dy);
2430       DrawAllPlayers();
2431
2432       /* scroll in two steps of half tile size to make things smoother */
2433       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
2434       FlushDisplay();
2435       Delay(wait_delay_value);
2436
2437       /* scroll second step to align at full tile size */
2438       BackToFront();
2439       Delay(wait_delay_value);
2440     }
2441   }
2442 }
2443
2444 void Explode(int ex, int ey, int phase, int mode)
2445 {
2446   int x, y;
2447   int num_phase = 9;
2448   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
2449   int last_phase = num_phase * delay;
2450   int half_phase = (num_phase / 2) * delay;
2451   int first_phase_after_start = EX_PHASE_START + 1;
2452   int border_element;
2453
2454   int last_phase_TEST = last_phase;
2455
2456   if (game.explosions_delayed)
2457   {
2458     ExplodeField[ex][ey] = mode;
2459     return;
2460   }
2461
2462   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
2463   {
2464     int center_element = Feld[ex][ey];
2465
2466 #if 0
2467     /* --- This is only really needed (and now handled) in "Impact()". --- */
2468     /* do not explode moving elements that left the explode field in time */
2469     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
2470         center_element == EL_EMPTY && (mode == EX_NORMAL || mode == EX_CENTER))
2471       return;
2472 #endif
2473
2474     if (mode == EX_NORMAL || mode == EX_CENTER)
2475       PlayLevelSoundAction(ex, ey, ACTION_EXPLODING);
2476
2477     /* remove things displayed in background while burning dynamite */
2478     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
2479       Back[ex][ey] = 0;
2480
2481     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
2482     {
2483       /* put moving element to center field (and let it explode there) */
2484       center_element = MovingOrBlocked2Element(ex, ey);
2485       RemoveMovingField(ex, ey);
2486       Feld[ex][ey] = center_element;
2487     }
2488
2489 #if 1
2490     last_phase = element_info[center_element].explosion_delay;
2491 #endif
2492
2493     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
2494     {
2495       int xx = x - ex + 1;
2496       int yy = y - ey + 1;
2497       int element;
2498
2499       if (!IN_LEV_FIELD(x, y) ||
2500           ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
2501            (x != ex || y != ey)))
2502         continue;
2503
2504       element = Feld[x][y];
2505
2506       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
2507       {
2508         element = MovingOrBlocked2Element(x, y);
2509
2510         if (!IS_EXPLOSION_PROOF(element))
2511           RemoveMovingField(x, y);
2512       }
2513
2514 #if 1
2515
2516 #if 0
2517       if (IS_EXPLOSION_PROOF(element))
2518         continue;
2519 #else
2520       /* indestructible elements can only explode in center (but not flames) */
2521       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey)) ||
2522           element == EL_FLAMES)
2523         continue;
2524 #endif
2525
2526 #else
2527       if ((IS_INDESTRUCTIBLE(element) &&
2528            (game.engine_version < VERSION_IDENT(2,2,0,0) ||
2529             (!IS_WALKABLE_OVER(element) && !IS_WALKABLE_UNDER(element)))) ||
2530           element == EL_FLAMES)
2531         continue;
2532 #endif
2533
2534       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
2535       {
2536         if (IS_ACTIVE_BOMB(element))
2537         {
2538           /* re-activate things under the bomb like gate or penguin */
2539           Feld[x][y] = (Store[x][y] ? Store[x][y] : EL_EMPTY);
2540           Store[x][y] = 0;
2541         }
2542
2543         continue;
2544       }
2545
2546       /* save walkable background elements while explosion on same tile */
2547 #if 0
2548       if (IS_INDESTRUCTIBLE(element))
2549         Back[x][y] = element;
2550 #else
2551       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element))
2552         Back[x][y] = element;
2553 #endif
2554
2555       /* ignite explodable elements reached by other explosion */
2556       if (element == EL_EXPLOSION)
2557         element = Store2[x][y];
2558
2559 #if 1
2560       if (AmoebaNr[x][y] &&
2561           (element == EL_AMOEBA_FULL ||
2562            element == EL_BD_AMOEBA ||
2563            element == EL_AMOEBA_GROWING))
2564       {
2565         AmoebaCnt[AmoebaNr[x][y]]--;
2566         AmoebaCnt2[AmoebaNr[x][y]]--;
2567       }
2568
2569       RemoveField(x, y);
2570 #endif
2571
2572       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
2573       {
2574         switch(StorePlayer[ex][ey])
2575         {
2576           case EL_PLAYER_2:
2577             Store[x][y] = EL_PLAYER_IS_EXPLODING_2;
2578             break;
2579           case EL_PLAYER_3:
2580             Store[x][y] = EL_PLAYER_IS_EXPLODING_3;
2581             break;
2582           case EL_PLAYER_4:
2583             Store[x][y] = EL_PLAYER_IS_EXPLODING_4;
2584             break;
2585           case EL_PLAYER_1:
2586           default:
2587             Store[x][y] = EL_PLAYER_IS_EXPLODING_1;
2588             break;
2589         }
2590
2591         if (game.emulation == EMU_SUPAPLEX)
2592           Store[x][y] = EL_EMPTY;
2593       }
2594       else if (center_element == EL_MOLE)
2595         Store[x][y] = EL_EMERALD_RED;
2596       else if (center_element == EL_PENGUIN)
2597         Store[x][y] = EL_EMERALD_PURPLE;
2598       else if (center_element == EL_BUG)
2599         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMOND : EL_EMERALD);
2600       else if (center_element == EL_BD_BUTTERFLY)
2601         Store[x][y] = EL_BD_DIAMOND;
2602       else if (center_element == EL_SP_ELECTRON)
2603         Store[x][y] = EL_SP_INFOTRON;
2604       else if (center_element == EL_AMOEBA_TO_DIAMOND)
2605         Store[x][y] = level.amoeba_content;
2606       else if (center_element == EL_YAMYAM)
2607         Store[x][y] = level.yamyam_content[game.yamyam_content_nr][xx][yy];
2608       else if (IS_CUSTOM_ELEMENT(center_element) &&
2609                element_info[center_element].content[xx][yy] != EL_EMPTY)
2610         Store[x][y] = element_info[center_element].content[xx][yy];
2611       else if (element == EL_WALL_EMERALD)
2612         Store[x][y] = EL_EMERALD;
2613       else if (element == EL_WALL_DIAMOND)
2614         Store[x][y] = EL_DIAMOND;
2615       else if (element == EL_WALL_BD_DIAMOND)
2616         Store[x][y] = EL_BD_DIAMOND;
2617       else if (element == EL_WALL_EMERALD_YELLOW)
2618         Store[x][y] = EL_EMERALD_YELLOW;
2619       else if (element == EL_WALL_EMERALD_RED)
2620         Store[x][y] = EL_EMERALD_RED;
2621       else if (element == EL_WALL_EMERALD_PURPLE)
2622         Store[x][y] = EL_EMERALD_PURPLE;
2623       else if (element == EL_WALL_PEARL)
2624         Store[x][y] = EL_PEARL;
2625       else if (element == EL_WALL_CRYSTAL)
2626         Store[x][y] = EL_CRYSTAL;
2627       else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
2628         Store[x][y] = element_info[element].content[1][1];
2629       else
2630         Store[x][y] = EL_EMPTY;
2631
2632       if (x != ex || y != ey ||
2633           center_element == EL_AMOEBA_TO_DIAMOND || mode == EX_BORDER)
2634         Store2[x][y] = element;
2635
2636 #if 0
2637       if (AmoebaNr[x][y] &&
2638           (element == EL_AMOEBA_FULL ||
2639            element == EL_BD_AMOEBA ||
2640            element == EL_AMOEBA_GROWING))
2641       {
2642         AmoebaCnt[AmoebaNr[x][y]]--;
2643         AmoebaCnt2[AmoebaNr[x][y]]--;
2644       }
2645
2646 #if 1
2647       RemoveField(x, y);
2648 #else
2649       MovDir[x][y] = MovPos[x][y] = 0;
2650       GfxDir[x][y] = MovDir[x][y];
2651       AmoebaNr[x][y] = 0;
2652 #endif
2653 #endif
2654
2655       Feld[x][y] = EL_EXPLOSION;
2656 #if 1
2657       GfxElement[x][y] = center_element;
2658 #else
2659       GfxElement[x][y] = EL_UNDEFINED;
2660 #endif
2661
2662       ExplodePhase[x][y] = 1;
2663 #if 1
2664       ExplodeDelay[x][y] = last_phase;
2665 #endif
2666       Stop[x][y] = TRUE;
2667     }
2668
2669     if (center_element == EL_YAMYAM)
2670       game.yamyam_content_nr =
2671         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
2672
2673     return;
2674   }
2675
2676   if (Stop[ex][ey])
2677     return;
2678
2679   x = ex;
2680   y = ey;
2681
2682 #if 1
2683   last_phase = ExplodeDelay[x][y];
2684 #endif
2685
2686   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
2687
2688 #ifdef DEBUG
2689
2690   /* activate this even in non-DEBUG version until cause for crash in
2691      getGraphicAnimationFrame() (see below) is found and eliminated */
2692 #endif
2693 #if 1
2694
2695   if (GfxElement[x][y] == EL_UNDEFINED)
2696   {
2697     printf("\n\n");
2698     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
2699     printf("Explode(): This should never happen!\n");
2700     printf("\n\n");
2701
2702     GfxElement[x][y] = EL_EMPTY;
2703   }
2704 #endif
2705
2706 #if 1
2707
2708   border_element = Store2[x][y];
2709   if (IS_PLAYER(x, y))
2710     border_element = StorePlayer[x][y];
2711
2712   if (phase == element_info[border_element].ignition_delay ||
2713       phase == last_phase)
2714   {
2715     boolean border_explosion = FALSE;
2716
2717     if (IS_PLAYER(x, y))
2718     {
2719       KillHeroUnlessExplosionProtected(x, y);
2720       border_explosion = TRUE;
2721
2722       if (phase == last_phase)
2723         printf("::: IS_PLAYER\n");
2724     }
2725     else if (CAN_EXPLODE_BY_FIRE(border_element))
2726     {
2727       Feld[x][y] = Store2[x][y];
2728       Store2[x][y] = 0;
2729       Bang(x, y);
2730       border_explosion = TRUE;
2731
2732       if (phase == last_phase)
2733         printf("::: CAN_EXPLODE_BY_FIRE\n");
2734     }
2735     else if (border_element == EL_AMOEBA_TO_DIAMOND)
2736     {
2737       AmoebeUmwandeln(x, y);
2738       border_explosion = TRUE;
2739
2740       if (phase == last_phase)
2741         printf("::: EL_AMOEBA_TO_DIAMOND\n");
2742     }
2743
2744 #if 0
2745     if (border_explosion && phase == last_phase)
2746       return;
2747 #endif
2748   }
2749
2750 #else
2751
2752   if (phase == first_phase_after_start)
2753   {
2754     int element = Store2[x][y];
2755
2756     if (element == EL_BLACK_ORB)
2757     {
2758       Feld[x][y] = Store2[x][y];
2759       Store2[x][y] = 0;
2760       Bang(x, y);
2761     }
2762   }
2763   else if (phase == half_phase)
2764   {
2765     int element = Store2[x][y];
2766
2767     if (IS_PLAYER(x, y))
2768       KillHeroUnlessExplosionProtected(x, y);
2769     else if (CAN_EXPLODE_BY_FIRE(element))
2770     {
2771       Feld[x][y] = Store2[x][y];
2772       Store2[x][y] = 0;
2773       Bang(x, y);
2774     }
2775     else if (element == EL_AMOEBA_TO_DIAMOND)
2776       AmoebeUmwandeln(x, y);
2777   }
2778 #endif
2779
2780   if (phase == last_phase)
2781   {
2782     int element;
2783
2784     element = Feld[x][y] = Store[x][y];
2785     Store[x][y] = Store2[x][y] = 0;
2786     GfxElement[x][y] = EL_UNDEFINED;
2787
2788     /* player can escape from explosions and might therefore be still alive */
2789     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2790         element <= EL_PLAYER_IS_EXPLODING_4)
2791       Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2792                     EL_EMPTY :
2793                     element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2794                     element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2795                     element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2796                     EL_EMERALD_PURPLE);
2797
2798     /* restore probably existing indestructible background element */
2799     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2800       element = Feld[x][y] = Back[x][y];
2801     Back[x][y] = 0;
2802
2803     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2804     GfxDir[x][y] = MV_NO_MOVING;
2805     ChangeDelay[x][y] = 0;
2806     ChangePage[x][y] = -1;
2807
2808     InitField(x, y, FALSE);
2809 #if 1
2810     /* !!! not needed !!! */
2811     if (CAN_MOVE(element))
2812       InitMovDir(x, y);
2813 #endif
2814     DrawLevelField(x, y);
2815
2816     TestIfElementTouchesCustomElement(x, y);
2817
2818     if (GFX_CRUMBLED(element))
2819       DrawLevelFieldCrumbledSandNeighbours(x, y);
2820
2821     if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2822       StorePlayer[x][y] = 0;
2823
2824     if (ELEM_IS_PLAYER(element))
2825       RelocatePlayer(x, y, element);
2826   }
2827 #if 1
2828   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2829 #else
2830   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2831 #endif
2832   {
2833 #if 1
2834     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2835 #else
2836     int stored = Store[x][y];
2837     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2838                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2839                    IMG_SP_EXPLOSION);
2840 #endif
2841     int frame = getGraphicAnimationFrame(graphic, phase - delay);
2842
2843 #if 0
2844     printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2845            element_info[GfxElement[x][y]].token_name,
2846            graphic);
2847 #endif
2848
2849     if (phase == delay)
2850       DrawLevelFieldCrumbledSand(x, y);
2851
2852     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2853     {
2854       DrawLevelElement(x, y, Back[x][y]);
2855       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2856     }
2857     else if (IS_WALKABLE_UNDER(Back[x][y]))
2858     {
2859       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2860       DrawLevelElementThruMask(x, y, Back[x][y]);
2861     }
2862     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2863       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2864   }
2865 }
2866
2867 void DynaExplode(int ex, int ey)
2868 {
2869   int i, j;
2870   int dynabomb_element = Feld[ex][ey];
2871   int dynabomb_size = 1;
2872   boolean dynabomb_xl = FALSE;
2873   struct PlayerInfo *player;
2874   static int xy[4][2] =
2875   {
2876     { 0, -1 },
2877     { -1, 0 },
2878     { +1, 0 },
2879     { 0, +1 }
2880   };
2881
2882   if (IS_ACTIVE_BOMB(dynabomb_element))
2883   {
2884     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2885     dynabomb_size = player->dynabomb_size;
2886     dynabomb_xl = player->dynabomb_xl;
2887     player->dynabombs_left++;
2888   }
2889
2890   Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2891
2892   for (i = 0; i < 4; i++)
2893   {
2894     for (j = 1; j <= dynabomb_size; j++)
2895     {
2896       int x = ex + j * xy[i % 4][0];
2897       int y = ey + j * xy[i % 4][1];
2898       int element;
2899
2900       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2901         break;
2902
2903       element = Feld[x][y];
2904
2905       /* do not restart explosions of fields with active bombs */
2906       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2907         continue;
2908
2909       Explode(x, y, EX_PHASE_START, EX_BORDER);
2910
2911       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2912       if (element != EL_EMPTY &&
2913           element != EL_SAND &&
2914           element != EL_EXPLOSION &&
2915           !dynabomb_xl)
2916         break;
2917     }
2918   }
2919 }
2920
2921 void Bang(int x, int y)
2922 {
2923 #if 1
2924   int element = MovingOrBlocked2Element(x, y);
2925 #else
2926   int element = Feld[x][y];
2927 #endif
2928
2929 #if 1
2930   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
2931 #else
2932   if (IS_PLAYER(x, y))
2933 #endif
2934   {
2935     struct PlayerInfo *player = PLAYERINFO(x, y);
2936
2937     element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
2938                             player->element_nr);
2939   }
2940
2941 #if 0
2942 #if 1
2943   PlayLevelSoundAction(x, y, ACTION_EXPLODING);
2944 #else
2945   if (game.emulation == EMU_SUPAPLEX)
2946     PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
2947   else
2948     PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
2949 #endif
2950 #endif
2951
2952 #if 0
2953   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
2954     element = EL_EMPTY;
2955 #endif
2956
2957   switch(element)
2958   {
2959     case EL_BUG:
2960     case EL_SPACESHIP:
2961     case EL_BD_BUTTERFLY:
2962     case EL_BD_FIREFLY:
2963     case EL_YAMYAM:
2964     case EL_DARK_YAMYAM:
2965     case EL_ROBOT:
2966     case EL_PACMAN:
2967     case EL_MOLE:
2968       RaiseScoreElement(element);
2969       Explode(x, y, EX_PHASE_START, EX_NORMAL);
2970       break;
2971     case EL_DYNABOMB_PLAYER_1_ACTIVE:
2972     case EL_DYNABOMB_PLAYER_2_ACTIVE:
2973     case EL_DYNABOMB_PLAYER_3_ACTIVE:
2974     case EL_DYNABOMB_PLAYER_4_ACTIVE:
2975     case EL_DYNABOMB_INCREASE_NUMBER:
2976     case EL_DYNABOMB_INCREASE_SIZE:
2977     case EL_DYNABOMB_INCREASE_POWER:
2978       DynaExplode(x, y);
2979       break;
2980     case EL_PENGUIN:
2981     case EL_LAMP:
2982     case EL_LAMP_ACTIVE:
2983       if (IS_PLAYER(x, y))
2984         Explode(x, y, EX_PHASE_START, EX_NORMAL);
2985       else
2986         Explode(x, y, EX_PHASE_START, EX_CENTER);
2987       break;
2988     default:
2989       if (CAN_EXPLODE_DYNA(element))
2990         DynaExplode(x, y);
2991       else if (CAN_EXPLODE_1X1(element))
2992         Explode(x, y, EX_PHASE_START, EX_CENTER);
2993       else
2994         Explode(x, y, EX_PHASE_START, EX_NORMAL);
2995       break;
2996   }
2997
2998   CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
2999 }
3000
3001 void SplashAcid(int x, int y)
3002 {
3003   int element = Feld[x][y];
3004
3005   if (element != EL_ACID_SPLASH_LEFT &&
3006       element != EL_ACID_SPLASH_RIGHT)
3007   {
3008     PlayLevelSound(x, y, SND_ACID_SPLASHING);
3009
3010     if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
3011         (!IN_LEV_FIELD(x-1, y-1) ||
3012          !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
3013       Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
3014
3015     if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
3016         (!IN_LEV_FIELD(x+1, y-1) ||
3017          !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
3018       Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
3019   }
3020 }
3021
3022 static void InitBeltMovement()
3023 {
3024   static int belt_base_element[4] =
3025   {
3026     EL_CONVEYOR_BELT_1_LEFT,
3027     EL_CONVEYOR_BELT_2_LEFT,
3028     EL_CONVEYOR_BELT_3_LEFT,
3029     EL_CONVEYOR_BELT_4_LEFT
3030   };
3031   static int belt_base_active_element[4] =
3032   {
3033     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3034     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3035     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3036     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3037   };
3038
3039   int x, y, i, j;
3040
3041   /* set frame order for belt animation graphic according to belt direction */
3042   for (i = 0; i < 4; i++)
3043   {
3044     int belt_nr = i;
3045
3046     for (j = 0; j < 3; j++)
3047     {
3048       int element = belt_base_active_element[belt_nr] + j;
3049       int graphic = el2img(element);
3050
3051       if (game.belt_dir[i] == MV_LEFT)
3052         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3053       else
3054         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3055     }
3056   }
3057
3058   for (y = 0; y < lev_fieldy; y++)
3059   {
3060     for (x = 0; x < lev_fieldx; x++)
3061     {
3062       int element = Feld[x][y];
3063
3064       for (i = 0; i < 4; i++)
3065       {
3066         if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3067         {
3068           int e_belt_nr = getBeltNrFromBeltElement(element);
3069           int belt_nr = i;
3070
3071           if (e_belt_nr == belt_nr)
3072           {
3073             int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3074
3075             Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3076           }
3077         }
3078       }
3079     }
3080   }
3081 }
3082
3083 static void ToggleBeltSwitch(int x, int y)
3084 {
3085   static int belt_base_element[4] =
3086   {
3087     EL_CONVEYOR_BELT_1_LEFT,
3088     EL_CONVEYOR_BELT_2_LEFT,
3089     EL_CONVEYOR_BELT_3_LEFT,
3090     EL_CONVEYOR_BELT_4_LEFT
3091   };
3092   static int belt_base_active_element[4] =
3093   {
3094     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3095     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3096     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3097     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3098   };
3099   static int belt_base_switch_element[4] =
3100   {
3101     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3102     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3103     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3104     EL_CONVEYOR_BELT_4_SWITCH_LEFT
3105   };
3106   static int belt_move_dir[4] =
3107   {
3108     MV_LEFT,
3109     MV_NO_MOVING,
3110     MV_RIGHT,
3111     MV_NO_MOVING,
3112   };
3113
3114   int element = Feld[x][y];
3115   int belt_nr = getBeltNrFromBeltSwitchElement(element);
3116   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3117   int belt_dir = belt_move_dir[belt_dir_nr];
3118   int xx, yy, i;
3119
3120   if (!IS_BELT_SWITCH(element))
3121     return;
3122
3123   game.belt_dir_nr[belt_nr] = belt_dir_nr;
3124   game.belt_dir[belt_nr] = belt_dir;
3125
3126   if (belt_dir_nr == 3)
3127     belt_dir_nr = 1;
3128
3129   /* set frame order for belt animation graphic according to belt direction */
3130   for (i = 0; i < 3; i++)
3131   {
3132     int element = belt_base_active_element[belt_nr] + i;
3133     int graphic = el2img(element);
3134
3135     if (belt_dir == MV_LEFT)
3136       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3137     else
3138       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3139   }
3140
3141   for (yy = 0; yy < lev_fieldy; yy++)
3142   {
3143     for (xx = 0; xx < lev_fieldx; xx++)
3144     {
3145       int element = Feld[xx][yy];
3146
3147       if (IS_BELT_SWITCH(element))
3148       {
3149         int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3150
3151         if (e_belt_nr == belt_nr)
3152         {
3153           Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3154           DrawLevelField(xx, yy);
3155         }
3156       }
3157       else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3158       {
3159         int e_belt_nr = getBeltNrFromBeltElement(element);
3160
3161         if (e_belt_nr == belt_nr)
3162         {
3163           int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3164
3165           Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3166           DrawLevelField(xx, yy);
3167         }
3168       }
3169       else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3170       {
3171         int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3172
3173         if (e_belt_nr == belt_nr)
3174         {
3175           int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3176
3177           Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3178           DrawLevelField(xx, yy);
3179         }
3180       }
3181     }
3182   }
3183 }
3184
3185 static void ToggleSwitchgateSwitch(int x, int y)
3186 {
3187   int xx, yy;
3188
3189   game.switchgate_pos = !game.switchgate_pos;
3190
3191   for (yy = 0; yy < lev_fieldy; yy++)
3192   {
3193     for (xx = 0; xx < lev_fieldx; xx++)
3194     {
3195       int element = Feld[xx][yy];
3196
3197       if (element == EL_SWITCHGATE_SWITCH_UP ||
3198           element == EL_SWITCHGATE_SWITCH_DOWN)
3199       {
3200         Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3201         DrawLevelField(xx, yy);
3202       }
3203       else if (element == EL_SWITCHGATE_OPEN ||
3204                element == EL_SWITCHGATE_OPENING)
3205       {
3206         Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3207 #if 1
3208         PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3209 #else
3210         PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3211 #endif
3212       }
3213       else if (element == EL_SWITCHGATE_CLOSED ||
3214                element == EL_SWITCHGATE_CLOSING)
3215       {
3216         Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3217 #if 1
3218         PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3219 #else
3220         PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3221 #endif
3222       }
3223     }
3224   }
3225 }
3226
3227 static int getInvisibleActiveFromInvisibleElement(int element)
3228 {
3229   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3230           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
3231           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
3232           element);
3233 }
3234
3235 static int getInvisibleFromInvisibleActiveElement(int element)
3236 {
3237   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3238           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
3239           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
3240           element);
3241 }
3242
3243 static void RedrawAllLightSwitchesAndInvisibleElements()
3244 {
3245   int x, y;
3246
3247   for (y = 0; y < lev_fieldy; y++)
3248   {
3249     for (x = 0; x < lev_fieldx; x++)
3250     {
3251       int element = Feld[x][y];
3252
3253       if (element == EL_LIGHT_SWITCH &&
3254           game.light_time_left > 0)
3255       {
3256         Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3257         DrawLevelField(x, y);
3258       }
3259       else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3260                game.light_time_left == 0)
3261       {
3262         Feld[x][y] = EL_LIGHT_SWITCH;
3263         DrawLevelField(x, y);
3264       }
3265       else if (element == EL_INVISIBLE_STEELWALL ||
3266                element == EL_INVISIBLE_WALL ||
3267                element == EL_INVISIBLE_SAND)
3268       {
3269         if (game.light_time_left > 0)
3270           Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3271
3272         DrawLevelField(x, y);
3273       }
3274       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3275                element == EL_INVISIBLE_WALL_ACTIVE ||
3276                element == EL_INVISIBLE_SAND_ACTIVE)
3277       {
3278         if (game.light_time_left == 0)
3279           Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3280
3281         DrawLevelField(x, y);
3282       }
3283     }
3284   }
3285 }
3286
3287 static void ToggleLightSwitch(int x, int y)
3288 {
3289   int element = Feld[x][y];
3290
3291   game.light_time_left =
3292     (element == EL_LIGHT_SWITCH ?
3293      level.time_light * FRAMES_PER_SECOND : 0);
3294
3295   RedrawAllLightSwitchesAndInvisibleElements();
3296 }
3297
3298 static void ActivateTimegateSwitch(int x, int y)
3299 {
3300   int xx, yy;
3301
3302   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3303
3304   for (yy = 0; yy < lev_fieldy; yy++)
3305   {
3306     for (xx = 0; xx < lev_fieldx; xx++)
3307     {
3308       int element = Feld[xx][yy];
3309
3310       if (element == EL_TIMEGATE_CLOSED ||
3311           element == EL_TIMEGATE_CLOSING)
3312       {
3313         Feld[xx][yy] = EL_TIMEGATE_OPENING;
3314         PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3315       }
3316
3317       /*
3318       else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3319       {
3320         Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3321         DrawLevelField(xx, yy);
3322       }
3323       */
3324
3325     }
3326   }
3327
3328   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3329 }
3330
3331 inline static int getElementMoveStepsize(int x, int y)
3332 {
3333   int element = Feld[x][y];
3334   int direction = MovDir[x][y];
3335   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3336   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3337   int horiz_move = (dx != 0);
3338   int sign = (horiz_move ? dx : dy);
3339   int step = sign * element_info[element].move_stepsize;
3340
3341   /* special values for move stepsize for spring and things on conveyor belt */
3342   if (horiz_move)
3343   {
3344     if (CAN_FALL(element) &&
3345         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3346       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3347     else if (element == EL_SPRING)
3348       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3349   }
3350
3351   return step;
3352 }
3353
3354 void Impact(int x, int y)
3355 {
3356   boolean lastline = (y == lev_fieldy-1);
3357   boolean object_hit = FALSE;
3358   boolean impact = (lastline || object_hit);
3359   int element = Feld[x][y];
3360   int smashed = EL_UNDEFINED;
3361
3362   if (!lastline)        /* check if element below was hit */
3363   {
3364     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3365       return;
3366
3367     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3368                                          MovDir[x][y + 1] != MV_DOWN ||
3369                                          MovPos[x][y + 1] <= TILEY / 2));
3370
3371 #if 0
3372     object_hit = !IS_FREE(x, y + 1);
3373 #endif
3374
3375     /* do not smash moving elements that left the smashed field in time */
3376     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3377         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3378       object_hit = FALSE;
3379
3380     if (object_hit)
3381       smashed = MovingOrBlocked2Element(x, y + 1);
3382
3383     impact = (lastline || object_hit);
3384   }
3385
3386   if (!lastline && smashed == EL_ACID)  /* element falls into acid */
3387   {
3388     SplashAcid(x, y);
3389     return;
3390   }
3391
3392   /* only reset graphic animation if graphic really changes after impact */
3393   if (impact &&
3394       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3395   {
3396     ResetGfxAnimation(x, y);
3397     DrawLevelField(x, y);
3398   }
3399
3400   if (impact && CAN_EXPLODE_IMPACT(element))
3401   {
3402     Bang(x, y);
3403     return;
3404   }
3405   else if (impact && element == EL_PEARL)
3406   {
3407     Feld[x][y] = EL_PEARL_BREAKING;
3408     PlayLevelSound(x, y, SND_PEARL_BREAKING);
3409     return;
3410   }
3411   else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3412   {
3413     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3414
3415     return;
3416   }
3417
3418   if (impact && element == EL_AMOEBA_DROP)
3419   {
3420     if (object_hit && IS_PLAYER(x, y + 1))
3421       KillHeroUnlessEnemyProtected(x, y + 1);
3422     else if (object_hit && smashed == EL_PENGUIN)
3423       Bang(x, y + 1);
3424     else
3425     {
3426       Feld[x][y] = EL_AMOEBA_GROWING;
3427       Store[x][y] = EL_AMOEBA_WET;
3428
3429       ResetRandomAnimationValue(x, y);
3430     }
3431     return;
3432   }
3433
3434   if (object_hit)               /* check which object was hit */
3435   {
3436     if (CAN_PASS_MAGIC_WALL(element) && 
3437         (smashed == EL_MAGIC_WALL ||
3438          smashed == EL_BD_MAGIC_WALL))
3439     {
3440       int xx, yy;
3441       int activated_magic_wall =
3442         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3443          EL_BD_MAGIC_WALL_ACTIVE);
3444
3445       /* activate magic wall / mill */
3446       for (yy = 0; yy < lev_fieldy; yy++)
3447         for (xx = 0; xx < lev_fieldx; xx++)
3448           if (Feld[xx][yy] == smashed)
3449             Feld[xx][yy] = activated_magic_wall;
3450
3451       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3452       game.magic_wall_active = TRUE;
3453
3454       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3455                             SND_MAGIC_WALL_ACTIVATING :
3456                             SND_BD_MAGIC_WALL_ACTIVATING));
3457     }
3458
3459     if (IS_PLAYER(x, y + 1))
3460     {
3461       if (CAN_SMASH_PLAYER(element))
3462       {
3463         KillHeroUnlessEnemyProtected(x, y + 1);
3464         return;
3465       }
3466     }
3467     else if (smashed == EL_PENGUIN)
3468     {
3469       if (CAN_SMASH_PLAYER(element))
3470       {
3471         Bang(x, y + 1);
3472         return;
3473       }
3474     }
3475     else if (element == EL_BD_DIAMOND)
3476     {
3477       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3478       {
3479         Bang(x, y + 1);
3480         return;
3481       }
3482     }
3483     else if (((element == EL_SP_INFOTRON ||
3484                element == EL_SP_ZONK) &&
3485               (smashed == EL_SP_SNIKSNAK ||
3486                smashed == EL_SP_ELECTRON ||
3487                smashed == EL_SP_DISK_ORANGE)) ||
3488              (element == EL_SP_INFOTRON &&
3489               smashed == EL_SP_DISK_YELLOW))
3490     {
3491       Bang(x, y + 1);
3492       return;
3493     }
3494 #if 0
3495     else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3496     {
3497       Bang(x, y + 1);
3498       return;
3499     }
3500 #endif
3501     else if (CAN_SMASH_EVERYTHING(element))
3502     {
3503       if (IS_CLASSIC_ENEMY(smashed) ||
3504           CAN_EXPLODE_SMASHED(smashed))
3505       {
3506         Bang(x, y + 1);
3507         return;
3508       }
3509       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3510       {
3511         if (smashed == EL_LAMP ||
3512             smashed == EL_LAMP_ACTIVE)
3513         {
3514           Bang(x, y + 1);
3515           return;
3516         }
3517         else if (smashed == EL_NUT)
3518         {
3519           Feld[x][y + 1] = EL_NUT_BREAKING;
3520           PlayLevelSound(x, y, SND_NUT_BREAKING);
3521           RaiseScoreElement(EL_NUT);
3522           return;
3523         }
3524         else if (smashed == EL_PEARL)
3525         {
3526           Feld[x][y + 1] = EL_PEARL_BREAKING;
3527           PlayLevelSound(x, y, SND_PEARL_BREAKING);
3528           return;
3529         }
3530         else if (smashed == EL_DIAMOND)
3531         {
3532           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3533           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3534           return;
3535         }
3536         else if (IS_BELT_SWITCH(smashed))
3537         {
3538           ToggleBeltSwitch(x, y + 1);
3539         }
3540         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3541                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
3542         {
3543           ToggleSwitchgateSwitch(x, y + 1);
3544         }
3545         else if (smashed == EL_LIGHT_SWITCH ||
3546                  smashed == EL_LIGHT_SWITCH_ACTIVE)
3547         {
3548           ToggleLightSwitch(x, y + 1);
3549         }
3550         else
3551         {
3552           CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3553
3554           CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3555                                           CE_OTHER_IS_SWITCHING);
3556           CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3557                                  CE_SWITCHED, -1);
3558         }
3559       }
3560       else
3561       {
3562         CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3563       }
3564     }
3565   }
3566
3567   /* play sound of magic wall / mill */
3568   if (!lastline &&
3569       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3570        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3571   {
3572     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3573       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3574     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3575       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3576
3577     return;
3578   }
3579
3580   /* play sound of object that hits the ground */
3581   if (lastline || object_hit)
3582     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3583 }
3584
3585 inline static void TurnRoundExt(int x, int y)
3586 {
3587   static struct
3588   {
3589     int x, y;
3590   } move_xy[] =
3591   {
3592     {  0,  0 },
3593     { -1,  0 },
3594     { +1,  0 },
3595     {  0,  0 },
3596     {  0, -1 },
3597     {  0,  0 }, { 0, 0 }, { 0, 0 },
3598     {  0, +1 }
3599   };
3600   static struct
3601   {
3602     int left, right, back;
3603   } turn[] =
3604   {
3605     { 0,        0,              0        },
3606     { MV_DOWN,  MV_UP,          MV_RIGHT },
3607     { MV_UP,    MV_DOWN,        MV_LEFT  },
3608     { 0,        0,              0        },
3609     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
3610     { 0,        0,              0        },
3611     { 0,        0,              0        },
3612     { 0,        0,              0        },
3613     { MV_RIGHT, MV_LEFT,        MV_UP    }
3614   };
3615
3616   int element = Feld[x][y];
3617   int move_pattern = element_info[element].move_pattern;
3618
3619   int old_move_dir = MovDir[x][y];
3620   int left_dir  = turn[old_move_dir].left;
3621   int right_dir = turn[old_move_dir].right;
3622   int back_dir  = turn[old_move_dir].back;
3623
3624   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
3625   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
3626   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
3627   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
3628
3629   int left_x  = x + left_dx,  left_y  = y + left_dy;
3630   int right_x = x + right_dx, right_y = y + right_dy;
3631   int move_x  = x + move_dx,  move_y  = y + move_dy;
3632
3633   int xx, yy;
3634
3635   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3636   {
3637     TestIfBadThingTouchesOtherBadThing(x, y);
3638
3639     if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3640       MovDir[x][y] = right_dir;
3641     else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3642       MovDir[x][y] = left_dir;
3643
3644     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3645       MovDelay[x][y] = 9;
3646     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
3647       MovDelay[x][y] = 1;
3648   }
3649   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3650            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3651   {
3652     TestIfBadThingTouchesOtherBadThing(x, y);
3653
3654     if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3655       MovDir[x][y] = left_dir;
3656     else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3657       MovDir[x][y] = right_dir;
3658
3659     if ((element == EL_SPACESHIP ||
3660          element == EL_SP_SNIKSNAK ||
3661          element == EL_SP_ELECTRON)
3662         && MovDir[x][y] != old_move_dir)
3663       MovDelay[x][y] = 9;
3664     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
3665       MovDelay[x][y] = 1;
3666   }
3667   else if (element == EL_YAMYAM)
3668   {
3669     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3670     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3671
3672     if (can_turn_left && can_turn_right)
3673       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3674     else if (can_turn_left)
3675       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3676     else if (can_turn_right)
3677       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3678     else
3679       MovDir[x][y] = back_dir;
3680
3681     MovDelay[x][y] = 16 + 16 * RND(3);
3682   }
3683   else if (element == EL_DARK_YAMYAM)
3684   {
3685     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3686     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3687
3688     if (can_turn_left && can_turn_right)
3689       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3690     else if (can_turn_left)
3691       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3692     else if (can_turn_right)
3693       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3694     else
3695       MovDir[x][y] = back_dir;
3696
3697     MovDelay[x][y] = 16 + 16 * RND(3);
3698   }
3699   else if (element == EL_PACMAN)
3700   {
3701     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3702     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3703
3704     if (can_turn_left && can_turn_right)
3705       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3706     else if (can_turn_left)
3707       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3708     else if (can_turn_right)
3709       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3710     else
3711       MovDir[x][y] = back_dir;
3712
3713     MovDelay[x][y] = 6 + RND(40);
3714   }
3715   else if (element == EL_PIG)
3716   {
3717     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(left_x, left_y);
3718     boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3719     boolean can_move_on    = PIG_CAN_ENTER_FIELD(move_x, move_y);
3720     boolean should_turn_left, should_turn_right, should_move_on;
3721     int rnd_value = 24;
3722     int rnd = RND(rnd_value);
3723
3724     should_turn_left = (can_turn_left &&
3725                         (!can_move_on ||
3726                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3727                                                    y + back_dy + left_dy)));
3728     should_turn_right = (can_turn_right &&
3729                          (!can_move_on ||
3730                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3731                                                     y + back_dy + right_dy)));
3732     should_move_on = (can_move_on &&
3733                       (!can_turn_left ||
3734                        !can_turn_right ||
3735                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3736                                                  y + move_dy + left_dy) ||
3737                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3738                                                  y + move_dy + right_dy)));
3739
3740     if (should_turn_left || should_turn_right || should_move_on)
3741     {
3742       if (should_turn_left && should_turn_right && should_move_on)
3743         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
3744                         rnd < 2 * rnd_value / 3 ? right_dir :
3745                         old_move_dir);
3746       else if (should_turn_left && should_turn_right)
3747         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3748       else if (should_turn_left && should_move_on)
3749         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3750       else if (should_turn_right && should_move_on)
3751         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3752       else if (should_turn_left)
3753         MovDir[x][y] = left_dir;
3754       else if (should_turn_right)
3755         MovDir[x][y] = right_dir;
3756       else if (should_move_on)
3757         MovDir[x][y] = old_move_dir;
3758     }
3759     else if (can_move_on && rnd > rnd_value / 8)
3760       MovDir[x][y] = old_move_dir;
3761     else if (can_turn_left && can_turn_right)
3762       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3763     else if (can_turn_left && rnd > rnd_value / 8)
3764       MovDir[x][y] = left_dir;
3765     else if (can_turn_right && rnd > rnd_value/8)
3766       MovDir[x][y] = right_dir;
3767     else
3768       MovDir[x][y] = back_dir;
3769
3770     xx = x + move_xy[MovDir[x][y]].x;
3771     yy = y + move_xy[MovDir[x][y]].y;
3772
3773     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3774       MovDir[x][y] = old_move_dir;
3775
3776     MovDelay[x][y] = 0;
3777   }
3778   else if (element == EL_DRAGON)
3779   {
3780     boolean can_turn_left  = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3781     boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3782     boolean can_move_on    = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3783     int rnd_value = 24;
3784     int rnd = RND(rnd_value);
3785
3786 #if 0
3787     if (FrameCounter < 1 && x == 0 && y == 29)
3788       printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3789 #endif
3790
3791     if (can_move_on && rnd > rnd_value / 8)
3792       MovDir[x][y] = old_move_dir;
3793     else if (can_turn_left && can_turn_right)
3794       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3795     else if (can_turn_left && rnd > rnd_value / 8)
3796       MovDir[x][y] = left_dir;
3797     else if (can_turn_right && rnd > rnd_value / 8)
3798       MovDir[x][y] = right_dir;
3799     else
3800       MovDir[x][y] = back_dir;
3801
3802     xx = x + move_xy[MovDir[x][y]].x;
3803     yy = y + move_xy[MovDir[x][y]].y;
3804
3805 #if 0
3806     if (FrameCounter < 1 && x == 0 && y == 29)
3807       printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3808              xx, yy, Feld[xx][yy],
3809              FrameCounter);
3810 #endif
3811
3812 #if 1
3813     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3814       MovDir[x][y] = old_move_dir;
3815 #else
3816     if (!IS_FREE(xx, yy))
3817       MovDir[x][y] = old_move_dir;
3818 #endif
3819
3820 #if 0
3821     if (FrameCounter < 1 && x == 0 && y == 29)
3822       printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3823 #endif
3824
3825     MovDelay[x][y] = 0;
3826   }
3827   else if (element == EL_MOLE)
3828   {
3829     boolean can_move_on =
3830       (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3831                             IS_AMOEBOID(Feld[move_x][move_y]) ||
3832                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3833     if (!can_move_on)
3834     {
3835       boolean can_turn_left =
3836         (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3837                               IS_AMOEBOID(Feld[left_x][left_y])));
3838
3839       boolean can_turn_right =
3840         (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3841                               IS_AMOEBOID(Feld[right_x][right_y])));
3842
3843       if (can_turn_left && can_turn_right)
3844         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3845       else if (can_turn_left)
3846         MovDir[x][y] = left_dir;
3847       else
3848         MovDir[x][y] = right_dir;
3849     }
3850
3851     if (MovDir[x][y] != old_move_dir)
3852       MovDelay[x][y] = 9;
3853   }
3854   else if (element == EL_BALLOON)
3855   {
3856     MovDir[x][y] = game.balloon_dir;
3857     MovDelay[x][y] = 0;
3858   }
3859   else if (element == EL_SPRING)
3860   {
3861     if (MovDir[x][y] & MV_HORIZONTAL &&
3862         (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3863          IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3864       MovDir[x][y] = MV_NO_MOVING;
3865
3866     MovDelay[x][y] = 0;
3867   }
3868   else if (element == EL_ROBOT ||
3869            element == EL_SATELLITE ||
3870            element == EL_PENGUIN)
3871   {
3872     int attr_x = -1, attr_y = -1;
3873
3874     if (AllPlayersGone)
3875     {
3876       attr_x = ExitX;
3877       attr_y = ExitY;
3878     }
3879     else
3880     {
3881       int i;
3882
3883       for (i = 0; i < MAX_PLAYERS; i++)
3884       {
3885         struct PlayerInfo *player = &stored_player[i];
3886         int jx = player->jx, jy = player->jy;
3887
3888         if (!player->active)
3889           continue;
3890
3891         if (attr_x == -1 ||
3892             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3893         {
3894           attr_x = jx;
3895           attr_y = jy;
3896         }
3897       }
3898     }
3899
3900     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3901     {
3902       attr_x = ZX;
3903       attr_y = ZY;
3904     }
3905
3906     if (element == EL_PENGUIN)
3907     {
3908       int i;
3909       static int xy[4][2] =
3910       {
3911         { 0, -1 },
3912         { -1, 0 },
3913         { +1, 0 },
3914         { 0, +1 }
3915       };
3916
3917       for (i = 0; i < 4; i++)
3918       {
3919         int ex = x + xy[i % 4][0];
3920         int ey = y + xy[i % 4][1];
3921
3922         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3923         {
3924           attr_x = ex;
3925           attr_y = ey;
3926           break;
3927         }
3928       }
3929     }
3930
3931     MovDir[x][y] = MV_NO_MOVING;
3932     if (attr_x < x)
3933       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
3934     else if (attr_x > x)
3935       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
3936     if (attr_y < y)
3937       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
3938     else if (attr_y > y)
3939       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
3940
3941     if (element == EL_ROBOT)
3942     {
3943       int newx, newy;
3944
3945       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3946         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
3947       Moving2Blocked(x, y, &newx, &newy);
3948
3949       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
3950         MovDelay[x][y] = 8 + 8 * !RND(3);
3951       else
3952         MovDelay[x][y] = 16;
3953     }
3954     else if (element == EL_PENGUIN)
3955     {
3956       int newx, newy;
3957
3958       MovDelay[x][y] = 1;
3959
3960       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3961       {
3962         boolean first_horiz = RND(2);
3963         int new_move_dir = MovDir[x][y];
3964
3965         MovDir[x][y] =
3966           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3967         Moving2Blocked(x, y, &newx, &newy);
3968
3969         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3970           return;
3971
3972         MovDir[x][y] =
3973           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3974         Moving2Blocked(x, y, &newx, &newy);
3975
3976         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
3977           return;
3978
3979         MovDir[x][y] = old_move_dir;
3980         return;
3981       }
3982     }
3983     else        /* (element == EL_SATELLITE) */
3984     {
3985       int newx, newy;
3986
3987       MovDelay[x][y] = 1;
3988
3989       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
3990       {
3991         boolean first_horiz = RND(2);
3992         int new_move_dir = MovDir[x][y];
3993
3994         MovDir[x][y] =
3995           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
3996         Moving2Blocked(x, y, &newx, &newy);
3997
3998         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
3999           return;
4000
4001         MovDir[x][y] =
4002           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4003         Moving2Blocked(x, y, &newx, &newy);
4004
4005         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4006           return;
4007
4008         MovDir[x][y] = old_move_dir;
4009         return;
4010       }
4011     }
4012   }
4013   else if (move_pattern == MV_TURNING_LEFT ||
4014            move_pattern == MV_TURNING_RIGHT ||
4015            move_pattern == MV_TURNING_LEFT_RIGHT ||
4016            move_pattern == MV_TURNING_RIGHT_LEFT ||
4017            move_pattern == MV_TURNING_RANDOM ||
4018            move_pattern == MV_ALL_DIRECTIONS)
4019   {
4020     boolean can_turn_left =
4021       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4022     boolean can_turn_right =
4023       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4024
4025     if (move_pattern == MV_TURNING_LEFT)
4026       MovDir[x][y] = left_dir;
4027     else if (move_pattern == MV_TURNING_RIGHT)
4028       MovDir[x][y] = right_dir;
4029     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4030       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4031     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4032       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4033     else if (move_pattern == MV_TURNING_RANDOM)
4034       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4035                       can_turn_right && !can_turn_left ? right_dir :
4036                       RND(2) ? left_dir : right_dir);
4037     else if (can_turn_left && can_turn_right)
4038       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4039     else if (can_turn_left)
4040       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4041     else if (can_turn_right)
4042       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4043     else
4044       MovDir[x][y] = back_dir;
4045
4046     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4047   }
4048   else if (move_pattern == MV_HORIZONTAL ||
4049            move_pattern == MV_VERTICAL)
4050   {
4051     if (move_pattern & old_move_dir)
4052       MovDir[x][y] = back_dir;
4053     else if (move_pattern == MV_HORIZONTAL)
4054       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4055     else if (move_pattern == MV_VERTICAL)
4056       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4057
4058     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4059   }
4060   else if (move_pattern & MV_ANY_DIRECTION)
4061   {
4062     MovDir[x][y] = move_pattern;
4063     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4064   }
4065   else if (move_pattern == MV_ALONG_LEFT_SIDE)
4066   {
4067     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4068       MovDir[x][y] = left_dir;
4069     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4070       MovDir[x][y] = right_dir;
4071
4072     if (MovDir[x][y] != old_move_dir)
4073       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4074   }
4075   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4076   {
4077     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4078       MovDir[x][y] = right_dir;
4079     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4080       MovDir[x][y] = left_dir;
4081
4082     if (MovDir[x][y] != old_move_dir)
4083       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4084   }
4085   else if (move_pattern == MV_TOWARDS_PLAYER ||
4086            move_pattern == MV_AWAY_FROM_PLAYER)
4087   {
4088     int attr_x = -1, attr_y = -1;
4089     int newx, newy;
4090     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4091
4092     if (AllPlayersGone)
4093     {
4094       attr_x = ExitX;
4095       attr_y = ExitY;
4096     }
4097     else
4098     {
4099       int i;
4100
4101       for (i = 0; i < MAX_PLAYERS; i++)
4102       {
4103         struct PlayerInfo *player = &stored_player[i];
4104         int jx = player->jx, jy = player->jy;
4105
4106         if (!player->active)
4107           continue;
4108
4109         if (attr_x == -1 ||
4110             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4111         {
4112           attr_x = jx;
4113           attr_y = jy;
4114         }
4115       }
4116     }
4117
4118     MovDir[x][y] = MV_NO_MOVING;
4119     if (attr_x < x)
4120       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4121     else if (attr_x > x)
4122       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4123     if (attr_y < y)
4124       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4125     else if (attr_y > y)
4126       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4127
4128     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4129
4130     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4131     {
4132       boolean first_horiz = RND(2);
4133       int new_move_dir = MovDir[x][y];
4134
4135       MovDir[x][y] =
4136         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4137       Moving2Blocked(x, y, &newx, &newy);
4138
4139       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4140         return;
4141
4142       MovDir[x][y] =
4143         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4144       Moving2Blocked(x, y, &newx, &newy);
4145
4146       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4147         return;
4148
4149       MovDir[x][y] = old_move_dir;
4150     }
4151   }
4152   else if (move_pattern == MV_WHEN_PUSHED ||
4153            move_pattern == MV_WHEN_DROPPED)
4154   {
4155     if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4156       MovDir[x][y] = MV_NO_MOVING;
4157
4158     MovDelay[x][y] = 0;
4159   }
4160   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4161   {
4162     static int test_xy[7][2] =
4163     {
4164       { 0, -1 },
4165       { -1, 0 },
4166       { +1, 0 },
4167       { 0, +1 },
4168       { 0, -1 },
4169       { -1, 0 },
4170       { +1, 0 },
4171     };
4172     static int test_dir[7] =
4173     {
4174       MV_UP,
4175       MV_LEFT,
4176       MV_RIGHT,
4177       MV_DOWN,
4178       MV_UP,
4179       MV_LEFT,
4180       MV_RIGHT,
4181     };
4182     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4183     int move_preference = -1000000;     /* start with very low preference */
4184     int new_move_dir = MV_NO_MOVING;
4185     int start_test = RND(4);
4186     int i;
4187
4188     for (i = 0; i < 4; i++)
4189     {
4190       int move_dir = test_dir[start_test + i];
4191       int move_dir_preference;
4192
4193       xx = x + test_xy[start_test + i][0];
4194       yy = y + test_xy[start_test + i][1];
4195
4196       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4197           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4198       {
4199         new_move_dir = move_dir;
4200
4201         break;
4202       }
4203
4204       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4205         continue;
4206
4207       move_dir_preference = -1 * RunnerVisit[xx][yy];
4208       if (hunter_mode && PlayerVisit[xx][yy] > 0)
4209         move_dir_preference = PlayerVisit[xx][yy];
4210
4211       if (move_dir_preference > move_preference)
4212       {
4213         /* prefer field that has not been visited for the longest time */
4214         move_preference = move_dir_preference;
4215         new_move_dir = move_dir;
4216       }
4217       else if (move_dir_preference == move_preference &&
4218                move_dir == old_move_dir)
4219       {
4220         /* prefer last direction when all directions are preferred equally */
4221         move_preference = move_dir_preference;
4222         new_move_dir = move_dir;
4223       }
4224     }
4225
4226     MovDir[x][y] = new_move_dir;
4227     if (old_move_dir != new_move_dir)
4228       MovDelay[x][y] = 9;
4229   }
4230 }
4231
4232 static void TurnRound(int x, int y)
4233 {
4234   int direction = MovDir[x][y];
4235
4236 #if 0
4237   GfxDir[x][y] = MovDir[x][y];
4238 #endif
4239
4240   TurnRoundExt(x, y);
4241
4242 #if 1
4243   GfxDir[x][y] = MovDir[x][y];
4244 #endif
4245
4246   if (direction != MovDir[x][y])
4247     GfxFrame[x][y] = 0;
4248
4249 #if 1
4250   if (MovDelay[x][y])
4251     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4252 #else
4253   if (MovDelay[x][y])
4254     GfxAction[x][y] = ACTION_WAITING;
4255 #endif
4256 }
4257
4258 static boolean JustBeingPushed(int x, int y)
4259 {
4260   int i;
4261
4262   for (i = 0; i < MAX_PLAYERS; i++)
4263   {
4264     struct PlayerInfo *player = &stored_player[i];
4265
4266     if (player->active && player->is_pushing && player->MovPos)
4267     {
4268       int next_jx = player->jx + (player->jx - player->last_jx);
4269       int next_jy = player->jy + (player->jy - player->last_jy);
4270
4271       if (x == next_jx && y == next_jy)
4272         return TRUE;
4273     }
4274   }
4275
4276   return FALSE;
4277 }
4278
4279 void StartMoving(int x, int y)
4280 {
4281   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4282   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
4283   int element = Feld[x][y];
4284
4285   if (Stop[x][y])
4286     return;
4287
4288 #if 1
4289   if (MovDelay[x][y] == 0)
4290     GfxAction[x][y] = ACTION_DEFAULT;
4291 #else
4292   /* !!! this should be handled more generic (not only for mole) !!! */
4293   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4294     GfxAction[x][y] = ACTION_DEFAULT;
4295 #endif
4296
4297   if (CAN_FALL(element) && y < lev_fieldy - 1)
4298   {
4299     if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4300         (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4301       if (JustBeingPushed(x, y))
4302         return;
4303
4304     if (element == EL_QUICKSAND_FULL)
4305     {
4306       if (IS_FREE(x, y + 1))
4307       {
4308         InitMovingField(x, y, MV_DOWN);
4309         started_moving = TRUE;
4310
4311         Feld[x][y] = EL_QUICKSAND_EMPTYING;
4312         Store[x][y] = EL_ROCK;
4313 #if 1
4314         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4315 #else
4316         PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4317 #endif
4318       }
4319       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4320       {
4321         if (!MovDelay[x][y])
4322           MovDelay[x][y] = TILEY + 1;
4323
4324         if (MovDelay[x][y])
4325         {
4326           MovDelay[x][y]--;
4327           if (MovDelay[x][y])
4328             return;
4329         }
4330
4331         Feld[x][y] = EL_QUICKSAND_EMPTY;
4332         Feld[x][y + 1] = EL_QUICKSAND_FULL;
4333         Store[x][y + 1] = Store[x][y];
4334         Store[x][y] = 0;
4335 #if 1
4336         PlayLevelSoundAction(x, y, ACTION_FILLING);
4337 #else
4338         PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4339 #endif
4340       }
4341     }
4342     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4343              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4344     {
4345       InitMovingField(x, y, MV_DOWN);
4346       started_moving = TRUE;
4347
4348       Feld[x][y] = EL_QUICKSAND_FILLING;
4349       Store[x][y] = element;
4350 #if 1
4351       PlayLevelSoundAction(x, y, ACTION_FILLING);
4352 #else
4353       PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4354 #endif
4355     }
4356     else if (element == EL_MAGIC_WALL_FULL)
4357     {
4358       if (IS_FREE(x, y + 1))
4359       {
4360         InitMovingField(x, y, MV_DOWN);
4361         started_moving = TRUE;
4362
4363         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4364         Store[x][y] = EL_CHANGED(Store[x][y]);
4365       }
4366       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4367       {
4368         if (!MovDelay[x][y])
4369           MovDelay[x][y] = TILEY/4 + 1;
4370
4371         if (MovDelay[x][y])
4372         {
4373           MovDelay[x][y]--;
4374           if (MovDelay[x][y])
4375             return;
4376         }
4377
4378         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4379         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4380         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4381         Store[x][y] = 0;
4382       }
4383     }
4384     else if (element == EL_BD_MAGIC_WALL_FULL)
4385     {
4386       if (IS_FREE(x, y + 1))
4387       {
4388         InitMovingField(x, y, MV_DOWN);
4389         started_moving = TRUE;
4390
4391         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4392         Store[x][y] = EL_CHANGED2(Store[x][y]);
4393       }
4394       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4395       {
4396         if (!MovDelay[x][y])
4397           MovDelay[x][y] = TILEY/4 + 1;
4398
4399         if (MovDelay[x][y])
4400         {
4401           MovDelay[x][y]--;
4402           if (MovDelay[x][y])
4403             return;
4404         }
4405
4406         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4407         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4408         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4409         Store[x][y] = 0;
4410       }
4411     }
4412     else if (CAN_PASS_MAGIC_WALL(element) &&
4413              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4414               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4415     {
4416       InitMovingField(x, y, MV_DOWN);
4417       started_moving = TRUE;
4418
4419       Feld[x][y] =
4420         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4421          EL_BD_MAGIC_WALL_FILLING);
4422       Store[x][y] = element;
4423     }
4424 #if 0
4425     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4426 #else
4427     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4428 #endif
4429     {
4430       SplashAcid(x, y);
4431
4432       InitMovingField(x, y, MV_DOWN);
4433       started_moving = TRUE;
4434
4435       Store[x][y] = EL_ACID;
4436 #if 0
4437       /* !!! TEST !!! better use "_FALLING" etc. !!! */
4438       GfxAction[x][y + 1] = ACTION_ACTIVE;
4439 #endif
4440     }
4441 #if 1
4442     else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4443               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4444               (Feld[x][y + 1] == EL_BLOCKED)) ||
4445              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4446               CAN_SMASH(element) && WasJustFalling[x][y] &&
4447               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4448
4449 #else
4450 #if 1
4451     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4452              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4453              WasJustMoving[x][y] && !Pushed[x][y + 1])
4454 #else
4455     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4456              WasJustMoving[x][y])
4457 #endif
4458 #endif
4459
4460     {
4461       /* this is needed for a special case not covered by calling "Impact()"
4462          from "ContinueMoving()": if an element moves to a tile directly below
4463          another element which was just falling on that tile (which was empty
4464          in the previous frame), the falling element above would just stop
4465          instead of smashing the element below (in previous version, the above
4466          element was just checked for "moving" instead of "falling", resulting
4467          in incorrect smashes caused by horizontal movement of the above
4468          element; also, the case of the player being the element to smash was
4469          simply not covered here... :-/ ) */
4470
4471 #if 0
4472       WasJustMoving[x][y] = 0;
4473       WasJustFalling[x][y] = 0;
4474 #endif
4475
4476       Impact(x, y);
4477     }
4478     else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4479     {
4480       if (MovDir[x][y] == MV_NO_MOVING)
4481       {
4482         InitMovingField(x, y, MV_DOWN);
4483         started_moving = TRUE;
4484       }
4485     }
4486     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4487     {
4488       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4489         MovDir[x][y] = MV_DOWN;
4490
4491       InitMovingField(x, y, MV_DOWN);
4492       started_moving = TRUE;
4493     }
4494     else if (element == EL_AMOEBA_DROP)
4495     {
4496       Feld[x][y] = EL_AMOEBA_GROWING;
4497       Store[x][y] = EL_AMOEBA_WET;
4498     }
4499     /* Store[x][y + 1] must be zero, because:
4500        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4501     */
4502 #if 0
4503 #if OLD_GAME_BEHAVIOUR
4504     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4505 #else
4506     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4507              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4508              element != EL_DX_SUPABOMB)
4509 #endif
4510 #else
4511     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4512               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4513              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4514              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4515 #endif
4516     {
4517       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
4518                                 (IS_FREE(x - 1, y + 1) ||
4519                                  Feld[x - 1][y + 1] == EL_ACID));
4520       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4521                                 (IS_FREE(x + 1, y + 1) ||
4522                                  Feld[x + 1][y + 1] == EL_ACID));
4523       boolean can_fall_any  = (can_fall_left || can_fall_right);
4524       boolean can_fall_both = (can_fall_left && can_fall_right);
4525
4526       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4527       {
4528         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4529
4530         if (slippery_type == SLIPPERY_ONLY_LEFT)
4531           can_fall_right = FALSE;
4532         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4533           can_fall_left = FALSE;
4534         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4535           can_fall_right = FALSE;
4536         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4537           can_fall_left = FALSE;
4538
4539         can_fall_any  = (can_fall_left || can_fall_right);
4540         can_fall_both = (can_fall_left && can_fall_right);
4541       }
4542
4543       if (can_fall_any)
4544       {
4545         if (can_fall_both &&
4546             (game.emulation != EMU_BOULDERDASH &&
4547              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4548           can_fall_left = !(can_fall_right = RND(2));
4549
4550         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4551         started_moving = TRUE;
4552       }
4553     }
4554     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4555     {
4556       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
4557       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4558       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4559       int belt_dir = game.belt_dir[belt_nr];
4560
4561       if ((belt_dir == MV_LEFT  && left_is_free) ||
4562           (belt_dir == MV_RIGHT && right_is_free))
4563       {
4564         InitMovingField(x, y, belt_dir);
4565         started_moving = TRUE;
4566
4567         GfxAction[x][y] = ACTION_DEFAULT;
4568       }
4569     }
4570   }
4571
4572   /* not "else if" because of elements that can fall and move (EL_SPRING) */
4573   if (CAN_MOVE(element) && !started_moving)
4574   {
4575     int move_pattern = element_info[element].move_pattern;
4576     int newx, newy;
4577
4578     Moving2Blocked(x, y, &newx, &newy);
4579
4580 #if 1
4581     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4582       return;
4583 #else
4584     if ((element == EL_SATELLITE ||
4585          element == EL_BALLOON ||
4586          element == EL_SPRING)
4587         && JustBeingPushed(x, y))
4588       return;
4589 #endif
4590
4591 #if 1
4592     if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4593         WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4594         (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4595     {
4596 #if 0
4597       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4598              element, element_info[element].token_name,
4599              WasJustMoving[x][y],
4600              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4601              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4602              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4603              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4604 #endif
4605
4606 #if 1
4607       WasJustMoving[x][y] = 0;
4608 #endif
4609
4610       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4611
4612 #if 0
4613       if (Feld[x][y] != element)        /* element has changed */
4614       {
4615         element = Feld[x][y];
4616         move_pattern = element_info[element].move_pattern;
4617
4618         if (!CAN_MOVE(element))
4619           return;
4620       }
4621 #else
4622       if (Feld[x][y] != element)        /* element has changed */
4623         return;
4624 #endif
4625     }
4626 #endif
4627
4628 #if 0
4629 #if 0
4630     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4631       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
4632 #else
4633     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4634     {
4635       Moving2Blocked(x, y, &newx, &newy);
4636       if (Feld[newx][newy] == EL_BLOCKED)
4637         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
4638     }
4639 #endif
4640 #endif
4641
4642 #if 0
4643     if (FrameCounter < 1 && x == 0 && y == 29)
4644       printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4645 #endif
4646
4647     if (!MovDelay[x][y])        /* start new movement phase */
4648     {
4649       /* all objects that can change their move direction after each step
4650          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4651
4652       if (element != EL_YAMYAM &&
4653           element != EL_DARK_YAMYAM &&
4654           element != EL_PACMAN &&
4655           !(move_pattern & MV_ANY_DIRECTION) &&
4656           move_pattern != MV_TURNING_LEFT &&
4657           move_pattern != MV_TURNING_RIGHT &&
4658           move_pattern != MV_TURNING_LEFT_RIGHT &&
4659           move_pattern != MV_TURNING_RIGHT_LEFT &&
4660           move_pattern != MV_TURNING_RANDOM)
4661       {
4662         TurnRound(x, y);
4663
4664 #if 0
4665         if (FrameCounter < 1 && x == 0 && y == 29)
4666           printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4667 #endif
4668
4669         if (MovDelay[x][y] && (element == EL_BUG ||
4670                                element == EL_SPACESHIP ||
4671                                element == EL_SP_SNIKSNAK ||
4672                                element == EL_SP_ELECTRON ||
4673                                element == EL_MOLE))
4674           DrawLevelField(x, y);
4675       }
4676     }
4677
4678     if (MovDelay[x][y])         /* wait some time before next movement */
4679     {
4680       MovDelay[x][y]--;
4681
4682 #if 0
4683       if (element == EL_YAMYAM)
4684       {
4685         printf("::: %d\n",
4686                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4687         DrawLevelElementAnimation(x, y, element);
4688       }
4689 #endif
4690
4691       if (MovDelay[x][y])       /* element still has to wait some time */
4692       {
4693 #if 0
4694         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4695         ResetGfxAnimation(x, y);
4696 #endif
4697
4698 #if 0
4699         if (GfxAction[x][y] != ACTION_WAITING)
4700           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4701
4702         GfxAction[x][y] = ACTION_WAITING;
4703 #endif
4704       }
4705
4706       if (element == EL_ROBOT ||
4707 #if 0
4708           element == EL_PACMAN ||
4709 #endif
4710           element == EL_YAMYAM ||
4711           element == EL_DARK_YAMYAM)
4712       {
4713 #if 0
4714         DrawLevelElementAnimation(x, y, element);
4715 #else
4716         DrawLevelElementAnimationIfNeeded(x, y, element);
4717 #endif
4718         PlayLevelSoundAction(x, y, ACTION_WAITING);
4719       }
4720       else if (element == EL_SP_ELECTRON)
4721         DrawLevelElementAnimationIfNeeded(x, y, element);
4722       else if (element == EL_DRAGON)
4723       {
4724         int i;
4725         int dir = MovDir[x][y];
4726         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4727         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
4728         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
4729                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
4730                        dir == MV_UP     ? IMG_FLAMES_1_UP :
4731                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4732         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4733
4734 #if 0
4735         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4736 #endif
4737
4738         GfxAction[x][y] = ACTION_ATTACKING;
4739
4740         if (IS_PLAYER(x, y))
4741           DrawPlayerField(x, y);
4742         else
4743           DrawLevelField(x, y);
4744
4745         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4746
4747         for (i = 1; i <= 3; i++)
4748         {
4749           int xx = x + i * dx;
4750           int yy = y + i * dy;
4751           int sx = SCREENX(xx);
4752           int sy = SCREENY(yy);
4753           int flame_graphic = graphic + (i - 1);
4754
4755           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4756             break;
4757
4758           if (MovDelay[x][y])
4759           {
4760             int flamed = MovingOrBlocked2Element(xx, yy);
4761
4762             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4763               Bang(xx, yy);
4764             else
4765               RemoveMovingField(xx, yy);
4766
4767             Feld[xx][yy] = EL_FLAMES;
4768             if (IN_SCR_FIELD(sx, sy))
4769             {
4770               DrawLevelFieldCrumbledSand(xx, yy);
4771               DrawGraphic(sx, sy, flame_graphic, frame);
4772             }
4773           }
4774           else
4775           {
4776             if (Feld[xx][yy] == EL_FLAMES)
4777               Feld[xx][yy] = EL_EMPTY;
4778             DrawLevelField(xx, yy);
4779           }
4780         }
4781       }
4782
4783       if (MovDelay[x][y])       /* element still has to wait some time */
4784       {
4785         PlayLevelSoundAction(x, y, ACTION_WAITING);
4786
4787         return;
4788       }
4789
4790 #if 0
4791       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4792          for all other elements GfxAction will be set by InitMovingField() */
4793       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4794         GfxAction[x][y] = ACTION_MOVING;
4795 #endif
4796     }
4797
4798     /* now make next step */
4799
4800     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4801
4802     if (DONT_COLLIDE_WITH(element) &&
4803         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4804         !PLAYER_ENEMY_PROTECTED(newx, newy))
4805     {
4806 #if 1
4807       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4808
4809       return;
4810 #else
4811       /* player killed by element which is deadly when colliding with */
4812       MovDir[x][y] = 0;
4813       KillHero(PLAYERINFO(newx, newy));
4814       return;
4815 #endif
4816
4817     }
4818     else if ((element == EL_PENGUIN ||
4819               element == EL_ROBOT ||
4820               element == EL_SATELLITE ||
4821               element == EL_BALLOON ||
4822               IS_CUSTOM_ELEMENT(element)) &&
4823              IN_LEV_FIELD(newx, newy) &&
4824              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4825     {
4826       SplashAcid(x, y);
4827       Store[x][y] = EL_ACID;
4828     }
4829     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4830     {
4831       if (Feld[newx][newy] == EL_EXIT_OPEN)
4832       {
4833 #if 1
4834         RemoveField(x, y);
4835         DrawLevelField(x, y);
4836 #else
4837         Feld[x][y] = EL_EMPTY;
4838         DrawLevelField(x, y);
4839 #endif
4840
4841         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4842         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4843           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4844
4845         local_player->friends_still_needed--;
4846         if (!local_player->friends_still_needed &&
4847             !local_player->GameOver && AllPlayersGone)
4848           local_player->LevelSolved = local_player->GameOver = TRUE;
4849
4850         return;
4851       }
4852       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4853       {
4854         if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4855           DrawLevelField(newx, newy);
4856         else
4857           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4858       }
4859       else if (!IS_FREE(newx, newy))
4860       {
4861         GfxAction[x][y] = ACTION_WAITING;
4862
4863         if (IS_PLAYER(x, y))
4864           DrawPlayerField(x, y);
4865         else
4866           DrawLevelField(x, y);
4867
4868         return;
4869       }
4870     }
4871     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4872     {
4873       if (IS_FOOD_PIG(Feld[newx][newy]))
4874       {
4875         if (IS_MOVING(newx, newy))
4876           RemoveMovingField(newx, newy);
4877         else
4878         {
4879           Feld[newx][newy] = EL_EMPTY;
4880           DrawLevelField(newx, newy);
4881         }
4882
4883         PlayLevelSound(x, y, SND_PIG_DIGGING);
4884       }
4885       else if (!IS_FREE(newx, newy))
4886       {
4887         if (IS_PLAYER(x, y))
4888           DrawPlayerField(x, y);
4889         else
4890           DrawLevelField(x, y);
4891
4892         return;
4893       }
4894     }
4895
4896 #if 1
4897
4898     /*
4899     else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4900     */
4901
4902     else if (IS_CUSTOM_ELEMENT(element) &&
4903              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4904
4905 #if 0
4906  &&
4907              !IS_FREE(newx, newy)
4908 #endif
4909
4910 )
4911     {
4912       int new_element = Feld[newx][newy];
4913
4914 #if 0
4915       printf("::: '%s' digs '%s' [%d]\n",
4916              element_info[element].token_name,
4917              element_info[Feld[newx][newy]].token_name,
4918              StorePlayer[newx][newy]);
4919 #endif
4920
4921       if (!IS_FREE(newx, newy))
4922       {
4923         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4924                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4925                       ACTION_BREAKING);
4926
4927         /* no element can dig solid indestructible elements */
4928         if (IS_INDESTRUCTIBLE(new_element) &&
4929             !IS_DIGGABLE(new_element) &&
4930             !IS_COLLECTIBLE(new_element))
4931           return;
4932
4933         if (AmoebaNr[newx][newy] &&
4934             (new_element == EL_AMOEBA_FULL ||
4935              new_element == EL_BD_AMOEBA ||
4936              new_element == EL_AMOEBA_GROWING))
4937         {
4938           AmoebaCnt[AmoebaNr[newx][newy]]--;
4939           AmoebaCnt2[AmoebaNr[newx][newy]]--;
4940         }
4941
4942         if (IS_MOVING(newx, newy))
4943           RemoveMovingField(newx, newy);
4944         else
4945         {
4946           RemoveField(newx, newy);
4947           DrawLevelField(newx, newy);
4948         }
4949
4950         PlayLevelSoundAction(x, y, action);
4951       }
4952
4953       if (new_element == element_info[element].move_enter_element)
4954         element_info[element].can_leave_element = TRUE;
4955
4956       if (move_pattern & MV_MAZE_RUNNER_STYLE)
4957       {
4958         RunnerVisit[x][y] = FrameCounter;
4959         PlayerVisit[x][y] /= 8;         /* expire player visit path */
4960       }
4961     }
4962
4963 #endif
4964
4965     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
4966     {
4967       if (!IS_FREE(newx, newy))
4968       {
4969         if (IS_PLAYER(x, y))
4970           DrawPlayerField(x, y);
4971         else
4972           DrawLevelField(x, y);
4973
4974         return;
4975       }
4976       else
4977       {
4978         boolean wanna_flame = !RND(10);
4979         int dx = newx - x, dy = newy - y;
4980         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
4981         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
4982         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
4983                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
4984         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
4985                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
4986
4987         if ((wanna_flame ||
4988              IS_CLASSIC_ENEMY(element1) ||
4989              IS_CLASSIC_ENEMY(element2)) &&
4990             element1 != EL_DRAGON && element2 != EL_DRAGON &&
4991             element1 != EL_FLAMES && element2 != EL_FLAMES)
4992         {
4993 #if 1
4994           ResetGfxAnimation(x, y);
4995           GfxAction[x][y] = ACTION_ATTACKING;
4996 #endif
4997
4998           if (IS_PLAYER(x, y))
4999             DrawPlayerField(x, y);
5000           else
5001             DrawLevelField(x, y);
5002
5003           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5004
5005           MovDelay[x][y] = 50;
5006
5007           Feld[newx][newy] = EL_FLAMES;
5008           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5009             Feld[newx1][newy1] = EL_FLAMES;
5010           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5011             Feld[newx2][newy2] = EL_FLAMES;
5012
5013           return;
5014         }
5015       }
5016     }
5017     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5018              Feld[newx][newy] == EL_DIAMOND)
5019     {
5020       if (IS_MOVING(newx, newy))
5021         RemoveMovingField(newx, newy);
5022       else
5023       {
5024         Feld[newx][newy] = EL_EMPTY;
5025         DrawLevelField(newx, newy);
5026       }
5027
5028       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5029     }
5030     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5031              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5032     {
5033       if (AmoebaNr[newx][newy])
5034       {
5035         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5036         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5037             Feld[newx][newy] == EL_BD_AMOEBA)
5038           AmoebaCnt[AmoebaNr[newx][newy]]--;
5039       }
5040
5041       if (IS_MOVING(newx, newy))
5042         RemoveMovingField(newx, newy);
5043       else
5044       {
5045         Feld[newx][newy] = EL_EMPTY;
5046         DrawLevelField(newx, newy);
5047       }
5048
5049       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5050     }
5051     else if ((element == EL_PACMAN || element == EL_MOLE)
5052              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5053     {
5054       if (AmoebaNr[newx][newy])
5055       {
5056         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5057         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5058             Feld[newx][newy] == EL_BD_AMOEBA)
5059           AmoebaCnt[AmoebaNr[newx][newy]]--;
5060       }
5061
5062       if (element == EL_MOLE)
5063       {
5064         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5065         PlayLevelSound(x, y, SND_MOLE_DIGGING);
5066
5067         ResetGfxAnimation(x, y);
5068         GfxAction[x][y] = ACTION_DIGGING;
5069         DrawLevelField(x, y);
5070
5071         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
5072
5073         return;                         /* wait for shrinking amoeba */
5074       }
5075       else      /* element == EL_PACMAN */
5076       {
5077         Feld[newx][newy] = EL_EMPTY;
5078         DrawLevelField(newx, newy);
5079         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5080       }
5081     }
5082     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5083              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5084               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5085     {
5086       /* wait for shrinking amoeba to completely disappear */
5087       return;
5088     }
5089     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5090     {
5091       /* object was running against a wall */
5092
5093       TurnRound(x, y);
5094
5095 #if 1
5096       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
5097         DrawLevelElementAnimation(x, y, element);
5098 #else
5099       if (element == EL_BUG ||
5100           element == EL_SPACESHIP ||
5101           element == EL_SP_SNIKSNAK)
5102         DrawLevelField(x, y);
5103       else if (element == EL_MOLE)
5104         DrawLevelField(x, y);
5105       else if (element == EL_BD_BUTTERFLY ||
5106                element == EL_BD_FIREFLY)
5107         DrawLevelElementAnimationIfNeeded(x, y, element);
5108       else if (element == EL_SATELLITE)
5109         DrawLevelElementAnimationIfNeeded(x, y, element);
5110       else if (element == EL_SP_ELECTRON)
5111         DrawLevelElementAnimationIfNeeded(x, y, element);
5112 #endif
5113
5114       if (DONT_TOUCH(element))
5115         TestIfBadThingTouchesHero(x, y);
5116
5117 #if 0
5118       PlayLevelSoundAction(x, y, ACTION_WAITING);
5119 #endif
5120
5121       return;
5122     }
5123
5124     InitMovingField(x, y, MovDir[x][y]);
5125
5126     PlayLevelSoundAction(x, y, ACTION_MOVING);
5127   }
5128
5129   if (MovDir[x][y])
5130     ContinueMoving(x, y);
5131 }
5132
5133 void ContinueMoving(int x, int y)
5134 {
5135   int element = Feld[x][y];
5136   struct ElementInfo *ei = &element_info[element];
5137   int direction = MovDir[x][y];
5138   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5139   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5140   int newx = x + dx, newy = y + dy;
5141 #if 0
5142   int nextx = newx + dx, nexty = newy + dy;
5143 #endif
5144   boolean pushed = Pushed[x][y];
5145
5146   MovPos[x][y] += getElementMoveStepsize(x, y);
5147
5148   if (pushed)           /* special case: moving object pushed by player */
5149     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5150
5151   if (ABS(MovPos[x][y]) < TILEX)
5152   {
5153     DrawLevelField(x, y);
5154
5155     return;     /* element is still moving */
5156   }
5157
5158   /* element reached destination field */
5159
5160   Feld[x][y] = EL_EMPTY;
5161   Feld[newx][newy] = element;
5162   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
5163
5164   if (element == EL_MOLE)
5165   {
5166     Feld[x][y] = EL_SAND;
5167
5168     DrawLevelFieldCrumbledSandNeighbours(x, y);
5169   }
5170   else if (element == EL_QUICKSAND_FILLING)
5171   {
5172     element = Feld[newx][newy] = get_next_element(element);
5173     Store[newx][newy] = Store[x][y];
5174   }
5175   else if (element == EL_QUICKSAND_EMPTYING)
5176   {
5177     Feld[x][y] = get_next_element(element);
5178     element = Feld[newx][newy] = Store[x][y];
5179   }
5180   else if (element == EL_MAGIC_WALL_FILLING)
5181   {
5182     element = Feld[newx][newy] = get_next_element(element);
5183     if (!game.magic_wall_active)
5184       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5185     Store[newx][newy] = Store[x][y];
5186   }
5187   else if (element == EL_MAGIC_WALL_EMPTYING)
5188   {
5189     Feld[x][y] = get_next_element(element);
5190     if (!game.magic_wall_active)
5191       Feld[x][y] = EL_MAGIC_WALL_DEAD;
5192     element = Feld[newx][newy] = Store[x][y];
5193   }
5194   else if (element == EL_BD_MAGIC_WALL_FILLING)
5195   {
5196     element = Feld[newx][newy] = get_next_element(element);
5197     if (!game.magic_wall_active)
5198       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5199     Store[newx][newy] = Store[x][y];
5200   }
5201   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5202   {
5203     Feld[x][y] = get_next_element(element);
5204     if (!game.magic_wall_active)
5205       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5206     element = Feld[newx][newy] = Store[x][y];
5207   }
5208   else if (element == EL_AMOEBA_DROPPING)
5209   {
5210     Feld[x][y] = get_next_element(element);
5211     element = Feld[newx][newy] = Store[x][y];
5212   }
5213   else if (element == EL_SOKOBAN_OBJECT)
5214   {
5215     if (Back[x][y])
5216       Feld[x][y] = Back[x][y];
5217
5218     if (Back[newx][newy])
5219       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5220
5221     Back[x][y] = Back[newx][newy] = 0;
5222   }
5223   else if (Store[x][y] == EL_ACID)
5224   {
5225     element = Feld[newx][newy] = EL_ACID;
5226   }
5227
5228   Store[x][y] = 0;
5229   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5230   MovDelay[newx][newy] = 0;
5231
5232   /* copy element change control values to new field */
5233   ChangeDelay[newx][newy] = ChangeDelay[x][y];
5234   ChangePage[newx][newy] = ChangePage[x][y];
5235   Changed[newx][newy] = Changed[x][y];
5236   ChangeEvent[newx][newy] = ChangeEvent[x][y];
5237
5238   ChangeDelay[x][y] = 0;
5239   ChangePage[x][y] = -1;
5240   Changed[x][y] = CE_BITMASK_DEFAULT;
5241   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5242
5243   /* copy animation control values to new field */
5244   GfxFrame[newx][newy]  = GfxFrame[x][y];
5245   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
5246   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
5247   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
5248
5249   Pushed[x][y] = Pushed[newx][newy] = FALSE;
5250
5251   ResetGfxAnimation(x, y);      /* reset animation values for old field */
5252
5253 #if 1
5254   /* some elements can leave other elements behind after moving */
5255   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5256       ei->move_leave_element != EL_EMPTY &&
5257       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5258        ei->can_leave_element_last))
5259   {
5260     Feld[x][y] = ei->move_leave_element;
5261     InitField(x, y, FALSE);
5262
5263     if (GFX_CRUMBLED(Feld[x][y]))
5264       DrawLevelFieldCrumbledSandNeighbours(x, y);
5265   }
5266
5267   ei->can_leave_element_last = ei->can_leave_element;
5268   ei->can_leave_element = FALSE;
5269 #endif
5270
5271 #if 0
5272   /* 2.1.1 (does not work correctly for spring) */
5273   if (!CAN_MOVE(element))
5274     MovDir[newx][newy] = 0;
5275 #else
5276
5277 #if 0
5278   /* (does not work for falling objects that slide horizontally) */
5279   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5280     MovDir[newx][newy] = 0;
5281 #else
5282   /*
5283   if (!CAN_MOVE(element) ||
5284       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5285     MovDir[newx][newy] = 0;
5286   */
5287
5288   if (!CAN_MOVE(element) ||
5289       (CAN_FALL(element) && direction == MV_DOWN))
5290     GfxDir[x][y] = MovDir[newx][newy] = 0;
5291
5292 #endif
5293 #endif
5294
5295   DrawLevelField(x, y);
5296   DrawLevelField(newx, newy);
5297
5298   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
5299
5300   /* prevent pushed element from moving on in pushed direction */
5301   if (pushed && CAN_MOVE(element) &&
5302       element_info[element].move_pattern & MV_ANY_DIRECTION &&
5303       !(element_info[element].move_pattern & direction))
5304     TurnRound(newx, newy);
5305
5306   if (!pushed)  /* special case: moving object pushed by player */
5307   {
5308     WasJustMoving[newx][newy] = 3;
5309
5310     if (CAN_FALL(element) && direction == MV_DOWN)
5311       WasJustFalling[newx][newy] = 3;
5312   }
5313
5314   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
5315   {
5316     TestIfBadThingTouchesHero(newx, newy);
5317     TestIfBadThingTouchesFriend(newx, newy);
5318
5319     if (!IS_CUSTOM_ELEMENT(element))
5320       TestIfBadThingTouchesOtherBadThing(newx, newy);
5321   }
5322   else if (element == EL_PENGUIN)
5323     TestIfFriendTouchesBadThing(newx, newy);
5324
5325   if (CAN_FALL(element) && direction == MV_DOWN &&
5326       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5327     Impact(x, newy);
5328
5329 #if 1
5330   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
5331 #endif
5332
5333 #if 0
5334   if (ChangePage[newx][newy] != -1)                     /* delayed change */
5335     ChangeElement(newx, newy, ChangePage[newx][newy]);
5336 #endif
5337
5338 #if 1
5339
5340   TestIfElementHitsCustomElement(newx, newy, direction);
5341
5342 #else
5343
5344   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5345   {
5346     int hitting_element = Feld[newx][newy];
5347
5348     /* !!! fix side (direction) orientation here and elsewhere !!! */
5349     CheckElementSideChange(newx, newy, hitting_element,
5350                            direction, CE_HITTING_SOMETHING, -1);
5351
5352 #if 0
5353     if (IN_LEV_FIELD(nextx, nexty))
5354     {
5355       int opposite_direction = MV_DIR_OPPOSITE(direction);
5356       int hitting_side = direction;
5357       int touched_side = opposite_direction;
5358       int touched_element = MovingOrBlocked2Element(nextx, nexty);
5359       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5360                             MovDir[nextx][nexty] != direction ||
5361                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5362
5363       if (object_hit)
5364       {
5365         int i;
5366
5367         CheckElementSideChange(nextx, nexty, touched_element,
5368                                opposite_direction, CE_HIT_BY_SOMETHING, -1);
5369
5370         if (IS_CUSTOM_ELEMENT(hitting_element) &&
5371             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5372         {
5373           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5374           {
5375             struct ElementChangeInfo *change =
5376               &element_info[hitting_element].change_page[i];
5377
5378             if (change->can_change &&
5379                 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5380                 change->sides & touched_side &&
5381                 change->trigger_element == touched_element)
5382             {
5383               CheckElementSideChange(newx, newy, hitting_element,
5384                                      CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5385               break;
5386             }
5387           }
5388         }
5389
5390         if (IS_CUSTOM_ELEMENT(touched_element) &&
5391             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5392         {
5393           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5394           {
5395             struct ElementChangeInfo *change =
5396               &element_info[touched_element].change_page[i];
5397
5398             if (change->can_change &&
5399                 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5400                 change->sides & hitting_side &&
5401                 change->trigger_element == hitting_element)
5402             {
5403               CheckElementSideChange(nextx, nexty, touched_element,
5404                                      CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5405               break;
5406             }
5407           }
5408         }
5409       }
5410     }
5411 #endif
5412   }
5413 #endif
5414
5415   TestIfPlayerTouchesCustomElement(newx, newy);
5416   TestIfElementTouchesCustomElement(newx, newy);
5417 }
5418
5419 int AmoebeNachbarNr(int ax, int ay)
5420 {
5421   int i;
5422   int element = Feld[ax][ay];
5423   int group_nr = 0;
5424   static int xy[4][2] =
5425   {
5426     { 0, -1 },
5427     { -1, 0 },
5428     { +1, 0 },
5429     { 0, +1 }
5430   };
5431
5432   for (i = 0; i < 4; i++)
5433   {
5434     int x = ax + xy[i][0];
5435     int y = ay + xy[i][1];
5436
5437     if (!IN_LEV_FIELD(x, y))
5438       continue;
5439
5440     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5441       group_nr = AmoebaNr[x][y];
5442   }
5443
5444   return group_nr;
5445 }
5446
5447 void AmoebenVereinigen(int ax, int ay)
5448 {
5449   int i, x, y, xx, yy;
5450   int new_group_nr = AmoebaNr[ax][ay];
5451   static int xy[4][2] =
5452   {
5453     { 0, -1 },
5454     { -1, 0 },
5455     { +1, 0 },
5456     { 0, +1 }
5457   };
5458
5459   if (new_group_nr == 0)
5460     return;
5461
5462   for (i = 0; i < 4; i++)
5463   {
5464     x = ax + xy[i][0];
5465     y = ay + xy[i][1];
5466
5467     if (!IN_LEV_FIELD(x, y))
5468       continue;
5469
5470     if ((Feld[x][y] == EL_AMOEBA_FULL ||
5471          Feld[x][y] == EL_BD_AMOEBA ||
5472          Feld[x][y] == EL_AMOEBA_DEAD) &&
5473         AmoebaNr[x][y] != new_group_nr)
5474     {
5475       int old_group_nr = AmoebaNr[x][y];
5476
5477       if (old_group_nr == 0)
5478         return;
5479
5480       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5481       AmoebaCnt[old_group_nr] = 0;
5482       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5483       AmoebaCnt2[old_group_nr] = 0;
5484
5485       for (yy = 0; yy < lev_fieldy; yy++)
5486       {
5487         for (xx = 0; xx < lev_fieldx; xx++)
5488         {
5489           if (AmoebaNr[xx][yy] == old_group_nr)
5490             AmoebaNr[xx][yy] = new_group_nr;
5491         }
5492       }
5493     }
5494   }
5495 }
5496
5497 void AmoebeUmwandeln(int ax, int ay)
5498 {
5499   int i, x, y;
5500
5501   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5502   {
5503     int group_nr = AmoebaNr[ax][ay];
5504
5505 #ifdef DEBUG
5506     if (group_nr == 0)
5507     {
5508       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5509       printf("AmoebeUmwandeln(): This should never happen!\n");
5510       return;
5511     }
5512 #endif
5513
5514     for (y = 0; y < lev_fieldy; y++)
5515     {
5516       for (x = 0; x < lev_fieldx; x++)
5517       {
5518         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5519         {
5520           AmoebaNr[x][y] = 0;
5521           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5522         }
5523       }
5524     }
5525     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5526                             SND_AMOEBA_TURNING_TO_GEM :
5527                             SND_AMOEBA_TURNING_TO_ROCK));
5528     Bang(ax, ay);
5529   }
5530   else
5531   {
5532     static int xy[4][2] =
5533     {
5534       { 0, -1 },
5535       { -1, 0 },
5536       { +1, 0 },
5537       { 0, +1 }
5538     };
5539
5540     for (i = 0; i < 4; i++)
5541     {
5542       x = ax + xy[i][0];
5543       y = ay + xy[i][1];
5544
5545       if (!IN_LEV_FIELD(x, y))
5546         continue;
5547
5548       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5549       {
5550         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5551                               SND_AMOEBA_TURNING_TO_GEM :
5552                               SND_AMOEBA_TURNING_TO_ROCK));
5553         Bang(x, y);
5554       }
5555     }
5556   }
5557 }
5558
5559 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5560 {
5561   int x, y;
5562   int group_nr = AmoebaNr[ax][ay];
5563   boolean done = FALSE;
5564
5565 #ifdef DEBUG
5566   if (group_nr == 0)
5567   {
5568     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5569     printf("AmoebeUmwandelnBD(): This should never happen!\n");
5570     return;
5571   }
5572 #endif
5573
5574   for (y = 0; y < lev_fieldy; y++)
5575   {
5576     for (x = 0; x < lev_fieldx; x++)
5577     {
5578       if (AmoebaNr[x][y] == group_nr &&
5579           (Feld[x][y] == EL_AMOEBA_DEAD ||
5580            Feld[x][y] == EL_BD_AMOEBA ||
5581            Feld[x][y] == EL_AMOEBA_GROWING))
5582       {
5583         AmoebaNr[x][y] = 0;
5584         Feld[x][y] = new_element;
5585         InitField(x, y, FALSE);
5586         DrawLevelField(x, y);
5587         done = TRUE;
5588       }
5589     }
5590   }
5591
5592   if (done)
5593     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5594                             SND_BD_AMOEBA_TURNING_TO_ROCK :
5595                             SND_BD_AMOEBA_TURNING_TO_GEM));
5596 }
5597
5598 void AmoebeWaechst(int x, int y)
5599 {
5600   static unsigned long sound_delay = 0;
5601   static unsigned long sound_delay_value = 0;
5602
5603   if (!MovDelay[x][y])          /* start new growing cycle */
5604   {
5605     MovDelay[x][y] = 7;
5606
5607     if (DelayReached(&sound_delay, sound_delay_value))
5608     {
5609 #if 1
5610       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5611 #else
5612       if (Store[x][y] == EL_BD_AMOEBA)
5613         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5614       else
5615         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5616 #endif
5617       sound_delay_value = 30;
5618     }
5619   }
5620
5621   if (MovDelay[x][y])           /* wait some time before growing bigger */
5622   {
5623     MovDelay[x][y]--;
5624     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5625     {
5626       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5627                                            6 - MovDelay[x][y]);
5628
5629       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5630     }
5631
5632     if (!MovDelay[x][y])
5633     {
5634       Feld[x][y] = Store[x][y];
5635       Store[x][y] = 0;
5636       DrawLevelField(x, y);
5637     }
5638   }
5639 }
5640
5641 void AmoebaDisappearing(int x, int y)
5642 {
5643   static unsigned long sound_delay = 0;
5644   static unsigned long sound_delay_value = 0;
5645
5646   if (!MovDelay[x][y])          /* start new shrinking cycle */
5647   {
5648     MovDelay[x][y] = 7;
5649
5650     if (DelayReached(&sound_delay, sound_delay_value))
5651       sound_delay_value = 30;
5652   }
5653
5654   if (MovDelay[x][y])           /* wait some time before shrinking */
5655   {
5656     MovDelay[x][y]--;
5657     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5658     {
5659       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5660                                            6 - MovDelay[x][y]);
5661
5662       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5663     }
5664
5665     if (!MovDelay[x][y])
5666     {
5667       Feld[x][y] = EL_EMPTY;
5668       DrawLevelField(x, y);
5669
5670       /* don't let mole enter this field in this cycle;
5671          (give priority to objects falling to this field from above) */
5672       Stop[x][y] = TRUE;
5673     }
5674   }
5675 }
5676
5677 void AmoebeAbleger(int ax, int ay)
5678 {
5679   int i;
5680   int element = Feld[ax][ay];
5681   int graphic = el2img(element);
5682   int newax = ax, neway = ay;
5683   static int xy[4][2] =
5684   {
5685     { 0, -1 },
5686     { -1, 0 },
5687     { +1, 0 },
5688     { 0, +1 }
5689   };
5690
5691   if (!level.amoeba_speed)
5692   {
5693     Feld[ax][ay] = EL_AMOEBA_DEAD;
5694     DrawLevelField(ax, ay);
5695     return;
5696   }
5697
5698   if (IS_ANIMATED(graphic))
5699     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5700
5701   if (!MovDelay[ax][ay])        /* start making new amoeba field */
5702     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5703
5704   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
5705   {
5706     MovDelay[ax][ay]--;
5707     if (MovDelay[ax][ay])
5708       return;
5709   }
5710
5711   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5712   {
5713     int start = RND(4);
5714     int x = ax + xy[start][0];
5715     int y = ay + xy[start][1];
5716
5717     if (!IN_LEV_FIELD(x, y))
5718       return;
5719
5720     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5721     if (IS_FREE(x, y) ||
5722         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5723     {
5724       newax = x;
5725       neway = y;
5726     }
5727
5728     if (newax == ax && neway == ay)
5729       return;
5730   }
5731   else                          /* normal or "filled" (BD style) amoeba */
5732   {
5733     int start = RND(4);
5734     boolean waiting_for_player = FALSE;
5735
5736     for (i = 0; i < 4; i++)
5737     {
5738       int j = (start + i) % 4;
5739       int x = ax + xy[j][0];
5740       int y = ay + xy[j][1];
5741
5742       if (!IN_LEV_FIELD(x, y))
5743         continue;
5744
5745       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5746       if (IS_FREE(x, y) ||
5747           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5748       {
5749         newax = x;
5750         neway = y;
5751         break;
5752       }
5753       else if (IS_PLAYER(x, y))
5754         waiting_for_player = TRUE;
5755     }
5756
5757     if (newax == ax && neway == ay)             /* amoeba cannot grow */
5758     {
5759       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5760       {
5761         Feld[ax][ay] = EL_AMOEBA_DEAD;
5762         DrawLevelField(ax, ay);
5763         AmoebaCnt[AmoebaNr[ax][ay]]--;
5764
5765         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
5766         {
5767           if (element == EL_AMOEBA_FULL)
5768             AmoebeUmwandeln(ax, ay);
5769           else if (element == EL_BD_AMOEBA)
5770             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5771         }
5772       }
5773       return;
5774     }
5775     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5776     {
5777       /* amoeba gets larger by growing in some direction */
5778
5779       int new_group_nr = AmoebaNr[ax][ay];
5780
5781 #ifdef DEBUG
5782   if (new_group_nr == 0)
5783   {
5784     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5785     printf("AmoebeAbleger(): This should never happen!\n");
5786     return;
5787   }
5788 #endif
5789
5790       AmoebaNr[newax][neway] = new_group_nr;
5791       AmoebaCnt[new_group_nr]++;
5792       AmoebaCnt2[new_group_nr]++;
5793
5794       /* if amoeba touches other amoeba(s) after growing, unify them */
5795       AmoebenVereinigen(newax, neway);
5796
5797       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5798       {
5799         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5800         return;
5801       }
5802     }
5803   }
5804
5805   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5806       (neway == lev_fieldy - 1 && newax != ax))
5807   {
5808     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
5809     Store[newax][neway] = element;
5810   }
5811   else if (neway == ay)
5812   {
5813     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
5814 #if 1
5815     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5816 #else
5817     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5818 #endif
5819   }
5820   else
5821   {
5822     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
5823     Feld[ax][ay] = EL_AMOEBA_DROPPING;
5824     Store[ax][ay] = EL_AMOEBA_DROP;
5825     ContinueMoving(ax, ay);
5826     return;
5827   }
5828
5829   DrawLevelField(newax, neway);
5830 }
5831
5832 void Life(int ax, int ay)
5833 {
5834   int x1, y1, x2, y2;
5835   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
5836   int life_time = 40;
5837   int element = Feld[ax][ay];
5838   int graphic = el2img(element);
5839   boolean changed = FALSE;
5840
5841   if (IS_ANIMATED(graphic))
5842     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5843
5844   if (Stop[ax][ay])
5845     return;
5846
5847   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
5848     MovDelay[ax][ay] = life_time;
5849
5850   if (MovDelay[ax][ay])         /* wait some time before next cycle */
5851   {
5852     MovDelay[ax][ay]--;
5853     if (MovDelay[ax][ay])
5854       return;
5855   }
5856
5857   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5858   {
5859     int xx = ax+x1, yy = ay+y1;
5860     int nachbarn = 0;
5861
5862     if (!IN_LEV_FIELD(xx, yy))
5863       continue;
5864
5865     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5866     {
5867       int x = xx+x2, y = yy+y2;
5868
5869       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5870         continue;
5871
5872       if (((Feld[x][y] == element ||
5873             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5874            !Stop[x][y]) ||
5875           (IS_FREE(x, y) && Stop[x][y]))
5876         nachbarn++;
5877     }
5878
5879     if (xx == ax && yy == ay)           /* field in the middle */
5880     {
5881       if (nachbarn < life[0] || nachbarn > life[1])
5882       {
5883         Feld[xx][yy] = EL_EMPTY;
5884         if (!Stop[xx][yy])
5885           DrawLevelField(xx, yy);
5886         Stop[xx][yy] = TRUE;
5887         changed = TRUE;
5888       }
5889     }
5890     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5891     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5892     {                                   /* free border field */
5893       if (nachbarn >= life[2] && nachbarn <= life[3])
5894       {
5895         Feld[xx][yy] = element;
5896         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5897         if (!Stop[xx][yy])
5898           DrawLevelField(xx, yy);
5899         Stop[xx][yy] = TRUE;
5900         changed = TRUE;
5901       }
5902     }
5903   }
5904
5905   if (changed)
5906     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5907                    SND_GAME_OF_LIFE_GROWING);
5908 }
5909
5910 static void InitRobotWheel(int x, int y)
5911 {
5912   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5913 }
5914
5915 static void RunRobotWheel(int x, int y)
5916 {
5917   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5918 }
5919
5920 static void StopRobotWheel(int x, int y)
5921 {
5922   if (ZX == x && ZY == y)
5923     ZX = ZY = -1;
5924 }
5925
5926 static void InitTimegateWheel(int x, int y)
5927 {
5928   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5929 }
5930
5931 static void RunTimegateWheel(int x, int y)
5932 {
5933   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
5934 }
5935
5936 void CheckExit(int x, int y)
5937 {
5938   if (local_player->gems_still_needed > 0 ||
5939       local_player->sokobanfields_still_needed > 0 ||
5940       local_player->lights_still_needed > 0)
5941   {
5942     int element = Feld[x][y];
5943     int graphic = el2img(element);
5944
5945     if (IS_ANIMATED(graphic))
5946       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5947
5948     return;
5949   }
5950
5951   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5952     return;
5953
5954   Feld[x][y] = EL_EXIT_OPENING;
5955
5956   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
5957 }
5958
5959 void CheckExitSP(int x, int y)
5960 {
5961   if (local_player->gems_still_needed > 0)
5962   {
5963     int element = Feld[x][y];
5964     int graphic = el2img(element);
5965
5966     if (IS_ANIMATED(graphic))
5967       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
5968
5969     return;
5970   }
5971
5972   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
5973     return;
5974
5975   Feld[x][y] = EL_SP_EXIT_OPENING;
5976
5977   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
5978 }
5979
5980 static void CloseAllOpenTimegates()
5981 {
5982   int x, y;
5983
5984   for (y = 0; y < lev_fieldy; y++)
5985   {
5986     for (x = 0; x < lev_fieldx; x++)
5987     {
5988       int element = Feld[x][y];
5989
5990       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
5991       {
5992         Feld[x][y] = EL_TIMEGATE_CLOSING;
5993 #if 1
5994         PlayLevelSoundAction(x, y, ACTION_CLOSING);
5995 #else
5996         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
5997 #endif
5998       }
5999     }
6000   }
6001 }
6002
6003 void EdelsteinFunkeln(int x, int y)
6004 {
6005   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6006     return;
6007
6008   if (Feld[x][y] == EL_BD_DIAMOND)
6009     return;
6010
6011   if (MovDelay[x][y] == 0)      /* next animation frame */
6012     MovDelay[x][y] = 11 * !SimpleRND(500);
6013
6014   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
6015   {
6016     MovDelay[x][y]--;
6017
6018     if (setup.direct_draw && MovDelay[x][y])
6019       SetDrawtoField(DRAW_BUFFERED);
6020
6021     DrawLevelElementAnimation(x, y, Feld[x][y]);
6022
6023     if (MovDelay[x][y] != 0)
6024     {
6025       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6026                                            10 - MovDelay[x][y]);
6027
6028       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6029
6030       if (setup.direct_draw)
6031       {
6032         int dest_x, dest_y;
6033
6034         dest_x = FX + SCREENX(x) * TILEX;
6035         dest_y = FY + SCREENY(y) * TILEY;
6036
6037         BlitBitmap(drawto_field, window,
6038                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6039         SetDrawtoField(DRAW_DIRECT);
6040       }
6041     }
6042   }
6043 }
6044
6045 void MauerWaechst(int x, int y)
6046 {
6047   int delay = 6;
6048
6049   if (!MovDelay[x][y])          /* next animation frame */
6050     MovDelay[x][y] = 3 * delay;
6051
6052   if (MovDelay[x][y])           /* wait some time before next frame */
6053   {
6054     MovDelay[x][y]--;
6055
6056     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6057     {
6058       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6059       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6060
6061       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6062     }
6063
6064     if (!MovDelay[x][y])
6065     {
6066       if (MovDir[x][y] == MV_LEFT)
6067       {
6068         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6069           DrawLevelField(x - 1, y);
6070       }
6071       else if (MovDir[x][y] == MV_RIGHT)
6072       {
6073         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6074           DrawLevelField(x + 1, y);
6075       }
6076       else if (MovDir[x][y] == MV_UP)
6077       {
6078         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6079           DrawLevelField(x, y - 1);
6080       }
6081       else
6082       {
6083         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6084           DrawLevelField(x, y + 1);
6085       }
6086
6087       Feld[x][y] = Store[x][y];
6088       Store[x][y] = 0;
6089       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6090       DrawLevelField(x, y);
6091     }
6092   }
6093 }
6094
6095 void MauerAbleger(int ax, int ay)
6096 {
6097   int element = Feld[ax][ay];
6098   int graphic = el2img(element);
6099   boolean oben_frei = FALSE, unten_frei = FALSE;
6100   boolean links_frei = FALSE, rechts_frei = FALSE;
6101   boolean oben_massiv = FALSE, unten_massiv = FALSE;
6102   boolean links_massiv = FALSE, rechts_massiv = FALSE;
6103   boolean new_wall = FALSE;
6104
6105   if (IS_ANIMATED(graphic))
6106     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6107
6108   if (!MovDelay[ax][ay])        /* start building new wall */
6109     MovDelay[ax][ay] = 6;
6110
6111   if (MovDelay[ax][ay])         /* wait some time before building new wall */
6112   {
6113     MovDelay[ax][ay]--;
6114     if (MovDelay[ax][ay])
6115       return;
6116   }
6117
6118   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6119     oben_frei = TRUE;
6120   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6121     unten_frei = TRUE;
6122   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6123     links_frei = TRUE;
6124   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6125     rechts_frei = TRUE;
6126
6127   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6128       element == EL_EXPANDABLE_WALL_ANY)
6129   {
6130     if (oben_frei)
6131     {
6132       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6133       Store[ax][ay-1] = element;
6134       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6135       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6136         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6137                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6138       new_wall = TRUE;
6139     }
6140     if (unten_frei)
6141     {
6142       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6143       Store[ax][ay+1] = element;
6144       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6145       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6146         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6147                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6148       new_wall = TRUE;
6149     }
6150   }
6151
6152   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6153       element == EL_EXPANDABLE_WALL_ANY ||
6154       element == EL_EXPANDABLE_WALL)
6155   {
6156     if (links_frei)
6157     {
6158       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6159       Store[ax-1][ay] = element;
6160       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6161       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6162         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6163                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6164       new_wall = TRUE;
6165     }
6166
6167     if (rechts_frei)
6168     {
6169       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6170       Store[ax+1][ay] = element;
6171       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6172       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6173         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6174                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6175       new_wall = TRUE;
6176     }
6177   }
6178
6179   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6180     DrawLevelField(ax, ay);
6181
6182   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6183     oben_massiv = TRUE;
6184   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6185     unten_massiv = TRUE;
6186   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6187     links_massiv = TRUE;
6188   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6189     rechts_massiv = TRUE;
6190
6191   if (((oben_massiv && unten_massiv) ||
6192        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6193        element == EL_EXPANDABLE_WALL) &&
6194       ((links_massiv && rechts_massiv) ||
6195        element == EL_EXPANDABLE_WALL_VERTICAL))
6196     Feld[ax][ay] = EL_WALL;
6197
6198   if (new_wall)
6199 #if 1
6200     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6201 #else
6202     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6203 #endif
6204 }
6205
6206 void CheckForDragon(int x, int y)
6207 {
6208   int i, j;
6209   boolean dragon_found = FALSE;
6210   static int xy[4][2] =
6211   {
6212     { 0, -1 },
6213     { -1, 0 },
6214     { +1, 0 },
6215     { 0, +1 }
6216   };
6217
6218   for (i = 0; i < 4; i++)
6219   {
6220     for (j = 0; j < 4; j++)
6221     {
6222       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6223
6224       if (IN_LEV_FIELD(xx, yy) &&
6225           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6226       {
6227         if (Feld[xx][yy] == EL_DRAGON)
6228           dragon_found = TRUE;
6229       }
6230       else
6231         break;
6232     }
6233   }
6234
6235   if (!dragon_found)
6236   {
6237     for (i = 0; i < 4; i++)
6238     {
6239       for (j = 0; j < 3; j++)
6240       {
6241         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6242   
6243         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6244         {
6245           Feld[xx][yy] = EL_EMPTY;
6246           DrawLevelField(xx, yy);
6247         }
6248         else
6249           break;
6250       }
6251     }
6252   }
6253 }
6254
6255 static void InitBuggyBase(int x, int y)
6256 {
6257   int element = Feld[x][y];
6258   int activating_delay = FRAMES_PER_SECOND / 4;
6259
6260   ChangeDelay[x][y] =
6261     (element == EL_SP_BUGGY_BASE ?
6262      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6263      element == EL_SP_BUGGY_BASE_ACTIVATING ?
6264      activating_delay :
6265      element == EL_SP_BUGGY_BASE_ACTIVE ?
6266      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6267 }
6268
6269 static void WarnBuggyBase(int x, int y)
6270 {
6271   int i;
6272   static int xy[4][2] =
6273   {
6274     { 0, -1 },
6275     { -1, 0 },
6276     { +1, 0 },
6277     { 0, +1 }
6278   };
6279
6280   for (i = 0; i < 4; i++)
6281   {
6282     int xx = x + xy[i][0], yy = y + xy[i][1];
6283
6284     if (IS_PLAYER(xx, yy))
6285     {
6286       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6287
6288       break;
6289     }
6290   }
6291 }
6292
6293 static void InitTrap(int x, int y)
6294 {
6295   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6296 }
6297
6298 static void ActivateTrap(int x, int y)
6299 {
6300   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6301 }
6302
6303 static void ChangeActiveTrap(int x, int y)
6304 {
6305   int graphic = IMG_TRAP_ACTIVE;
6306
6307   /* if new animation frame was drawn, correct crumbled sand border */
6308   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6309     DrawLevelFieldCrumbledSand(x, y);
6310 }
6311
6312 static void ChangeElementNowExt(int x, int y, int target_element)
6313 {
6314   int previous_move_direction = MovDir[x][y];
6315
6316   /* check if element under player changes from accessible to unaccessible
6317      (needed for special case of dropping element which then changes) */
6318   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6319       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6320   {
6321     Bang(x, y);
6322     return;
6323   }
6324
6325   RemoveField(x, y);
6326   Feld[x][y] = target_element;
6327
6328   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6329
6330   ResetGfxAnimation(x, y);
6331   ResetRandomAnimationValue(x, y);
6332
6333   if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6334     MovDir[x][y] = previous_move_direction;
6335
6336   InitField(x, y, FALSE);
6337   if (CAN_MOVE(Feld[x][y]))
6338     InitMovDir(x, y);
6339
6340   DrawLevelField(x, y);
6341
6342   if (GFX_CRUMBLED(Feld[x][y]))
6343     DrawLevelFieldCrumbledSandNeighbours(x, y);
6344
6345   TestIfBadThingTouchesHero(x, y);
6346   TestIfPlayerTouchesCustomElement(x, y);
6347   TestIfElementTouchesCustomElement(x, y);
6348
6349   if (ELEM_IS_PLAYER(target_element))
6350     RelocatePlayer(x, y, target_element);
6351 }
6352
6353 static boolean ChangeElementNow(int x, int y, int element, int page)
6354 {
6355   struct ElementChangeInfo *change = &element_info[element].change_page[page];
6356
6357   /* always use default change event to prevent running into a loop */
6358   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6359     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6360
6361   /* do not change already changed elements with same change event */
6362 #if 0
6363   if (Changed[x][y] & ChangeEvent[x][y])
6364     return FALSE;
6365 #else
6366   if (Changed[x][y])
6367     return FALSE;
6368 #endif
6369
6370   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6371
6372   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6373
6374   if (change->explode)
6375   {
6376     Bang(x, y);
6377
6378     return TRUE;
6379   }
6380
6381   if (change->use_content)
6382   {
6383     boolean complete_change = TRUE;
6384     boolean can_change[3][3];
6385     int xx, yy;
6386
6387     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6388     {
6389       boolean half_destructible;
6390       int ex = x + xx - 1;
6391       int ey = y + yy - 1;
6392       int e;
6393
6394       can_change[xx][yy] = TRUE;
6395
6396       if (ex == x && ey == y)   /* do not check changing element itself */
6397         continue;
6398
6399       if (change->content[xx][yy] == EL_EMPTY_SPACE)
6400       {
6401         can_change[xx][yy] = FALSE;     /* do not change empty borders */
6402
6403         continue;
6404       }
6405
6406       if (!IN_LEV_FIELD(ex, ey))
6407       {
6408         can_change[xx][yy] = FALSE;
6409         complete_change = FALSE;
6410
6411         continue;
6412       }
6413
6414       e = Feld[ex][ey];
6415
6416       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6417         e = MovingOrBlocked2Element(ex, ey);
6418
6419       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6420
6421       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
6422           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6423           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6424       {
6425         can_change[xx][yy] = FALSE;
6426         complete_change = FALSE;
6427       }
6428     }
6429
6430     if (!change->only_complete || complete_change)
6431     {
6432       boolean something_has_changed = FALSE;
6433
6434       if (change->only_complete && change->use_random_change &&
6435           RND(100) < change->random)
6436         return FALSE;
6437
6438       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6439       {
6440         int ex = x + xx - 1;
6441         int ey = y + yy - 1;
6442
6443         if (can_change[xx][yy] && (!change->use_random_change ||
6444                                    RND(100) < change->random))
6445         {
6446           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6447             RemoveMovingField(ex, ey);
6448
6449           ChangeEvent[ex][ey] = ChangeEvent[x][y];
6450
6451           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6452
6453           something_has_changed = TRUE;
6454
6455           /* for symmetry reasons, freeze newly created border elements */
6456           if (ex != x || ey != y)
6457             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
6458         }
6459       }
6460
6461       if (something_has_changed)
6462         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6463     }
6464   }
6465   else
6466   {
6467     ChangeElementNowExt(x, y, change->target_element);
6468
6469     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6470   }
6471
6472   return TRUE;
6473 }
6474
6475 static void ChangeElement(int x, int y, int page)
6476 {
6477   int element = MovingOrBlocked2Element(x, y);
6478   struct ElementInfo *ei = &element_info[element];
6479   struct ElementChangeInfo *change = &ei->change_page[page];
6480
6481 #if 0
6482 #ifdef DEBUG
6483   if (!CAN_CHANGE(element))
6484   {
6485     printf("\n\n");
6486     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6487            x, y, element, element_info[element].token_name);
6488     printf("ChangeElement(): This should never happen!\n");
6489     printf("\n\n");
6490   }
6491 #endif
6492 #endif
6493
6494   if (ChangeDelay[x][y] == 0)           /* initialize element change */
6495   {
6496     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
6497                          RND(change->delay_random * change->delay_frames)) + 1;
6498
6499     ResetGfxAnimation(x, y);
6500     ResetRandomAnimationValue(x, y);
6501
6502     if (change->pre_change_function)
6503       change->pre_change_function(x, y);
6504   }
6505
6506   ChangeDelay[x][y]--;
6507
6508   if (ChangeDelay[x][y] != 0)           /* continue element change */
6509   {
6510     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6511
6512     if (IS_ANIMATED(graphic))
6513       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6514
6515     if (change->change_function)
6516       change->change_function(x, y);
6517   }
6518   else                                  /* finish element change */
6519   {
6520     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
6521     {
6522       page = ChangePage[x][y];
6523       ChangePage[x][y] = -1;
6524     }
6525
6526 #if 0
6527     if (IS_MOVING(x, y) && !change->explode)
6528 #else
6529     if (IS_MOVING(x, y))                /* never change a running system ;-) */
6530 #endif
6531     {
6532       ChangeDelay[x][y] = 1;            /* try change after next move step */
6533       ChangePage[x][y] = page;          /* remember page to use for change */
6534
6535       return;
6536     }
6537
6538     if (ChangeElementNow(x, y, element, page))
6539     {
6540       if (change->post_change_function)
6541         change->post_change_function(x, y);
6542     }
6543   }
6544 }
6545
6546 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6547                                                int trigger_element,
6548                                                int trigger_side,
6549                                                int trigger_event)
6550 {
6551   int i, j, x, y;
6552
6553   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6554     return FALSE;
6555
6556   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6557   {
6558     int element = EL_CUSTOM_START + i;
6559
6560     boolean change_element = FALSE;
6561     int page = 0;
6562
6563     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6564       continue;
6565
6566     for (j = 0; j < element_info[element].num_change_pages; j++)
6567     {
6568       struct ElementChangeInfo *change = &element_info[element].change_page[j];
6569
6570       if (change->can_change &&
6571 #if 1
6572           change->events & CH_EVENT_BIT(trigger_event) &&
6573 #endif
6574           change->sides & trigger_side &&
6575 #if 1
6576           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6577 #else
6578           change->trigger_element == trigger_element
6579 #endif
6580           )
6581       {
6582 #if 0
6583         if (!(change->events & CH_EVENT_BIT(trigger_event)))
6584           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6585                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6586 #endif
6587
6588         change_element = TRUE;
6589         page = j;
6590
6591         break;
6592       }
6593     }
6594
6595     if (!change_element)
6596       continue;
6597
6598     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6599     {
6600 #if 0
6601       if (x == lx && y == ly)   /* do not change trigger element itself */
6602         continue;
6603 #endif
6604
6605       if (Feld[x][y] == element)
6606       {
6607         ChangeDelay[x][y] = 1;
6608         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6609         ChangeElement(x, y, page);
6610       }
6611     }
6612   }
6613
6614   return TRUE;
6615 }
6616
6617 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6618                                            int trigger_event)
6619 {
6620   return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6621                                          trigger_event);
6622 }
6623
6624 static boolean CheckElementSideChange(int x, int y, int element, int side,
6625                                       int trigger_event, int page)
6626 {
6627   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6628     return FALSE;
6629
6630   if (Feld[x][y] == EL_BLOCKED)
6631   {
6632     Blocked2Moving(x, y, &x, &y);
6633     element = Feld[x][y];
6634   }
6635
6636   if (page < 0)
6637     page = element_info[element].event_page_nr[trigger_event];
6638
6639   if (!(element_info[element].change_page[page].sides & side))
6640     return FALSE;
6641
6642   ChangeDelay[x][y] = 1;
6643   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6644   ChangeElement(x, y, page);
6645
6646   return TRUE;
6647 }
6648
6649 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6650 {
6651   return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6652 }
6653
6654 static void PlayPlayerSound(struct PlayerInfo *player)
6655 {
6656   int jx = player->jx, jy = player->jy;
6657   int element = player->element_nr;
6658   int last_action = player->last_action_waiting;
6659   int action = player->action_waiting;
6660
6661   if (player->is_waiting)
6662   {
6663     if (action != last_action)
6664       PlayLevelSoundElementAction(jx, jy, element, action);
6665     else
6666       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6667   }
6668   else
6669   {
6670     if (action != last_action)
6671       StopSound(element_info[element].sound[last_action]);
6672
6673     if (last_action == ACTION_SLEEPING)
6674       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6675   }
6676 }
6677
6678 static void PlayAllPlayersSound()
6679 {
6680   int i;
6681
6682   for (i = 0; i < MAX_PLAYERS; i++)
6683     if (stored_player[i].active)
6684       PlayPlayerSound(&stored_player[i]);
6685 }
6686
6687 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6688 {
6689   boolean last_waiting = player->is_waiting;
6690   int move_dir = player->MovDir;
6691
6692   player->last_action_waiting = player->action_waiting;
6693
6694   if (is_waiting)
6695   {
6696     if (!last_waiting)          /* not waiting -> waiting */
6697     {
6698       player->is_waiting = TRUE;
6699
6700       player->frame_counter_bored =
6701         FrameCounter +
6702         game.player_boring_delay_fixed +
6703         SimpleRND(game.player_boring_delay_random);
6704       player->frame_counter_sleeping =
6705         FrameCounter +
6706         game.player_sleeping_delay_fixed +
6707         SimpleRND(game.player_sleeping_delay_random);
6708
6709       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6710     }
6711
6712     if (game.player_sleeping_delay_fixed +
6713         game.player_sleeping_delay_random > 0 &&
6714         player->anim_delay_counter == 0 &&
6715         player->post_delay_counter == 0 &&
6716         FrameCounter >= player->frame_counter_sleeping)
6717       player->is_sleeping = TRUE;
6718     else if (game.player_boring_delay_fixed +
6719              game.player_boring_delay_random > 0 &&
6720              FrameCounter >= player->frame_counter_bored)
6721       player->is_bored = TRUE;
6722
6723     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6724                               player->is_bored ? ACTION_BORING :
6725                               ACTION_WAITING);
6726
6727     if (player->is_sleeping)
6728     {
6729       if (player->num_special_action_sleeping > 0)
6730       {
6731         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6732         {
6733           int last_special_action = player->special_action_sleeping;
6734           int num_special_action = player->num_special_action_sleeping;
6735           int special_action =
6736             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6737              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6738              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6739              last_special_action + 1 : ACTION_SLEEPING);
6740           int special_graphic =
6741             el_act_dir2img(player->element_nr, special_action, move_dir);
6742
6743           player->anim_delay_counter =
6744             graphic_info[special_graphic].anim_delay_fixed +
6745             SimpleRND(graphic_info[special_graphic].anim_delay_random);
6746           player->post_delay_counter =
6747             graphic_info[special_graphic].post_delay_fixed +
6748             SimpleRND(graphic_info[special_graphic].post_delay_random);
6749
6750           player->special_action_sleeping = special_action;
6751         }
6752
6753         if (player->anim_delay_counter > 0)
6754         {
6755           player->action_waiting = player->special_action_sleeping;
6756           player->anim_delay_counter--;
6757         }
6758         else if (player->post_delay_counter > 0)
6759         {
6760           player->post_delay_counter--;
6761         }
6762       }
6763     }
6764     else if (player->is_bored)
6765     {
6766       if (player->num_special_action_bored > 0)
6767       {
6768         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6769         {
6770           int special_action =
6771             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6772           int special_graphic =
6773             el_act_dir2img(player->element_nr, special_action, move_dir);
6774
6775           player->anim_delay_counter =
6776             graphic_info[special_graphic].anim_delay_fixed +
6777             SimpleRND(graphic_info[special_graphic].anim_delay_random);
6778           player->post_delay_counter =
6779             graphic_info[special_graphic].post_delay_fixed +
6780             SimpleRND(graphic_info[special_graphic].post_delay_random);
6781
6782           player->special_action_bored = special_action;
6783         }
6784
6785         if (player->anim_delay_counter > 0)
6786         {
6787           player->action_waiting = player->special_action_bored;
6788           player->anim_delay_counter--;
6789         }
6790         else if (player->post_delay_counter > 0)
6791         {
6792           player->post_delay_counter--;
6793         }
6794       }
6795     }
6796   }
6797   else if (last_waiting)        /* waiting -> not waiting */
6798   {
6799     player->is_waiting = FALSE;
6800     player->is_bored = FALSE;
6801     player->is_sleeping = FALSE;
6802
6803     player->frame_counter_bored = -1;
6804     player->frame_counter_sleeping = -1;
6805
6806     player->anim_delay_counter = 0;
6807     player->post_delay_counter = 0;
6808
6809     player->action_waiting = ACTION_DEFAULT;
6810
6811     player->special_action_bored = ACTION_DEFAULT;
6812     player->special_action_sleeping = ACTION_DEFAULT;
6813   }
6814 }
6815
6816 #if 1
6817 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6818 {
6819 #if 0
6820   static byte stored_player_action[MAX_PLAYERS];
6821   static int num_stored_actions = 0;
6822 #endif
6823   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6824   int left      = player_action & JOY_LEFT;
6825   int right     = player_action & JOY_RIGHT;
6826   int up        = player_action & JOY_UP;
6827   int down      = player_action & JOY_DOWN;
6828   int button1   = player_action & JOY_BUTTON_1;
6829   int button2   = player_action & JOY_BUTTON_2;
6830   int dx        = (left ? -1    : right ? 1     : 0);
6831   int dy        = (up   ? -1    : down  ? 1     : 0);
6832
6833 #if 0
6834   stored_player_action[player->index_nr] = 0;
6835   num_stored_actions++;
6836 #endif
6837
6838 #if 0
6839   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6840 #endif
6841
6842   if (!player->active || tape.pausing)
6843     return 0;
6844
6845 #if 0
6846   printf("::: [%d %d %d %d] [%d %d]\n",
6847          left, right, up, down, button1, button2);
6848 #endif
6849
6850   if (player_action)
6851   {
6852 #if 0
6853     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6854 #endif
6855
6856     if (button1)
6857       snapped = SnapField(player, dx, dy);
6858     else
6859     {
6860       if (button2)
6861         dropped = DropElement(player);
6862
6863       moved = MovePlayer(player, dx, dy);
6864     }
6865
6866     if (tape.single_step && tape.recording && !tape.pausing)
6867     {
6868       if (button1 || (dropped && !moved))
6869       {
6870         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6871         SnapField(player, 0, 0);                /* stop snapping */
6872       }
6873     }
6874
6875     SetPlayerWaiting(player, FALSE);
6876
6877 #if 1
6878     return player_action;
6879 #else
6880     stored_player_action[player->index_nr] = player_action;
6881 #endif
6882   }
6883   else
6884   {
6885 #if 0
6886     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6887 #endif
6888
6889     /* no actions for this player (no input at player's configured device) */
6890
6891     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6892     SnapField(player, 0, 0);
6893     CheckGravityMovement(player);
6894
6895     if (player->MovPos == 0)
6896       SetPlayerWaiting(player, TRUE);
6897
6898     if (player->MovPos == 0)    /* needed for tape.playing */
6899       player->is_moving = FALSE;
6900
6901     player->is_dropping = FALSE;
6902
6903     return 0;
6904   }
6905
6906 #if 0
6907   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6908   {
6909     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6910
6911     TapeRecordAction(stored_player_action);
6912     num_stored_actions = 0;
6913   }
6914 #endif
6915 }
6916
6917 #else
6918
6919 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6920 {
6921   static byte stored_player_action[MAX_PLAYERS];
6922   static int num_stored_actions = 0;
6923   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6924   int left      = player_action & JOY_LEFT;
6925   int right     = player_action & JOY_RIGHT;
6926   int up        = player_action & JOY_UP;
6927   int down      = player_action & JOY_DOWN;
6928   int button1   = player_action & JOY_BUTTON_1;
6929   int button2   = player_action & JOY_BUTTON_2;
6930   int dx        = (left ? -1    : right ? 1     : 0);
6931   int dy        = (up   ? -1    : down  ? 1     : 0);
6932
6933   stored_player_action[player->index_nr] = 0;
6934   num_stored_actions++;
6935
6936   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6937
6938   if (!player->active || tape.pausing)
6939     return;
6940
6941   if (player_action)
6942   {
6943     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6944
6945     if (button1)
6946       snapped = SnapField(player, dx, dy);
6947     else
6948     {
6949       if (button2)
6950         dropped = DropElement(player);
6951
6952       moved = MovePlayer(player, dx, dy);
6953     }
6954
6955     if (tape.single_step && tape.recording && !tape.pausing)
6956     {
6957       if (button1 || (dropped && !moved))
6958       {
6959         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6960         SnapField(player, 0, 0);                /* stop snapping */
6961       }
6962     }
6963
6964     stored_player_action[player->index_nr] = player_action;
6965   }
6966   else
6967   {
6968     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6969
6970     /* no actions for this player (no input at player's configured device) */
6971
6972     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6973     SnapField(player, 0, 0);
6974     CheckGravityMovement(player);
6975
6976     if (player->MovPos == 0)
6977       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
6978
6979     if (player->MovPos == 0)    /* needed for tape.playing */
6980       player->is_moving = FALSE;
6981   }
6982
6983   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6984   {
6985     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6986
6987     TapeRecordAction(stored_player_action);
6988     num_stored_actions = 0;
6989   }
6990 }
6991 #endif
6992
6993 void GameActions()
6994 {
6995   static unsigned long action_delay = 0;
6996   unsigned long action_delay_value;
6997   int magic_wall_x = 0, magic_wall_y = 0;
6998   int i, x, y, element, graphic;
6999   byte *recorded_player_action;
7000   byte summarized_player_action = 0;
7001 #if 1
7002   byte tape_action[MAX_PLAYERS];
7003 #endif
7004
7005   if (game_status != GAME_MODE_PLAYING)
7006     return;
7007
7008   action_delay_value =
7009     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7010
7011   if (tape.playing && tape.index_search && !tape.pausing)
7012     action_delay_value = 0;
7013
7014   /* ---------- main game synchronization point ---------- */
7015
7016   WaitUntilDelayReached(&action_delay, action_delay_value);
7017
7018   if (network_playing && !network_player_action_received)
7019   {
7020     /*
7021 #ifdef DEBUG
7022     printf("DEBUG: try to get network player actions in time\n");
7023 #endif
7024     */
7025
7026 #if defined(PLATFORM_UNIX)
7027     /* last chance to get network player actions without main loop delay */
7028     HandleNetworking();
7029 #endif
7030
7031     if (game_status != GAME_MODE_PLAYING)
7032       return;
7033
7034     if (!network_player_action_received)
7035     {
7036       /*
7037 #ifdef DEBUG
7038       printf("DEBUG: failed to get network player actions in time\n");
7039 #endif
7040       */
7041       return;
7042     }
7043   }
7044
7045   if (tape.pausing)
7046     return;
7047
7048 #if 0
7049   printf("::: getting new tape action [%d]\n", FrameCounter);
7050 #endif
7051
7052   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7053
7054   for (i = 0; i < MAX_PLAYERS; i++)
7055   {
7056     summarized_player_action |= stored_player[i].action;
7057
7058     if (!network_playing)
7059       stored_player[i].effective_action = stored_player[i].action;
7060   }
7061
7062 #if defined(PLATFORM_UNIX)
7063   if (network_playing)
7064     SendToServer_MovePlayer(summarized_player_action);
7065 #endif
7066
7067   if (!options.network && !setup.team_mode)
7068     local_player->effective_action = summarized_player_action;
7069
7070   for (i = 0; i < MAX_PLAYERS; i++)
7071   {
7072     int actual_player_action = stored_player[i].effective_action;
7073
7074     if (stored_player[i].programmed_action)
7075       actual_player_action = stored_player[i].programmed_action;
7076
7077     if (recorded_player_action)
7078       actual_player_action = recorded_player_action[i];
7079
7080     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7081
7082     if (tape.recording && tape_action[i] && !tape.player_participates[i])
7083       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
7084
7085     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7086   }
7087
7088 #if 1
7089   if (tape.recording)
7090     TapeRecordAction(tape_action);
7091 #endif
7092
7093   network_player_action_received = FALSE;
7094
7095   ScrollScreen(NULL, SCROLL_GO_ON);
7096
7097 #if 0
7098   FrameCounter++;
7099   TimeFrames++;
7100
7101   for (i = 0; i < MAX_PLAYERS; i++)
7102     stored_player[i].Frame++;
7103 #endif
7104
7105 #if 1
7106   if (game.engine_version < VERSION_IDENT(2,2,0,7))
7107   {
7108     for (i = 0; i < MAX_PLAYERS; i++)
7109     {
7110       struct PlayerInfo *player = &stored_player[i];
7111       int x = player->jx;
7112       int y = player->jy;
7113
7114       if (player->active && player->is_pushing && player->is_moving &&
7115           IS_MOVING(x, y))
7116       {
7117         ContinueMoving(x, y);
7118
7119         /* continue moving after pushing (this is actually a bug) */
7120         if (!IS_MOVING(x, y))
7121         {
7122           Stop[x][y] = FALSE;
7123         }
7124       }
7125     }
7126   }
7127 #endif
7128
7129   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7130   {
7131     Changed[x][y] = CE_BITMASK_DEFAULT;
7132     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7133
7134 #if DEBUG
7135     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7136     {
7137       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7138       printf("GameActions(): This should never happen!\n");
7139
7140       ChangePage[x][y] = -1;
7141     }
7142 #endif
7143
7144     Stop[x][y] = FALSE;
7145     if (WasJustMoving[x][y] > 0)
7146       WasJustMoving[x][y]--;
7147     if (WasJustFalling[x][y] > 0)
7148       WasJustFalling[x][y]--;
7149
7150     GfxFrame[x][y]++;
7151
7152 #if 1
7153     /* reset finished pushing action (not done in ContinueMoving() to allow
7154        continous pushing animation for elements with zero push delay) */
7155     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7156     {
7157       ResetGfxAnimation(x, y);
7158       DrawLevelField(x, y);
7159     }
7160 #endif
7161
7162 #if DEBUG
7163     if (IS_BLOCKED(x, y))
7164     {
7165       int oldx, oldy;
7166
7167       Blocked2Moving(x, y, &oldx, &oldy);
7168       if (!IS_MOVING(oldx, oldy))
7169       {
7170         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7171         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7172         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7173         printf("GameActions(): This should never happen!\n");
7174       }
7175     }
7176 #endif
7177   }
7178
7179   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7180   {
7181     element = Feld[x][y];
7182 #if 1
7183     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7184 #else
7185     graphic = el2img(element);
7186 #endif
7187
7188 #if 0
7189     if (element == -1)
7190     {
7191       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7192
7193       element = graphic = 0;
7194     }
7195 #endif
7196
7197     if (graphic_info[graphic].anim_global_sync)
7198       GfxFrame[x][y] = FrameCounter;
7199
7200     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7201         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7202       ResetRandomAnimationValue(x, y);
7203
7204     SetRandomAnimationValue(x, y);
7205
7206 #if 1
7207     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7208 #endif
7209
7210     if (IS_INACTIVE(element))
7211     {
7212       if (IS_ANIMATED(graphic))
7213         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7214
7215       continue;
7216     }
7217
7218 #if 1
7219     /* this may take place after moving, so 'element' may have changed */
7220 #if 0
7221     if (IS_CHANGING(x, y))
7222 #else
7223     if (IS_CHANGING(x, y) &&
7224         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7225 #endif
7226     {
7227 #if 0
7228       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7229                     element_info[element].event_page_nr[CE_DELAY]);
7230 #else
7231       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7232 #endif
7233
7234       element = Feld[x][y];
7235       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7236     }
7237 #endif
7238
7239     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7240     {
7241       StartMoving(x, y);
7242
7243 #if 1
7244       element = Feld[x][y];
7245       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7246 #if 0
7247       if (element == EL_MOLE)
7248         printf("::: %d, %d, %d [%d]\n",
7249                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7250                GfxAction[x][y]);
7251 #endif
7252 #if 0
7253       if (element == EL_YAMYAM)
7254         printf("::: %d, %d, %d\n",
7255                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7256 #endif
7257 #endif
7258
7259       if (IS_ANIMATED(graphic) &&
7260           !IS_MOVING(x, y) &&
7261           !Stop[x][y])
7262       {
7263         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7264
7265 #if 0
7266         if (element == EL_BUG)
7267           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7268 #endif
7269
7270 #if 0
7271         if (element == EL_MOLE)
7272           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7273 #endif
7274       }
7275
7276       if (IS_GEM(element) || element == EL_SP_INFOTRON)
7277         EdelsteinFunkeln(x, y);
7278     }
7279     else if ((element == EL_ACID ||
7280               element == EL_EXIT_OPEN ||
7281               element == EL_SP_EXIT_OPEN ||
7282               element == EL_SP_TERMINAL ||
7283               element == EL_SP_TERMINAL_ACTIVE ||
7284               element == EL_EXTRA_TIME ||
7285               element == EL_SHIELD_NORMAL ||
7286               element == EL_SHIELD_DEADLY) &&
7287              IS_ANIMATED(graphic))
7288       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7289     else if (IS_MOVING(x, y))
7290       ContinueMoving(x, y);
7291     else if (IS_ACTIVE_BOMB(element))
7292       CheckDynamite(x, y);
7293 #if 0
7294     else if (element == EL_EXPLOSION && !game.explosions_delayed)
7295       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7296 #endif
7297     else if (element == EL_AMOEBA_GROWING)
7298       AmoebeWaechst(x, y);
7299     else if (element == EL_AMOEBA_SHRINKING)
7300       AmoebaDisappearing(x, y);
7301
7302 #if !USE_NEW_AMOEBA_CODE
7303     else if (IS_AMOEBALIVE(element))
7304       AmoebeAbleger(x, y);
7305 #endif
7306
7307     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7308       Life(x, y);
7309     else if (element == EL_EXIT_CLOSED)
7310       CheckExit(x, y);
7311     else if (element == EL_SP_EXIT_CLOSED)
7312       CheckExitSP(x, y);
7313     else if (element == EL_EXPANDABLE_WALL_GROWING)
7314       MauerWaechst(x, y);
7315     else if (element == EL_EXPANDABLE_WALL ||
7316              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7317              element == EL_EXPANDABLE_WALL_VERTICAL ||
7318              element == EL_EXPANDABLE_WALL_ANY)
7319       MauerAbleger(x, y);
7320     else if (element == EL_FLAMES)
7321       CheckForDragon(x, y);
7322 #if 0
7323     else if (IS_AUTO_CHANGING(element))
7324       ChangeElement(x, y);
7325 #endif
7326     else if (element == EL_EXPLOSION)
7327       ; /* drawing of correct explosion animation is handled separately */
7328     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7329       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7330
7331 #if 0
7332     /* this may take place after moving, so 'element' may have changed */
7333     if (IS_AUTO_CHANGING(Feld[x][y]))
7334       ChangeElement(x, y);
7335 #endif
7336
7337     if (IS_BELT_ACTIVE(element))
7338       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7339
7340     if (game.magic_wall_active)
7341     {
7342       int jx = local_player->jx, jy = local_player->jy;
7343
7344       /* play the element sound at the position nearest to the player */
7345       if ((element == EL_MAGIC_WALL_FULL ||
7346            element == EL_MAGIC_WALL_ACTIVE ||
7347            element == EL_MAGIC_WALL_EMPTYING ||
7348            element == EL_BD_MAGIC_WALL_FULL ||
7349            element == EL_BD_MAGIC_WALL_ACTIVE ||
7350            element == EL_BD_MAGIC_WALL_EMPTYING) &&
7351           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7352       {
7353         magic_wall_x = x;
7354         magic_wall_y = y;
7355       }
7356     }
7357   }
7358
7359 #if USE_NEW_AMOEBA_CODE
7360   /* new experimental amoeba growth stuff */
7361 #if 1
7362   if (!(FrameCounter % 8))
7363 #endif
7364   {
7365     static unsigned long random = 1684108901;
7366
7367     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7368     {
7369 #if 0
7370       x = (random >> 10) % lev_fieldx;
7371       y = (random >> 20) % lev_fieldy;
7372 #else
7373       x = RND(lev_fieldx);
7374       y = RND(lev_fieldy);
7375 #endif
7376       element = Feld[x][y];
7377
7378       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7379       if (!IS_PLAYER(x,y) &&
7380           (element == EL_EMPTY ||
7381            element == EL_SAND ||
7382            element == EL_QUICKSAND_EMPTY ||
7383            element == EL_ACID_SPLASH_LEFT ||
7384            element == EL_ACID_SPLASH_RIGHT))
7385       {
7386         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7387             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7388             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7389             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7390           Feld[x][y] = EL_AMOEBA_DROP;
7391       }
7392
7393       random = random * 129 + 1;
7394     }
7395   }
7396 #endif
7397
7398 #if 0
7399   if (game.explosions_delayed)
7400 #endif
7401   {
7402     game.explosions_delayed = FALSE;
7403
7404     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7405     {
7406       element = Feld[x][y];
7407
7408       if (ExplodeField[x][y])
7409         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7410       else if (element == EL_EXPLOSION)
7411         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7412
7413       ExplodeField[x][y] = EX_NO_EXPLOSION;
7414     }
7415
7416     game.explosions_delayed = TRUE;
7417   }
7418
7419   if (game.magic_wall_active)
7420   {
7421     if (!(game.magic_wall_time_left % 4))
7422     {
7423       int element = Feld[magic_wall_x][magic_wall_y];
7424
7425       if (element == EL_BD_MAGIC_WALL_FULL ||
7426           element == EL_BD_MAGIC_WALL_ACTIVE ||
7427           element == EL_BD_MAGIC_WALL_EMPTYING)
7428         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7429       else
7430         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7431     }
7432
7433     if (game.magic_wall_time_left > 0)
7434     {
7435       game.magic_wall_time_left--;
7436       if (!game.magic_wall_time_left)
7437       {
7438         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7439         {
7440           element = Feld[x][y];
7441
7442           if (element == EL_MAGIC_WALL_ACTIVE ||
7443               element == EL_MAGIC_WALL_FULL)
7444           {
7445             Feld[x][y] = EL_MAGIC_WALL_DEAD;
7446             DrawLevelField(x, y);
7447           }
7448           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7449                    element == EL_BD_MAGIC_WALL_FULL)
7450           {
7451             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7452             DrawLevelField(x, y);
7453           }
7454         }
7455
7456         game.magic_wall_active = FALSE;
7457       }
7458     }
7459   }
7460
7461   if (game.light_time_left > 0)
7462   {
7463     game.light_time_left--;
7464
7465     if (game.light_time_left == 0)
7466       RedrawAllLightSwitchesAndInvisibleElements();
7467   }
7468
7469   if (game.timegate_time_left > 0)
7470   {
7471     game.timegate_time_left--;
7472
7473     if (game.timegate_time_left == 0)
7474       CloseAllOpenTimegates();
7475   }
7476
7477   for (i = 0; i < MAX_PLAYERS; i++)
7478   {
7479     struct PlayerInfo *player = &stored_player[i];
7480
7481     if (SHIELD_ON(player))
7482     {
7483       if (player->shield_deadly_time_left)
7484         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7485       else if (player->shield_normal_time_left)
7486         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7487     }
7488   }
7489
7490   if (TimeFrames >= FRAMES_PER_SECOND)
7491   {
7492     TimeFrames = 0;
7493     TimePlayed++;
7494
7495     for (i = 0; i < MAX_PLAYERS; i++)
7496     {
7497       struct PlayerInfo *player = &stored_player[i];
7498
7499       if (SHIELD_ON(player))
7500       {
7501         player->shield_normal_time_left--;
7502
7503         if (player->shield_deadly_time_left > 0)
7504           player->shield_deadly_time_left--;
7505       }
7506     }
7507
7508     if (tape.recording || tape.playing)
7509       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7510
7511     if (TimeLeft > 0)
7512     {
7513       TimeLeft--;
7514
7515       if (TimeLeft <= 10 && setup.time_limit)
7516         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7517
7518       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7519
7520       if (!TimeLeft && setup.time_limit)
7521         for (i = 0; i < MAX_PLAYERS; i++)
7522           KillHero(&stored_player[i]);
7523     }
7524     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7525       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7526   }
7527
7528   DrawAllPlayers();
7529   PlayAllPlayersSound();
7530
7531   if (options.debug)                    /* calculate frames per second */
7532   {
7533     static unsigned long fps_counter = 0;
7534     static int fps_frames = 0;
7535     unsigned long fps_delay_ms = Counter() - fps_counter;
7536
7537     fps_frames++;
7538
7539     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
7540     {
7541       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7542
7543       fps_frames = 0;
7544       fps_counter = Counter();
7545     }
7546
7547     redraw_mask |= REDRAW_FPS;
7548   }
7549
7550 #if 0
7551   if (stored_player[0].jx != stored_player[0].last_jx ||
7552       stored_player[0].jy != stored_player[0].last_jy)
7553     printf("::: %d, %d, %d, %d, %d\n",
7554            stored_player[0].MovDir,
7555            stored_player[0].MovPos,
7556            stored_player[0].GfxPos,
7557            stored_player[0].Frame,
7558            stored_player[0].StepFrame);
7559 #endif
7560
7561 #if 1
7562   FrameCounter++;
7563   TimeFrames++;
7564
7565   for (i = 0; i < MAX_PLAYERS; i++)
7566   {
7567     int move_frames =
7568       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
7569
7570     stored_player[i].Frame += move_frames;
7571
7572     if (stored_player[i].MovPos != 0)
7573       stored_player[i].StepFrame += move_frames;
7574
7575     if (stored_player[i].drop_delay > 0)
7576       stored_player[i].drop_delay--;
7577   }
7578 #endif
7579
7580 #if 1
7581   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7582   {
7583     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7584
7585     local_player->show_envelope = 0;
7586   }
7587 #endif
7588 }
7589
7590 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7591 {
7592   int min_x = x, min_y = y, max_x = x, max_y = y;
7593   int i;
7594
7595   for (i = 0; i < MAX_PLAYERS; i++)
7596   {
7597     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7598
7599     if (!stored_player[i].active || &stored_player[i] == player)
7600       continue;
7601
7602     min_x = MIN(min_x, jx);
7603     min_y = MIN(min_y, jy);
7604     max_x = MAX(max_x, jx);
7605     max_y = MAX(max_y, jy);
7606   }
7607
7608   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7609 }
7610
7611 static boolean AllPlayersInVisibleScreen()
7612 {
7613   int i;
7614
7615   for (i = 0; i < MAX_PLAYERS; i++)
7616   {
7617     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7618
7619     if (!stored_player[i].active)
7620       continue;
7621
7622     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7623       return FALSE;
7624   }
7625
7626   return TRUE;
7627 }
7628
7629 void ScrollLevel(int dx, int dy)
7630 {
7631   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7632   int x, y;
7633
7634   BlitBitmap(drawto_field, drawto_field,
7635              FX + TILEX * (dx == -1) - softscroll_offset,
7636              FY + TILEY * (dy == -1) - softscroll_offset,
7637              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7638              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7639              FX + TILEX * (dx == 1) - softscroll_offset,
7640              FY + TILEY * (dy == 1) - softscroll_offset);
7641
7642   if (dx)
7643   {
7644     x = (dx == 1 ? BX1 : BX2);
7645     for (y = BY1; y <= BY2; y++)
7646       DrawScreenField(x, y);
7647   }
7648
7649   if (dy)
7650   {
7651     y = (dy == 1 ? BY1 : BY2);
7652     for (x = BX1; x <= BX2; x++)
7653       DrawScreenField(x, y);
7654   }
7655
7656   redraw_mask |= REDRAW_FIELD;
7657 }
7658
7659 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7660 {
7661   int nextx = x + dx, nexty = y + dy;
7662   int element = Feld[x][y];
7663
7664   if ((dx == -1 &&
7665        element != EL_SP_PORT_LEFT &&
7666        element != EL_SP_GRAVITY_PORT_LEFT &&
7667        element != EL_SP_PORT_HORIZONTAL &&
7668        element != EL_SP_PORT_ANY) ||
7669       (dx == +1 &&
7670        element != EL_SP_PORT_RIGHT &&
7671        element != EL_SP_GRAVITY_PORT_RIGHT &&
7672        element != EL_SP_PORT_HORIZONTAL &&
7673        element != EL_SP_PORT_ANY) ||
7674       (dy == -1 &&
7675        element != EL_SP_PORT_UP &&
7676        element != EL_SP_GRAVITY_PORT_UP &&
7677        element != EL_SP_PORT_VERTICAL &&
7678        element != EL_SP_PORT_ANY) ||
7679       (dy == +1 &&
7680        element != EL_SP_PORT_DOWN &&
7681        element != EL_SP_GRAVITY_PORT_DOWN &&
7682        element != EL_SP_PORT_VERTICAL &&
7683        element != EL_SP_PORT_ANY) ||
7684       !IN_LEV_FIELD(nextx, nexty) ||
7685       !IS_FREE(nextx, nexty))
7686     return FALSE;
7687
7688   return TRUE;
7689 }
7690
7691 static void CheckGravityMovement(struct PlayerInfo *player)
7692 {
7693   if (game.gravity && !player->programmed_action)
7694   {
7695     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7696     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7697     int move_dir =
7698       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7699        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7700        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7701     int jx = player->jx, jy = player->jy;
7702     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7703     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7704     int new_jx = jx + dx, new_jy = jy + dy;
7705     boolean field_under_player_is_free =
7706       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7707     boolean player_is_moving_to_valid_field =
7708       (IN_LEV_FIELD(new_jx, new_jy) &&
7709        (Feld[new_jx][new_jy] == EL_SP_BASE ||
7710         Feld[new_jx][new_jy] == EL_SAND ||
7711         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7712          canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7713     /* !!! extend EL_SAND to anything diggable !!! */
7714
7715     if (field_under_player_is_free &&
7716         !player_is_moving_to_valid_field &&
7717         !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7718       player->programmed_action = MV_DOWN;
7719   }
7720 }
7721
7722 /*
7723   MovePlayerOneStep()
7724   -----------------------------------------------------------------------------
7725   dx, dy:               direction (non-diagonal) to try to move the player to
7726   real_dx, real_dy:     direction as read from input device (can be diagonal)
7727 */
7728
7729 boolean MovePlayerOneStep(struct PlayerInfo *player,
7730                           int dx, int dy, int real_dx, int real_dy)
7731 {
7732 #if 0
7733   static int change_sides[4][2] =
7734   {
7735     /* enter side        leave side */
7736     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
7737     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
7738     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
7739     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
7740   };
7741   int move_direction = (dx == -1 ? MV_LEFT :
7742                         dx == +1 ? MV_RIGHT :
7743                         dy == -1 ? MV_UP :
7744                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7745   int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7746   int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7747 #endif
7748   int jx = player->jx, jy = player->jy;
7749   int new_jx = jx + dx, new_jy = jy + dy;
7750   int element;
7751   int can_move;
7752
7753   if (!player->active || (!dx && !dy))
7754     return MF_NO_ACTION;
7755
7756   player->MovDir = (dx < 0 ? MV_LEFT :
7757                     dx > 0 ? MV_RIGHT :
7758                     dy < 0 ? MV_UP :
7759                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
7760
7761   if (!IN_LEV_FIELD(new_jx, new_jy))
7762     return MF_NO_ACTION;
7763
7764   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7765     return MF_NO_ACTION;
7766
7767 #if 0
7768   element = MovingOrBlocked2Element(new_jx, new_jy);
7769 #else
7770   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7771 #endif
7772
7773   if (DONT_RUN_INTO(element))
7774   {
7775     if (element == EL_ACID && dx == 0 && dy == 1)
7776     {
7777       SplashAcid(jx, jy);
7778       Feld[jx][jy] = EL_PLAYER_1;
7779       InitMovingField(jx, jy, MV_DOWN);
7780       Store[jx][jy] = EL_ACID;
7781       ContinueMoving(jx, jy);
7782       BuryHero(player);
7783     }
7784     else
7785       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7786
7787     return MF_MOVING;
7788   }
7789
7790   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7791   if (can_move != MF_MOVING)
7792     return can_move;
7793
7794   /* check if DigField() has caused relocation of the player */
7795   if (player->jx != jx || player->jy != jy)
7796     return MF_NO_ACTION;
7797
7798   StorePlayer[jx][jy] = 0;
7799   player->last_jx = jx;
7800   player->last_jy = jy;
7801   player->jx = new_jx;
7802   player->jy = new_jy;
7803   StorePlayer[new_jx][new_jy] = player->element_nr;
7804
7805   player->MovPos =
7806     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7807
7808   player->step_counter++;
7809
7810   player->drop_delay = 0;
7811
7812   PlayerVisit[jx][jy] = FrameCounter;
7813
7814   ScrollPlayer(player, SCROLL_INIT);
7815
7816 #if 0
7817   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7818   {
7819     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7820                                     CE_OTHER_GETS_LEFT);
7821     CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7822                            CE_LEFT_BY_PLAYER, -1);
7823   }
7824
7825   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7826   {
7827     CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7828                                     enter_side, CE_OTHER_GETS_ENTERED);
7829     CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7830                            CE_ENTERED_BY_PLAYER, -1);
7831   }
7832 #endif
7833
7834   return MF_MOVING;
7835 }
7836
7837 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7838 {
7839   int jx = player->jx, jy = player->jy;
7840   int old_jx = jx, old_jy = jy;
7841   int moved = MF_NO_ACTION;
7842
7843 #if 1
7844   if (!player->active)
7845     return FALSE;
7846
7847   if (!dx && !dy)
7848   {
7849     if (player->MovPos == 0)
7850     {
7851       player->is_moving = FALSE;
7852       player->is_digging = FALSE;
7853       player->is_collecting = FALSE;
7854       player->is_snapping = FALSE;
7855       player->is_pushing = FALSE;
7856     }
7857
7858     return FALSE;
7859   }
7860 #else
7861   if (!player->active || (!dx && !dy))
7862     return FALSE;
7863 #endif
7864
7865 #if 0
7866   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7867       !tape.playing)
7868     return FALSE;
7869 #else
7870   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7871       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7872     return FALSE;
7873 #endif
7874
7875   /* remove the last programmed player action */
7876   player->programmed_action = 0;
7877
7878   if (player->MovPos)
7879   {
7880     /* should only happen if pre-1.2 tape recordings are played */
7881     /* this is only for backward compatibility */
7882
7883     int original_move_delay_value = player->move_delay_value;
7884
7885 #if DEBUG
7886     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7887            tape.counter);
7888 #endif
7889
7890     /* scroll remaining steps with finest movement resolution */
7891     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7892
7893     while (player->MovPos)
7894     {
7895       ScrollPlayer(player, SCROLL_GO_ON);
7896       ScrollScreen(NULL, SCROLL_GO_ON);
7897       FrameCounter++;
7898       DrawAllPlayers();
7899       BackToFront();
7900     }
7901
7902     player->move_delay_value = original_move_delay_value;
7903   }
7904
7905   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7906   {
7907     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7908       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7909   }
7910   else
7911   {
7912     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7913       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7914   }
7915
7916   jx = player->jx;
7917   jy = player->jy;
7918
7919   if (moved & MF_MOVING && !ScreenMovPos &&
7920       (player == local_player || !options.network))
7921   {
7922     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7923     int offset = (setup.scroll_delay ? 3 : 0);
7924
7925     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7926     {
7927       /* actual player has left the screen -- scroll in that direction */
7928       if (jx != old_jx)         /* player has moved horizontally */
7929         scroll_x += (jx - old_jx);
7930       else                      /* player has moved vertically */
7931         scroll_y += (jy - old_jy);
7932     }
7933     else
7934     {
7935       if (jx != old_jx)         /* player has moved horizontally */
7936       {
7937         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
7938             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
7939           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
7940
7941         /* don't scroll over playfield boundaries */
7942         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
7943           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
7944
7945         /* don't scroll more than one field at a time */
7946         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
7947
7948         /* don't scroll against the player's moving direction */
7949         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
7950             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
7951           scroll_x = old_scroll_x;
7952       }
7953       else                      /* player has moved vertically */
7954       {
7955         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
7956             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
7957           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
7958
7959         /* don't scroll over playfield boundaries */
7960         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
7961           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
7962
7963         /* don't scroll more than one field at a time */
7964         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
7965
7966         /* don't scroll against the player's moving direction */
7967         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
7968             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
7969           scroll_y = old_scroll_y;
7970       }
7971     }
7972
7973     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
7974     {
7975       if (!options.network && !AllPlayersInVisibleScreen())
7976       {
7977         scroll_x = old_scroll_x;
7978         scroll_y = old_scroll_y;
7979       }
7980       else
7981       {
7982         ScrollScreen(player, SCROLL_INIT);
7983         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
7984       }
7985     }
7986   }
7987
7988 #if 0
7989 #if 1
7990   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
7991 #else
7992   if (!(moved & MF_MOVING) && !player->is_pushing)
7993     player->Frame = 0;
7994 #endif
7995 #endif
7996
7997   player->StepFrame = 0;
7998
7999   if (moved & MF_MOVING)
8000   {
8001     if (old_jx != jx && old_jy == jy)
8002       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8003     else if (old_jx == jx && old_jy != jy)
8004       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8005
8006     DrawLevelField(jx, jy);     /* for "crumbled sand" */
8007
8008     player->last_move_dir = player->MovDir;
8009     player->is_moving = TRUE;
8010 #if 1
8011     player->is_snapping = FALSE;
8012 #endif
8013
8014 #if 1
8015     player->is_switching = FALSE;
8016 #endif
8017
8018     player->is_dropping = FALSE;
8019
8020
8021 #if 1
8022     {
8023       static int change_sides[4][2] =
8024       {
8025         /* enter side           leave side */
8026         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
8027         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
8028         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
8029         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
8030       };
8031       int move_direction = player->MovDir;
8032       int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8033       int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8034
8035 #if 1
8036       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8037       {
8038         CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8039                                         leave_side, CE_OTHER_GETS_LEFT);
8040         CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8041                                leave_side, CE_LEFT_BY_PLAYER, -1);
8042       }
8043
8044       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8045       {
8046         CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8047                                         enter_side, CE_OTHER_GETS_ENTERED);
8048         CheckElementSideChange(jx, jy, Feld[jx][jy],
8049                                enter_side, CE_ENTERED_BY_PLAYER, -1);
8050       }
8051 #endif
8052
8053     }
8054 #endif
8055
8056
8057   }
8058   else
8059   {
8060     CheckGravityMovement(player);
8061
8062     /*
8063     player->last_move_dir = MV_NO_MOVING;
8064     */
8065     player->is_moving = FALSE;
8066   }
8067
8068   if (game.engine_version < VERSION_IDENT(3,0,7,0))
8069   {
8070     TestIfHeroTouchesBadThing(jx, jy);
8071     TestIfPlayerTouchesCustomElement(jx, jy);
8072   }
8073
8074   if (!player->active)
8075     RemoveHero(player);
8076
8077   return moved;
8078 }
8079
8080 void ScrollPlayer(struct PlayerInfo *player, int mode)
8081 {
8082   int jx = player->jx, jy = player->jy;
8083   int last_jx = player->last_jx, last_jy = player->last_jy;
8084   int move_stepsize = TILEX / player->move_delay_value;
8085
8086   if (!player->active || !player->MovPos)
8087     return;
8088
8089   if (mode == SCROLL_INIT)
8090   {
8091     player->actual_frame_counter = FrameCounter;
8092     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8093
8094     if (Feld[last_jx][last_jy] == EL_EMPTY)
8095       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8096
8097 #if 0
8098     DrawPlayer(player);
8099 #endif
8100
8101     return;
8102   }
8103   else if (!FrameReached(&player->actual_frame_counter, 1))
8104     return;
8105
8106   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8107   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8108
8109   if (!player->block_last_field &&
8110       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8111     Feld[last_jx][last_jy] = EL_EMPTY;
8112
8113   /* before DrawPlayer() to draw correct player graphic for this case */
8114   if (player->MovPos == 0)
8115     CheckGravityMovement(player);
8116
8117 #if 0
8118   DrawPlayer(player);   /* needed here only to cleanup last field */
8119 #endif
8120
8121   if (player->MovPos == 0)      /* player reached destination field */
8122   {
8123 #if 1
8124     if (player->move_delay_reset_counter > 0)
8125     {
8126       player->move_delay_reset_counter--;
8127
8128       if (player->move_delay_reset_counter == 0)
8129       {
8130         /* continue with normal speed after quickly moving through gate */
8131         HALVE_PLAYER_SPEED(player);
8132
8133         /* be able to make the next move without delay */
8134         player->move_delay = 0;
8135       }
8136     }
8137 #else
8138     if (IS_PASSABLE(Feld[last_jx][last_jy]))
8139     {
8140       /* continue with normal speed after quickly moving through gate */
8141       HALVE_PLAYER_SPEED(player);
8142
8143       /* be able to make the next move without delay */
8144       player->move_delay = 0;
8145     }
8146 #endif
8147
8148     if (player->block_last_field &&
8149         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8150       Feld[last_jx][last_jy] = EL_EMPTY;
8151
8152     player->last_jx = jx;
8153     player->last_jy = jy;
8154
8155     if (Feld[jx][jy] == EL_EXIT_OPEN ||
8156         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8157         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
8158     {
8159       DrawPlayer(player);       /* needed here only to cleanup last field */
8160       RemoveHero(player);
8161
8162       if (local_player->friends_still_needed == 0 ||
8163           IS_SP_ELEMENT(Feld[jx][jy]))
8164         player->LevelSolved = player->GameOver = TRUE;
8165     }
8166
8167     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8168     {
8169       TestIfHeroTouchesBadThing(jx, jy);
8170       TestIfPlayerTouchesCustomElement(jx, jy);
8171 #if 1
8172       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
8173 #endif
8174
8175       if (!player->active)
8176         RemoveHero(player);
8177     }
8178
8179     if (tape.single_step && tape.recording && !tape.pausing &&
8180         !player->programmed_action)
8181       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8182   }
8183 }
8184
8185 void ScrollScreen(struct PlayerInfo *player, int mode)
8186 {
8187   static unsigned long screen_frame_counter = 0;
8188
8189   if (mode == SCROLL_INIT)
8190   {
8191     /* set scrolling step size according to actual player's moving speed */
8192     ScrollStepSize = TILEX / player->move_delay_value;
8193
8194     screen_frame_counter = FrameCounter;
8195     ScreenMovDir = player->MovDir;
8196     ScreenMovPos = player->MovPos;
8197     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8198     return;
8199   }
8200   else if (!FrameReached(&screen_frame_counter, 1))
8201     return;
8202
8203   if (ScreenMovPos)
8204   {
8205     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8206     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8207     redraw_mask |= REDRAW_FIELD;
8208   }
8209   else
8210     ScreenMovDir = MV_NO_MOVING;
8211 }
8212
8213 void TestIfPlayerTouchesCustomElement(int x, int y)
8214 {
8215   static int xy[4][2] =
8216   {
8217     { 0, -1 },
8218     { -1, 0 },
8219     { +1, 0 },
8220     { 0, +1 }
8221   };
8222   static int change_sides[4][2] =
8223   {
8224     /* center side       border side */
8225     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8226     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8227     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8228     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8229   };
8230   static int touch_dir[4] =
8231   {
8232     MV_LEFT | MV_RIGHT,
8233     MV_UP   | MV_DOWN,
8234     MV_UP   | MV_DOWN,
8235     MV_LEFT | MV_RIGHT
8236   };
8237   int center_element = Feld[x][y];      /* should always be non-moving! */
8238   int i;
8239
8240   for (i = 0; i < 4; i++)
8241   {
8242     int xx = x + xy[i][0];
8243     int yy = y + xy[i][1];
8244     int center_side = change_sides[i][0];
8245     int border_side = change_sides[i][1];
8246     int border_element;
8247
8248     if (!IN_LEV_FIELD(xx, yy))
8249       continue;
8250
8251     if (IS_PLAYER(x, y))
8252     {
8253       if (game.engine_version < VERSION_IDENT(3,0,7,0))
8254         border_element = Feld[xx][yy];          /* may be moving! */
8255       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8256         border_element = Feld[xx][yy];
8257       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
8258         border_element = MovingOrBlocked2Element(xx, yy);
8259       else
8260         continue;               /* center and border element do not touch */
8261
8262       CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8263                                       CE_OTHER_GETS_TOUCHED);
8264       CheckElementSideChange(xx, yy, border_element, border_side,
8265                              CE_TOUCHED_BY_PLAYER, -1);
8266     }
8267     else if (IS_PLAYER(xx, yy))
8268     {
8269       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8270       {
8271         struct PlayerInfo *player = PLAYERINFO(xx, yy);
8272
8273         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8274           continue;             /* center and border element do not touch */
8275       }
8276
8277       CheckTriggeredElementSideChange(x, y, center_element, center_side,
8278                                       CE_OTHER_GETS_TOUCHED);
8279       CheckElementSideChange(x, y, center_element, center_side,
8280                              CE_TOUCHED_BY_PLAYER, -1);
8281
8282       break;
8283     }
8284   }
8285 }
8286
8287 void TestIfElementTouchesCustomElement(int x, int y)
8288 {
8289   static int xy[4][2] =
8290   {
8291     { 0, -1 },
8292     { -1, 0 },
8293     { +1, 0 },
8294     { 0, +1 }
8295   };
8296   static int change_sides[4][2] =
8297   {
8298     /* center side      border side */
8299     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8300     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8301     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8302     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8303   };
8304   static int touch_dir[4] =
8305   {
8306     MV_LEFT | MV_RIGHT,
8307     MV_UP   | MV_DOWN,
8308     MV_UP   | MV_DOWN,
8309     MV_LEFT | MV_RIGHT
8310   };
8311   boolean change_center_element = FALSE;
8312   int center_element_change_page = 0;
8313   int center_element = Feld[x][y];      /* should always be non-moving! */
8314   int i, j;
8315
8316   for (i = 0; i < 4; i++)
8317   {
8318     int xx = x + xy[i][0];
8319     int yy = y + xy[i][1];
8320     int center_side = change_sides[i][0];
8321     int border_side = change_sides[i][1];
8322     int border_element;
8323
8324     if (!IN_LEV_FIELD(xx, yy))
8325       continue;
8326
8327     if (game.engine_version < VERSION_IDENT(3,0,7,0))
8328       border_element = Feld[xx][yy];    /* may be moving! */
8329     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8330       border_element = Feld[xx][yy];
8331     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
8332       border_element = MovingOrBlocked2Element(xx, yy);
8333     else
8334       continue;                 /* center and border element do not touch */
8335
8336     /* check for change of center element (but change it only once) */
8337     if (IS_CUSTOM_ELEMENT(center_element) &&
8338         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8339         !change_center_element)
8340     {
8341       for (j = 0; j < element_info[center_element].num_change_pages; j++)
8342       {
8343         struct ElementChangeInfo *change =
8344           &element_info[center_element].change_page[j];
8345
8346         if (change->can_change &&
8347             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8348             change->sides & border_side &&
8349 #if 1
8350             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8351 #else
8352             change->trigger_element == border_element
8353 #endif
8354             )
8355         {
8356           change_center_element = TRUE;
8357           center_element_change_page = j;
8358
8359           break;
8360         }
8361       }
8362     }
8363
8364     /* check for change of border element */
8365     if (IS_CUSTOM_ELEMENT(border_element) &&
8366         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8367     {
8368       for (j = 0; j < element_info[border_element].num_change_pages; j++)
8369       {
8370         struct ElementChangeInfo *change =
8371           &element_info[border_element].change_page[j];
8372
8373         if (change->can_change &&
8374             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8375             change->sides & center_side &&
8376 #if 1
8377             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8378 #else
8379             change->trigger_element == center_element
8380 #endif
8381             )
8382         {
8383           CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8384                                  CE_OTHER_IS_TOUCHING, j);
8385           break;
8386         }
8387       }
8388     }
8389   }
8390
8391   if (change_center_element)
8392     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8393                            CE_OTHER_IS_TOUCHING, center_element_change_page);
8394 }
8395
8396 void TestIfElementHitsCustomElement(int x, int y, int direction)
8397 {
8398   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8399   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8400   int hitx = x + dx, hity = y + dy;
8401   int hitting_element = Feld[x][y];
8402 #if 0
8403   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8404                         !IS_FREE(hitx, hity) &&
8405                         (!IS_MOVING(hitx, hity) ||
8406                          MovDir[hitx][hity] != direction ||
8407                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
8408 #endif
8409
8410   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8411     return;
8412
8413 #if 0
8414   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8415     return;
8416 #endif
8417
8418   CheckElementSideChange(x, y, hitting_element,
8419                          direction, CE_HITTING_SOMETHING, -1);
8420
8421   if (IN_LEV_FIELD(hitx, hity))
8422   {
8423     int opposite_direction = MV_DIR_OPPOSITE(direction);
8424     int hitting_side = direction;
8425     int touched_side = opposite_direction;
8426     int touched_element = MovingOrBlocked2Element(hitx, hity);
8427 #if 1
8428     boolean object_hit = (!IS_MOVING(hitx, hity) ||
8429                           MovDir[hitx][hity] != direction ||
8430                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
8431
8432     object_hit = TRUE;
8433 #endif
8434
8435     if (object_hit)
8436     {
8437       int i;
8438
8439       CheckElementSideChange(hitx, hity, touched_element,
8440                              opposite_direction, CE_HIT_BY_SOMETHING, -1);
8441
8442       if (IS_CUSTOM_ELEMENT(hitting_element) &&
8443           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8444       {
8445         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8446         {
8447           struct ElementChangeInfo *change =
8448             &element_info[hitting_element].change_page[i];
8449
8450           if (change->can_change &&
8451               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8452               change->sides & touched_side &&
8453           
8454 #if 1
8455               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8456 #else
8457               change->trigger_element == touched_element
8458 #endif
8459               )
8460           {
8461             CheckElementSideChange(x, y, hitting_element,
8462                                    CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8463             break;
8464           }
8465         }
8466       }
8467
8468       if (IS_CUSTOM_ELEMENT(touched_element) &&
8469           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8470       {
8471         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8472         {
8473           struct ElementChangeInfo *change =
8474             &element_info[touched_element].change_page[i];
8475
8476           if (change->can_change &&
8477               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8478               change->sides & hitting_side &&
8479 #if 1
8480               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8481 #else
8482               change->trigger_element == hitting_element
8483 #endif
8484               )
8485           {
8486             CheckElementSideChange(hitx, hity, touched_element,
8487                                    CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8488             break;
8489           }
8490         }
8491       }
8492     }
8493   }
8494 }
8495
8496 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8497 {
8498   int i, kill_x = -1, kill_y = -1;
8499   static int test_xy[4][2] =
8500   {
8501     { 0, -1 },
8502     { -1, 0 },
8503     { +1, 0 },
8504     { 0, +1 }
8505   };
8506   static int test_dir[4] =
8507   {
8508     MV_UP,
8509     MV_LEFT,
8510     MV_RIGHT,
8511     MV_DOWN
8512   };
8513
8514   for (i = 0; i < 4; i++)
8515   {
8516     int test_x, test_y, test_move_dir, test_element;
8517
8518     test_x = good_x + test_xy[i][0];
8519     test_y = good_y + test_xy[i][1];
8520     if (!IN_LEV_FIELD(test_x, test_y))
8521       continue;
8522
8523     test_move_dir =
8524       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8525
8526 #if 0
8527     test_element = Feld[test_x][test_y];
8528 #else
8529     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8530 #endif
8531
8532     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8533        2nd case: DONT_TOUCH style bad thing does not move away from good thing
8534     */
8535     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
8536         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
8537     {
8538       kill_x = test_x;
8539       kill_y = test_y;
8540       break;
8541     }
8542   }
8543
8544   if (kill_x != -1 || kill_y != -1)
8545   {
8546     if (IS_PLAYER(good_x, good_y))
8547     {
8548       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8549
8550       if (player->shield_deadly_time_left > 0)
8551         Bang(kill_x, kill_y);
8552       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8553         KillHero(player);
8554     }
8555     else
8556       Bang(good_x, good_y);
8557   }
8558 }
8559
8560 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8561 {
8562   int i, kill_x = -1, kill_y = -1;
8563   int bad_element = Feld[bad_x][bad_y];
8564   static int test_xy[4][2] =
8565   {
8566     { 0, -1 },
8567     { -1, 0 },
8568     { +1, 0 },
8569     { 0, +1 }
8570   };
8571   static int touch_dir[4] =
8572   {
8573     MV_LEFT | MV_RIGHT,
8574     MV_UP   | MV_DOWN,
8575     MV_UP   | MV_DOWN,
8576     MV_LEFT | MV_RIGHT
8577   };
8578   static int test_dir[4] =
8579   {
8580     MV_UP,
8581     MV_LEFT,
8582     MV_RIGHT,
8583     MV_DOWN
8584   };
8585
8586   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
8587     return;
8588
8589   for (i = 0; i < 4; i++)
8590   {
8591     int test_x, test_y, test_move_dir, test_element;
8592
8593     test_x = bad_x + test_xy[i][0];
8594     test_y = bad_y + test_xy[i][1];
8595     if (!IN_LEV_FIELD(test_x, test_y))
8596       continue;
8597
8598     test_move_dir =
8599       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8600
8601     test_element = Feld[test_x][test_y];
8602
8603     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8604        2nd case: DONT_TOUCH style bad thing does not move away from good thing
8605     */
8606     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
8607         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
8608     {
8609       /* good thing is player or penguin that does not move away */
8610       if (IS_PLAYER(test_x, test_y))
8611       {
8612         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8613
8614         if (bad_element == EL_ROBOT && player->is_moving)
8615           continue;     /* robot does not kill player if he is moving */
8616
8617         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8618         {
8619           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8620             continue;           /* center and border element do not touch */
8621         }
8622
8623         kill_x = test_x;
8624         kill_y = test_y;
8625         break;
8626       }
8627       else if (test_element == EL_PENGUIN)
8628       {
8629         kill_x = test_x;
8630         kill_y = test_y;
8631         break;
8632       }
8633     }
8634   }
8635
8636   if (kill_x != -1 || kill_y != -1)
8637   {
8638     if (IS_PLAYER(kill_x, kill_y))
8639     {
8640       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8641
8642       if (player->shield_deadly_time_left > 0)
8643         Bang(bad_x, bad_y);
8644       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8645         KillHero(player);
8646     }
8647     else
8648       Bang(kill_x, kill_y);
8649   }
8650 }
8651
8652 void TestIfHeroTouchesBadThing(int x, int y)
8653 {
8654   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8655 }
8656
8657 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8658 {
8659   TestIfGoodThingHitsBadThing(x, y, move_dir);
8660 }
8661
8662 void TestIfBadThingTouchesHero(int x, int y)
8663 {
8664   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8665 }
8666
8667 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8668 {
8669   TestIfBadThingHitsGoodThing(x, y, move_dir);
8670 }
8671
8672 void TestIfFriendTouchesBadThing(int x, int y)
8673 {
8674   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8675 }
8676
8677 void TestIfBadThingTouchesFriend(int x, int y)
8678 {
8679   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8680 }
8681
8682 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8683 {
8684   int i, kill_x = bad_x, kill_y = bad_y;
8685   static int xy[4][2] =
8686   {
8687     { 0, -1 },
8688     { -1, 0 },
8689     { +1, 0 },
8690     { 0, +1 }
8691   };
8692
8693   for (i = 0; i < 4; i++)
8694   {
8695     int x, y, element;
8696
8697     x = bad_x + xy[i][0];
8698     y = bad_y + xy[i][1];
8699     if (!IN_LEV_FIELD(x, y))
8700       continue;
8701
8702     element = Feld[x][y];
8703     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8704         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8705     {
8706       kill_x = x;
8707       kill_y = y;
8708       break;
8709     }
8710   }
8711
8712   if (kill_x != bad_x || kill_y != bad_y)
8713     Bang(bad_x, bad_y);
8714 }
8715
8716 void KillHero(struct PlayerInfo *player)
8717 {
8718   int jx = player->jx, jy = player->jy;
8719
8720   if (!player->active)
8721     return;
8722
8723   /* remove accessible field at the player's position */
8724   Feld[jx][jy] = EL_EMPTY;
8725
8726   /* deactivate shield (else Bang()/Explode() would not work right) */
8727   player->shield_normal_time_left = 0;
8728   player->shield_deadly_time_left = 0;
8729
8730   Bang(jx, jy);
8731   BuryHero(player);
8732 }
8733
8734 static void KillHeroUnlessEnemyProtected(int x, int y)
8735 {
8736   if (!PLAYER_ENEMY_PROTECTED(x, y))
8737     KillHero(PLAYERINFO(x, y));
8738 }
8739
8740 static void KillHeroUnlessExplosionProtected(int x, int y)
8741 {
8742   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8743     KillHero(PLAYERINFO(x, y));
8744 }
8745
8746 void BuryHero(struct PlayerInfo *player)
8747 {
8748   int jx = player->jx, jy = player->jy;
8749
8750   if (!player->active)
8751     return;
8752
8753 #if 1
8754   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8755 #else
8756   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8757 #endif
8758   PlayLevelSound(jx, jy, SND_GAME_LOSING);
8759
8760   player->GameOver = TRUE;
8761   RemoveHero(player);
8762 }
8763
8764 void RemoveHero(struct PlayerInfo *player)
8765 {
8766   int jx = player->jx, jy = player->jy;
8767   int i, found = FALSE;
8768
8769   player->present = FALSE;
8770   player->active = FALSE;
8771
8772   if (!ExplodeField[jx][jy])
8773     StorePlayer[jx][jy] = 0;
8774
8775   for (i = 0; i < MAX_PLAYERS; i++)
8776     if (stored_player[i].active)
8777       found = TRUE;
8778
8779   if (!found)
8780     AllPlayersGone = TRUE;
8781
8782   ExitX = ZX = jx;
8783   ExitY = ZY = jy;
8784 }
8785
8786 /*
8787   =============================================================================
8788   checkDiagonalPushing()
8789   -----------------------------------------------------------------------------
8790   check if diagonal input device direction results in pushing of object
8791   (by checking if the alternative direction is walkable, diggable, ...)
8792   =============================================================================
8793 */
8794
8795 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8796                                     int x, int y, int real_dx, int real_dy)
8797 {
8798   int jx, jy, dx, dy, xx, yy;
8799
8800   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
8801     return TRUE;
8802
8803   /* diagonal direction: check alternative direction */
8804   jx = player->jx;
8805   jy = player->jy;
8806   dx = x - jx;
8807   dy = y - jy;
8808   xx = jx + (dx == 0 ? real_dx : 0);
8809   yy = jy + (dy == 0 ? real_dy : 0);
8810
8811   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8812 }
8813
8814 /*
8815   =============================================================================
8816   DigField()
8817   -----------------------------------------------------------------------------
8818   x, y:                 field next to player (non-diagonal) to try to dig to
8819   real_dx, real_dy:     direction as read from input device (can be diagonal)
8820   =============================================================================
8821 */
8822
8823 int DigField(struct PlayerInfo *player,
8824              int x, int y, int real_dx, int real_dy, int mode)
8825 {
8826   static int change_sides[4] =
8827   {
8828     CH_SIDE_RIGHT,      /* moving left  */
8829     CH_SIDE_LEFT,       /* moving right */
8830     CH_SIDE_BOTTOM,     /* moving up    */
8831     CH_SIDE_TOP,        /* moving down  */
8832   };
8833   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8834   int jx = player->jx, jy = player->jy;
8835   int dx = x - jx, dy = y - jy;
8836   int nextx = x + dx, nexty = y + dy;
8837   int move_direction = (dx == -1 ? MV_LEFT :
8838                         dx == +1 ? MV_RIGHT :
8839                         dy == -1 ? MV_UP :
8840                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8841   int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8842   int element;
8843
8844   if (player->MovPos == 0)
8845   {
8846     player->is_digging = FALSE;
8847     player->is_collecting = FALSE;
8848   }
8849
8850   if (player->MovPos == 0)      /* last pushing move finished */
8851     player->is_pushing = FALSE;
8852
8853   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
8854   {
8855     player->is_switching = FALSE;
8856     player->push_delay = 0;
8857
8858     return MF_NO_ACTION;
8859   }
8860
8861   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8862     return MF_NO_ACTION;
8863
8864 #if 0
8865   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8866 #else
8867   if (IS_TUBE(Feld[jx][jy]) ||
8868       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8869 #endif
8870   {
8871     int i = 0;
8872     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8873     int tube_leave_directions[][2] =
8874     {
8875       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8876       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
8877       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
8878       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
8879       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
8880       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
8881       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
8882       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
8883       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
8884       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
8885       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
8886       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8887     };
8888
8889     while (tube_leave_directions[i][0] != tube_element)
8890     {
8891       i++;
8892       if (tube_leave_directions[i][0] == -1)    /* should not happen */
8893         break;
8894     }
8895
8896     if (!(tube_leave_directions[i][1] & move_direction))
8897       return MF_NO_ACTION;      /* tube has no opening in this direction */
8898   }
8899
8900   element = Feld[x][y];
8901
8902   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8903       game.engine_version >= VERSION_IDENT(2,2,0,0))
8904     return MF_NO_ACTION;
8905
8906   switch (element)
8907   {
8908     case EL_SP_PORT_LEFT:
8909     case EL_SP_PORT_RIGHT:
8910     case EL_SP_PORT_UP:
8911     case EL_SP_PORT_DOWN:
8912     case EL_SP_PORT_HORIZONTAL:
8913     case EL_SP_PORT_VERTICAL:
8914     case EL_SP_PORT_ANY:
8915     case EL_SP_GRAVITY_PORT_LEFT:
8916     case EL_SP_GRAVITY_PORT_RIGHT:
8917     case EL_SP_GRAVITY_PORT_UP:
8918     case EL_SP_GRAVITY_PORT_DOWN:
8919 #if 1
8920       if (!canEnterSupaplexPort(x, y, dx, dy))
8921         return MF_NO_ACTION;
8922 #else
8923       if ((dx == -1 &&
8924            element != EL_SP_PORT_LEFT &&
8925            element != EL_SP_GRAVITY_PORT_LEFT &&
8926            element != EL_SP_PORT_HORIZONTAL &&
8927            element != EL_SP_PORT_ANY) ||
8928           (dx == +1 &&
8929            element != EL_SP_PORT_RIGHT &&
8930            element != EL_SP_GRAVITY_PORT_RIGHT &&
8931            element != EL_SP_PORT_HORIZONTAL &&
8932            element != EL_SP_PORT_ANY) ||
8933           (dy == -1 &&
8934            element != EL_SP_PORT_UP &&
8935            element != EL_SP_GRAVITY_PORT_UP &&
8936            element != EL_SP_PORT_VERTICAL &&
8937            element != EL_SP_PORT_ANY) ||
8938           (dy == +1 &&
8939            element != EL_SP_PORT_DOWN &&
8940            element != EL_SP_GRAVITY_PORT_DOWN &&
8941            element != EL_SP_PORT_VERTICAL &&
8942            element != EL_SP_PORT_ANY) ||
8943           !IN_LEV_FIELD(nextx, nexty) ||
8944           !IS_FREE(nextx, nexty))
8945         return MF_NO_ACTION;
8946 #endif
8947
8948       if (element == EL_SP_GRAVITY_PORT_LEFT ||
8949           element == EL_SP_GRAVITY_PORT_RIGHT ||
8950           element == EL_SP_GRAVITY_PORT_UP ||
8951           element == EL_SP_GRAVITY_PORT_DOWN)
8952         game.gravity = !game.gravity;
8953
8954       /* automatically move to the next field with double speed */
8955       player->programmed_action = move_direction;
8956 #if 1
8957       if (player->move_delay_reset_counter == 0)
8958       {
8959         player->move_delay_reset_counter = 2;   /* two double speed steps */
8960
8961         DOUBLE_PLAYER_SPEED(player);
8962       }
8963 #else
8964       player->move_delay_reset_counter = 2;
8965
8966       DOUBLE_PLAYER_SPEED(player);
8967 #endif
8968
8969       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
8970       break;
8971
8972     case EL_TUBE_ANY:
8973     case EL_TUBE_VERTICAL:
8974     case EL_TUBE_HORIZONTAL:
8975     case EL_TUBE_VERTICAL_LEFT:
8976     case EL_TUBE_VERTICAL_RIGHT:
8977     case EL_TUBE_HORIZONTAL_UP:
8978     case EL_TUBE_HORIZONTAL_DOWN:
8979     case EL_TUBE_LEFT_UP:
8980     case EL_TUBE_LEFT_DOWN:
8981     case EL_TUBE_RIGHT_UP:
8982     case EL_TUBE_RIGHT_DOWN:
8983       {
8984         int i = 0;
8985         int tube_enter_directions[][2] =
8986         {
8987           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8988           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
8989           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
8990           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
8991           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
8992           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
8993           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
8994           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
8995           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
8996           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
8997           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
8998           { -1,                         MV_NO_MOVING                         }
8999         };
9000
9001         while (tube_enter_directions[i][0] != element)
9002         {
9003           i++;
9004           if (tube_enter_directions[i][0] == -1)        /* should not happen */
9005             break;
9006         }
9007
9008         if (!(tube_enter_directions[i][1] & move_direction))
9009           return MF_NO_ACTION;  /* tube has no opening in this direction */
9010
9011         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9012       }
9013       break;
9014
9015     default:
9016
9017       if (IS_WALKABLE(element))
9018       {
9019         int sound_action = ACTION_WALKING;
9020
9021         if (element >= EL_GATE_1 && element <= EL_GATE_4)
9022         {
9023           if (!player->key[element - EL_GATE_1])
9024             return MF_NO_ACTION;
9025         }
9026         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9027         {
9028           if (!player->key[element - EL_GATE_1_GRAY])
9029             return MF_NO_ACTION;
9030         }
9031         else if (element == EL_EXIT_OPEN ||
9032                  element == EL_SP_EXIT_OPEN ||
9033                  element == EL_SP_EXIT_OPENING)
9034         {
9035           sound_action = ACTION_PASSING;        /* player is passing exit */
9036         }
9037         else if (element == EL_EMPTY)
9038         {
9039           sound_action = ACTION_MOVING;         /* nothing to walk on */
9040         }
9041
9042         /* play sound from background or player, whatever is available */
9043         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9044           PlayLevelSoundElementAction(x, y, element, sound_action);
9045         else
9046           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9047
9048         break;
9049       }
9050       else if (IS_PASSABLE(element))
9051       {
9052         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9053           return MF_NO_ACTION;
9054
9055 #if 1
9056         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
9057           return MF_NO_ACTION;
9058 #endif
9059
9060         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9061         {
9062           if (!player->key[element - EL_EM_GATE_1])
9063             return MF_NO_ACTION;
9064         }
9065         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9066         {
9067           if (!player->key[element - EL_EM_GATE_1_GRAY])
9068             return MF_NO_ACTION;
9069         }
9070
9071         /* automatically move to the next field with double speed */
9072         player->programmed_action = move_direction;
9073 #if 1
9074         if (player->move_delay_reset_counter == 0)
9075         {
9076           player->move_delay_reset_counter = 2; /* two double speed steps */
9077
9078           DOUBLE_PLAYER_SPEED(player);
9079         }
9080 #else
9081         player->move_delay_reset_counter = 2;
9082
9083         DOUBLE_PLAYER_SPEED(player);
9084 #endif
9085
9086         PlayLevelSoundAction(x, y, ACTION_PASSING);
9087
9088         break;
9089       }
9090       else if (IS_DIGGABLE(element))
9091       {
9092         RemoveField(x, y);
9093
9094         if (mode != DF_SNAP)
9095         {
9096 #if 1
9097           GfxElement[x][y] = GFX_ELEMENT(element);
9098 #else
9099           GfxElement[x][y] =
9100             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9101 #endif
9102           player->is_digging = TRUE;
9103         }
9104
9105         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9106
9107         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9108
9109 #if 1
9110         if (mode == DF_SNAP)
9111           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9112 #endif
9113
9114         break;
9115       }
9116       else if (IS_COLLECTIBLE(element))
9117       {
9118         RemoveField(x, y);
9119
9120         if (mode != DF_SNAP)
9121         {
9122           GfxElement[x][y] = element;
9123           player->is_collecting = TRUE;
9124         }
9125
9126         if (element == EL_SPEED_PILL)
9127           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9128         else if (element == EL_EXTRA_TIME && level.time > 0)
9129         {
9130           TimeLeft += 10;
9131           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9132         }
9133         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9134         {
9135           player->shield_normal_time_left += 10;
9136           if (element == EL_SHIELD_DEADLY)
9137             player->shield_deadly_time_left += 10;
9138         }
9139         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9140         {
9141           if (player->inventory_size < MAX_INVENTORY_SIZE)
9142             player->inventory_element[player->inventory_size++] = element;
9143
9144           DrawText(DX_DYNAMITE, DY_DYNAMITE,
9145                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9146         }
9147         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9148         {
9149           player->dynabomb_count++;
9150           player->dynabombs_left++;
9151         }
9152         else if (element == EL_DYNABOMB_INCREASE_SIZE)
9153         {
9154           player->dynabomb_size++;
9155         }
9156         else if (element == EL_DYNABOMB_INCREASE_POWER)
9157         {
9158           player->dynabomb_xl = TRUE;
9159         }
9160         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9161                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9162         {
9163           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9164                         element - EL_KEY_1 : element - EL_EM_KEY_1);
9165
9166           player->key[key_nr] = TRUE;
9167
9168           DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9169                              el2edimg(EL_KEY_1 + key_nr));
9170           redraw_mask |= REDRAW_DOOR_1;
9171         }
9172         else if (IS_ENVELOPE(element))
9173         {
9174 #if 1
9175           player->show_envelope = element;
9176 #else
9177           ShowEnvelope(element - EL_ENVELOPE_1);
9178 #endif
9179         }
9180         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9181         {
9182           int i;
9183
9184           for (i = 0; i < element_info[element].collect_count; i++)
9185             if (player->inventory_size < MAX_INVENTORY_SIZE)
9186               player->inventory_element[player->inventory_size++] = element;
9187
9188           DrawText(DX_DYNAMITE, DY_DYNAMITE,
9189                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9190         }
9191         else if (element_info[element].collect_count > 0)
9192         {
9193           local_player->gems_still_needed -=
9194             element_info[element].collect_count;
9195           if (local_player->gems_still_needed < 0)
9196             local_player->gems_still_needed = 0;
9197
9198           DrawText(DX_EMERALDS, DY_EMERALDS,
9199                    int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9200         }
9201
9202         RaiseScoreElement(element);
9203         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9204
9205         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9206
9207 #if 1
9208         if (mode == DF_SNAP)
9209           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9210 #endif
9211
9212         break;
9213       }
9214       else if (IS_PUSHABLE(element))
9215       {
9216         if (mode == DF_SNAP && element != EL_BD_ROCK)
9217           return MF_NO_ACTION;
9218
9219         if (CAN_FALL(element) && dy)
9220           return MF_NO_ACTION;
9221
9222         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9223             !(element == EL_SPRING && use_spring_bug))
9224           return MF_NO_ACTION;
9225
9226 #if 1
9227         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9228             ((move_direction & MV_VERTICAL &&
9229               ((element_info[element].move_pattern & MV_LEFT &&
9230                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9231                (element_info[element].move_pattern & MV_RIGHT &&
9232                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9233              (move_direction & MV_HORIZONTAL &&
9234               ((element_info[element].move_pattern & MV_UP &&
9235                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9236                (element_info[element].move_pattern & MV_DOWN &&
9237                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9238           return MF_NO_ACTION;
9239 #endif
9240
9241 #if 1
9242         /* do not push elements already moving away faster than player */
9243         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9244             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9245           return MF_NO_ACTION;
9246 #else
9247         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9248           return MF_NO_ACTION;
9249 #endif
9250
9251 #if 1
9252         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9253         {
9254           if (player->push_delay_value == -1)
9255             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9256         }
9257         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9258         {
9259           if (!player->is_pushing)
9260             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9261         }
9262
9263         /*
9264         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9265             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9266              !player_is_pushing))
9267           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9268         */
9269 #else
9270         if (!player->is_pushing &&
9271             game.engine_version >= VERSION_IDENT(2,2,0,7))
9272           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9273 #endif
9274
9275 #if 0
9276         printf("::: push delay: %ld [%d, %d] [%d]\n",
9277                player->push_delay_value, FrameCounter, game.engine_version,
9278                player->is_pushing);
9279 #endif
9280
9281         player->is_pushing = TRUE;
9282
9283         if (!(IN_LEV_FIELD(nextx, nexty) &&
9284               (IS_FREE(nextx, nexty) ||
9285                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9286                 IS_SB_ELEMENT(element)))))
9287           return MF_NO_ACTION;
9288
9289         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9290           return MF_NO_ACTION;
9291
9292         if (player->push_delay == 0)    /* new pushing; restart delay */
9293           player->push_delay = FrameCounter;
9294
9295         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9296             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9297             element != EL_SPRING && element != EL_BALLOON)
9298         {
9299           /* make sure that there is no move delay before next try to push */
9300           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9301             player->move_delay = INITIAL_MOVE_DELAY_OFF;
9302
9303           return MF_NO_ACTION;
9304         }
9305
9306 #if 0
9307         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9308 #endif
9309
9310         if (IS_SB_ELEMENT(element))
9311         {
9312           if (element == EL_SOKOBAN_FIELD_FULL)
9313           {
9314             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9315             local_player->sokobanfields_still_needed++;
9316           }
9317
9318           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9319           {
9320             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9321             local_player->sokobanfields_still_needed--;
9322           }
9323
9324           Feld[x][y] = EL_SOKOBAN_OBJECT;
9325
9326           if (Back[x][y] == Back[nextx][nexty])
9327             PlayLevelSoundAction(x, y, ACTION_PUSHING);
9328           else if (Back[x][y] != 0)
9329             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9330                                         ACTION_EMPTYING);
9331           else
9332             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9333                                         ACTION_FILLING);
9334
9335           if (local_player->sokobanfields_still_needed == 0 &&
9336               game.emulation == EMU_SOKOBAN)
9337           {
9338             player->LevelSolved = player->GameOver = TRUE;
9339             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9340           }
9341         }
9342         else
9343           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9344
9345         InitMovingField(x, y, move_direction);
9346         GfxAction[x][y] = ACTION_PUSHING;
9347
9348         if (mode == DF_SNAP)
9349           ContinueMoving(x, y);
9350         else
9351           MovPos[x][y] = (dx != 0 ? dx : dy);
9352
9353         Pushed[x][y] = TRUE;
9354         Pushed[nextx][nexty] = TRUE;
9355
9356         if (game.engine_version < VERSION_IDENT(2,2,0,7))
9357           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9358         else
9359           player->push_delay_value = -1;        /* get new value later */
9360
9361         CheckTriggeredElementSideChange(x, y, element, dig_side,
9362                                         CE_OTHER_GETS_PUSHED);
9363         CheckElementSideChange(x, y, element, dig_side,
9364                                CE_PUSHED_BY_PLAYER, -1);
9365
9366         break;
9367       }
9368       else if (IS_SWITCHABLE(element))
9369       {
9370         if (PLAYER_SWITCHING(player, x, y))
9371           return MF_ACTION;
9372
9373         player->is_switching = TRUE;
9374         player->switch_x = x;
9375         player->switch_y = y;
9376
9377         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9378
9379         if (element == EL_ROBOT_WHEEL)
9380         {
9381           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9382           ZX = x;
9383           ZY = y;
9384
9385           DrawLevelField(x, y);
9386         }
9387         else if (element == EL_SP_TERMINAL)
9388         {
9389           int xx, yy;
9390
9391           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9392           {
9393             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9394               Bang(xx, yy);
9395             else if (Feld[xx][yy] == EL_SP_TERMINAL)
9396               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9397           }
9398         }
9399         else if (IS_BELT_SWITCH(element))
9400         {
9401           ToggleBeltSwitch(x, y);
9402         }
9403         else if (element == EL_SWITCHGATE_SWITCH_UP ||
9404                  element == EL_SWITCHGATE_SWITCH_DOWN)
9405         {
9406           ToggleSwitchgateSwitch(x, y);
9407         }
9408         else if (element == EL_LIGHT_SWITCH ||
9409                  element == EL_LIGHT_SWITCH_ACTIVE)
9410         {
9411           ToggleLightSwitch(x, y);
9412
9413 #if 0
9414           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9415                          SND_LIGHT_SWITCH_ACTIVATING :
9416                          SND_LIGHT_SWITCH_DEACTIVATING);
9417 #endif
9418         }
9419         else if (element == EL_TIMEGATE_SWITCH)
9420         {
9421           ActivateTimegateSwitch(x, y);
9422         }
9423         else if (element == EL_BALLOON_SWITCH_LEFT ||
9424                  element == EL_BALLOON_SWITCH_RIGHT ||
9425                  element == EL_BALLOON_SWITCH_UP ||
9426                  element == EL_BALLOON_SWITCH_DOWN ||
9427                  element == EL_BALLOON_SWITCH_ANY)
9428         {
9429           if (element == EL_BALLOON_SWITCH_ANY)
9430             game.balloon_dir = move_direction;
9431           else
9432             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
9433                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9434                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
9435                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
9436                                 MV_NO_MOVING);
9437         }
9438         else if (element == EL_LAMP)
9439         {
9440           Feld[x][y] = EL_LAMP_ACTIVE;
9441           local_player->lights_still_needed--;
9442
9443           DrawLevelField(x, y);
9444         }
9445         else if (element == EL_TIME_ORB_FULL)
9446         {
9447           Feld[x][y] = EL_TIME_ORB_EMPTY;
9448           TimeLeft += 10;
9449           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9450
9451           DrawLevelField(x, y);
9452
9453 #if 0
9454           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9455 #endif
9456         }
9457
9458         return MF_ACTION;
9459       }
9460       else
9461       {
9462         if (!PLAYER_SWITCHING(player, x, y))
9463         {
9464           player->is_switching = TRUE;
9465           player->switch_x = x;
9466           player->switch_y = y;
9467
9468           CheckTriggeredElementSideChange(x, y, element, dig_side,
9469                                           CE_OTHER_IS_SWITCHING);
9470           CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9471         }
9472
9473         CheckTriggeredElementSideChange(x, y, element, dig_side,
9474                                         CE_OTHER_GETS_PRESSED);
9475         CheckElementSideChange(x, y, element, dig_side,
9476                                CE_PRESSED_BY_PLAYER, -1);
9477       }
9478
9479       return MF_NO_ACTION;
9480   }
9481
9482   player->push_delay = 0;
9483
9484   if (Feld[x][y] != element)            /* really digged/collected something */
9485     player->is_collecting = !player->is_digging;
9486
9487   return MF_MOVING;
9488 }
9489
9490 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9491 {
9492   int jx = player->jx, jy = player->jy;
9493   int x = jx + dx, y = jy + dy;
9494   int snap_direction = (dx == -1 ? MV_LEFT :
9495                         dx == +1 ? MV_RIGHT :
9496                         dy == -1 ? MV_UP :
9497                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9498
9499   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9500     return FALSE;
9501
9502   if (!player->active || !IN_LEV_FIELD(x, y))
9503     return FALSE;
9504
9505   if (dx && dy)
9506     return FALSE;
9507
9508   if (!dx && !dy)
9509   {
9510     if (player->MovPos == 0)
9511       player->is_pushing = FALSE;
9512
9513     player->is_snapping = FALSE;
9514
9515     if (player->MovPos == 0)
9516     {
9517       player->is_moving = FALSE;
9518       player->is_digging = FALSE;
9519       player->is_collecting = FALSE;
9520     }
9521
9522     return FALSE;
9523   }
9524
9525   if (player->is_snapping)
9526     return FALSE;
9527
9528   player->MovDir = snap_direction;
9529
9530 #if 1
9531   if (player->MovPos == 0)
9532 #endif
9533   {
9534     player->is_moving = FALSE;
9535     player->is_digging = FALSE;
9536     player->is_collecting = FALSE;
9537   }
9538
9539   player->is_dropping = FALSE;
9540
9541   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9542     return FALSE;
9543
9544   player->is_snapping = TRUE;
9545
9546 #if 1
9547   if (player->MovPos == 0)
9548 #endif
9549   {
9550     player->is_moving = FALSE;
9551     player->is_digging = FALSE;
9552     player->is_collecting = FALSE;
9553   }
9554
9555   DrawLevelField(x, y);
9556   BackToFront();
9557
9558   return TRUE;
9559 }
9560
9561 boolean DropElement(struct PlayerInfo *player)
9562 {
9563   int jx = player->jx, jy = player->jy;
9564   int old_element = Feld[jx][jy];
9565   int new_element;
9566
9567   /* check if player is active, not moving and ready to drop */
9568   if (!player->active || player->MovPos || player->drop_delay > 0)
9569     return FALSE;
9570
9571   /* check if player has anything that can be dropped */
9572   if (player->inventory_size == 0 && player->dynabombs_left == 0)
9573     return FALSE;
9574
9575   /* check if anything can be dropped at the current position */
9576   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9577     return FALSE;
9578
9579   /* collected custom elements can only be dropped on empty fields */
9580   if (player->inventory_size > 0 &&
9581       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9582       && old_element != EL_EMPTY)
9583     return FALSE;
9584
9585   if (old_element != EL_EMPTY)
9586     Back[jx][jy] = old_element;         /* store old element on this field */
9587
9588   ResetGfxAnimation(jx, jy);
9589   ResetRandomAnimationValue(jx, jy);
9590
9591   if (player->inventory_size > 0)
9592   {
9593     player->inventory_size--;
9594     new_element = player->inventory_element[player->inventory_size];
9595
9596     if (new_element == EL_DYNAMITE)
9597       new_element = EL_DYNAMITE_ACTIVE;
9598     else if (new_element == EL_SP_DISK_RED)
9599       new_element = EL_SP_DISK_RED_ACTIVE;
9600
9601     Feld[jx][jy] = new_element;
9602
9603     DrawText(DX_DYNAMITE, DY_DYNAMITE,
9604              int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9605
9606     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9607       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9608
9609     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9610
9611 #if 1
9612     /* needed if previous element just changed to "empty" in the last frame */
9613     Changed[jx][jy] = 0;                /* allow another change */
9614 #endif
9615
9616     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9617     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9618
9619     TestIfElementTouchesCustomElement(jx, jy);
9620   }
9621   else          /* player is dropping a dyna bomb */
9622   {
9623     player->dynabombs_left--;
9624     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9625
9626     Feld[jx][jy] = new_element;
9627
9628     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9629       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9630
9631     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9632   }
9633
9634
9635
9636 #if 1
9637
9638   if (Feld[jx][jy] == new_element)      /* uninitialized unless CE change */
9639   {
9640     InitField(jx, jy, FALSE);
9641     if (CAN_MOVE(Feld[jx][jy]))
9642       InitMovDir(jx, jy);
9643   }
9644
9645   new_element = Feld[jx][jy];
9646
9647   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9648       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9649   {
9650     int move_stepsize = element_info[new_element].move_stepsize;
9651     int direction, dx, dy, nextx, nexty;
9652
9653     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9654       MovDir[jx][jy] = player->MovDir;
9655
9656     direction = MovDir[jx][jy];
9657     dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9658     dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9659     nextx = jx + dx;
9660     nexty = jy + dy;
9661
9662     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9663     {
9664 #if 0
9665       WasJustMoving[jx][jy] = 3;
9666 #else
9667       InitMovingField(jx, jy, direction);
9668       ContinueMoving(jx, jy);
9669 #endif
9670     }
9671     else
9672     {
9673       Changed[jx][jy] = 0;              /* allow another change */
9674
9675 #if 1
9676       TestIfElementHitsCustomElement(jx, jy, direction);
9677 #else
9678       CheckElementSideChange(jx, jy, new_element,
9679                              direction, CE_HITTING_SOMETHING, -1);
9680 #endif
9681     }
9682
9683     player->drop_delay = 2 * TILEX / move_stepsize + 1;
9684   }
9685
9686 #if 0
9687   player->drop_delay = 8 + 8 + 8;
9688 #endif
9689
9690 #endif
9691
9692   player->is_dropping = TRUE;
9693
9694
9695   return TRUE;
9696 }
9697
9698 /* ------------------------------------------------------------------------- */
9699 /* game sound playing functions                                              */
9700 /* ------------------------------------------------------------------------- */
9701
9702 static int *loop_sound_frame = NULL;
9703 static int *loop_sound_volume = NULL;
9704
9705 void InitPlayLevelSound()
9706 {
9707   int num_sounds = getSoundListSize();
9708
9709   checked_free(loop_sound_frame);
9710   checked_free(loop_sound_volume);
9711
9712   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
9713   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9714 }
9715
9716 static void PlayLevelSound(int x, int y, int nr)
9717 {
9718   int sx = SCREENX(x), sy = SCREENY(y);
9719   int volume, stereo_position;
9720   int max_distance = 8;
9721   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9722
9723   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9724       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9725     return;
9726
9727   if (!IN_LEV_FIELD(x, y) ||
9728       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9729       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9730     return;
9731
9732   volume = SOUND_MAX_VOLUME;
9733
9734   if (!IN_SCR_FIELD(sx, sy))
9735   {
9736     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9737     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9738
9739     volume -= volume * (dx > dy ? dx : dy) / max_distance;
9740   }
9741
9742   stereo_position = (SOUND_MAX_LEFT +
9743                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9744                      (SCR_FIELDX + 2 * max_distance));
9745
9746   if (IS_LOOP_SOUND(nr))
9747   {
9748     /* This assures that quieter loop sounds do not overwrite louder ones,
9749        while restarting sound volume comparison with each new game frame. */
9750
9751     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9752       return;
9753
9754     loop_sound_volume[nr] = volume;
9755     loop_sound_frame[nr] = FrameCounter;
9756   }
9757
9758   PlaySoundExt(nr, volume, stereo_position, type);
9759 }
9760
9761 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9762 {
9763   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9764                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
9765                  y < LEVELY(BY1) ? LEVELY(BY1) :
9766                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
9767                  sound_action);
9768 }
9769
9770 static void PlayLevelSoundAction(int x, int y, int action)
9771 {
9772   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9773 }
9774
9775 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9776 {
9777   int sound_effect = element_info[element].sound[action];
9778
9779   if (sound_effect != SND_UNDEFINED)
9780     PlayLevelSound(x, y, sound_effect);
9781 }
9782
9783 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9784                                               int action)
9785 {
9786   int sound_effect = element_info[element].sound[action];
9787
9788   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9789     PlayLevelSound(x, y, sound_effect);
9790 }
9791
9792 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9793 {
9794   int sound_effect = element_info[Feld[x][y]].sound[action];
9795
9796   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9797     PlayLevelSound(x, y, sound_effect);
9798 }
9799
9800 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9801 {
9802   int sound_effect = element_info[Feld[x][y]].sound[action];
9803
9804   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9805     StopSound(sound_effect);
9806 }
9807
9808 static void PlayLevelMusic()
9809 {
9810   if (levelset.music[level_nr] != MUS_UNDEFINED)
9811     PlayMusic(levelset.music[level_nr]);        /* from config file */
9812   else
9813     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
9814 }
9815
9816 void RaiseScore(int value)
9817 {
9818   local_player->score += value;
9819   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9820 }
9821
9822 void RaiseScoreElement(int element)
9823 {
9824   switch(element)
9825   {
9826     case EL_EMERALD:
9827     case EL_BD_DIAMOND:
9828     case EL_EMERALD_YELLOW:
9829     case EL_EMERALD_RED:
9830     case EL_EMERALD_PURPLE:
9831     case EL_SP_INFOTRON:
9832       RaiseScore(level.score[SC_EMERALD]);
9833       break;
9834     case EL_DIAMOND:
9835       RaiseScore(level.score[SC_DIAMOND]);
9836       break;
9837     case EL_CRYSTAL:
9838       RaiseScore(level.score[SC_CRYSTAL]);
9839       break;
9840     case EL_PEARL:
9841       RaiseScore(level.score[SC_PEARL]);
9842       break;
9843     case EL_BUG:
9844     case EL_BD_BUTTERFLY:
9845     case EL_SP_ELECTRON:
9846       RaiseScore(level.score[SC_BUG]);
9847       break;
9848     case EL_SPACESHIP:
9849     case EL_BD_FIREFLY:
9850     case EL_SP_SNIKSNAK:
9851       RaiseScore(level.score[SC_SPACESHIP]);
9852       break;
9853     case EL_YAMYAM:
9854     case EL_DARK_YAMYAM:
9855       RaiseScore(level.score[SC_YAMYAM]);
9856       break;
9857     case EL_ROBOT:
9858       RaiseScore(level.score[SC_ROBOT]);
9859       break;
9860     case EL_PACMAN:
9861       RaiseScore(level.score[SC_PACMAN]);
9862       break;
9863     case EL_NUT:
9864       RaiseScore(level.score[SC_NUT]);
9865       break;
9866     case EL_DYNAMITE:
9867     case EL_SP_DISK_RED:
9868     case EL_DYNABOMB_INCREASE_NUMBER:
9869     case EL_DYNABOMB_INCREASE_SIZE:
9870     case EL_DYNABOMB_INCREASE_POWER:
9871       RaiseScore(level.score[SC_DYNAMITE]);
9872       break;
9873     case EL_SHIELD_NORMAL:
9874     case EL_SHIELD_DEADLY:
9875       RaiseScore(level.score[SC_SHIELD]);
9876       break;
9877     case EL_EXTRA_TIME:
9878       RaiseScore(level.score[SC_TIME_BONUS]);
9879       break;
9880     case EL_KEY_1:
9881     case EL_KEY_2:
9882     case EL_KEY_3:
9883     case EL_KEY_4:
9884       RaiseScore(level.score[SC_KEY]);
9885       break;
9886     default:
9887       RaiseScore(element_info[element].collect_score);
9888       break;
9889   }
9890 }
9891
9892 void RequestQuitGame(boolean ask_if_really_quit)
9893 {
9894   if (AllPlayersGone ||
9895       !ask_if_really_quit ||
9896       level_editor_test_game ||
9897       Request("Do you really want to quit the game ?",
9898               REQ_ASK | REQ_STAY_CLOSED))
9899   {
9900 #if defined(PLATFORM_UNIX)
9901     if (options.network)
9902       SendToServer_StopPlaying();
9903     else
9904 #endif
9905     {
9906       game_status = GAME_MODE_MAIN;
9907       DrawMainMenu();
9908     }
9909   }
9910   else
9911   {
9912     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9913   }
9914 }
9915
9916
9917 /* ---------- new game button stuff ---------------------------------------- */
9918
9919 /* graphic position values for game buttons */
9920 #define GAME_BUTTON_XSIZE       30
9921 #define GAME_BUTTON_YSIZE       30
9922 #define GAME_BUTTON_XPOS        5
9923 #define GAME_BUTTON_YPOS        215
9924 #define SOUND_BUTTON_XPOS       5
9925 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9926
9927 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9928 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9929 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9930 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9931 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
9932 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
9933
9934 static struct
9935 {
9936   int x, y;
9937   int gadget_id;
9938   char *infotext;
9939 } gamebutton_info[NUM_GAME_BUTTONS] =
9940 {
9941   {
9942     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
9943     GAME_CTRL_ID_STOP,
9944     "stop game"
9945   },
9946   {
9947     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
9948     GAME_CTRL_ID_PAUSE,
9949     "pause game"
9950   },
9951   {
9952     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
9953     GAME_CTRL_ID_PLAY,
9954     "play game"
9955   },
9956   {
9957     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
9958     SOUND_CTRL_ID_MUSIC,
9959     "background music on/off"
9960   },
9961   {
9962     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
9963     SOUND_CTRL_ID_LOOPS,
9964     "sound loops on/off"
9965   },
9966   {
9967     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
9968     SOUND_CTRL_ID_SIMPLE,
9969     "normal sounds on/off"
9970   }
9971 };
9972
9973 void CreateGameButtons()
9974 {
9975   int i;
9976
9977   for (i = 0; i < NUM_GAME_BUTTONS; i++)
9978   {
9979     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
9980     struct GadgetInfo *gi;
9981     int button_type;
9982     boolean checked;
9983     unsigned long event_mask;
9984     int gd_xoffset, gd_yoffset;
9985     int gd_x1, gd_x2, gd_y1, gd_y2;
9986     int id = i;
9987
9988     gd_xoffset = gamebutton_info[i].x;
9989     gd_yoffset = gamebutton_info[i].y;
9990     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
9991     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
9992
9993     if (id == GAME_CTRL_ID_STOP ||
9994         id == GAME_CTRL_ID_PAUSE ||
9995         id == GAME_CTRL_ID_PLAY)
9996     {
9997       button_type = GD_TYPE_NORMAL_BUTTON;
9998       checked = FALSE;
9999       event_mask = GD_EVENT_RELEASED;
10000       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10001       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10002     }
10003     else
10004     {
10005       button_type = GD_TYPE_CHECK_BUTTON;
10006       checked =
10007         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10008          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10009          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10010       event_mask = GD_EVENT_PRESSED;
10011       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
10012       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10013     }
10014
10015     gi = CreateGadget(GDI_CUSTOM_ID, id,
10016                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
10017                       GDI_X, DX + gd_xoffset,
10018                       GDI_Y, DY + gd_yoffset,
10019                       GDI_WIDTH, GAME_BUTTON_XSIZE,
10020                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
10021                       GDI_TYPE, button_type,
10022                       GDI_STATE, GD_BUTTON_UNPRESSED,
10023                       GDI_CHECKED, checked,
10024                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10025                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10026                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10027                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10028                       GDI_EVENT_MASK, event_mask,
10029                       GDI_CALLBACK_ACTION, HandleGameButtons,
10030                       GDI_END);
10031
10032     if (gi == NULL)
10033       Error(ERR_EXIT, "cannot create gadget");
10034
10035     game_gadget[id] = gi;
10036   }
10037 }
10038
10039 void FreeGameButtons()
10040 {
10041   int i;
10042
10043   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10044     FreeGadget(game_gadget[i]);
10045 }
10046
10047 static void MapGameButtons()
10048 {
10049   int i;
10050
10051   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10052     MapGadget(game_gadget[i]);
10053 }
10054
10055 void UnmapGameButtons()
10056 {
10057   int i;
10058
10059   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10060     UnmapGadget(game_gadget[i]);
10061 }
10062
10063 static void HandleGameButtons(struct GadgetInfo *gi)
10064 {
10065   int id = gi->custom_id;
10066
10067   if (game_status != GAME_MODE_PLAYING)
10068     return;
10069
10070   switch (id)
10071   {
10072     case GAME_CTRL_ID_STOP:
10073       RequestQuitGame(TRUE);
10074       break;
10075
10076     case GAME_CTRL_ID_PAUSE:
10077       if (options.network)
10078       {
10079 #if defined(PLATFORM_UNIX)
10080         if (tape.pausing)
10081           SendToServer_ContinuePlaying();
10082         else
10083           SendToServer_PausePlaying();
10084 #endif
10085       }
10086       else
10087         TapeTogglePause(TAPE_TOGGLE_MANUAL);
10088       break;
10089
10090     case GAME_CTRL_ID_PLAY:
10091       if (tape.pausing)
10092       {
10093 #if defined(PLATFORM_UNIX)
10094         if (options.network)
10095           SendToServer_ContinuePlaying();
10096         else
10097 #endif
10098         {
10099           tape.pausing = FALSE;
10100           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10101         }
10102       }
10103       break;
10104
10105     case SOUND_CTRL_ID_MUSIC:
10106       if (setup.sound_music)
10107       { 
10108         setup.sound_music = FALSE;
10109         FadeMusic();
10110       }
10111       else if (audio.music_available)
10112       { 
10113         setup.sound = setup.sound_music = TRUE;
10114
10115         SetAudioMode(setup.sound);
10116
10117         PlayLevelMusic();
10118       }
10119       break;
10120
10121     case SOUND_CTRL_ID_LOOPS:
10122       if (setup.sound_loops)
10123         setup.sound_loops = FALSE;
10124       else if (audio.loops_available)
10125       {
10126         setup.sound = setup.sound_loops = TRUE;
10127         SetAudioMode(setup.sound);
10128       }
10129       break;
10130
10131     case SOUND_CTRL_ID_SIMPLE:
10132       if (setup.sound_simple)
10133         setup.sound_simple = FALSE;
10134       else if (audio.sound_available)
10135       {
10136         setup.sound = setup.sound_simple = TRUE;
10137         SetAudioMode(setup.sound);
10138       }
10139       break;
10140
10141     default:
10142       break;
10143   }
10144 }