rnd-20040207-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* 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     if (IS_PLAYER(x, y))
2716     {
2717       if (phase == 2)
2718         printf("::: IS_PLAYER\n");
2719
2720       KillHeroUnlessExplosionProtected(x, y);
2721       return;
2722     }
2723     else if (CAN_EXPLODE_BY_FIRE(border_element))
2724     {
2725       if (phase == 2)
2726         printf("::: CAN_EXPLODE_BY_FIRE\n");
2727
2728       Feld[x][y] = Store2[x][y];
2729       Store2[x][y] = 0;
2730       Bang(x, y);
2731       return;
2732     }
2733     else if (border_element == EL_AMOEBA_TO_DIAMOND)
2734     {
2735       if (phase == 2)
2736         printf("::: EL_AMOEBA_TO_DIAMOND\n");
2737
2738       AmoebeUmwandeln(x, y);
2739       return;
2740     }
2741   }
2742
2743   if (phase == last_phase)
2744   {
2745     int element;
2746
2747     element = Feld[x][y] = Store[x][y];
2748     Store[x][y] = Store2[x][y] = 0;
2749     GfxElement[x][y] = EL_UNDEFINED;
2750
2751     /* player can escape from explosions and might therefore be still alive */
2752     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2753         element <= EL_PLAYER_IS_EXPLODING_4)
2754       Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2755                     EL_EMPTY :
2756                     element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2757                     element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2758                     element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2759                     EL_EMERALD_PURPLE);
2760
2761     /* restore probably existing indestructible background element */
2762     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2763       element = Feld[x][y] = Back[x][y];
2764     Back[x][y] = 0;
2765
2766     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2767     GfxDir[x][y] = MV_NO_MOVING;
2768     ChangeDelay[x][y] = 0;
2769     ChangePage[x][y] = -1;
2770
2771     InitField(x, y, FALSE);
2772 #if 1
2773     /* !!! not needed !!! */
2774     if (CAN_MOVE(element))
2775       InitMovDir(x, y);
2776 #endif
2777     DrawLevelField(x, y);
2778
2779     TestIfElementTouchesCustomElement(x, y);
2780
2781     if (GFX_CRUMBLED(element))
2782       DrawLevelFieldCrumbledSandNeighbours(x, y);
2783
2784     if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2785       StorePlayer[x][y] = 0;
2786
2787     if (ELEM_IS_PLAYER(element))
2788       RelocatePlayer(x, y, element);
2789   }
2790   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2791   {
2792 #if 1
2793     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2794 #else
2795     int stored = Store[x][y];
2796     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2797                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2798                    IMG_SP_EXPLOSION);
2799 #endif
2800     int frame = getGraphicAnimationFrame(graphic, phase - delay);
2801
2802 #if 0
2803     printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2804            element_info[GfxElement[x][y]].token_name,
2805            graphic);
2806 #endif
2807
2808     if (phase == delay)
2809       DrawLevelFieldCrumbledSand(x, y);
2810
2811     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2812     {
2813       DrawLevelElement(x, y, Back[x][y]);
2814       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2815     }
2816     else if (IS_WALKABLE_UNDER(Back[x][y]))
2817     {
2818       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2819       DrawLevelElementThruMask(x, y, Back[x][y]);
2820     }
2821     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2822       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2823   }
2824
2825 #else
2826
2827   if (phase == first_phase_after_start)
2828   {
2829     int element = Store2[x][y];
2830
2831     if (element == EL_BLACK_ORB)
2832     {
2833       Feld[x][y] = Store2[x][y];
2834       Store2[x][y] = 0;
2835       Bang(x, y);
2836     }
2837   }
2838   else if (phase == half_phase)
2839   {
2840     int element = Store2[x][y];
2841
2842     if (IS_PLAYER(x, y))
2843       KillHeroUnlessExplosionProtected(x, y);
2844     else if (CAN_EXPLODE_BY_FIRE(element))
2845     {
2846       Feld[x][y] = Store2[x][y];
2847       Store2[x][y] = 0;
2848       Bang(x, y);
2849     }
2850     else if (element == EL_AMOEBA_TO_DIAMOND)
2851       AmoebeUmwandeln(x, y);
2852   }
2853
2854   if (phase == last_phase)
2855   {
2856     int element;
2857
2858     element = Feld[x][y] = Store[x][y];
2859     Store[x][y] = Store2[x][y] = 0;
2860     GfxElement[x][y] = EL_UNDEFINED;
2861
2862     /* player can escape from explosions and might therefore be still alive */
2863     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
2864         element <= EL_PLAYER_IS_EXPLODING_4)
2865       Feld[x][y] = (stored_player[element - EL_PLAYER_IS_EXPLODING_1].active ?
2866                     EL_EMPTY :
2867                     element == EL_PLAYER_IS_EXPLODING_1 ? EL_EMERALD_YELLOW :
2868                     element == EL_PLAYER_IS_EXPLODING_2 ? EL_EMERALD_RED :
2869                     element == EL_PLAYER_IS_EXPLODING_3 ? EL_EMERALD :
2870                     EL_EMERALD_PURPLE);
2871
2872     /* restore probably existing indestructible background element */
2873     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
2874       element = Feld[x][y] = Back[x][y];
2875     Back[x][y] = 0;
2876
2877     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
2878     GfxDir[x][y] = MV_NO_MOVING;
2879     ChangeDelay[x][y] = 0;
2880     ChangePage[x][y] = -1;
2881
2882     InitField(x, y, FALSE);
2883 #if 1
2884     /* !!! not needed !!! */
2885     if (CAN_MOVE(element))
2886       InitMovDir(x, y);
2887 #endif
2888     DrawLevelField(x, y);
2889
2890     TestIfElementTouchesCustomElement(x, y);
2891
2892     if (GFX_CRUMBLED(element))
2893       DrawLevelFieldCrumbledSandNeighbours(x, y);
2894
2895     if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
2896       StorePlayer[x][y] = 0;
2897
2898     if (ELEM_IS_PLAYER(element))
2899       RelocatePlayer(x, y, element);
2900   }
2901   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2902   {
2903 #if 1
2904     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
2905 #else
2906     int stored = Store[x][y];
2907     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
2908                    stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
2909                    IMG_SP_EXPLOSION);
2910 #endif
2911     int frame = getGraphicAnimationFrame(graphic, phase - delay);
2912
2913 #if 0
2914     printf("::: %d ['%s'] -> %d\n", GfxElement[x][y],
2915            element_info[GfxElement[x][y]].token_name,
2916            graphic);
2917 #endif
2918
2919     if (phase == delay)
2920       DrawLevelFieldCrumbledSand(x, y);
2921
2922     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
2923     {
2924       DrawLevelElement(x, y, Back[x][y]);
2925       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
2926     }
2927     else if (IS_WALKABLE_UNDER(Back[x][y]))
2928     {
2929       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2930       DrawLevelElementThruMask(x, y, Back[x][y]);
2931     }
2932     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
2933       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
2934   }
2935 #endif
2936 }
2937
2938 void DynaExplode(int ex, int ey)
2939 {
2940   int i, j;
2941   int dynabomb_element = Feld[ex][ey];
2942   int dynabomb_size = 1;
2943   boolean dynabomb_xl = FALSE;
2944   struct PlayerInfo *player;
2945   static int xy[4][2] =
2946   {
2947     { 0, -1 },
2948     { -1, 0 },
2949     { +1, 0 },
2950     { 0, +1 }
2951   };
2952
2953   if (IS_ACTIVE_BOMB(dynabomb_element))
2954   {
2955     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
2956     dynabomb_size = player->dynabomb_size;
2957     dynabomb_xl = player->dynabomb_xl;
2958     player->dynabombs_left++;
2959   }
2960
2961   Explode(ex, ey, EX_PHASE_START, EX_CENTER);
2962
2963   for (i = 0; i < 4; i++)
2964   {
2965     for (j = 1; j <= dynabomb_size; j++)
2966     {
2967       int x = ex + j * xy[i % 4][0];
2968       int y = ey + j * xy[i % 4][1];
2969       int element;
2970
2971       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
2972         break;
2973
2974       element = Feld[x][y];
2975
2976       /* do not restart explosions of fields with active bombs */
2977       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
2978         continue;
2979
2980       Explode(x, y, EX_PHASE_START, EX_BORDER);
2981
2982       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
2983       if (element != EL_EMPTY &&
2984           element != EL_SAND &&
2985           element != EL_EXPLOSION &&
2986           !dynabomb_xl)
2987         break;
2988     }
2989   }
2990 }
2991
2992 void Bang(int x, int y)
2993 {
2994 #if 1
2995   int element = MovingOrBlocked2Element(x, y);
2996 #else
2997   int element = Feld[x][y];
2998 #endif
2999
3000 #if 1
3001   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
3002 #else
3003   if (IS_PLAYER(x, y))
3004 #endif
3005   {
3006     struct PlayerInfo *player = PLAYERINFO(x, y);
3007
3008     element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
3009                             player->element_nr);
3010   }
3011
3012 #if 0
3013 #if 1
3014   PlayLevelSoundAction(x, y, ACTION_EXPLODING);
3015 #else
3016   if (game.emulation == EMU_SUPAPLEX)
3017     PlayLevelSound(x, y, SND_SP_ELEMENT_EXPLODING);
3018   else
3019     PlayLevelSound(x, y, SND_ELEMENT_EXPLODING);
3020 #endif
3021 #endif
3022
3023 #if 0
3024   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
3025     element = EL_EMPTY;
3026 #endif
3027
3028   switch(element)
3029   {
3030     case EL_BUG:
3031     case EL_SPACESHIP:
3032     case EL_BD_BUTTERFLY:
3033     case EL_BD_FIREFLY:
3034     case EL_YAMYAM:
3035     case EL_DARK_YAMYAM:
3036     case EL_ROBOT:
3037     case EL_PACMAN:
3038     case EL_MOLE:
3039       RaiseScoreElement(element);
3040       Explode(x, y, EX_PHASE_START, EX_NORMAL);
3041       break;
3042     case EL_DYNABOMB_PLAYER_1_ACTIVE:
3043     case EL_DYNABOMB_PLAYER_2_ACTIVE:
3044     case EL_DYNABOMB_PLAYER_3_ACTIVE:
3045     case EL_DYNABOMB_PLAYER_4_ACTIVE:
3046     case EL_DYNABOMB_INCREASE_NUMBER:
3047     case EL_DYNABOMB_INCREASE_SIZE:
3048     case EL_DYNABOMB_INCREASE_POWER:
3049       DynaExplode(x, y);
3050       break;
3051     case EL_PENGUIN:
3052     case EL_LAMP:
3053     case EL_LAMP_ACTIVE:
3054       if (IS_PLAYER(x, y))
3055         Explode(x, y, EX_PHASE_START, EX_NORMAL);
3056       else
3057         Explode(x, y, EX_PHASE_START, EX_CENTER);
3058       break;
3059     default:
3060       if (CAN_EXPLODE_DYNA(element))
3061         DynaExplode(x, y);
3062       else if (CAN_EXPLODE_1X1(element))
3063         Explode(x, y, EX_PHASE_START, EX_CENTER);
3064       else
3065         Explode(x, y, EX_PHASE_START, EX_NORMAL);
3066       break;
3067   }
3068
3069   CheckTriggeredElementChange(x, y, element, CE_OTHER_IS_EXPLODING);
3070 }
3071
3072 void SplashAcid(int x, int y)
3073 {
3074   int element = Feld[x][y];
3075
3076   if (element != EL_ACID_SPLASH_LEFT &&
3077       element != EL_ACID_SPLASH_RIGHT)
3078   {
3079     PlayLevelSound(x, y, SND_ACID_SPLASHING);
3080
3081     if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
3082         (!IN_LEV_FIELD(x-1, y-1) ||
3083          !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
3084       Feld[x-1][y] = EL_ACID_SPLASH_LEFT;
3085
3086     if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
3087         (!IN_LEV_FIELD(x+1, y-1) ||
3088          !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
3089       Feld[x+1][y] = EL_ACID_SPLASH_RIGHT;
3090   }
3091 }
3092
3093 static void InitBeltMovement()
3094 {
3095   static int belt_base_element[4] =
3096   {
3097     EL_CONVEYOR_BELT_1_LEFT,
3098     EL_CONVEYOR_BELT_2_LEFT,
3099     EL_CONVEYOR_BELT_3_LEFT,
3100     EL_CONVEYOR_BELT_4_LEFT
3101   };
3102   static int belt_base_active_element[4] =
3103   {
3104     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3105     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3106     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3107     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3108   };
3109
3110   int x, y, i, j;
3111
3112   /* set frame order for belt animation graphic according to belt direction */
3113   for (i = 0; i < 4; i++)
3114   {
3115     int belt_nr = i;
3116
3117     for (j = 0; j < 3; j++)
3118     {
3119       int element = belt_base_active_element[belt_nr] + j;
3120       int graphic = el2img(element);
3121
3122       if (game.belt_dir[i] == MV_LEFT)
3123         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3124       else
3125         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3126     }
3127   }
3128
3129   for (y = 0; y < lev_fieldy; y++)
3130   {
3131     for (x = 0; x < lev_fieldx; x++)
3132     {
3133       int element = Feld[x][y];
3134
3135       for (i = 0; i < 4; i++)
3136       {
3137         if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
3138         {
3139           int e_belt_nr = getBeltNrFromBeltElement(element);
3140           int belt_nr = i;
3141
3142           if (e_belt_nr == belt_nr)
3143           {
3144             int belt_part = Feld[x][y] - belt_base_element[belt_nr];
3145
3146             Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
3147           }
3148         }
3149       }
3150     }
3151   }
3152 }
3153
3154 static void ToggleBeltSwitch(int x, int y)
3155 {
3156   static int belt_base_element[4] =
3157   {
3158     EL_CONVEYOR_BELT_1_LEFT,
3159     EL_CONVEYOR_BELT_2_LEFT,
3160     EL_CONVEYOR_BELT_3_LEFT,
3161     EL_CONVEYOR_BELT_4_LEFT
3162   };
3163   static int belt_base_active_element[4] =
3164   {
3165     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
3166     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
3167     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
3168     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
3169   };
3170   static int belt_base_switch_element[4] =
3171   {
3172     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
3173     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
3174     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
3175     EL_CONVEYOR_BELT_4_SWITCH_LEFT
3176   };
3177   static int belt_move_dir[4] =
3178   {
3179     MV_LEFT,
3180     MV_NO_MOVING,
3181     MV_RIGHT,
3182     MV_NO_MOVING,
3183   };
3184
3185   int element = Feld[x][y];
3186   int belt_nr = getBeltNrFromBeltSwitchElement(element);
3187   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
3188   int belt_dir = belt_move_dir[belt_dir_nr];
3189   int xx, yy, i;
3190
3191   if (!IS_BELT_SWITCH(element))
3192     return;
3193
3194   game.belt_dir_nr[belt_nr] = belt_dir_nr;
3195   game.belt_dir[belt_nr] = belt_dir;
3196
3197   if (belt_dir_nr == 3)
3198     belt_dir_nr = 1;
3199
3200   /* set frame order for belt animation graphic according to belt direction */
3201   for (i = 0; i < 3; i++)
3202   {
3203     int element = belt_base_active_element[belt_nr] + i;
3204     int graphic = el2img(element);
3205
3206     if (belt_dir == MV_LEFT)
3207       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
3208     else
3209       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
3210   }
3211
3212   for (yy = 0; yy < lev_fieldy; yy++)
3213   {
3214     for (xx = 0; xx < lev_fieldx; xx++)
3215     {
3216       int element = Feld[xx][yy];
3217
3218       if (IS_BELT_SWITCH(element))
3219       {
3220         int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
3221
3222         if (e_belt_nr == belt_nr)
3223         {
3224           Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
3225           DrawLevelField(xx, yy);
3226         }
3227       }
3228       else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
3229       {
3230         int e_belt_nr = getBeltNrFromBeltElement(element);
3231
3232         if (e_belt_nr == belt_nr)
3233         {
3234           int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
3235
3236           Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
3237           DrawLevelField(xx, yy);
3238         }
3239       }
3240       else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
3241       {
3242         int e_belt_nr = getBeltNrFromBeltActiveElement(element);
3243
3244         if (e_belt_nr == belt_nr)
3245         {
3246           int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
3247
3248           Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
3249           DrawLevelField(xx, yy);
3250         }
3251       }
3252     }
3253   }
3254 }
3255
3256 static void ToggleSwitchgateSwitch(int x, int y)
3257 {
3258   int xx, yy;
3259
3260   game.switchgate_pos = !game.switchgate_pos;
3261
3262   for (yy = 0; yy < lev_fieldy; yy++)
3263   {
3264     for (xx = 0; xx < lev_fieldx; xx++)
3265     {
3266       int element = Feld[xx][yy];
3267
3268       if (element == EL_SWITCHGATE_SWITCH_UP ||
3269           element == EL_SWITCHGATE_SWITCH_DOWN)
3270       {
3271         Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
3272         DrawLevelField(xx, yy);
3273       }
3274       else if (element == EL_SWITCHGATE_OPEN ||
3275                element == EL_SWITCHGATE_OPENING)
3276       {
3277         Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
3278 #if 1
3279         PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
3280 #else
3281         PlayLevelSound(xx, yy, SND_SWITCHGATE_CLOSING);
3282 #endif
3283       }
3284       else if (element == EL_SWITCHGATE_CLOSED ||
3285                element == EL_SWITCHGATE_CLOSING)
3286       {
3287         Feld[xx][yy] = EL_SWITCHGATE_OPENING;
3288 #if 1
3289         PlayLevelSoundAction(xx, yy, ACTION_OPENING);
3290 #else
3291         PlayLevelSound(xx, yy, SND_SWITCHGATE_OPENING);
3292 #endif
3293       }
3294     }
3295   }
3296 }
3297
3298 static int getInvisibleActiveFromInvisibleElement(int element)
3299 {
3300   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
3301           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
3302           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
3303           element);
3304 }
3305
3306 static int getInvisibleFromInvisibleActiveElement(int element)
3307 {
3308   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
3309           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
3310           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
3311           element);
3312 }
3313
3314 static void RedrawAllLightSwitchesAndInvisibleElements()
3315 {
3316   int x, y;
3317
3318   for (y = 0; y < lev_fieldy; y++)
3319   {
3320     for (x = 0; x < lev_fieldx; x++)
3321     {
3322       int element = Feld[x][y];
3323
3324       if (element == EL_LIGHT_SWITCH &&
3325           game.light_time_left > 0)
3326       {
3327         Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
3328         DrawLevelField(x, y);
3329       }
3330       else if (element == EL_LIGHT_SWITCH_ACTIVE &&
3331                game.light_time_left == 0)
3332       {
3333         Feld[x][y] = EL_LIGHT_SWITCH;
3334         DrawLevelField(x, y);
3335       }
3336       else if (element == EL_INVISIBLE_STEELWALL ||
3337                element == EL_INVISIBLE_WALL ||
3338                element == EL_INVISIBLE_SAND)
3339       {
3340         if (game.light_time_left > 0)
3341           Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
3342
3343         DrawLevelField(x, y);
3344       }
3345       else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
3346                element == EL_INVISIBLE_WALL_ACTIVE ||
3347                element == EL_INVISIBLE_SAND_ACTIVE)
3348       {
3349         if (game.light_time_left == 0)
3350           Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
3351
3352         DrawLevelField(x, y);
3353       }
3354     }
3355   }
3356 }
3357
3358 static void ToggleLightSwitch(int x, int y)
3359 {
3360   int element = Feld[x][y];
3361
3362   game.light_time_left =
3363     (element == EL_LIGHT_SWITCH ?
3364      level.time_light * FRAMES_PER_SECOND : 0);
3365
3366   RedrawAllLightSwitchesAndInvisibleElements();
3367 }
3368
3369 static void ActivateTimegateSwitch(int x, int y)
3370 {
3371   int xx, yy;
3372
3373   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
3374
3375   for (yy = 0; yy < lev_fieldy; yy++)
3376   {
3377     for (xx = 0; xx < lev_fieldx; xx++)
3378     {
3379       int element = Feld[xx][yy];
3380
3381       if (element == EL_TIMEGATE_CLOSED ||
3382           element == EL_TIMEGATE_CLOSING)
3383       {
3384         Feld[xx][yy] = EL_TIMEGATE_OPENING;
3385         PlayLevelSound(xx, yy, SND_TIMEGATE_OPENING);
3386       }
3387
3388       /*
3389       else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
3390       {
3391         Feld[xx][yy] = EL_TIMEGATE_SWITCH;
3392         DrawLevelField(xx, yy);
3393       }
3394       */
3395
3396     }
3397   }
3398
3399   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
3400 }
3401
3402 inline static int getElementMoveStepsize(int x, int y)
3403 {
3404   int element = Feld[x][y];
3405   int direction = MovDir[x][y];
3406   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
3407   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
3408   int horiz_move = (dx != 0);
3409   int sign = (horiz_move ? dx : dy);
3410   int step = sign * element_info[element].move_stepsize;
3411
3412   /* special values for move stepsize for spring and things on conveyor belt */
3413   if (horiz_move)
3414   {
3415     if (CAN_FALL(element) &&
3416         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
3417       step = sign * MOVE_STEPSIZE_NORMAL / 2;
3418     else if (element == EL_SPRING)
3419       step = sign * MOVE_STEPSIZE_NORMAL * 2;
3420   }
3421
3422   return step;
3423 }
3424
3425 void Impact(int x, int y)
3426 {
3427   boolean lastline = (y == lev_fieldy-1);
3428   boolean object_hit = FALSE;
3429   boolean impact = (lastline || object_hit);
3430   int element = Feld[x][y];
3431   int smashed = EL_UNDEFINED;
3432
3433   if (!lastline)        /* check if element below was hit */
3434   {
3435     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
3436       return;
3437
3438     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
3439                                          MovDir[x][y + 1] != MV_DOWN ||
3440                                          MovPos[x][y + 1] <= TILEY / 2));
3441
3442 #if 0
3443     object_hit = !IS_FREE(x, y + 1);
3444 #endif
3445
3446     /* do not smash moving elements that left the smashed field in time */
3447     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
3448         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
3449       object_hit = FALSE;
3450
3451     if (object_hit)
3452       smashed = MovingOrBlocked2Element(x, y + 1);
3453
3454     impact = (lastline || object_hit);
3455   }
3456
3457   if (!lastline && smashed == EL_ACID)  /* element falls into acid */
3458   {
3459     SplashAcid(x, y);
3460     return;
3461   }
3462
3463   /* only reset graphic animation if graphic really changes after impact */
3464   if (impact &&
3465       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
3466   {
3467     ResetGfxAnimation(x, y);
3468     DrawLevelField(x, y);
3469   }
3470
3471   if (impact && CAN_EXPLODE_IMPACT(element))
3472   {
3473     Bang(x, y);
3474     return;
3475   }
3476   else if (impact && element == EL_PEARL)
3477   {
3478     Feld[x][y] = EL_PEARL_BREAKING;
3479     PlayLevelSound(x, y, SND_PEARL_BREAKING);
3480     return;
3481   }
3482   else if (impact && CheckElementChange(x, y, element, CE_IMPACT))
3483   {
3484     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3485
3486     return;
3487   }
3488
3489   if (impact && element == EL_AMOEBA_DROP)
3490   {
3491     if (object_hit && IS_PLAYER(x, y + 1))
3492       KillHeroUnlessEnemyProtected(x, y + 1);
3493     else if (object_hit && smashed == EL_PENGUIN)
3494       Bang(x, y + 1);
3495     else
3496     {
3497       Feld[x][y] = EL_AMOEBA_GROWING;
3498       Store[x][y] = EL_AMOEBA_WET;
3499
3500       ResetRandomAnimationValue(x, y);
3501     }
3502     return;
3503   }
3504
3505   if (object_hit)               /* check which object was hit */
3506   {
3507     if (CAN_PASS_MAGIC_WALL(element) && 
3508         (smashed == EL_MAGIC_WALL ||
3509          smashed == EL_BD_MAGIC_WALL))
3510     {
3511       int xx, yy;
3512       int activated_magic_wall =
3513         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
3514          EL_BD_MAGIC_WALL_ACTIVE);
3515
3516       /* activate magic wall / mill */
3517       for (yy = 0; yy < lev_fieldy; yy++)
3518         for (xx = 0; xx < lev_fieldx; xx++)
3519           if (Feld[xx][yy] == smashed)
3520             Feld[xx][yy] = activated_magic_wall;
3521
3522       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
3523       game.magic_wall_active = TRUE;
3524
3525       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
3526                             SND_MAGIC_WALL_ACTIVATING :
3527                             SND_BD_MAGIC_WALL_ACTIVATING));
3528     }
3529
3530     if (IS_PLAYER(x, y + 1))
3531     {
3532       if (CAN_SMASH_PLAYER(element))
3533       {
3534         KillHeroUnlessEnemyProtected(x, y + 1);
3535         return;
3536       }
3537     }
3538     else if (smashed == EL_PENGUIN)
3539     {
3540       if (CAN_SMASH_PLAYER(element))
3541       {
3542         Bang(x, y + 1);
3543         return;
3544       }
3545     }
3546     else if (element == EL_BD_DIAMOND)
3547     {
3548       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
3549       {
3550         Bang(x, y + 1);
3551         return;
3552       }
3553     }
3554     else if (((element == EL_SP_INFOTRON ||
3555                element == EL_SP_ZONK) &&
3556               (smashed == EL_SP_SNIKSNAK ||
3557                smashed == EL_SP_ELECTRON ||
3558                smashed == EL_SP_DISK_ORANGE)) ||
3559              (element == EL_SP_INFOTRON &&
3560               smashed == EL_SP_DISK_YELLOW))
3561     {
3562       Bang(x, y + 1);
3563       return;
3564     }
3565 #if 0
3566     else if (CAN_SMASH_ENEMIES(element) && IS_CLASSIC_ENEMY(smashed))
3567     {
3568       Bang(x, y + 1);
3569       return;
3570     }
3571 #endif
3572     else if (CAN_SMASH_EVERYTHING(element))
3573     {
3574       if (IS_CLASSIC_ENEMY(smashed) ||
3575           CAN_EXPLODE_SMASHED(smashed))
3576       {
3577         Bang(x, y + 1);
3578         return;
3579       }
3580       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
3581       {
3582         if (smashed == EL_LAMP ||
3583             smashed == EL_LAMP_ACTIVE)
3584         {
3585           Bang(x, y + 1);
3586           return;
3587         }
3588         else if (smashed == EL_NUT)
3589         {
3590           Feld[x][y + 1] = EL_NUT_BREAKING;
3591           PlayLevelSound(x, y, SND_NUT_BREAKING);
3592           RaiseScoreElement(EL_NUT);
3593           return;
3594         }
3595         else if (smashed == EL_PEARL)
3596         {
3597           Feld[x][y + 1] = EL_PEARL_BREAKING;
3598           PlayLevelSound(x, y, SND_PEARL_BREAKING);
3599           return;
3600         }
3601         else if (smashed == EL_DIAMOND)
3602         {
3603           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
3604           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
3605           return;
3606         }
3607         else if (IS_BELT_SWITCH(smashed))
3608         {
3609           ToggleBeltSwitch(x, y + 1);
3610         }
3611         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
3612                  smashed == EL_SWITCHGATE_SWITCH_DOWN)
3613         {
3614           ToggleSwitchgateSwitch(x, y + 1);
3615         }
3616         else if (smashed == EL_LIGHT_SWITCH ||
3617                  smashed == EL_LIGHT_SWITCH_ACTIVE)
3618         {
3619           ToggleLightSwitch(x, y + 1);
3620         }
3621         else
3622         {
3623           CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3624
3625           CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3626                                           CE_OTHER_IS_SWITCHING);
3627           CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
3628                                  CE_SWITCHED, -1);
3629         }
3630       }
3631       else
3632       {
3633         CheckElementChange(x, y + 1, smashed, CE_SMASHED);
3634       }
3635     }
3636   }
3637
3638   /* play sound of magic wall / mill */
3639   if (!lastline &&
3640       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
3641        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
3642   {
3643     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
3644       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
3645     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
3646       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
3647
3648     return;
3649   }
3650
3651   /* play sound of object that hits the ground */
3652   if (lastline || object_hit)
3653     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
3654 }
3655
3656 inline static void TurnRoundExt(int x, int y)
3657 {
3658   static struct
3659   {
3660     int x, y;
3661   } move_xy[] =
3662   {
3663     {  0,  0 },
3664     { -1,  0 },
3665     { +1,  0 },
3666     {  0,  0 },
3667     {  0, -1 },
3668     {  0,  0 }, { 0, 0 }, { 0, 0 },
3669     {  0, +1 }
3670   };
3671   static struct
3672   {
3673     int left, right, back;
3674   } turn[] =
3675   {
3676     { 0,        0,              0        },
3677     { MV_DOWN,  MV_UP,          MV_RIGHT },
3678     { MV_UP,    MV_DOWN,        MV_LEFT  },
3679     { 0,        0,              0        },
3680     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
3681     { 0,        0,              0        },
3682     { 0,        0,              0        },
3683     { 0,        0,              0        },
3684     { MV_RIGHT, MV_LEFT,        MV_UP    }
3685   };
3686
3687   int element = Feld[x][y];
3688   int move_pattern = element_info[element].move_pattern;
3689
3690   int old_move_dir = MovDir[x][y];
3691   int left_dir  = turn[old_move_dir].left;
3692   int right_dir = turn[old_move_dir].right;
3693   int back_dir  = turn[old_move_dir].back;
3694
3695   int left_dx  = move_xy[left_dir].x,     left_dy  = move_xy[left_dir].y;
3696   int right_dx = move_xy[right_dir].x,    right_dy = move_xy[right_dir].y;
3697   int move_dx  = move_xy[old_move_dir].x, move_dy  = move_xy[old_move_dir].y;
3698   int back_dx  = move_xy[back_dir].x,     back_dy  = move_xy[back_dir].y;
3699
3700   int left_x  = x + left_dx,  left_y  = y + left_dy;
3701   int right_x = x + right_dx, right_y = y + right_dy;
3702   int move_x  = x + move_dx,  move_y  = y + move_dy;
3703
3704   int xx, yy;
3705
3706   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3707   {
3708     TestIfBadThingTouchesOtherBadThing(x, y);
3709
3710     if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
3711       MovDir[x][y] = right_dir;
3712     else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3713       MovDir[x][y] = left_dir;
3714
3715     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
3716       MovDelay[x][y] = 9;
3717     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
3718       MovDelay[x][y] = 1;
3719   }
3720   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3721            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3722   {
3723     TestIfBadThingTouchesOtherBadThing(x, y);
3724
3725     if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
3726       MovDir[x][y] = left_dir;
3727     else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
3728       MovDir[x][y] = right_dir;
3729
3730     if ((element == EL_SPACESHIP ||
3731          element == EL_SP_SNIKSNAK ||
3732          element == EL_SP_ELECTRON)
3733         && MovDir[x][y] != old_move_dir)
3734       MovDelay[x][y] = 9;
3735     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
3736       MovDelay[x][y] = 1;
3737   }
3738   else if (element == EL_YAMYAM)
3739   {
3740     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3741     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3742
3743     if (can_turn_left && can_turn_right)
3744       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3745     else if (can_turn_left)
3746       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3747     else if (can_turn_right)
3748       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3749     else
3750       MovDir[x][y] = back_dir;
3751
3752     MovDelay[x][y] = 16 + 16 * RND(3);
3753   }
3754   else if (element == EL_DARK_YAMYAM)
3755   {
3756     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
3757     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
3758
3759     if (can_turn_left && can_turn_right)
3760       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3761     else if (can_turn_left)
3762       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3763     else if (can_turn_right)
3764       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3765     else
3766       MovDir[x][y] = back_dir;
3767
3768     MovDelay[x][y] = 16 + 16 * RND(3);
3769   }
3770   else if (element == EL_PACMAN)
3771   {
3772     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
3773     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
3774
3775     if (can_turn_left && can_turn_right)
3776       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
3777     else if (can_turn_left)
3778       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
3779     else if (can_turn_right)
3780       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
3781     else
3782       MovDir[x][y] = back_dir;
3783
3784     MovDelay[x][y] = 6 + RND(40);
3785   }
3786   else if (element == EL_PIG)
3787   {
3788     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(left_x, left_y);
3789     boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
3790     boolean can_move_on    = PIG_CAN_ENTER_FIELD(move_x, move_y);
3791     boolean should_turn_left, should_turn_right, should_move_on;
3792     int rnd_value = 24;
3793     int rnd = RND(rnd_value);
3794
3795     should_turn_left = (can_turn_left &&
3796                         (!can_move_on ||
3797                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
3798                                                    y + back_dy + left_dy)));
3799     should_turn_right = (can_turn_right &&
3800                          (!can_move_on ||
3801                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
3802                                                     y + back_dy + right_dy)));
3803     should_move_on = (can_move_on &&
3804                       (!can_turn_left ||
3805                        !can_turn_right ||
3806                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
3807                                                  y + move_dy + left_dy) ||
3808                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
3809                                                  y + move_dy + right_dy)));
3810
3811     if (should_turn_left || should_turn_right || should_move_on)
3812     {
3813       if (should_turn_left && should_turn_right && should_move_on)
3814         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
3815                         rnd < 2 * rnd_value / 3 ? right_dir :
3816                         old_move_dir);
3817       else if (should_turn_left && should_turn_right)
3818         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3819       else if (should_turn_left && should_move_on)
3820         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
3821       else if (should_turn_right && should_move_on)
3822         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
3823       else if (should_turn_left)
3824         MovDir[x][y] = left_dir;
3825       else if (should_turn_right)
3826         MovDir[x][y] = right_dir;
3827       else if (should_move_on)
3828         MovDir[x][y] = old_move_dir;
3829     }
3830     else if (can_move_on && rnd > rnd_value / 8)
3831       MovDir[x][y] = old_move_dir;
3832     else if (can_turn_left && can_turn_right)
3833       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3834     else if (can_turn_left && rnd > rnd_value / 8)
3835       MovDir[x][y] = left_dir;
3836     else if (can_turn_right && rnd > rnd_value/8)
3837       MovDir[x][y] = right_dir;
3838     else
3839       MovDir[x][y] = back_dir;
3840
3841     xx = x + move_xy[MovDir[x][y]].x;
3842     yy = y + move_xy[MovDir[x][y]].y;
3843
3844     if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
3845       MovDir[x][y] = old_move_dir;
3846
3847     MovDelay[x][y] = 0;
3848   }
3849   else if (element == EL_DRAGON)
3850   {
3851     boolean can_turn_left  = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
3852     boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
3853     boolean can_move_on    = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
3854     int rnd_value = 24;
3855     int rnd = RND(rnd_value);
3856
3857 #if 0
3858     if (FrameCounter < 1 && x == 0 && y == 29)
3859       printf(":2: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3860 #endif
3861
3862     if (can_move_on && rnd > rnd_value / 8)
3863       MovDir[x][y] = old_move_dir;
3864     else if (can_turn_left && can_turn_right)
3865       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
3866     else if (can_turn_left && rnd > rnd_value / 8)
3867       MovDir[x][y] = left_dir;
3868     else if (can_turn_right && rnd > rnd_value / 8)
3869       MovDir[x][y] = right_dir;
3870     else
3871       MovDir[x][y] = back_dir;
3872
3873     xx = x + move_xy[MovDir[x][y]].x;
3874     yy = y + move_xy[MovDir[x][y]].y;
3875
3876 #if 0
3877     if (FrameCounter < 1 && x == 0 && y == 29)
3878       printf(":3: %d/%d: %d (%d/%d: %d) [%d]\n", x, y, MovDir[x][y],
3879              xx, yy, Feld[xx][yy],
3880              FrameCounter);
3881 #endif
3882
3883 #if 1
3884     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
3885       MovDir[x][y] = old_move_dir;
3886 #else
3887     if (!IS_FREE(xx, yy))
3888       MovDir[x][y] = old_move_dir;
3889 #endif
3890
3891 #if 0
3892     if (FrameCounter < 1 && x == 0 && y == 29)
3893       printf(":4: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
3894 #endif
3895
3896     MovDelay[x][y] = 0;
3897   }
3898   else if (element == EL_MOLE)
3899   {
3900     boolean can_move_on =
3901       (MOLE_CAN_ENTER_FIELD(move_x, move_y,
3902                             IS_AMOEBOID(Feld[move_x][move_y]) ||
3903                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
3904     if (!can_move_on)
3905     {
3906       boolean can_turn_left =
3907         (MOLE_CAN_ENTER_FIELD(left_x, left_y,
3908                               IS_AMOEBOID(Feld[left_x][left_y])));
3909
3910       boolean can_turn_right =
3911         (MOLE_CAN_ENTER_FIELD(right_x, right_y,
3912                               IS_AMOEBOID(Feld[right_x][right_y])));
3913
3914       if (can_turn_left && can_turn_right)
3915         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
3916       else if (can_turn_left)
3917         MovDir[x][y] = left_dir;
3918       else
3919         MovDir[x][y] = right_dir;
3920     }
3921
3922     if (MovDir[x][y] != old_move_dir)
3923       MovDelay[x][y] = 9;
3924   }
3925   else if (element == EL_BALLOON)
3926   {
3927     MovDir[x][y] = game.balloon_dir;
3928     MovDelay[x][y] = 0;
3929   }
3930   else if (element == EL_SPRING)
3931   {
3932     if (MovDir[x][y] & MV_HORIZONTAL &&
3933         (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
3934          IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
3935       MovDir[x][y] = MV_NO_MOVING;
3936
3937     MovDelay[x][y] = 0;
3938   }
3939   else if (element == EL_ROBOT ||
3940            element == EL_SATELLITE ||
3941            element == EL_PENGUIN)
3942   {
3943     int attr_x = -1, attr_y = -1;
3944
3945     if (AllPlayersGone)
3946     {
3947       attr_x = ExitX;
3948       attr_y = ExitY;
3949     }
3950     else
3951     {
3952       int i;
3953
3954       for (i = 0; i < MAX_PLAYERS; i++)
3955       {
3956         struct PlayerInfo *player = &stored_player[i];
3957         int jx = player->jx, jy = player->jy;
3958
3959         if (!player->active)
3960           continue;
3961
3962         if (attr_x == -1 ||
3963             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
3964         {
3965           attr_x = jx;
3966           attr_y = jy;
3967         }
3968       }
3969     }
3970
3971     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
3972     {
3973       attr_x = ZX;
3974       attr_y = ZY;
3975     }
3976
3977     if (element == EL_PENGUIN)
3978     {
3979       int i;
3980       static int xy[4][2] =
3981       {
3982         { 0, -1 },
3983         { -1, 0 },
3984         { +1, 0 },
3985         { 0, +1 }
3986       };
3987
3988       for (i = 0; i < 4; i++)
3989       {
3990         int ex = x + xy[i % 4][0];
3991         int ey = y + xy[i % 4][1];
3992
3993         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
3994         {
3995           attr_x = ex;
3996           attr_y = ey;
3997           break;
3998         }
3999       }
4000     }
4001
4002     MovDir[x][y] = MV_NO_MOVING;
4003     if (attr_x < x)
4004       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
4005     else if (attr_x > x)
4006       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
4007     if (attr_y < y)
4008       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
4009     else if (attr_y > y)
4010       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
4011
4012     if (element == EL_ROBOT)
4013     {
4014       int newx, newy;
4015
4016       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4017         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
4018       Moving2Blocked(x, y, &newx, &newy);
4019
4020       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
4021         MovDelay[x][y] = 8 + 8 * !RND(3);
4022       else
4023         MovDelay[x][y] = 16;
4024     }
4025     else if (element == EL_PENGUIN)
4026     {
4027       int newx, newy;
4028
4029       MovDelay[x][y] = 1;
4030
4031       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4032       {
4033         boolean first_horiz = RND(2);
4034         int new_move_dir = MovDir[x][y];
4035
4036         MovDir[x][y] =
4037           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4038         Moving2Blocked(x, y, &newx, &newy);
4039
4040         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4041           return;
4042
4043         MovDir[x][y] =
4044           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4045         Moving2Blocked(x, y, &newx, &newy);
4046
4047         if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
4048           return;
4049
4050         MovDir[x][y] = old_move_dir;
4051         return;
4052       }
4053     }
4054     else        /* (element == EL_SATELLITE) */
4055     {
4056       int newx, newy;
4057
4058       MovDelay[x][y] = 1;
4059
4060       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4061       {
4062         boolean first_horiz = RND(2);
4063         int new_move_dir = MovDir[x][y];
4064
4065         MovDir[x][y] =
4066           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4067         Moving2Blocked(x, y, &newx, &newy);
4068
4069         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4070           return;
4071
4072         MovDir[x][y] =
4073           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4074         Moving2Blocked(x, y, &newx, &newy);
4075
4076         if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
4077           return;
4078
4079         MovDir[x][y] = old_move_dir;
4080         return;
4081       }
4082     }
4083   }
4084   else if (move_pattern == MV_TURNING_LEFT ||
4085            move_pattern == MV_TURNING_RIGHT ||
4086            move_pattern == MV_TURNING_LEFT_RIGHT ||
4087            move_pattern == MV_TURNING_RIGHT_LEFT ||
4088            move_pattern == MV_TURNING_RANDOM ||
4089            move_pattern == MV_ALL_DIRECTIONS)
4090   {
4091     boolean can_turn_left =
4092       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
4093     boolean can_turn_right =
4094       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
4095
4096     if (move_pattern == MV_TURNING_LEFT)
4097       MovDir[x][y] = left_dir;
4098     else if (move_pattern == MV_TURNING_RIGHT)
4099       MovDir[x][y] = right_dir;
4100     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
4101       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
4102     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
4103       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
4104     else if (move_pattern == MV_TURNING_RANDOM)
4105       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
4106                       can_turn_right && !can_turn_left ? right_dir :
4107                       RND(2) ? left_dir : right_dir);
4108     else if (can_turn_left && can_turn_right)
4109       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
4110     else if (can_turn_left)
4111       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
4112     else if (can_turn_right)
4113       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
4114     else
4115       MovDir[x][y] = back_dir;
4116
4117     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4118   }
4119   else if (move_pattern == MV_HORIZONTAL ||
4120            move_pattern == MV_VERTICAL)
4121   {
4122     if (move_pattern & old_move_dir)
4123       MovDir[x][y] = back_dir;
4124     else if (move_pattern == MV_HORIZONTAL)
4125       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4126     else if (move_pattern == MV_VERTICAL)
4127       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4128
4129     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4130   }
4131   else if (move_pattern & MV_ANY_DIRECTION)
4132   {
4133     MovDir[x][y] = move_pattern;
4134     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4135   }
4136   else if (move_pattern == MV_ALONG_LEFT_SIDE)
4137   {
4138     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
4139       MovDir[x][y] = left_dir;
4140     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4141       MovDir[x][y] = right_dir;
4142
4143     if (MovDir[x][y] != old_move_dir)
4144       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4145   }
4146   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
4147   {
4148     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
4149       MovDir[x][y] = right_dir;
4150     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
4151       MovDir[x][y] = left_dir;
4152
4153     if (MovDir[x][y] != old_move_dir)
4154       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4155   }
4156   else if (move_pattern == MV_TOWARDS_PLAYER ||
4157            move_pattern == MV_AWAY_FROM_PLAYER)
4158   {
4159     int attr_x = -1, attr_y = -1;
4160     int newx, newy;
4161     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
4162
4163     if (AllPlayersGone)
4164     {
4165       attr_x = ExitX;
4166       attr_y = ExitY;
4167     }
4168     else
4169     {
4170       int i;
4171
4172       for (i = 0; i < MAX_PLAYERS; i++)
4173       {
4174         struct PlayerInfo *player = &stored_player[i];
4175         int jx = player->jx, jy = player->jy;
4176
4177         if (!player->active)
4178           continue;
4179
4180         if (attr_x == -1 ||
4181             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
4182         {
4183           attr_x = jx;
4184           attr_y = jy;
4185         }
4186       }
4187     }
4188
4189     MovDir[x][y] = MV_NO_MOVING;
4190     if (attr_x < x)
4191       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
4192     else if (attr_x > x)
4193       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
4194     if (attr_y < y)
4195       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
4196     else if (attr_y > y)
4197       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
4198
4199     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
4200
4201     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
4202     {
4203       boolean first_horiz = RND(2);
4204       int new_move_dir = MovDir[x][y];
4205
4206       MovDir[x][y] =
4207         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4208       Moving2Blocked(x, y, &newx, &newy);
4209
4210       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4211         return;
4212
4213       MovDir[x][y] =
4214         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
4215       Moving2Blocked(x, y, &newx, &newy);
4216
4217       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
4218         return;
4219
4220       MovDir[x][y] = old_move_dir;
4221     }
4222   }
4223   else if (move_pattern == MV_WHEN_PUSHED ||
4224            move_pattern == MV_WHEN_DROPPED)
4225   {
4226     if (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y))
4227       MovDir[x][y] = MV_NO_MOVING;
4228
4229     MovDelay[x][y] = 0;
4230   }
4231   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
4232   {
4233     static int test_xy[7][2] =
4234     {
4235       { 0, -1 },
4236       { -1, 0 },
4237       { +1, 0 },
4238       { 0, +1 },
4239       { 0, -1 },
4240       { -1, 0 },
4241       { +1, 0 },
4242     };
4243     static int test_dir[7] =
4244     {
4245       MV_UP,
4246       MV_LEFT,
4247       MV_RIGHT,
4248       MV_DOWN,
4249       MV_UP,
4250       MV_LEFT,
4251       MV_RIGHT,
4252     };
4253     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
4254     int move_preference = -1000000;     /* start with very low preference */
4255     int new_move_dir = MV_NO_MOVING;
4256     int start_test = RND(4);
4257     int i;
4258
4259     for (i = 0; i < 4; i++)
4260     {
4261       int move_dir = test_dir[start_test + i];
4262       int move_dir_preference;
4263
4264       xx = x + test_xy[start_test + i][0];
4265       yy = y + test_xy[start_test + i][1];
4266
4267       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
4268           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
4269       {
4270         new_move_dir = move_dir;
4271
4272         break;
4273       }
4274
4275       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
4276         continue;
4277
4278       move_dir_preference = -1 * RunnerVisit[xx][yy];
4279       if (hunter_mode && PlayerVisit[xx][yy] > 0)
4280         move_dir_preference = PlayerVisit[xx][yy];
4281
4282       if (move_dir_preference > move_preference)
4283       {
4284         /* prefer field that has not been visited for the longest time */
4285         move_preference = move_dir_preference;
4286         new_move_dir = move_dir;
4287       }
4288       else if (move_dir_preference == move_preference &&
4289                move_dir == old_move_dir)
4290       {
4291         /* prefer last direction when all directions are preferred equally */
4292         move_preference = move_dir_preference;
4293         new_move_dir = move_dir;
4294       }
4295     }
4296
4297     MovDir[x][y] = new_move_dir;
4298     if (old_move_dir != new_move_dir)
4299       MovDelay[x][y] = 9;
4300   }
4301 }
4302
4303 static void TurnRound(int x, int y)
4304 {
4305   int direction = MovDir[x][y];
4306
4307 #if 0
4308   GfxDir[x][y] = MovDir[x][y];
4309 #endif
4310
4311   TurnRoundExt(x, y);
4312
4313 #if 1
4314   GfxDir[x][y] = MovDir[x][y];
4315 #endif
4316
4317   if (direction != MovDir[x][y])
4318     GfxFrame[x][y] = 0;
4319
4320 #if 1
4321   if (MovDelay[x][y])
4322     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_BIT(direction);
4323 #else
4324   if (MovDelay[x][y])
4325     GfxAction[x][y] = ACTION_WAITING;
4326 #endif
4327 }
4328
4329 static boolean JustBeingPushed(int x, int y)
4330 {
4331   int i;
4332
4333   for (i = 0; i < MAX_PLAYERS; i++)
4334   {
4335     struct PlayerInfo *player = &stored_player[i];
4336
4337     if (player->active && player->is_pushing && player->MovPos)
4338     {
4339       int next_jx = player->jx + (player->jx - player->last_jx);
4340       int next_jy = player->jy + (player->jy - player->last_jy);
4341
4342       if (x == next_jx && y == next_jy)
4343         return TRUE;
4344     }
4345   }
4346
4347   return FALSE;
4348 }
4349
4350 void StartMoving(int x, int y)
4351 {
4352   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
4353   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
4354   int element = Feld[x][y];
4355
4356   if (Stop[x][y])
4357     return;
4358
4359 #if 1
4360   if (MovDelay[x][y] == 0)
4361     GfxAction[x][y] = ACTION_DEFAULT;
4362 #else
4363   /* !!! this should be handled more generic (not only for mole) !!! */
4364   if (element != EL_MOLE && GfxAction[x][y] != ACTION_DIGGING)
4365     GfxAction[x][y] = ACTION_DEFAULT;
4366 #endif
4367
4368   if (CAN_FALL(element) && y < lev_fieldy - 1)
4369   {
4370     if ((x > 0 && IS_PLAYER(x - 1, y)) ||
4371         (x < lev_fieldx-1 && IS_PLAYER(x + 1, y)))
4372       if (JustBeingPushed(x, y))
4373         return;
4374
4375     if (element == EL_QUICKSAND_FULL)
4376     {
4377       if (IS_FREE(x, y + 1))
4378       {
4379         InitMovingField(x, y, MV_DOWN);
4380         started_moving = TRUE;
4381
4382         Feld[x][y] = EL_QUICKSAND_EMPTYING;
4383         Store[x][y] = EL_ROCK;
4384 #if 1
4385         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
4386 #else
4387         PlayLevelSound(x, y, SND_QUICKSAND_EMPTYING);
4388 #endif
4389       }
4390       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4391       {
4392         if (!MovDelay[x][y])
4393           MovDelay[x][y] = TILEY + 1;
4394
4395         if (MovDelay[x][y])
4396         {
4397           MovDelay[x][y]--;
4398           if (MovDelay[x][y])
4399             return;
4400         }
4401
4402         Feld[x][y] = EL_QUICKSAND_EMPTY;
4403         Feld[x][y + 1] = EL_QUICKSAND_FULL;
4404         Store[x][y + 1] = Store[x][y];
4405         Store[x][y] = 0;
4406 #if 1
4407         PlayLevelSoundAction(x, y, ACTION_FILLING);
4408 #else
4409         PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4410 #endif
4411       }
4412     }
4413     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
4414              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
4415     {
4416       InitMovingField(x, y, MV_DOWN);
4417       started_moving = TRUE;
4418
4419       Feld[x][y] = EL_QUICKSAND_FILLING;
4420       Store[x][y] = element;
4421 #if 1
4422       PlayLevelSoundAction(x, y, ACTION_FILLING);
4423 #else
4424       PlayLevelSound(x, y, SND_QUICKSAND_FILLING);
4425 #endif
4426     }
4427     else if (element == EL_MAGIC_WALL_FULL)
4428     {
4429       if (IS_FREE(x, y + 1))
4430       {
4431         InitMovingField(x, y, MV_DOWN);
4432         started_moving = TRUE;
4433
4434         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
4435         Store[x][y] = EL_CHANGED(Store[x][y]);
4436       }
4437       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
4438       {
4439         if (!MovDelay[x][y])
4440           MovDelay[x][y] = TILEY/4 + 1;
4441
4442         if (MovDelay[x][y])
4443         {
4444           MovDelay[x][y]--;
4445           if (MovDelay[x][y])
4446             return;
4447         }
4448
4449         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
4450         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
4451         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
4452         Store[x][y] = 0;
4453       }
4454     }
4455     else if (element == EL_BD_MAGIC_WALL_FULL)
4456     {
4457       if (IS_FREE(x, y + 1))
4458       {
4459         InitMovingField(x, y, MV_DOWN);
4460         started_moving = TRUE;
4461
4462         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
4463         Store[x][y] = EL_CHANGED2(Store[x][y]);
4464       }
4465       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
4466       {
4467         if (!MovDelay[x][y])
4468           MovDelay[x][y] = TILEY/4 + 1;
4469
4470         if (MovDelay[x][y])
4471         {
4472           MovDelay[x][y]--;
4473           if (MovDelay[x][y])
4474             return;
4475         }
4476
4477         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
4478         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
4479         Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
4480         Store[x][y] = 0;
4481       }
4482     }
4483     else if (CAN_PASS_MAGIC_WALL(element) &&
4484              (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
4485               Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
4486     {
4487       InitMovingField(x, y, MV_DOWN);
4488       started_moving = TRUE;
4489
4490       Feld[x][y] =
4491         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
4492          EL_BD_MAGIC_WALL_FILLING);
4493       Store[x][y] = element;
4494     }
4495 #if 0
4496     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
4497 #else
4498     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
4499 #endif
4500     {
4501       SplashAcid(x, y);
4502
4503       InitMovingField(x, y, MV_DOWN);
4504       started_moving = TRUE;
4505
4506       Store[x][y] = EL_ACID;
4507 #if 0
4508       /* !!! TEST !!! better use "_FALLING" etc. !!! */
4509       GfxAction[x][y + 1] = ACTION_ACTIVE;
4510 #endif
4511     }
4512 #if 1
4513     else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
4514               CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
4515               (Feld[x][y + 1] == EL_BLOCKED)) ||
4516              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
4517               CAN_SMASH(element) && WasJustFalling[x][y] &&
4518               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
4519
4520 #else
4521 #if 1
4522     else if (game.engine_version < VERSION_IDENT(2,2,0,7) &&
4523              CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4524              WasJustMoving[x][y] && !Pushed[x][y + 1])
4525 #else
4526     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
4527              WasJustMoving[x][y])
4528 #endif
4529 #endif
4530
4531     {
4532       /* this is needed for a special case not covered by calling "Impact()"
4533          from "ContinueMoving()": if an element moves to a tile directly below
4534          another element which was just falling on that tile (which was empty
4535          in the previous frame), the falling element above would just stop
4536          instead of smashing the element below (in previous version, the above
4537          element was just checked for "moving" instead of "falling", resulting
4538          in incorrect smashes caused by horizontal movement of the above
4539          element; also, the case of the player being the element to smash was
4540          simply not covered here... :-/ ) */
4541
4542 #if 0
4543       WasJustMoving[x][y] = 0;
4544       WasJustFalling[x][y] = 0;
4545 #endif
4546
4547       Impact(x, y);
4548     }
4549     else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
4550     {
4551       if (MovDir[x][y] == MV_NO_MOVING)
4552       {
4553         InitMovingField(x, y, MV_DOWN);
4554         started_moving = TRUE;
4555       }
4556     }
4557     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
4558     {
4559       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
4560         MovDir[x][y] = MV_DOWN;
4561
4562       InitMovingField(x, y, MV_DOWN);
4563       started_moving = TRUE;
4564     }
4565     else if (element == EL_AMOEBA_DROP)
4566     {
4567       Feld[x][y] = EL_AMOEBA_GROWING;
4568       Store[x][y] = EL_AMOEBA_WET;
4569     }
4570     /* Store[x][y + 1] must be zero, because:
4571        (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
4572     */
4573 #if 0
4574 #if OLD_GAME_BEHAVIOUR
4575     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
4576 #else
4577     else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
4578              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4579              element != EL_DX_SUPABOMB)
4580 #endif
4581 #else
4582     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
4583               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
4584              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
4585              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
4586 #endif
4587     {
4588       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
4589                                 (IS_FREE(x - 1, y + 1) ||
4590                                  Feld[x - 1][y + 1] == EL_ACID));
4591       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
4592                                 (IS_FREE(x + 1, y + 1) ||
4593                                  Feld[x + 1][y + 1] == EL_ACID));
4594       boolean can_fall_any  = (can_fall_left || can_fall_right);
4595       boolean can_fall_both = (can_fall_left && can_fall_right);
4596
4597       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
4598       {
4599         int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
4600
4601         if (slippery_type == SLIPPERY_ONLY_LEFT)
4602           can_fall_right = FALSE;
4603         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
4604           can_fall_left = FALSE;
4605         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
4606           can_fall_right = FALSE;
4607         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
4608           can_fall_left = FALSE;
4609
4610         can_fall_any  = (can_fall_left || can_fall_right);
4611         can_fall_both = (can_fall_left && can_fall_right);
4612       }
4613
4614       if (can_fall_any)
4615       {
4616         if (can_fall_both &&
4617             (game.emulation != EMU_BOULDERDASH &&
4618              element != EL_BD_ROCK && element != EL_BD_DIAMOND))
4619           can_fall_left = !(can_fall_right = RND(2));
4620
4621         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
4622         started_moving = TRUE;
4623       }
4624     }
4625     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
4626     {
4627       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
4628       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
4629       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
4630       int belt_dir = game.belt_dir[belt_nr];
4631
4632       if ((belt_dir == MV_LEFT  && left_is_free) ||
4633           (belt_dir == MV_RIGHT && right_is_free))
4634       {
4635         InitMovingField(x, y, belt_dir);
4636         started_moving = TRUE;
4637
4638         GfxAction[x][y] = ACTION_DEFAULT;
4639       }
4640     }
4641   }
4642
4643   /* not "else if" because of elements that can fall and move (EL_SPRING) */
4644   if (CAN_MOVE(element) && !started_moving)
4645   {
4646     int move_pattern = element_info[element].move_pattern;
4647     int newx, newy;
4648
4649     Moving2Blocked(x, y, &newx, &newy);
4650
4651 #if 1
4652     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
4653       return;
4654 #else
4655     if ((element == EL_SATELLITE ||
4656          element == EL_BALLOON ||
4657          element == EL_SPRING)
4658         && JustBeingPushed(x, y))
4659       return;
4660 #endif
4661
4662 #if 1
4663     if (game.engine_version >= VERSION_IDENT(3,0,9,0) &&
4664         WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
4665         (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
4666     {
4667 #if 0
4668       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
4669              element, element_info[element].token_name,
4670              WasJustMoving[x][y],
4671              HAS_ANY_CHANGE_EVENT(element, CE_HITTING_SOMETHING),
4672              HAS_ANY_CHANGE_EVENT(element, CE_HIT_BY_SOMETHING),
4673              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_IS_HITTING),
4674              HAS_ANY_CHANGE_EVENT(element, CE_OTHER_GETS_HIT));
4675 #endif
4676
4677 #if 1
4678       WasJustMoving[x][y] = 0;
4679 #endif
4680
4681       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
4682
4683 #if 0
4684       if (Feld[x][y] != element)        /* element has changed */
4685       {
4686         element = Feld[x][y];
4687         move_pattern = element_info[element].move_pattern;
4688
4689         if (!CAN_MOVE(element))
4690           return;
4691       }
4692 #else
4693       if (Feld[x][y] != element)        /* element has changed */
4694         return;
4695 #endif
4696     }
4697 #endif
4698
4699 #if 0
4700 #if 0
4701     if (element == EL_SPRING && MovDir[x][y] == MV_DOWN)
4702       Feld[x][y + 1] = EL_EMPTY;        /* was set to EL_BLOCKED above */
4703 #else
4704     if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
4705     {
4706       Moving2Blocked(x, y, &newx, &newy);
4707       if (Feld[newx][newy] == EL_BLOCKED)
4708         Feld[newx][newy] = EL_EMPTY;    /* was set to EL_BLOCKED above */
4709     }
4710 #endif
4711 #endif
4712
4713 #if 0
4714     if (FrameCounter < 1 && x == 0 && y == 29)
4715       printf(":1: %d/%d: %d [%d]\n", x, y, MovDir[x][y], FrameCounter);
4716 #endif
4717
4718     if (!MovDelay[x][y])        /* start new movement phase */
4719     {
4720       /* all objects that can change their move direction after each step
4721          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
4722
4723       if (element != EL_YAMYAM &&
4724           element != EL_DARK_YAMYAM &&
4725           element != EL_PACMAN &&
4726           !(move_pattern & MV_ANY_DIRECTION) &&
4727           move_pattern != MV_TURNING_LEFT &&
4728           move_pattern != MV_TURNING_RIGHT &&
4729           move_pattern != MV_TURNING_LEFT_RIGHT &&
4730           move_pattern != MV_TURNING_RIGHT_LEFT &&
4731           move_pattern != MV_TURNING_RANDOM)
4732       {
4733         TurnRound(x, y);
4734
4735 #if 0
4736         if (FrameCounter < 1 && x == 0 && y == 29)
4737           printf(":9: %d: %d [%d]\n", y, MovDir[x][y], FrameCounter);
4738 #endif
4739
4740         if (MovDelay[x][y] && (element == EL_BUG ||
4741                                element == EL_SPACESHIP ||
4742                                element == EL_SP_SNIKSNAK ||
4743                                element == EL_SP_ELECTRON ||
4744                                element == EL_MOLE))
4745           DrawLevelField(x, y);
4746       }
4747     }
4748
4749     if (MovDelay[x][y])         /* wait some time before next movement */
4750     {
4751       MovDelay[x][y]--;
4752
4753 #if 0
4754       if (element == EL_YAMYAM)
4755       {
4756         printf("::: %d\n",
4757                el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
4758         DrawLevelElementAnimation(x, y, element);
4759       }
4760 #endif
4761
4762       if (MovDelay[x][y])       /* element still has to wait some time */
4763       {
4764 #if 0
4765         /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
4766         ResetGfxAnimation(x, y);
4767 #endif
4768
4769 #if 0
4770         if (GfxAction[x][y] != ACTION_WAITING)
4771           printf("::: %d: %d != ACTION_WAITING\n", element, GfxAction[x][y]);
4772
4773         GfxAction[x][y] = ACTION_WAITING;
4774 #endif
4775       }
4776
4777       if (element == EL_ROBOT ||
4778 #if 0
4779           element == EL_PACMAN ||
4780 #endif
4781           element == EL_YAMYAM ||
4782           element == EL_DARK_YAMYAM)
4783       {
4784 #if 0
4785         DrawLevelElementAnimation(x, y, element);
4786 #else
4787         DrawLevelElementAnimationIfNeeded(x, y, element);
4788 #endif
4789         PlayLevelSoundAction(x, y, ACTION_WAITING);
4790       }
4791       else if (element == EL_SP_ELECTRON)
4792         DrawLevelElementAnimationIfNeeded(x, y, element);
4793       else if (element == EL_DRAGON)
4794       {
4795         int i;
4796         int dir = MovDir[x][y];
4797         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
4798         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
4799         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
4800                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
4801                        dir == MV_UP     ? IMG_FLAMES_1_UP :
4802                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
4803         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4804
4805 #if 0
4806         printf("::: %d, %d\n", GfxAction[x][y], GfxFrame[x][y]);
4807 #endif
4808
4809         GfxAction[x][y] = ACTION_ATTACKING;
4810
4811         if (IS_PLAYER(x, y))
4812           DrawPlayerField(x, y);
4813         else
4814           DrawLevelField(x, y);
4815
4816         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
4817
4818         for (i = 1; i <= 3; i++)
4819         {
4820           int xx = x + i * dx;
4821           int yy = y + i * dy;
4822           int sx = SCREENX(xx);
4823           int sy = SCREENY(yy);
4824           int flame_graphic = graphic + (i - 1);
4825
4826           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
4827             break;
4828
4829           if (MovDelay[x][y])
4830           {
4831             int flamed = MovingOrBlocked2Element(xx, yy);
4832
4833             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
4834               Bang(xx, yy);
4835             else
4836               RemoveMovingField(xx, yy);
4837
4838             Feld[xx][yy] = EL_FLAMES;
4839             if (IN_SCR_FIELD(sx, sy))
4840             {
4841               DrawLevelFieldCrumbledSand(xx, yy);
4842               DrawGraphic(sx, sy, flame_graphic, frame);
4843             }
4844           }
4845           else
4846           {
4847             if (Feld[xx][yy] == EL_FLAMES)
4848               Feld[xx][yy] = EL_EMPTY;
4849             DrawLevelField(xx, yy);
4850           }
4851         }
4852       }
4853
4854       if (MovDelay[x][y])       /* element still has to wait some time */
4855       {
4856         PlayLevelSoundAction(x, y, ACTION_WAITING);
4857
4858         return;
4859       }
4860
4861 #if 0
4862       /* special case of "moving" animation of waiting elements (FIX THIS !!!);
4863          for all other elements GfxAction will be set by InitMovingField() */
4864       if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
4865         GfxAction[x][y] = ACTION_MOVING;
4866 #endif
4867     }
4868
4869     /* now make next step */
4870
4871     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
4872
4873     if (DONT_COLLIDE_WITH(element) &&
4874         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
4875         !PLAYER_ENEMY_PROTECTED(newx, newy))
4876     {
4877 #if 1
4878       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
4879
4880       return;
4881 #else
4882       /* player killed by element which is deadly when colliding with */
4883       MovDir[x][y] = 0;
4884       KillHero(PLAYERINFO(newx, newy));
4885       return;
4886 #endif
4887
4888     }
4889     else if ((element == EL_PENGUIN ||
4890               element == EL_ROBOT ||
4891               element == EL_SATELLITE ||
4892               element == EL_BALLOON ||
4893               IS_CUSTOM_ELEMENT(element)) &&
4894              IN_LEV_FIELD(newx, newy) &&
4895              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
4896     {
4897       SplashAcid(x, y);
4898       Store[x][y] = EL_ACID;
4899     }
4900     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
4901     {
4902       if (Feld[newx][newy] == EL_EXIT_OPEN)
4903       {
4904 #if 1
4905         RemoveField(x, y);
4906         DrawLevelField(x, y);
4907 #else
4908         Feld[x][y] = EL_EMPTY;
4909         DrawLevelField(x, y);
4910 #endif
4911
4912         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
4913         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
4914           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
4915
4916         local_player->friends_still_needed--;
4917         if (!local_player->friends_still_needed &&
4918             !local_player->GameOver && AllPlayersGone)
4919           local_player->LevelSolved = local_player->GameOver = TRUE;
4920
4921         return;
4922       }
4923       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
4924       {
4925         if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
4926           DrawLevelField(newx, newy);
4927         else
4928           GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
4929       }
4930       else if (!IS_FREE(newx, newy))
4931       {
4932         GfxAction[x][y] = ACTION_WAITING;
4933
4934         if (IS_PLAYER(x, y))
4935           DrawPlayerField(x, y);
4936         else
4937           DrawLevelField(x, y);
4938
4939         return;
4940       }
4941     }
4942     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
4943     {
4944       if (IS_FOOD_PIG(Feld[newx][newy]))
4945       {
4946         if (IS_MOVING(newx, newy))
4947           RemoveMovingField(newx, newy);
4948         else
4949         {
4950           Feld[newx][newy] = EL_EMPTY;
4951           DrawLevelField(newx, newy);
4952         }
4953
4954         PlayLevelSound(x, y, SND_PIG_DIGGING);
4955       }
4956       else if (!IS_FREE(newx, newy))
4957       {
4958         if (IS_PLAYER(x, y))
4959           DrawPlayerField(x, y);
4960         else
4961           DrawLevelField(x, y);
4962
4963         return;
4964       }
4965     }
4966
4967 #if 1
4968
4969     /*
4970     else if (move_pattern & MV_MAZE_RUNNER_STYLE && IN_LEV_FIELD(newx, newy))
4971     */
4972
4973     else if (IS_CUSTOM_ELEMENT(element) &&
4974              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy)
4975
4976 #if 0
4977  &&
4978              !IS_FREE(newx, newy)
4979 #endif
4980
4981 )
4982     {
4983       int new_element = Feld[newx][newy];
4984
4985 #if 0
4986       printf("::: '%s' digs '%s' [%d]\n",
4987              element_info[element].token_name,
4988              element_info[Feld[newx][newy]].token_name,
4989              StorePlayer[newx][newy]);
4990 #endif
4991
4992       if (!IS_FREE(newx, newy))
4993       {
4994         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
4995                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
4996                       ACTION_BREAKING);
4997
4998         /* no element can dig solid indestructible elements */
4999         if (IS_INDESTRUCTIBLE(new_element) &&
5000             !IS_DIGGABLE(new_element) &&
5001             !IS_COLLECTIBLE(new_element))
5002           return;
5003
5004         if (AmoebaNr[newx][newy] &&
5005             (new_element == EL_AMOEBA_FULL ||
5006              new_element == EL_BD_AMOEBA ||
5007              new_element == EL_AMOEBA_GROWING))
5008         {
5009           AmoebaCnt[AmoebaNr[newx][newy]]--;
5010           AmoebaCnt2[AmoebaNr[newx][newy]]--;
5011         }
5012
5013         if (IS_MOVING(newx, newy))
5014           RemoveMovingField(newx, newy);
5015         else
5016         {
5017           RemoveField(newx, newy);
5018           DrawLevelField(newx, newy);
5019         }
5020
5021         PlayLevelSoundAction(x, y, action);
5022       }
5023
5024       if (new_element == element_info[element].move_enter_element)
5025         element_info[element].can_leave_element = TRUE;
5026
5027       if (move_pattern & MV_MAZE_RUNNER_STYLE)
5028       {
5029         RunnerVisit[x][y] = FrameCounter;
5030         PlayerVisit[x][y] /= 8;         /* expire player visit path */
5031       }
5032     }
5033
5034 #endif
5035
5036     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
5037     {
5038       if (!IS_FREE(newx, newy))
5039       {
5040         if (IS_PLAYER(x, y))
5041           DrawPlayerField(x, y);
5042         else
5043           DrawLevelField(x, y);
5044
5045         return;
5046       }
5047       else
5048       {
5049         boolean wanna_flame = !RND(10);
5050         int dx = newx - x, dy = newy - y;
5051         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
5052         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
5053         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
5054                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
5055         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
5056                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
5057
5058         if ((wanna_flame ||
5059              IS_CLASSIC_ENEMY(element1) ||
5060              IS_CLASSIC_ENEMY(element2)) &&
5061             element1 != EL_DRAGON && element2 != EL_DRAGON &&
5062             element1 != EL_FLAMES && element2 != EL_FLAMES)
5063         {
5064 #if 1
5065           ResetGfxAnimation(x, y);
5066           GfxAction[x][y] = ACTION_ATTACKING;
5067 #endif
5068
5069           if (IS_PLAYER(x, y))
5070             DrawPlayerField(x, y);
5071           else
5072             DrawLevelField(x, y);
5073
5074           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
5075
5076           MovDelay[x][y] = 50;
5077
5078           Feld[newx][newy] = EL_FLAMES;
5079           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
5080             Feld[newx1][newy1] = EL_FLAMES;
5081           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
5082             Feld[newx2][newy2] = EL_FLAMES;
5083
5084           return;
5085         }
5086       }
5087     }
5088     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5089              Feld[newx][newy] == EL_DIAMOND)
5090     {
5091       if (IS_MOVING(newx, newy))
5092         RemoveMovingField(newx, newy);
5093       else
5094       {
5095         Feld[newx][newy] = EL_EMPTY;
5096         DrawLevelField(newx, newy);
5097       }
5098
5099       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
5100     }
5101     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
5102              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
5103     {
5104       if (AmoebaNr[newx][newy])
5105       {
5106         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5107         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5108             Feld[newx][newy] == EL_BD_AMOEBA)
5109           AmoebaCnt[AmoebaNr[newx][newy]]--;
5110       }
5111
5112       if (IS_MOVING(newx, newy))
5113         RemoveMovingField(newx, newy);
5114       else
5115       {
5116         Feld[newx][newy] = EL_EMPTY;
5117         DrawLevelField(newx, newy);
5118       }
5119
5120       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
5121     }
5122     else if ((element == EL_PACMAN || element == EL_MOLE)
5123              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
5124     {
5125       if (AmoebaNr[newx][newy])
5126       {
5127         AmoebaCnt2[AmoebaNr[newx][newy]]--;
5128         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
5129             Feld[newx][newy] == EL_BD_AMOEBA)
5130           AmoebaCnt[AmoebaNr[newx][newy]]--;
5131       }
5132
5133       if (element == EL_MOLE)
5134       {
5135         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
5136         PlayLevelSound(x, y, SND_MOLE_DIGGING);
5137
5138         ResetGfxAnimation(x, y);
5139         GfxAction[x][y] = ACTION_DIGGING;
5140         DrawLevelField(x, y);
5141
5142         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
5143
5144         return;                         /* wait for shrinking amoeba */
5145       }
5146       else      /* element == EL_PACMAN */
5147       {
5148         Feld[newx][newy] = EL_EMPTY;
5149         DrawLevelField(newx, newy);
5150         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
5151       }
5152     }
5153     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
5154              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
5155               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
5156     {
5157       /* wait for shrinking amoeba to completely disappear */
5158       return;
5159     }
5160     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
5161     {
5162       /* object was running against a wall */
5163
5164       TurnRound(x, y);
5165
5166 #if 1
5167       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
5168         DrawLevelElementAnimation(x, y, element);
5169 #else
5170       if (element == EL_BUG ||
5171           element == EL_SPACESHIP ||
5172           element == EL_SP_SNIKSNAK)
5173         DrawLevelField(x, y);
5174       else if (element == EL_MOLE)
5175         DrawLevelField(x, y);
5176       else if (element == EL_BD_BUTTERFLY ||
5177                element == EL_BD_FIREFLY)
5178         DrawLevelElementAnimationIfNeeded(x, y, element);
5179       else if (element == EL_SATELLITE)
5180         DrawLevelElementAnimationIfNeeded(x, y, element);
5181       else if (element == EL_SP_ELECTRON)
5182         DrawLevelElementAnimationIfNeeded(x, y, element);
5183 #endif
5184
5185       if (DONT_TOUCH(element))
5186         TestIfBadThingTouchesHero(x, y);
5187
5188 #if 0
5189       PlayLevelSoundAction(x, y, ACTION_WAITING);
5190 #endif
5191
5192       return;
5193     }
5194
5195     InitMovingField(x, y, MovDir[x][y]);
5196
5197     PlayLevelSoundAction(x, y, ACTION_MOVING);
5198   }
5199
5200   if (MovDir[x][y])
5201     ContinueMoving(x, y);
5202 }
5203
5204 void ContinueMoving(int x, int y)
5205 {
5206   int element = Feld[x][y];
5207   struct ElementInfo *ei = &element_info[element];
5208   int direction = MovDir[x][y];
5209   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5210   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5211   int newx = x + dx, newy = y + dy;
5212 #if 0
5213   int nextx = newx + dx, nexty = newy + dy;
5214 #endif
5215   boolean pushed = Pushed[x][y];
5216
5217   MovPos[x][y] += getElementMoveStepsize(x, y);
5218
5219   if (pushed)           /* special case: moving object pushed by player */
5220     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
5221
5222   if (ABS(MovPos[x][y]) < TILEX)
5223   {
5224     DrawLevelField(x, y);
5225
5226     return;     /* element is still moving */
5227   }
5228
5229   /* element reached destination field */
5230
5231   Feld[x][y] = EL_EMPTY;
5232   Feld[newx][newy] = element;
5233   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
5234
5235   if (element == EL_MOLE)
5236   {
5237     Feld[x][y] = EL_SAND;
5238
5239     DrawLevelFieldCrumbledSandNeighbours(x, y);
5240   }
5241   else if (element == EL_QUICKSAND_FILLING)
5242   {
5243     element = Feld[newx][newy] = get_next_element(element);
5244     Store[newx][newy] = Store[x][y];
5245   }
5246   else if (element == EL_QUICKSAND_EMPTYING)
5247   {
5248     Feld[x][y] = get_next_element(element);
5249     element = Feld[newx][newy] = Store[x][y];
5250   }
5251   else if (element == EL_MAGIC_WALL_FILLING)
5252   {
5253     element = Feld[newx][newy] = get_next_element(element);
5254     if (!game.magic_wall_active)
5255       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
5256     Store[newx][newy] = Store[x][y];
5257   }
5258   else if (element == EL_MAGIC_WALL_EMPTYING)
5259   {
5260     Feld[x][y] = get_next_element(element);
5261     if (!game.magic_wall_active)
5262       Feld[x][y] = EL_MAGIC_WALL_DEAD;
5263     element = Feld[newx][newy] = Store[x][y];
5264   }
5265   else if (element == EL_BD_MAGIC_WALL_FILLING)
5266   {
5267     element = Feld[newx][newy] = get_next_element(element);
5268     if (!game.magic_wall_active)
5269       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
5270     Store[newx][newy] = Store[x][y];
5271   }
5272   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
5273   {
5274     Feld[x][y] = get_next_element(element);
5275     if (!game.magic_wall_active)
5276       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
5277     element = Feld[newx][newy] = Store[x][y];
5278   }
5279   else if (element == EL_AMOEBA_DROPPING)
5280   {
5281     Feld[x][y] = get_next_element(element);
5282     element = Feld[newx][newy] = Store[x][y];
5283   }
5284   else if (element == EL_SOKOBAN_OBJECT)
5285   {
5286     if (Back[x][y])
5287       Feld[x][y] = Back[x][y];
5288
5289     if (Back[newx][newy])
5290       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
5291
5292     Back[x][y] = Back[newx][newy] = 0;
5293   }
5294   else if (Store[x][y] == EL_ACID)
5295   {
5296     element = Feld[newx][newy] = EL_ACID;
5297   }
5298
5299   Store[x][y] = 0;
5300   MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
5301   MovDelay[newx][newy] = 0;
5302
5303   /* copy element change control values to new field */
5304   ChangeDelay[newx][newy] = ChangeDelay[x][y];
5305   ChangePage[newx][newy] = ChangePage[x][y];
5306   Changed[newx][newy] = Changed[x][y];
5307   ChangeEvent[newx][newy] = ChangeEvent[x][y];
5308
5309   ChangeDelay[x][y] = 0;
5310   ChangePage[x][y] = -1;
5311   Changed[x][y] = CE_BITMASK_DEFAULT;
5312   ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
5313
5314   /* copy animation control values to new field */
5315   GfxFrame[newx][newy]  = GfxFrame[x][y];
5316   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
5317   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
5318   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
5319
5320   Pushed[x][y] = Pushed[newx][newy] = FALSE;
5321
5322   ResetGfxAnimation(x, y);      /* reset animation values for old field */
5323
5324 #if 1
5325   /* some elements can leave other elements behind after moving */
5326   if (IS_CUSTOM_ELEMENT(element) && !IS_PLAYER(x, y) &&
5327       ei->move_leave_element != EL_EMPTY &&
5328       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED ||
5329        ei->can_leave_element_last))
5330   {
5331     Feld[x][y] = ei->move_leave_element;
5332     InitField(x, y, FALSE);
5333
5334     if (GFX_CRUMBLED(Feld[x][y]))
5335       DrawLevelFieldCrumbledSandNeighbours(x, y);
5336   }
5337
5338   ei->can_leave_element_last = ei->can_leave_element;
5339   ei->can_leave_element = FALSE;
5340 #endif
5341
5342 #if 0
5343   /* 2.1.1 (does not work correctly for spring) */
5344   if (!CAN_MOVE(element))
5345     MovDir[newx][newy] = 0;
5346 #else
5347
5348 #if 0
5349   /* (does not work for falling objects that slide horizontally) */
5350   if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
5351     MovDir[newx][newy] = 0;
5352 #else
5353   /*
5354   if (!CAN_MOVE(element) ||
5355       (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
5356     MovDir[newx][newy] = 0;
5357   */
5358
5359   if (!CAN_MOVE(element) ||
5360       (CAN_FALL(element) && direction == MV_DOWN))
5361     GfxDir[x][y] = MovDir[newx][newy] = 0;
5362
5363 #endif
5364 #endif
5365
5366   DrawLevelField(x, y);
5367   DrawLevelField(newx, newy);
5368
5369   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
5370
5371   /* prevent pushed element from moving on in pushed direction */
5372   if (pushed && CAN_MOVE(element) &&
5373       element_info[element].move_pattern & MV_ANY_DIRECTION &&
5374       !(element_info[element].move_pattern & direction))
5375     TurnRound(newx, newy);
5376
5377   if (!pushed)  /* special case: moving object pushed by player */
5378   {
5379     WasJustMoving[newx][newy] = 3;
5380
5381     if (CAN_FALL(element) && direction == MV_DOWN)
5382       WasJustFalling[newx][newy] = 3;
5383   }
5384
5385   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
5386   {
5387     TestIfBadThingTouchesHero(newx, newy);
5388     TestIfBadThingTouchesFriend(newx, newy);
5389
5390     if (!IS_CUSTOM_ELEMENT(element))
5391       TestIfBadThingTouchesOtherBadThing(newx, newy);
5392   }
5393   else if (element == EL_PENGUIN)
5394     TestIfFriendTouchesBadThing(newx, newy);
5395
5396   if (CAN_FALL(element) && direction == MV_DOWN &&
5397       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
5398     Impact(x, newy);
5399
5400 #if 1
5401   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
5402 #endif
5403
5404 #if 0
5405   if (ChangePage[newx][newy] != -1)                     /* delayed change */
5406     ChangeElement(newx, newy, ChangePage[newx][newy]);
5407 #endif
5408
5409 #if 1
5410
5411   TestIfElementHitsCustomElement(newx, newy, direction);
5412
5413 #else
5414
5415   if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
5416   {
5417     int hitting_element = Feld[newx][newy];
5418
5419     /* !!! fix side (direction) orientation here and elsewhere !!! */
5420     CheckElementSideChange(newx, newy, hitting_element,
5421                            direction, CE_HITTING_SOMETHING, -1);
5422
5423 #if 0
5424     if (IN_LEV_FIELD(nextx, nexty))
5425     {
5426       int opposite_direction = MV_DIR_OPPOSITE(direction);
5427       int hitting_side = direction;
5428       int touched_side = opposite_direction;
5429       int touched_element = MovingOrBlocked2Element(nextx, nexty);
5430       boolean object_hit = (!IS_MOVING(nextx, nexty) ||
5431                             MovDir[nextx][nexty] != direction ||
5432                             ABS(MovPos[nextx][nexty]) <= TILEY / 2);
5433
5434       if (object_hit)
5435       {
5436         int i;
5437
5438         CheckElementSideChange(nextx, nexty, touched_element,
5439                                opposite_direction, CE_HIT_BY_SOMETHING, -1);
5440
5441         if (IS_CUSTOM_ELEMENT(hitting_element) &&
5442             HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
5443         {
5444           for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
5445           {
5446             struct ElementChangeInfo *change =
5447               &element_info[hitting_element].change_page[i];
5448
5449             if (change->can_change &&
5450                 change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
5451                 change->sides & touched_side &&
5452                 change->trigger_element == touched_element)
5453             {
5454               CheckElementSideChange(newx, newy, hitting_element,
5455                                      CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
5456               break;
5457             }
5458           }
5459         }
5460
5461         if (IS_CUSTOM_ELEMENT(touched_element) &&
5462             HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
5463         {
5464           for (i = 0; i < element_info[touched_element].num_change_pages; i++)
5465           {
5466             struct ElementChangeInfo *change =
5467               &element_info[touched_element].change_page[i];
5468
5469             if (change->can_change &&
5470                 change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
5471                 change->sides & hitting_side &&
5472                 change->trigger_element == hitting_element)
5473             {
5474               CheckElementSideChange(nextx, nexty, touched_element,
5475                                      CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
5476               break;
5477             }
5478           }
5479         }
5480       }
5481     }
5482 #endif
5483   }
5484 #endif
5485
5486   TestIfPlayerTouchesCustomElement(newx, newy);
5487   TestIfElementTouchesCustomElement(newx, newy);
5488 }
5489
5490 int AmoebeNachbarNr(int ax, int ay)
5491 {
5492   int i;
5493   int element = Feld[ax][ay];
5494   int group_nr = 0;
5495   static int xy[4][2] =
5496   {
5497     { 0, -1 },
5498     { -1, 0 },
5499     { +1, 0 },
5500     { 0, +1 }
5501   };
5502
5503   for (i = 0; i < 4; i++)
5504   {
5505     int x = ax + xy[i][0];
5506     int y = ay + xy[i][1];
5507
5508     if (!IN_LEV_FIELD(x, y))
5509       continue;
5510
5511     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
5512       group_nr = AmoebaNr[x][y];
5513   }
5514
5515   return group_nr;
5516 }
5517
5518 void AmoebenVereinigen(int ax, int ay)
5519 {
5520   int i, x, y, xx, yy;
5521   int new_group_nr = AmoebaNr[ax][ay];
5522   static int xy[4][2] =
5523   {
5524     { 0, -1 },
5525     { -1, 0 },
5526     { +1, 0 },
5527     { 0, +1 }
5528   };
5529
5530   if (new_group_nr == 0)
5531     return;
5532
5533   for (i = 0; i < 4; i++)
5534   {
5535     x = ax + xy[i][0];
5536     y = ay + xy[i][1];
5537
5538     if (!IN_LEV_FIELD(x, y))
5539       continue;
5540
5541     if ((Feld[x][y] == EL_AMOEBA_FULL ||
5542          Feld[x][y] == EL_BD_AMOEBA ||
5543          Feld[x][y] == EL_AMOEBA_DEAD) &&
5544         AmoebaNr[x][y] != new_group_nr)
5545     {
5546       int old_group_nr = AmoebaNr[x][y];
5547
5548       if (old_group_nr == 0)
5549         return;
5550
5551       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
5552       AmoebaCnt[old_group_nr] = 0;
5553       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
5554       AmoebaCnt2[old_group_nr] = 0;
5555
5556       for (yy = 0; yy < lev_fieldy; yy++)
5557       {
5558         for (xx = 0; xx < lev_fieldx; xx++)
5559         {
5560           if (AmoebaNr[xx][yy] == old_group_nr)
5561             AmoebaNr[xx][yy] = new_group_nr;
5562         }
5563       }
5564     }
5565   }
5566 }
5567
5568 void AmoebeUmwandeln(int ax, int ay)
5569 {
5570   int i, x, y;
5571
5572   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
5573   {
5574     int group_nr = AmoebaNr[ax][ay];
5575
5576 #ifdef DEBUG
5577     if (group_nr == 0)
5578     {
5579       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
5580       printf("AmoebeUmwandeln(): This should never happen!\n");
5581       return;
5582     }
5583 #endif
5584
5585     for (y = 0; y < lev_fieldy; y++)
5586     {
5587       for (x = 0; x < lev_fieldx; x++)
5588       {
5589         if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
5590         {
5591           AmoebaNr[x][y] = 0;
5592           Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
5593         }
5594       }
5595     }
5596     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
5597                             SND_AMOEBA_TURNING_TO_GEM :
5598                             SND_AMOEBA_TURNING_TO_ROCK));
5599     Bang(ax, ay);
5600   }
5601   else
5602   {
5603     static int xy[4][2] =
5604     {
5605       { 0, -1 },
5606       { -1, 0 },
5607       { +1, 0 },
5608       { 0, +1 }
5609     };
5610
5611     for (i = 0; i < 4; i++)
5612     {
5613       x = ax + xy[i][0];
5614       y = ay + xy[i][1];
5615
5616       if (!IN_LEV_FIELD(x, y))
5617         continue;
5618
5619       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
5620       {
5621         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
5622                               SND_AMOEBA_TURNING_TO_GEM :
5623                               SND_AMOEBA_TURNING_TO_ROCK));
5624         Bang(x, y);
5625       }
5626     }
5627   }
5628 }
5629
5630 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
5631 {
5632   int x, y;
5633   int group_nr = AmoebaNr[ax][ay];
5634   boolean done = FALSE;
5635
5636 #ifdef DEBUG
5637   if (group_nr == 0)
5638   {
5639     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
5640     printf("AmoebeUmwandelnBD(): This should never happen!\n");
5641     return;
5642   }
5643 #endif
5644
5645   for (y = 0; y < lev_fieldy; y++)
5646   {
5647     for (x = 0; x < lev_fieldx; x++)
5648     {
5649       if (AmoebaNr[x][y] == group_nr &&
5650           (Feld[x][y] == EL_AMOEBA_DEAD ||
5651            Feld[x][y] == EL_BD_AMOEBA ||
5652            Feld[x][y] == EL_AMOEBA_GROWING))
5653       {
5654         AmoebaNr[x][y] = 0;
5655         Feld[x][y] = new_element;
5656         InitField(x, y, FALSE);
5657         DrawLevelField(x, y);
5658         done = TRUE;
5659       }
5660     }
5661   }
5662
5663   if (done)
5664     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
5665                             SND_BD_AMOEBA_TURNING_TO_ROCK :
5666                             SND_BD_AMOEBA_TURNING_TO_GEM));
5667 }
5668
5669 void AmoebeWaechst(int x, int y)
5670 {
5671   static unsigned long sound_delay = 0;
5672   static unsigned long sound_delay_value = 0;
5673
5674   if (!MovDelay[x][y])          /* start new growing cycle */
5675   {
5676     MovDelay[x][y] = 7;
5677
5678     if (DelayReached(&sound_delay, sound_delay_value))
5679     {
5680 #if 1
5681       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
5682 #else
5683       if (Store[x][y] == EL_BD_AMOEBA)
5684         PlayLevelSound(x, y, SND_BD_AMOEBA_GROWING);
5685       else
5686         PlayLevelSound(x, y, SND_AMOEBA_GROWING);
5687 #endif
5688       sound_delay_value = 30;
5689     }
5690   }
5691
5692   if (MovDelay[x][y])           /* wait some time before growing bigger */
5693   {
5694     MovDelay[x][y]--;
5695     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5696     {
5697       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
5698                                            6 - MovDelay[x][y]);
5699
5700       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
5701     }
5702
5703     if (!MovDelay[x][y])
5704     {
5705       Feld[x][y] = Store[x][y];
5706       Store[x][y] = 0;
5707       DrawLevelField(x, y);
5708     }
5709   }
5710 }
5711
5712 void AmoebaDisappearing(int x, int y)
5713 {
5714   static unsigned long sound_delay = 0;
5715   static unsigned long sound_delay_value = 0;
5716
5717   if (!MovDelay[x][y])          /* start new shrinking cycle */
5718   {
5719     MovDelay[x][y] = 7;
5720
5721     if (DelayReached(&sound_delay, sound_delay_value))
5722       sound_delay_value = 30;
5723   }
5724
5725   if (MovDelay[x][y])           /* wait some time before shrinking */
5726   {
5727     MovDelay[x][y]--;
5728     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5729     {
5730       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
5731                                            6 - MovDelay[x][y]);
5732
5733       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
5734     }
5735
5736     if (!MovDelay[x][y])
5737     {
5738       Feld[x][y] = EL_EMPTY;
5739       DrawLevelField(x, y);
5740
5741       /* don't let mole enter this field in this cycle;
5742          (give priority to objects falling to this field from above) */
5743       Stop[x][y] = TRUE;
5744     }
5745   }
5746 }
5747
5748 void AmoebeAbleger(int ax, int ay)
5749 {
5750   int i;
5751   int element = Feld[ax][ay];
5752   int graphic = el2img(element);
5753   int newax = ax, neway = ay;
5754   static int xy[4][2] =
5755   {
5756     { 0, -1 },
5757     { -1, 0 },
5758     { +1, 0 },
5759     { 0, +1 }
5760   };
5761
5762   if (!level.amoeba_speed)
5763   {
5764     Feld[ax][ay] = EL_AMOEBA_DEAD;
5765     DrawLevelField(ax, ay);
5766     return;
5767   }
5768
5769   if (IS_ANIMATED(graphic))
5770     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5771
5772   if (!MovDelay[ax][ay])        /* start making new amoeba field */
5773     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
5774
5775   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
5776   {
5777     MovDelay[ax][ay]--;
5778     if (MovDelay[ax][ay])
5779       return;
5780   }
5781
5782   if (element == EL_AMOEBA_WET) /* object is an acid / amoeba drop */
5783   {
5784     int start = RND(4);
5785     int x = ax + xy[start][0];
5786     int y = ay + xy[start][1];
5787
5788     if (!IN_LEV_FIELD(x, y))
5789       return;
5790
5791     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5792     if (IS_FREE(x, y) ||
5793         Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5794     {
5795       newax = x;
5796       neway = y;
5797     }
5798
5799     if (newax == ax && neway == ay)
5800       return;
5801   }
5802   else                          /* normal or "filled" (BD style) amoeba */
5803   {
5804     int start = RND(4);
5805     boolean waiting_for_player = FALSE;
5806
5807     for (i = 0; i < 4; i++)
5808     {
5809       int j = (start + i) % 4;
5810       int x = ax + xy[j][0];
5811       int y = ay + xy[j][1];
5812
5813       if (!IN_LEV_FIELD(x, y))
5814         continue;
5815
5816       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5817       if (IS_FREE(x, y) ||
5818           Feld[x][y] == EL_SAND || Feld[x][y] == EL_QUICKSAND_EMPTY)
5819       {
5820         newax = x;
5821         neway = y;
5822         break;
5823       }
5824       else if (IS_PLAYER(x, y))
5825         waiting_for_player = TRUE;
5826     }
5827
5828     if (newax == ax && neway == ay)             /* amoeba cannot grow */
5829     {
5830       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
5831       {
5832         Feld[ax][ay] = EL_AMOEBA_DEAD;
5833         DrawLevelField(ax, ay);
5834         AmoebaCnt[AmoebaNr[ax][ay]]--;
5835
5836         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
5837         {
5838           if (element == EL_AMOEBA_FULL)
5839             AmoebeUmwandeln(ax, ay);
5840           else if (element == EL_BD_AMOEBA)
5841             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
5842         }
5843       }
5844       return;
5845     }
5846     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
5847     {
5848       /* amoeba gets larger by growing in some direction */
5849
5850       int new_group_nr = AmoebaNr[ax][ay];
5851
5852 #ifdef DEBUG
5853   if (new_group_nr == 0)
5854   {
5855     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
5856     printf("AmoebeAbleger(): This should never happen!\n");
5857     return;
5858   }
5859 #endif
5860
5861       AmoebaNr[newax][neway] = new_group_nr;
5862       AmoebaCnt[new_group_nr]++;
5863       AmoebaCnt2[new_group_nr]++;
5864
5865       /* if amoeba touches other amoeba(s) after growing, unify them */
5866       AmoebenVereinigen(newax, neway);
5867
5868       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
5869       {
5870         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
5871         return;
5872       }
5873     }
5874   }
5875
5876   if (element != EL_AMOEBA_WET || neway < ay || !IS_FREE(newax, neway) ||
5877       (neway == lev_fieldy - 1 && newax != ax))
5878   {
5879     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
5880     Store[newax][neway] = element;
5881   }
5882   else if (neway == ay)
5883   {
5884     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
5885 #if 1
5886     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
5887 #else
5888     PlayLevelSound(newax, neway, SND_AMOEBA_GROWING);
5889 #endif
5890   }
5891   else
5892   {
5893     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
5894     Feld[ax][ay] = EL_AMOEBA_DROPPING;
5895     Store[ax][ay] = EL_AMOEBA_DROP;
5896     ContinueMoving(ax, ay);
5897     return;
5898   }
5899
5900   DrawLevelField(newax, neway);
5901 }
5902
5903 void Life(int ax, int ay)
5904 {
5905   int x1, y1, x2, y2;
5906   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
5907   int life_time = 40;
5908   int element = Feld[ax][ay];
5909   int graphic = el2img(element);
5910   boolean changed = FALSE;
5911
5912   if (IS_ANIMATED(graphic))
5913     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
5914
5915   if (Stop[ax][ay])
5916     return;
5917
5918   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
5919     MovDelay[ax][ay] = life_time;
5920
5921   if (MovDelay[ax][ay])         /* wait some time before next cycle */
5922   {
5923     MovDelay[ax][ay]--;
5924     if (MovDelay[ax][ay])
5925       return;
5926   }
5927
5928   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
5929   {
5930     int xx = ax+x1, yy = ay+y1;
5931     int nachbarn = 0;
5932
5933     if (!IN_LEV_FIELD(xx, yy))
5934       continue;
5935
5936     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
5937     {
5938       int x = xx+x2, y = yy+y2;
5939
5940       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
5941         continue;
5942
5943       if (((Feld[x][y] == element ||
5944             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
5945            !Stop[x][y]) ||
5946           (IS_FREE(x, y) && Stop[x][y]))
5947         nachbarn++;
5948     }
5949
5950     if (xx == ax && yy == ay)           /* field in the middle */
5951     {
5952       if (nachbarn < life[0] || nachbarn > life[1])
5953       {
5954         Feld[xx][yy] = EL_EMPTY;
5955         if (!Stop[xx][yy])
5956           DrawLevelField(xx, yy);
5957         Stop[xx][yy] = TRUE;
5958         changed = TRUE;
5959       }
5960     }
5961     /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
5962     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_SAND)
5963     {                                   /* free border field */
5964       if (nachbarn >= life[2] && nachbarn <= life[3])
5965       {
5966         Feld[xx][yy] = element;
5967         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
5968         if (!Stop[xx][yy])
5969           DrawLevelField(xx, yy);
5970         Stop[xx][yy] = TRUE;
5971         changed = TRUE;
5972       }
5973     }
5974   }
5975
5976   if (changed)
5977     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
5978                    SND_GAME_OF_LIFE_GROWING);
5979 }
5980
5981 static void InitRobotWheel(int x, int y)
5982 {
5983   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
5984 }
5985
5986 static void RunRobotWheel(int x, int y)
5987 {
5988   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
5989 }
5990
5991 static void StopRobotWheel(int x, int y)
5992 {
5993   if (ZX == x && ZY == y)
5994     ZX = ZY = -1;
5995 }
5996
5997 static void InitTimegateWheel(int x, int y)
5998 {
5999   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
6000 }
6001
6002 static void RunTimegateWheel(int x, int y)
6003 {
6004   PlayLevelSound(x, y, SND_TIMEGATE_SWITCH_ACTIVE);
6005 }
6006
6007 void CheckExit(int x, int y)
6008 {
6009   if (local_player->gems_still_needed > 0 ||
6010       local_player->sokobanfields_still_needed > 0 ||
6011       local_player->lights_still_needed > 0)
6012   {
6013     int element = Feld[x][y];
6014     int graphic = el2img(element);
6015
6016     if (IS_ANIMATED(graphic))
6017       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6018
6019     return;
6020   }
6021
6022   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6023     return;
6024
6025   Feld[x][y] = EL_EXIT_OPENING;
6026
6027   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
6028 }
6029
6030 void CheckExitSP(int x, int y)
6031 {
6032   if (local_player->gems_still_needed > 0)
6033   {
6034     int element = Feld[x][y];
6035     int graphic = el2img(element);
6036
6037     if (IS_ANIMATED(graphic))
6038       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6039
6040     return;
6041   }
6042
6043   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
6044     return;
6045
6046   Feld[x][y] = EL_SP_EXIT_OPENING;
6047
6048   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
6049 }
6050
6051 static void CloseAllOpenTimegates()
6052 {
6053   int x, y;
6054
6055   for (y = 0; y < lev_fieldy; y++)
6056   {
6057     for (x = 0; x < lev_fieldx; x++)
6058     {
6059       int element = Feld[x][y];
6060
6061       if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
6062       {
6063         Feld[x][y] = EL_TIMEGATE_CLOSING;
6064 #if 1
6065         PlayLevelSoundAction(x, y, ACTION_CLOSING);
6066 #else
6067         PlayLevelSound(x, y, SND_TIMEGATE_CLOSING);
6068 #endif
6069       }
6070     }
6071   }
6072 }
6073
6074 void EdelsteinFunkeln(int x, int y)
6075 {
6076   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
6077     return;
6078
6079   if (Feld[x][y] == EL_BD_DIAMOND)
6080     return;
6081
6082   if (MovDelay[x][y] == 0)      /* next animation frame */
6083     MovDelay[x][y] = 11 * !SimpleRND(500);
6084
6085   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
6086   {
6087     MovDelay[x][y]--;
6088
6089     if (setup.direct_draw && MovDelay[x][y])
6090       SetDrawtoField(DRAW_BUFFERED);
6091
6092     DrawLevelElementAnimation(x, y, Feld[x][y]);
6093
6094     if (MovDelay[x][y] != 0)
6095     {
6096       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
6097                                            10 - MovDelay[x][y]);
6098
6099       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
6100
6101       if (setup.direct_draw)
6102       {
6103         int dest_x, dest_y;
6104
6105         dest_x = FX + SCREENX(x) * TILEX;
6106         dest_y = FY + SCREENY(y) * TILEY;
6107
6108         BlitBitmap(drawto_field, window,
6109                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
6110         SetDrawtoField(DRAW_DIRECT);
6111       }
6112     }
6113   }
6114 }
6115
6116 void MauerWaechst(int x, int y)
6117 {
6118   int delay = 6;
6119
6120   if (!MovDelay[x][y])          /* next animation frame */
6121     MovDelay[x][y] = 3 * delay;
6122
6123   if (MovDelay[x][y])           /* wait some time before next frame */
6124   {
6125     MovDelay[x][y]--;
6126
6127     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6128     {
6129       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
6130       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
6131
6132       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6133     }
6134
6135     if (!MovDelay[x][y])
6136     {
6137       if (MovDir[x][y] == MV_LEFT)
6138       {
6139         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
6140           DrawLevelField(x - 1, y);
6141       }
6142       else if (MovDir[x][y] == MV_RIGHT)
6143       {
6144         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
6145           DrawLevelField(x + 1, y);
6146       }
6147       else if (MovDir[x][y] == MV_UP)
6148       {
6149         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
6150           DrawLevelField(x, y - 1);
6151       }
6152       else
6153       {
6154         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
6155           DrawLevelField(x, y + 1);
6156       }
6157
6158       Feld[x][y] = Store[x][y];
6159       Store[x][y] = 0;
6160       GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
6161       DrawLevelField(x, y);
6162     }
6163   }
6164 }
6165
6166 void MauerAbleger(int ax, int ay)
6167 {
6168   int element = Feld[ax][ay];
6169   int graphic = el2img(element);
6170   boolean oben_frei = FALSE, unten_frei = FALSE;
6171   boolean links_frei = FALSE, rechts_frei = FALSE;
6172   boolean oben_massiv = FALSE, unten_massiv = FALSE;
6173   boolean links_massiv = FALSE, rechts_massiv = FALSE;
6174   boolean new_wall = FALSE;
6175
6176   if (IS_ANIMATED(graphic))
6177     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
6178
6179   if (!MovDelay[ax][ay])        /* start building new wall */
6180     MovDelay[ax][ay] = 6;
6181
6182   if (MovDelay[ax][ay])         /* wait some time before building new wall */
6183   {
6184     MovDelay[ax][ay]--;
6185     if (MovDelay[ax][ay])
6186       return;
6187   }
6188
6189   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
6190     oben_frei = TRUE;
6191   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
6192     unten_frei = TRUE;
6193   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
6194     links_frei = TRUE;
6195   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
6196     rechts_frei = TRUE;
6197
6198   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
6199       element == EL_EXPANDABLE_WALL_ANY)
6200   {
6201     if (oben_frei)
6202     {
6203       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
6204       Store[ax][ay-1] = element;
6205       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
6206       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
6207         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
6208                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
6209       new_wall = TRUE;
6210     }
6211     if (unten_frei)
6212     {
6213       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
6214       Store[ax][ay+1] = element;
6215       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
6216       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
6217         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
6218                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
6219       new_wall = TRUE;
6220     }
6221   }
6222
6223   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6224       element == EL_EXPANDABLE_WALL_ANY ||
6225       element == EL_EXPANDABLE_WALL)
6226   {
6227     if (links_frei)
6228     {
6229       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
6230       Store[ax-1][ay] = element;
6231       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
6232       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
6233         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
6234                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
6235       new_wall = TRUE;
6236     }
6237
6238     if (rechts_frei)
6239     {
6240       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
6241       Store[ax+1][ay] = element;
6242       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
6243       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
6244         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
6245                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
6246       new_wall = TRUE;
6247     }
6248   }
6249
6250   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
6251     DrawLevelField(ax, ay);
6252
6253   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
6254     oben_massiv = TRUE;
6255   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
6256     unten_massiv = TRUE;
6257   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
6258     links_massiv = TRUE;
6259   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
6260     rechts_massiv = TRUE;
6261
6262   if (((oben_massiv && unten_massiv) ||
6263        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
6264        element == EL_EXPANDABLE_WALL) &&
6265       ((links_massiv && rechts_massiv) ||
6266        element == EL_EXPANDABLE_WALL_VERTICAL))
6267     Feld[ax][ay] = EL_WALL;
6268
6269   if (new_wall)
6270 #if 1
6271     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
6272 #else
6273     PlayLevelSound(ax, ay, SND_EXPANDABLE_WALL_GROWING);
6274 #endif
6275 }
6276
6277 void CheckForDragon(int x, int y)
6278 {
6279   int i, j;
6280   boolean dragon_found = FALSE;
6281   static int xy[4][2] =
6282   {
6283     { 0, -1 },
6284     { -1, 0 },
6285     { +1, 0 },
6286     { 0, +1 }
6287   };
6288
6289   for (i = 0; i < 4; i++)
6290   {
6291     for (j = 0; j < 4; j++)
6292     {
6293       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6294
6295       if (IN_LEV_FIELD(xx, yy) &&
6296           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
6297       {
6298         if (Feld[xx][yy] == EL_DRAGON)
6299           dragon_found = TRUE;
6300       }
6301       else
6302         break;
6303     }
6304   }
6305
6306   if (!dragon_found)
6307   {
6308     for (i = 0; i < 4; i++)
6309     {
6310       for (j = 0; j < 3; j++)
6311       {
6312         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
6313   
6314         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
6315         {
6316           Feld[xx][yy] = EL_EMPTY;
6317           DrawLevelField(xx, yy);
6318         }
6319         else
6320           break;
6321       }
6322     }
6323   }
6324 }
6325
6326 static void InitBuggyBase(int x, int y)
6327 {
6328   int element = Feld[x][y];
6329   int activating_delay = FRAMES_PER_SECOND / 4;
6330
6331   ChangeDelay[x][y] =
6332     (element == EL_SP_BUGGY_BASE ?
6333      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
6334      element == EL_SP_BUGGY_BASE_ACTIVATING ?
6335      activating_delay :
6336      element == EL_SP_BUGGY_BASE_ACTIVE ?
6337      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
6338 }
6339
6340 static void WarnBuggyBase(int x, int y)
6341 {
6342   int i;
6343   static int xy[4][2] =
6344   {
6345     { 0, -1 },
6346     { -1, 0 },
6347     { +1, 0 },
6348     { 0, +1 }
6349   };
6350
6351   for (i = 0; i < 4; i++)
6352   {
6353     int xx = x + xy[i][0], yy = y + xy[i][1];
6354
6355     if (IS_PLAYER(xx, yy))
6356     {
6357       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
6358
6359       break;
6360     }
6361   }
6362 }
6363
6364 static void InitTrap(int x, int y)
6365 {
6366   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
6367 }
6368
6369 static void ActivateTrap(int x, int y)
6370 {
6371   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
6372 }
6373
6374 static void ChangeActiveTrap(int x, int y)
6375 {
6376   int graphic = IMG_TRAP_ACTIVE;
6377
6378   /* if new animation frame was drawn, correct crumbled sand border */
6379   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
6380     DrawLevelFieldCrumbledSand(x, y);
6381 }
6382
6383 static void ChangeElementNowExt(int x, int y, int target_element)
6384 {
6385   int previous_move_direction = MovDir[x][y];
6386
6387   /* check if element under player changes from accessible to unaccessible
6388      (needed for special case of dropping element which then changes) */
6389   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
6390       IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(target_element))
6391   {
6392     Bang(x, y);
6393     return;
6394   }
6395
6396   RemoveField(x, y);
6397   Feld[x][y] = target_element;
6398
6399   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6400
6401   ResetGfxAnimation(x, y);
6402   ResetRandomAnimationValue(x, y);
6403
6404   if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
6405     MovDir[x][y] = previous_move_direction;
6406
6407   InitField(x, y, FALSE);
6408   if (CAN_MOVE(Feld[x][y]))
6409     InitMovDir(x, y);
6410
6411   DrawLevelField(x, y);
6412
6413   if (GFX_CRUMBLED(Feld[x][y]))
6414     DrawLevelFieldCrumbledSandNeighbours(x, y);
6415
6416   TestIfBadThingTouchesHero(x, y);
6417   TestIfPlayerTouchesCustomElement(x, y);
6418   TestIfElementTouchesCustomElement(x, y);
6419
6420   if (ELEM_IS_PLAYER(target_element))
6421     RelocatePlayer(x, y, target_element);
6422 }
6423
6424 static boolean ChangeElementNow(int x, int y, int element, int page)
6425 {
6426   struct ElementChangeInfo *change = &element_info[element].change_page[page];
6427
6428   /* always use default change event to prevent running into a loop */
6429   if (ChangeEvent[x][y] == CE_BITMASK_DEFAULT)
6430     ChangeEvent[x][y] = CH_EVENT_BIT(CE_DELAY);
6431
6432   /* do not change already changed elements with same change event */
6433 #if 0
6434   if (Changed[x][y] & ChangeEvent[x][y])
6435     return FALSE;
6436 #else
6437   if (Changed[x][y])
6438     return FALSE;
6439 #endif
6440
6441   Changed[x][y] |= ChangeEvent[x][y];   /* ignore same changes in this frame */
6442
6443   CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
6444
6445   if (change->explode)
6446   {
6447     Bang(x, y);
6448
6449     return TRUE;
6450   }
6451
6452   if (change->use_content)
6453   {
6454     boolean complete_change = TRUE;
6455     boolean can_change[3][3];
6456     int xx, yy;
6457
6458     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6459     {
6460       boolean half_destructible;
6461       int ex = x + xx - 1;
6462       int ey = y + yy - 1;
6463       int e;
6464
6465       can_change[xx][yy] = TRUE;
6466
6467       if (ex == x && ey == y)   /* do not check changing element itself */
6468         continue;
6469
6470       if (change->content[xx][yy] == EL_EMPTY_SPACE)
6471       {
6472         can_change[xx][yy] = FALSE;     /* do not change empty borders */
6473
6474         continue;
6475       }
6476
6477       if (!IN_LEV_FIELD(ex, ey))
6478       {
6479         can_change[xx][yy] = FALSE;
6480         complete_change = FALSE;
6481
6482         continue;
6483       }
6484
6485       e = Feld[ex][ey];
6486
6487       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6488         e = MovingOrBlocked2Element(ex, ey);
6489
6490       half_destructible = (IS_FREE(ex, ey) || IS_DIGGABLE(e));
6491
6492       if ((change->power <= CP_NON_DESTRUCTIVE  && !IS_FREE(ex, ey)) ||
6493           (change->power <= CP_HALF_DESTRUCTIVE && !half_destructible) ||
6494           (change->power <= CP_FULL_DESTRUCTIVE && IS_INDESTRUCTIBLE(e)))
6495       {
6496         can_change[xx][yy] = FALSE;
6497         complete_change = FALSE;
6498       }
6499     }
6500
6501     if (!change->only_complete || complete_change)
6502     {
6503       boolean something_has_changed = FALSE;
6504
6505       if (change->only_complete && change->use_random_change &&
6506           RND(100) < change->random)
6507         return FALSE;
6508
6509       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
6510       {
6511         int ex = x + xx - 1;
6512         int ey = y + yy - 1;
6513
6514         if (can_change[xx][yy] && (!change->use_random_change ||
6515                                    RND(100) < change->random))
6516         {
6517           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6518             RemoveMovingField(ex, ey);
6519
6520           ChangeEvent[ex][ey] = ChangeEvent[x][y];
6521
6522           ChangeElementNowExt(ex, ey, change->content[xx][yy]);
6523
6524           something_has_changed = TRUE;
6525
6526           /* for symmetry reasons, freeze newly created border elements */
6527           if (ex != x || ey != y)
6528             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
6529         }
6530       }
6531
6532       if (something_has_changed)
6533         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6534     }
6535   }
6536   else
6537   {
6538     ChangeElementNowExt(x, y, change->target_element);
6539
6540     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
6541   }
6542
6543   return TRUE;
6544 }
6545
6546 static void ChangeElement(int x, int y, int page)
6547 {
6548   int element = MovingOrBlocked2Element(x, y);
6549   struct ElementInfo *ei = &element_info[element];
6550   struct ElementChangeInfo *change = &ei->change_page[page];
6551
6552 #if 0
6553 #ifdef DEBUG
6554   if (!CAN_CHANGE(element))
6555   {
6556     printf("\n\n");
6557     printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
6558            x, y, element, element_info[element].token_name);
6559     printf("ChangeElement(): This should never happen!\n");
6560     printf("\n\n");
6561   }
6562 #endif
6563 #endif
6564
6565   if (ChangeDelay[x][y] == 0)           /* initialize element change */
6566   {
6567     ChangeDelay[x][y] = (    change->delay_fixed  * change->delay_frames +
6568                          RND(change->delay_random * change->delay_frames)) + 1;
6569
6570     ResetGfxAnimation(x, y);
6571     ResetRandomAnimationValue(x, y);
6572
6573     if (change->pre_change_function)
6574       change->pre_change_function(x, y);
6575   }
6576
6577   ChangeDelay[x][y]--;
6578
6579   if (ChangeDelay[x][y] != 0)           /* continue element change */
6580   {
6581     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
6582
6583     if (IS_ANIMATED(graphic))
6584       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
6585
6586     if (change->change_function)
6587       change->change_function(x, y);
6588   }
6589   else                                  /* finish element change */
6590   {
6591     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
6592     {
6593       page = ChangePage[x][y];
6594       ChangePage[x][y] = -1;
6595     }
6596
6597 #if 0
6598     if (IS_MOVING(x, y) && !change->explode)
6599 #else
6600     if (IS_MOVING(x, y))                /* never change a running system ;-) */
6601 #endif
6602     {
6603       ChangeDelay[x][y] = 1;            /* try change after next move step */
6604       ChangePage[x][y] = page;          /* remember page to use for change */
6605
6606       return;
6607     }
6608
6609     if (ChangeElementNow(x, y, element, page))
6610     {
6611       if (change->post_change_function)
6612         change->post_change_function(x, y);
6613     }
6614   }
6615 }
6616
6617 static boolean CheckTriggeredElementSideChange(int lx, int ly,
6618                                                int trigger_element,
6619                                                int trigger_side,
6620                                                int trigger_event)
6621 {
6622   int i, j, x, y;
6623
6624   if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
6625     return FALSE;
6626
6627   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6628   {
6629     int element = EL_CUSTOM_START + i;
6630
6631     boolean change_element = FALSE;
6632     int page = 0;
6633
6634     if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6635       continue;
6636
6637     for (j = 0; j < element_info[element].num_change_pages; j++)
6638     {
6639       struct ElementChangeInfo *change = &element_info[element].change_page[j];
6640
6641       if (change->can_change &&
6642 #if 1
6643           change->events & CH_EVENT_BIT(trigger_event) &&
6644 #endif
6645           change->sides & trigger_side &&
6646 #if 1
6647           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
6648 #else
6649           change->trigger_element == trigger_element
6650 #endif
6651           )
6652       {
6653 #if 0
6654         if (!(change->events & CH_EVENT_BIT(trigger_event)))
6655           printf("::: !!! %d triggers %d: using wrong page %d [event %d]\n",
6656                  trigger_element-EL_CUSTOM_START+1, i+1, j, trigger_event);
6657 #endif
6658
6659         change_element = TRUE;
6660         page = j;
6661
6662         break;
6663       }
6664     }
6665
6666     if (!change_element)
6667       continue;
6668
6669     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
6670     {
6671 #if 0
6672       if (x == lx && y == ly)   /* do not change trigger element itself */
6673         continue;
6674 #endif
6675
6676       if (Feld[x][y] == element)
6677       {
6678         ChangeDelay[x][y] = 1;
6679         ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6680         ChangeElement(x, y, page);
6681       }
6682     }
6683   }
6684
6685   return TRUE;
6686 }
6687
6688 static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
6689                                            int trigger_event)
6690 {
6691   return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
6692                                          trigger_event);
6693 }
6694
6695 static boolean CheckElementSideChange(int x, int y, int element, int side,
6696                                       int trigger_event, int page)
6697 {
6698   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
6699     return FALSE;
6700
6701   if (Feld[x][y] == EL_BLOCKED)
6702   {
6703     Blocked2Moving(x, y, &x, &y);
6704     element = Feld[x][y];
6705   }
6706
6707   if (page < 0)
6708     page = element_info[element].event_page_nr[trigger_event];
6709
6710   if (!(element_info[element].change_page[page].sides & side))
6711     return FALSE;
6712
6713   ChangeDelay[x][y] = 1;
6714   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
6715   ChangeElement(x, y, page);
6716
6717   return TRUE;
6718 }
6719
6720 static boolean CheckElementChange(int x, int y, int element, int trigger_event)
6721 {
6722   return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
6723 }
6724
6725 static void PlayPlayerSound(struct PlayerInfo *player)
6726 {
6727   int jx = player->jx, jy = player->jy;
6728   int element = player->element_nr;
6729   int last_action = player->last_action_waiting;
6730   int action = player->action_waiting;
6731
6732   if (player->is_waiting)
6733   {
6734     if (action != last_action)
6735       PlayLevelSoundElementAction(jx, jy, element, action);
6736     else
6737       PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
6738   }
6739   else
6740   {
6741     if (action != last_action)
6742       StopSound(element_info[element].sound[last_action]);
6743
6744     if (last_action == ACTION_SLEEPING)
6745       PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
6746   }
6747 }
6748
6749 static void PlayAllPlayersSound()
6750 {
6751   int i;
6752
6753   for (i = 0; i < MAX_PLAYERS; i++)
6754     if (stored_player[i].active)
6755       PlayPlayerSound(&stored_player[i]);
6756 }
6757
6758 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
6759 {
6760   boolean last_waiting = player->is_waiting;
6761   int move_dir = player->MovDir;
6762
6763   player->last_action_waiting = player->action_waiting;
6764
6765   if (is_waiting)
6766   {
6767     if (!last_waiting)          /* not waiting -> waiting */
6768     {
6769       player->is_waiting = TRUE;
6770
6771       player->frame_counter_bored =
6772         FrameCounter +
6773         game.player_boring_delay_fixed +
6774         SimpleRND(game.player_boring_delay_random);
6775       player->frame_counter_sleeping =
6776         FrameCounter +
6777         game.player_sleeping_delay_fixed +
6778         SimpleRND(game.player_sleeping_delay_random);
6779
6780       InitPlayerGfxAnimation(player, ACTION_WAITING, player->MovDir);
6781     }
6782
6783     if (game.player_sleeping_delay_fixed +
6784         game.player_sleeping_delay_random > 0 &&
6785         player->anim_delay_counter == 0 &&
6786         player->post_delay_counter == 0 &&
6787         FrameCounter >= player->frame_counter_sleeping)
6788       player->is_sleeping = TRUE;
6789     else if (game.player_boring_delay_fixed +
6790              game.player_boring_delay_random > 0 &&
6791              FrameCounter >= player->frame_counter_bored)
6792       player->is_bored = TRUE;
6793
6794     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
6795                               player->is_bored ? ACTION_BORING :
6796                               ACTION_WAITING);
6797
6798     if (player->is_sleeping)
6799     {
6800       if (player->num_special_action_sleeping > 0)
6801       {
6802         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6803         {
6804           int last_special_action = player->special_action_sleeping;
6805           int num_special_action = player->num_special_action_sleeping;
6806           int special_action =
6807             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
6808              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
6809              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
6810              last_special_action + 1 : ACTION_SLEEPING);
6811           int special_graphic =
6812             el_act_dir2img(player->element_nr, special_action, move_dir);
6813
6814           player->anim_delay_counter =
6815             graphic_info[special_graphic].anim_delay_fixed +
6816             SimpleRND(graphic_info[special_graphic].anim_delay_random);
6817           player->post_delay_counter =
6818             graphic_info[special_graphic].post_delay_fixed +
6819             SimpleRND(graphic_info[special_graphic].post_delay_random);
6820
6821           player->special_action_sleeping = special_action;
6822         }
6823
6824         if (player->anim_delay_counter > 0)
6825         {
6826           player->action_waiting = player->special_action_sleeping;
6827           player->anim_delay_counter--;
6828         }
6829         else if (player->post_delay_counter > 0)
6830         {
6831           player->post_delay_counter--;
6832         }
6833       }
6834     }
6835     else if (player->is_bored)
6836     {
6837       if (player->num_special_action_bored > 0)
6838       {
6839         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
6840         {
6841           int special_action =
6842             ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
6843           int special_graphic =
6844             el_act_dir2img(player->element_nr, special_action, move_dir);
6845
6846           player->anim_delay_counter =
6847             graphic_info[special_graphic].anim_delay_fixed +
6848             SimpleRND(graphic_info[special_graphic].anim_delay_random);
6849           player->post_delay_counter =
6850             graphic_info[special_graphic].post_delay_fixed +
6851             SimpleRND(graphic_info[special_graphic].post_delay_random);
6852
6853           player->special_action_bored = special_action;
6854         }
6855
6856         if (player->anim_delay_counter > 0)
6857         {
6858           player->action_waiting = player->special_action_bored;
6859           player->anim_delay_counter--;
6860         }
6861         else if (player->post_delay_counter > 0)
6862         {
6863           player->post_delay_counter--;
6864         }
6865       }
6866     }
6867   }
6868   else if (last_waiting)        /* waiting -> not waiting */
6869   {
6870     player->is_waiting = FALSE;
6871     player->is_bored = FALSE;
6872     player->is_sleeping = FALSE;
6873
6874     player->frame_counter_bored = -1;
6875     player->frame_counter_sleeping = -1;
6876
6877     player->anim_delay_counter = 0;
6878     player->post_delay_counter = 0;
6879
6880     player->action_waiting = ACTION_DEFAULT;
6881
6882     player->special_action_bored = ACTION_DEFAULT;
6883     player->special_action_sleeping = ACTION_DEFAULT;
6884   }
6885 }
6886
6887 #if 1
6888 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
6889 {
6890 #if 0
6891   static byte stored_player_action[MAX_PLAYERS];
6892   static int num_stored_actions = 0;
6893 #endif
6894   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6895   int left      = player_action & JOY_LEFT;
6896   int right     = player_action & JOY_RIGHT;
6897   int up        = player_action & JOY_UP;
6898   int down      = player_action & JOY_DOWN;
6899   int button1   = player_action & JOY_BUTTON_1;
6900   int button2   = player_action & JOY_BUTTON_2;
6901   int dx        = (left ? -1    : right ? 1     : 0);
6902   int dy        = (up   ? -1    : down  ? 1     : 0);
6903
6904 #if 0
6905   stored_player_action[player->index_nr] = 0;
6906   num_stored_actions++;
6907 #endif
6908
6909 #if 0
6910   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
6911 #endif
6912
6913   if (!player->active || tape.pausing)
6914     return 0;
6915
6916 #if 0
6917   printf("::: [%d %d %d %d] [%d %d]\n",
6918          left, right, up, down, button1, button2);
6919 #endif
6920
6921   if (player_action)
6922   {
6923 #if 0
6924     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
6925 #endif
6926
6927     if (button1)
6928       snapped = SnapField(player, dx, dy);
6929     else
6930     {
6931       if (button2)
6932         dropped = DropElement(player);
6933
6934       moved = MovePlayer(player, dx, dy);
6935     }
6936
6937     if (tape.single_step && tape.recording && !tape.pausing)
6938     {
6939       if (button1 || (dropped && !moved))
6940       {
6941         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
6942         SnapField(player, 0, 0);                /* stop snapping */
6943       }
6944     }
6945
6946     SetPlayerWaiting(player, FALSE);
6947
6948 #if 1
6949     return player_action;
6950 #else
6951     stored_player_action[player->index_nr] = player_action;
6952 #endif
6953   }
6954   else
6955   {
6956 #if 0
6957     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
6958 #endif
6959
6960     /* no actions for this player (no input at player's configured device) */
6961
6962     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
6963     SnapField(player, 0, 0);
6964     CheckGravityMovement(player);
6965
6966     if (player->MovPos == 0)
6967       SetPlayerWaiting(player, TRUE);
6968
6969     if (player->MovPos == 0)    /* needed for tape.playing */
6970       player->is_moving = FALSE;
6971
6972     player->is_dropping = FALSE;
6973
6974     return 0;
6975   }
6976
6977 #if 0
6978   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
6979   {
6980     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
6981
6982     TapeRecordAction(stored_player_action);
6983     num_stored_actions = 0;
6984   }
6985 #endif
6986 }
6987
6988 #else
6989
6990 static void PlayerActions(struct PlayerInfo *player, byte player_action)
6991 {
6992   static byte stored_player_action[MAX_PLAYERS];
6993   static int num_stored_actions = 0;
6994   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
6995   int left      = player_action & JOY_LEFT;
6996   int right     = player_action & JOY_RIGHT;
6997   int up        = player_action & JOY_UP;
6998   int down      = player_action & JOY_DOWN;
6999   int button1   = player_action & JOY_BUTTON_1;
7000   int button2   = player_action & JOY_BUTTON_2;
7001   int dx        = (left ? -1    : right ? 1     : 0);
7002   int dy        = (up   ? -1    : down  ? 1     : 0);
7003
7004   stored_player_action[player->index_nr] = 0;
7005   num_stored_actions++;
7006
7007   printf("::: player %d [%d]\n", player->index_nr, FrameCounter);
7008
7009   if (!player->active || tape.pausing)
7010     return;
7011
7012   if (player_action)
7013   {
7014     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
7015
7016     if (button1)
7017       snapped = SnapField(player, dx, dy);
7018     else
7019     {
7020       if (button2)
7021         dropped = DropElement(player);
7022
7023       moved = MovePlayer(player, dx, dy);
7024     }
7025
7026     if (tape.single_step && tape.recording && !tape.pausing)
7027     {
7028       if (button1 || (dropped && !moved))
7029       {
7030         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7031         SnapField(player, 0, 0);                /* stop snapping */
7032       }
7033     }
7034
7035     stored_player_action[player->index_nr] = player_action;
7036   }
7037   else
7038   {
7039     printf("::: player %d waits [%d]\n", player->index_nr, FrameCounter);
7040
7041     /* no actions for this player (no input at player's configured device) */
7042
7043     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
7044     SnapField(player, 0, 0);
7045     CheckGravityMovement(player);
7046
7047     if (player->MovPos == 0)
7048       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
7049
7050     if (player->MovPos == 0)    /* needed for tape.playing */
7051       player->is_moving = FALSE;
7052   }
7053
7054   if (tape.recording && num_stored_actions >= MAX_PLAYERS)
7055   {
7056     printf("::: player %d recorded [%d]\n", player->index_nr, FrameCounter);
7057
7058     TapeRecordAction(stored_player_action);
7059     num_stored_actions = 0;
7060   }
7061 }
7062 #endif
7063
7064 void GameActions()
7065 {
7066   static unsigned long action_delay = 0;
7067   unsigned long action_delay_value;
7068   int magic_wall_x = 0, magic_wall_y = 0;
7069   int i, x, y, element, graphic;
7070   byte *recorded_player_action;
7071   byte summarized_player_action = 0;
7072 #if 1
7073   byte tape_action[MAX_PLAYERS];
7074 #endif
7075
7076   if (game_status != GAME_MODE_PLAYING)
7077     return;
7078
7079   action_delay_value =
7080     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
7081
7082   if (tape.playing && tape.index_search && !tape.pausing)
7083     action_delay_value = 0;
7084
7085   /* ---------- main game synchronization point ---------- */
7086
7087   WaitUntilDelayReached(&action_delay, action_delay_value);
7088
7089   if (network_playing && !network_player_action_received)
7090   {
7091     /*
7092 #ifdef DEBUG
7093     printf("DEBUG: try to get network player actions in time\n");
7094 #endif
7095     */
7096
7097 #if defined(PLATFORM_UNIX)
7098     /* last chance to get network player actions without main loop delay */
7099     HandleNetworking();
7100 #endif
7101
7102     if (game_status != GAME_MODE_PLAYING)
7103       return;
7104
7105     if (!network_player_action_received)
7106     {
7107       /*
7108 #ifdef DEBUG
7109       printf("DEBUG: failed to get network player actions in time\n");
7110 #endif
7111       */
7112       return;
7113     }
7114   }
7115
7116   if (tape.pausing)
7117     return;
7118
7119 #if 0
7120   printf("::: getting new tape action [%d]\n", FrameCounter);
7121 #endif
7122
7123   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
7124
7125   for (i = 0; i < MAX_PLAYERS; i++)
7126   {
7127     summarized_player_action |= stored_player[i].action;
7128
7129     if (!network_playing)
7130       stored_player[i].effective_action = stored_player[i].action;
7131   }
7132
7133 #if defined(PLATFORM_UNIX)
7134   if (network_playing)
7135     SendToServer_MovePlayer(summarized_player_action);
7136 #endif
7137
7138   if (!options.network && !setup.team_mode)
7139     local_player->effective_action = summarized_player_action;
7140
7141   for (i = 0; i < MAX_PLAYERS; i++)
7142   {
7143     int actual_player_action = stored_player[i].effective_action;
7144
7145     if (stored_player[i].programmed_action)
7146       actual_player_action = stored_player[i].programmed_action;
7147
7148     if (recorded_player_action)
7149       actual_player_action = recorded_player_action[i];
7150
7151     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
7152
7153     if (tape.recording && tape_action[i] && !tape.player_participates[i])
7154       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
7155
7156     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
7157   }
7158
7159 #if 1
7160   if (tape.recording)
7161     TapeRecordAction(tape_action);
7162 #endif
7163
7164   network_player_action_received = FALSE;
7165
7166   ScrollScreen(NULL, SCROLL_GO_ON);
7167
7168 #if 0
7169   FrameCounter++;
7170   TimeFrames++;
7171
7172   for (i = 0; i < MAX_PLAYERS; i++)
7173     stored_player[i].Frame++;
7174 #endif
7175
7176 #if 1
7177   if (game.engine_version < VERSION_IDENT(2,2,0,7))
7178   {
7179     for (i = 0; i < MAX_PLAYERS; i++)
7180     {
7181       struct PlayerInfo *player = &stored_player[i];
7182       int x = player->jx;
7183       int y = player->jy;
7184
7185       if (player->active && player->is_pushing && player->is_moving &&
7186           IS_MOVING(x, y))
7187       {
7188         ContinueMoving(x, y);
7189
7190         /* continue moving after pushing (this is actually a bug) */
7191         if (!IS_MOVING(x, y))
7192         {
7193           Stop[x][y] = FALSE;
7194         }
7195       }
7196     }
7197   }
7198 #endif
7199
7200   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7201   {
7202     Changed[x][y] = CE_BITMASK_DEFAULT;
7203     ChangeEvent[x][y] = CE_BITMASK_DEFAULT;
7204
7205 #if DEBUG
7206     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
7207     {
7208       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
7209       printf("GameActions(): This should never happen!\n");
7210
7211       ChangePage[x][y] = -1;
7212     }
7213 #endif
7214
7215     Stop[x][y] = FALSE;
7216     if (WasJustMoving[x][y] > 0)
7217       WasJustMoving[x][y]--;
7218     if (WasJustFalling[x][y] > 0)
7219       WasJustFalling[x][y]--;
7220
7221     GfxFrame[x][y]++;
7222
7223 #if 1
7224     /* reset finished pushing action (not done in ContinueMoving() to allow
7225        continous pushing animation for elements with zero push delay) */
7226     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
7227     {
7228       ResetGfxAnimation(x, y);
7229       DrawLevelField(x, y);
7230     }
7231 #endif
7232
7233 #if DEBUG
7234     if (IS_BLOCKED(x, y))
7235     {
7236       int oldx, oldy;
7237
7238       Blocked2Moving(x, y, &oldx, &oldy);
7239       if (!IS_MOVING(oldx, oldy))
7240       {
7241         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
7242         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
7243         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
7244         printf("GameActions(): This should never happen!\n");
7245       }
7246     }
7247 #endif
7248   }
7249
7250   for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7251   {
7252     element = Feld[x][y];
7253 #if 1
7254     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7255 #else
7256     graphic = el2img(element);
7257 #endif
7258
7259 #if 0
7260     if (element == -1)
7261     {
7262       printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
7263
7264       element = graphic = 0;
7265     }
7266 #endif
7267
7268     if (graphic_info[graphic].anim_global_sync)
7269       GfxFrame[x][y] = FrameCounter;
7270
7271     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
7272         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
7273       ResetRandomAnimationValue(x, y);
7274
7275     SetRandomAnimationValue(x, y);
7276
7277 #if 1
7278     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
7279 #endif
7280
7281     if (IS_INACTIVE(element))
7282     {
7283       if (IS_ANIMATED(graphic))
7284         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7285
7286       continue;
7287     }
7288
7289 #if 1
7290     /* this may take place after moving, so 'element' may have changed */
7291 #if 0
7292     if (IS_CHANGING(x, y))
7293 #else
7294     if (IS_CHANGING(x, y) &&
7295         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
7296 #endif
7297     {
7298 #if 0
7299       ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
7300                     element_info[element].event_page_nr[CE_DELAY]);
7301 #else
7302       ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
7303 #endif
7304
7305       element = Feld[x][y];
7306       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7307     }
7308 #endif
7309
7310     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
7311     {
7312       StartMoving(x, y);
7313
7314 #if 1
7315       element = Feld[x][y];
7316       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7317 #if 0
7318       if (element == EL_MOLE)
7319         printf("::: %d, %d, %d [%d]\n",
7320                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y],
7321                GfxAction[x][y]);
7322 #endif
7323 #if 0
7324       if (element == EL_YAMYAM)
7325         printf("::: %d, %d, %d\n",
7326                IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
7327 #endif
7328 #endif
7329
7330       if (IS_ANIMATED(graphic) &&
7331           !IS_MOVING(x, y) &&
7332           !Stop[x][y])
7333       {
7334         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7335
7336 #if 0
7337         if (element == EL_BUG)
7338           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7339 #endif
7340
7341 #if 0
7342         if (element == EL_MOLE)
7343           printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
7344 #endif
7345       }
7346
7347       if (IS_GEM(element) || element == EL_SP_INFOTRON)
7348         EdelsteinFunkeln(x, y);
7349     }
7350     else if ((element == EL_ACID ||
7351               element == EL_EXIT_OPEN ||
7352               element == EL_SP_EXIT_OPEN ||
7353               element == EL_SP_TERMINAL ||
7354               element == EL_SP_TERMINAL_ACTIVE ||
7355               element == EL_EXTRA_TIME ||
7356               element == EL_SHIELD_NORMAL ||
7357               element == EL_SHIELD_DEADLY) &&
7358              IS_ANIMATED(graphic))
7359       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7360     else if (IS_MOVING(x, y))
7361       ContinueMoving(x, y);
7362     else if (IS_ACTIVE_BOMB(element))
7363       CheckDynamite(x, y);
7364 #if 0
7365     else if (element == EL_EXPLOSION && !game.explosions_delayed)
7366       Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7367 #endif
7368     else if (element == EL_AMOEBA_GROWING)
7369       AmoebeWaechst(x, y);
7370     else if (element == EL_AMOEBA_SHRINKING)
7371       AmoebaDisappearing(x, y);
7372
7373 #if !USE_NEW_AMOEBA_CODE
7374     else if (IS_AMOEBALIVE(element))
7375       AmoebeAbleger(x, y);
7376 #endif
7377
7378     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
7379       Life(x, y);
7380     else if (element == EL_EXIT_CLOSED)
7381       CheckExit(x, y);
7382     else if (element == EL_SP_EXIT_CLOSED)
7383       CheckExitSP(x, y);
7384     else if (element == EL_EXPANDABLE_WALL_GROWING)
7385       MauerWaechst(x, y);
7386     else if (element == EL_EXPANDABLE_WALL ||
7387              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
7388              element == EL_EXPANDABLE_WALL_VERTICAL ||
7389              element == EL_EXPANDABLE_WALL_ANY)
7390       MauerAbleger(x, y);
7391     else if (element == EL_FLAMES)
7392       CheckForDragon(x, y);
7393 #if 0
7394     else if (IS_AUTO_CHANGING(element))
7395       ChangeElement(x, y);
7396 #endif
7397     else if (element == EL_EXPLOSION)
7398       ; /* drawing of correct explosion animation is handled separately */
7399     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
7400       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
7401
7402 #if 0
7403     /* this may take place after moving, so 'element' may have changed */
7404     if (IS_AUTO_CHANGING(Feld[x][y]))
7405       ChangeElement(x, y);
7406 #endif
7407
7408     if (IS_BELT_ACTIVE(element))
7409       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
7410
7411     if (game.magic_wall_active)
7412     {
7413       int jx = local_player->jx, jy = local_player->jy;
7414
7415       /* play the element sound at the position nearest to the player */
7416       if ((element == EL_MAGIC_WALL_FULL ||
7417            element == EL_MAGIC_WALL_ACTIVE ||
7418            element == EL_MAGIC_WALL_EMPTYING ||
7419            element == EL_BD_MAGIC_WALL_FULL ||
7420            element == EL_BD_MAGIC_WALL_ACTIVE ||
7421            element == EL_BD_MAGIC_WALL_EMPTYING) &&
7422           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
7423       {
7424         magic_wall_x = x;
7425         magic_wall_y = y;
7426       }
7427     }
7428   }
7429
7430 #if USE_NEW_AMOEBA_CODE
7431   /* new experimental amoeba growth stuff */
7432 #if 1
7433   if (!(FrameCounter % 8))
7434 #endif
7435   {
7436     static unsigned long random = 1684108901;
7437
7438     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
7439     {
7440 #if 0
7441       x = (random >> 10) % lev_fieldx;
7442       y = (random >> 20) % lev_fieldy;
7443 #else
7444       x = RND(lev_fieldx);
7445       y = RND(lev_fieldy);
7446 #endif
7447       element = Feld[x][y];
7448
7449       /* !!! extend EL_SAND to anything diggable (but maybe not SP_BASE) !!! */
7450       if (!IS_PLAYER(x,y) &&
7451           (element == EL_EMPTY ||
7452            element == EL_SAND ||
7453            element == EL_QUICKSAND_EMPTY ||
7454            element == EL_ACID_SPLASH_LEFT ||
7455            element == EL_ACID_SPLASH_RIGHT))
7456       {
7457         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
7458             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
7459             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
7460             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
7461           Feld[x][y] = EL_AMOEBA_DROP;
7462       }
7463
7464       random = random * 129 + 1;
7465     }
7466   }
7467 #endif
7468
7469 #if 0
7470   if (game.explosions_delayed)
7471 #endif
7472   {
7473     game.explosions_delayed = FALSE;
7474
7475     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7476     {
7477       element = Feld[x][y];
7478
7479       if (ExplodeField[x][y])
7480         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
7481       else if (element == EL_EXPLOSION)
7482         Explode(x, y, ExplodePhase[x][y], EX_NORMAL);
7483
7484       ExplodeField[x][y] = EX_NO_EXPLOSION;
7485     }
7486
7487     game.explosions_delayed = TRUE;
7488   }
7489
7490   if (game.magic_wall_active)
7491   {
7492     if (!(game.magic_wall_time_left % 4))
7493     {
7494       int element = Feld[magic_wall_x][magic_wall_y];
7495
7496       if (element == EL_BD_MAGIC_WALL_FULL ||
7497           element == EL_BD_MAGIC_WALL_ACTIVE ||
7498           element == EL_BD_MAGIC_WALL_EMPTYING)
7499         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
7500       else
7501         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
7502     }
7503
7504     if (game.magic_wall_time_left > 0)
7505     {
7506       game.magic_wall_time_left--;
7507       if (!game.magic_wall_time_left)
7508       {
7509         for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
7510         {
7511           element = Feld[x][y];
7512
7513           if (element == EL_MAGIC_WALL_ACTIVE ||
7514               element == EL_MAGIC_WALL_FULL)
7515           {
7516             Feld[x][y] = EL_MAGIC_WALL_DEAD;
7517             DrawLevelField(x, y);
7518           }
7519           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
7520                    element == EL_BD_MAGIC_WALL_FULL)
7521           {
7522             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
7523             DrawLevelField(x, y);
7524           }
7525         }
7526
7527         game.magic_wall_active = FALSE;
7528       }
7529     }
7530   }
7531
7532   if (game.light_time_left > 0)
7533   {
7534     game.light_time_left--;
7535
7536     if (game.light_time_left == 0)
7537       RedrawAllLightSwitchesAndInvisibleElements();
7538   }
7539
7540   if (game.timegate_time_left > 0)
7541   {
7542     game.timegate_time_left--;
7543
7544     if (game.timegate_time_left == 0)
7545       CloseAllOpenTimegates();
7546   }
7547
7548   for (i = 0; i < MAX_PLAYERS; i++)
7549   {
7550     struct PlayerInfo *player = &stored_player[i];
7551
7552     if (SHIELD_ON(player))
7553     {
7554       if (player->shield_deadly_time_left)
7555         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
7556       else if (player->shield_normal_time_left)
7557         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
7558     }
7559   }
7560
7561   if (TimeFrames >= FRAMES_PER_SECOND)
7562   {
7563     TimeFrames = 0;
7564     TimePlayed++;
7565
7566     for (i = 0; i < MAX_PLAYERS; i++)
7567     {
7568       struct PlayerInfo *player = &stored_player[i];
7569
7570       if (SHIELD_ON(player))
7571       {
7572         player->shield_normal_time_left--;
7573
7574         if (player->shield_deadly_time_left > 0)
7575           player->shield_deadly_time_left--;
7576       }
7577     }
7578
7579     if (tape.recording || tape.playing)
7580       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
7581
7582     if (TimeLeft > 0)
7583     {
7584       TimeLeft--;
7585
7586       if (TimeLeft <= 10 && setup.time_limit)
7587         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
7588
7589       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
7590
7591       if (!TimeLeft && setup.time_limit)
7592         for (i = 0; i < MAX_PLAYERS; i++)
7593           KillHero(&stored_player[i]);
7594     }
7595     else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
7596       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FONT_TEXT_2);
7597   }
7598
7599   DrawAllPlayers();
7600   PlayAllPlayersSound();
7601
7602   if (options.debug)                    /* calculate frames per second */
7603   {
7604     static unsigned long fps_counter = 0;
7605     static int fps_frames = 0;
7606     unsigned long fps_delay_ms = Counter() - fps_counter;
7607
7608     fps_frames++;
7609
7610     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
7611     {
7612       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
7613
7614       fps_frames = 0;
7615       fps_counter = Counter();
7616     }
7617
7618     redraw_mask |= REDRAW_FPS;
7619   }
7620
7621 #if 0
7622   if (stored_player[0].jx != stored_player[0].last_jx ||
7623       stored_player[0].jy != stored_player[0].last_jy)
7624     printf("::: %d, %d, %d, %d, %d\n",
7625            stored_player[0].MovDir,
7626            stored_player[0].MovPos,
7627            stored_player[0].GfxPos,
7628            stored_player[0].Frame,
7629            stored_player[0].StepFrame);
7630 #endif
7631
7632 #if 1
7633   FrameCounter++;
7634   TimeFrames++;
7635
7636   for (i = 0; i < MAX_PLAYERS; i++)
7637   {
7638     int move_frames =
7639       MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
7640
7641     stored_player[i].Frame += move_frames;
7642
7643     if (stored_player[i].MovPos != 0)
7644       stored_player[i].StepFrame += move_frames;
7645
7646     if (stored_player[i].drop_delay > 0)
7647       stored_player[i].drop_delay--;
7648   }
7649 #endif
7650
7651 #if 1
7652   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
7653   {
7654     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
7655
7656     local_player->show_envelope = 0;
7657   }
7658 #endif
7659 }
7660
7661 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
7662 {
7663   int min_x = x, min_y = y, max_x = x, max_y = y;
7664   int i;
7665
7666   for (i = 0; i < MAX_PLAYERS; i++)
7667   {
7668     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7669
7670     if (!stored_player[i].active || &stored_player[i] == player)
7671       continue;
7672
7673     min_x = MIN(min_x, jx);
7674     min_y = MIN(min_y, jy);
7675     max_x = MAX(max_x, jx);
7676     max_y = MAX(max_y, jy);
7677   }
7678
7679   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
7680 }
7681
7682 static boolean AllPlayersInVisibleScreen()
7683 {
7684   int i;
7685
7686   for (i = 0; i < MAX_PLAYERS; i++)
7687   {
7688     int jx = stored_player[i].jx, jy = stored_player[i].jy;
7689
7690     if (!stored_player[i].active)
7691       continue;
7692
7693     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7694       return FALSE;
7695   }
7696
7697   return TRUE;
7698 }
7699
7700 void ScrollLevel(int dx, int dy)
7701 {
7702   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
7703   int x, y;
7704
7705   BlitBitmap(drawto_field, drawto_field,
7706              FX + TILEX * (dx == -1) - softscroll_offset,
7707              FY + TILEY * (dy == -1) - softscroll_offset,
7708              SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
7709              SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
7710              FX + TILEX * (dx == 1) - softscroll_offset,
7711              FY + TILEY * (dy == 1) - softscroll_offset);
7712
7713   if (dx)
7714   {
7715     x = (dx == 1 ? BX1 : BX2);
7716     for (y = BY1; y <= BY2; y++)
7717       DrawScreenField(x, y);
7718   }
7719
7720   if (dy)
7721   {
7722     y = (dy == 1 ? BY1 : BY2);
7723     for (x = BX1; x <= BX2; x++)
7724       DrawScreenField(x, y);
7725   }
7726
7727   redraw_mask |= REDRAW_FIELD;
7728 }
7729
7730 static boolean canEnterSupaplexPort(int x, int y, int dx, int dy)
7731 {
7732   int nextx = x + dx, nexty = y + dy;
7733   int element = Feld[x][y];
7734
7735   if ((dx == -1 &&
7736        element != EL_SP_PORT_LEFT &&
7737        element != EL_SP_GRAVITY_PORT_LEFT &&
7738        element != EL_SP_PORT_HORIZONTAL &&
7739        element != EL_SP_PORT_ANY) ||
7740       (dx == +1 &&
7741        element != EL_SP_PORT_RIGHT &&
7742        element != EL_SP_GRAVITY_PORT_RIGHT &&
7743        element != EL_SP_PORT_HORIZONTAL &&
7744        element != EL_SP_PORT_ANY) ||
7745       (dy == -1 &&
7746        element != EL_SP_PORT_UP &&
7747        element != EL_SP_GRAVITY_PORT_UP &&
7748        element != EL_SP_PORT_VERTICAL &&
7749        element != EL_SP_PORT_ANY) ||
7750       (dy == +1 &&
7751        element != EL_SP_PORT_DOWN &&
7752        element != EL_SP_GRAVITY_PORT_DOWN &&
7753        element != EL_SP_PORT_VERTICAL &&
7754        element != EL_SP_PORT_ANY) ||
7755       !IN_LEV_FIELD(nextx, nexty) ||
7756       !IS_FREE(nextx, nexty))
7757     return FALSE;
7758
7759   return TRUE;
7760 }
7761
7762 static void CheckGravityMovement(struct PlayerInfo *player)
7763 {
7764   if (game.gravity && !player->programmed_action)
7765   {
7766     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
7767     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
7768     int move_dir =
7769       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
7770        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
7771        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
7772     int jx = player->jx, jy = player->jy;
7773     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
7774     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
7775     int new_jx = jx + dx, new_jy = jy + dy;
7776     boolean field_under_player_is_free =
7777       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
7778     boolean player_is_moving_to_valid_field =
7779       (IN_LEV_FIELD(new_jx, new_jy) &&
7780        (Feld[new_jx][new_jy] == EL_SP_BASE ||
7781         Feld[new_jx][new_jy] == EL_SAND ||
7782         (IS_SP_PORT(Feld[new_jx][new_jy]) &&
7783          canEnterSupaplexPort(new_jx, new_jy, dx, dy))));
7784     /* !!! extend EL_SAND to anything diggable !!! */
7785
7786     if (field_under_player_is_free &&
7787         !player_is_moving_to_valid_field &&
7788         !IS_WALKABLE_INSIDE(Feld[jx][jy]))
7789       player->programmed_action = MV_DOWN;
7790   }
7791 }
7792
7793 /*
7794   MovePlayerOneStep()
7795   -----------------------------------------------------------------------------
7796   dx, dy:               direction (non-diagonal) to try to move the player to
7797   real_dx, real_dy:     direction as read from input device (can be diagonal)
7798 */
7799
7800 boolean MovePlayerOneStep(struct PlayerInfo *player,
7801                           int dx, int dy, int real_dx, int real_dy)
7802 {
7803 #if 0
7804   static int change_sides[4][2] =
7805   {
7806     /* enter side        leave side */
7807     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* moving left  */
7808     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* moving right */
7809     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     },      /* moving up    */
7810     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  }       /* moving down  */
7811   };
7812   int move_direction = (dx == -1 ? MV_LEFT :
7813                         dx == +1 ? MV_RIGHT :
7814                         dy == -1 ? MV_UP :
7815                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
7816   int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
7817   int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
7818 #endif
7819   int jx = player->jx, jy = player->jy;
7820   int new_jx = jx + dx, new_jy = jy + dy;
7821   int element;
7822   int can_move;
7823
7824   if (!player->active || (!dx && !dy))
7825     return MF_NO_ACTION;
7826
7827   player->MovDir = (dx < 0 ? MV_LEFT :
7828                     dx > 0 ? MV_RIGHT :
7829                     dy < 0 ? MV_UP :
7830                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
7831
7832   if (!IN_LEV_FIELD(new_jx, new_jy))
7833     return MF_NO_ACTION;
7834
7835   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
7836     return MF_NO_ACTION;
7837
7838 #if 0
7839   element = MovingOrBlocked2Element(new_jx, new_jy);
7840 #else
7841   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
7842 #endif
7843
7844   if (DONT_RUN_INTO(element))
7845   {
7846     if (element == EL_ACID && dx == 0 && dy == 1)
7847     {
7848       SplashAcid(jx, jy);
7849       Feld[jx][jy] = EL_PLAYER_1;
7850       InitMovingField(jx, jy, MV_DOWN);
7851       Store[jx][jy] = EL_ACID;
7852       ContinueMoving(jx, jy);
7853       BuryHero(player);
7854     }
7855     else
7856       TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
7857
7858     return MF_MOVING;
7859   }
7860
7861   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
7862   if (can_move != MF_MOVING)
7863     return can_move;
7864
7865   /* check if DigField() has caused relocation of the player */
7866   if (player->jx != jx || player->jy != jy)
7867     return MF_NO_ACTION;
7868
7869   StorePlayer[jx][jy] = 0;
7870   player->last_jx = jx;
7871   player->last_jy = jy;
7872   player->jx = new_jx;
7873   player->jy = new_jy;
7874   StorePlayer[new_jx][new_jy] = player->element_nr;
7875
7876   player->MovPos =
7877     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
7878
7879   player->step_counter++;
7880
7881   player->drop_delay = 0;
7882
7883   PlayerVisit[jx][jy] = FrameCounter;
7884
7885   ScrollPlayer(player, SCROLL_INIT);
7886
7887 #if 0
7888   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
7889   {
7890     CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7891                                     CE_OTHER_GETS_LEFT);
7892     CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
7893                            CE_LEFT_BY_PLAYER, -1);
7894   }
7895
7896   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
7897   {
7898     CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
7899                                     enter_side, CE_OTHER_GETS_ENTERED);
7900     CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
7901                            CE_ENTERED_BY_PLAYER, -1);
7902   }
7903 #endif
7904
7905   return MF_MOVING;
7906 }
7907
7908 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
7909 {
7910   int jx = player->jx, jy = player->jy;
7911   int old_jx = jx, old_jy = jy;
7912   int moved = MF_NO_ACTION;
7913
7914 #if 1
7915   if (!player->active)
7916     return FALSE;
7917
7918   if (!dx && !dy)
7919   {
7920     if (player->MovPos == 0)
7921     {
7922       player->is_moving = FALSE;
7923       player->is_digging = FALSE;
7924       player->is_collecting = FALSE;
7925       player->is_snapping = FALSE;
7926       player->is_pushing = FALSE;
7927     }
7928
7929     return FALSE;
7930   }
7931 #else
7932   if (!player->active || (!dx && !dy))
7933     return FALSE;
7934 #endif
7935
7936 #if 0
7937   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7938       !tape.playing)
7939     return FALSE;
7940 #else
7941   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
7942       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
7943     return FALSE;
7944 #endif
7945
7946   /* remove the last programmed player action */
7947   player->programmed_action = 0;
7948
7949   if (player->MovPos)
7950   {
7951     /* should only happen if pre-1.2 tape recordings are played */
7952     /* this is only for backward compatibility */
7953
7954     int original_move_delay_value = player->move_delay_value;
7955
7956 #if DEBUG
7957     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
7958            tape.counter);
7959 #endif
7960
7961     /* scroll remaining steps with finest movement resolution */
7962     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
7963
7964     while (player->MovPos)
7965     {
7966       ScrollPlayer(player, SCROLL_GO_ON);
7967       ScrollScreen(NULL, SCROLL_GO_ON);
7968       FrameCounter++;
7969       DrawAllPlayers();
7970       BackToFront();
7971     }
7972
7973     player->move_delay_value = original_move_delay_value;
7974   }
7975
7976   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
7977   {
7978     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
7979       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
7980   }
7981   else
7982   {
7983     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
7984       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
7985   }
7986
7987   jx = player->jx;
7988   jy = player->jy;
7989
7990   if (moved & MF_MOVING && !ScreenMovPos &&
7991       (player == local_player || !options.network))
7992   {
7993     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
7994     int offset = (setup.scroll_delay ? 3 : 0);
7995
7996     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
7997     {
7998       /* actual player has left the screen -- scroll in that direction */
7999       if (jx != old_jx)         /* player has moved horizontally */
8000         scroll_x += (jx - old_jx);
8001       else                      /* player has moved vertically */
8002         scroll_y += (jy - old_jy);
8003     }
8004     else
8005     {
8006       if (jx != old_jx)         /* player has moved horizontally */
8007       {
8008         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
8009             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
8010           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
8011
8012         /* don't scroll over playfield boundaries */
8013         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
8014           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
8015
8016         /* don't scroll more than one field at a time */
8017         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
8018
8019         /* don't scroll against the player's moving direction */
8020         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
8021             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
8022           scroll_x = old_scroll_x;
8023       }
8024       else                      /* player has moved vertically */
8025       {
8026         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
8027             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
8028           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
8029
8030         /* don't scroll over playfield boundaries */
8031         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
8032           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
8033
8034         /* don't scroll more than one field at a time */
8035         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
8036
8037         /* don't scroll against the player's moving direction */
8038         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
8039             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
8040           scroll_y = old_scroll_y;
8041       }
8042     }
8043
8044     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
8045     {
8046       if (!options.network && !AllPlayersInVisibleScreen())
8047       {
8048         scroll_x = old_scroll_x;
8049         scroll_y = old_scroll_y;
8050       }
8051       else
8052       {
8053         ScrollScreen(player, SCROLL_INIT);
8054         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
8055       }
8056     }
8057   }
8058
8059 #if 0
8060 #if 1
8061   InitPlayerGfxAnimation(player, ACTION_DEFAULT);
8062 #else
8063   if (!(moved & MF_MOVING) && !player->is_pushing)
8064     player->Frame = 0;
8065 #endif
8066 #endif
8067
8068   player->StepFrame = 0;
8069
8070   if (moved & MF_MOVING)
8071   {
8072     if (old_jx != jx && old_jy == jy)
8073       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
8074     else if (old_jx == jx && old_jy != jy)
8075       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
8076
8077     DrawLevelField(jx, jy);     /* for "crumbled sand" */
8078
8079     player->last_move_dir = player->MovDir;
8080     player->is_moving = TRUE;
8081 #if 1
8082     player->is_snapping = FALSE;
8083 #endif
8084
8085 #if 1
8086     player->is_switching = FALSE;
8087 #endif
8088
8089     player->is_dropping = FALSE;
8090
8091
8092 #if 1
8093     {
8094       static int change_sides[4][2] =
8095       {
8096         /* enter side           leave side */
8097         { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
8098         { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
8099         { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
8100         { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
8101       };
8102       int move_direction = player->MovDir;
8103       int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
8104       int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
8105
8106 #if 1
8107       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
8108       {
8109         CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8110                                         leave_side, CE_OTHER_GETS_LEFT);
8111         CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
8112                                leave_side, CE_LEFT_BY_PLAYER, -1);
8113       }
8114
8115       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
8116       {
8117         CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
8118                                         enter_side, CE_OTHER_GETS_ENTERED);
8119         CheckElementSideChange(jx, jy, Feld[jx][jy],
8120                                enter_side, CE_ENTERED_BY_PLAYER, -1);
8121       }
8122 #endif
8123
8124     }
8125 #endif
8126
8127
8128   }
8129   else
8130   {
8131     CheckGravityMovement(player);
8132
8133     /*
8134     player->last_move_dir = MV_NO_MOVING;
8135     */
8136     player->is_moving = FALSE;
8137   }
8138
8139   if (game.engine_version < VERSION_IDENT(3,0,7,0))
8140   {
8141     TestIfHeroTouchesBadThing(jx, jy);
8142     TestIfPlayerTouchesCustomElement(jx, jy);
8143   }
8144
8145   if (!player->active)
8146     RemoveHero(player);
8147
8148   return moved;
8149 }
8150
8151 void ScrollPlayer(struct PlayerInfo *player, int mode)
8152 {
8153   int jx = player->jx, jy = player->jy;
8154   int last_jx = player->last_jx, last_jy = player->last_jy;
8155   int move_stepsize = TILEX / player->move_delay_value;
8156
8157   if (!player->active || !player->MovPos)
8158     return;
8159
8160   if (mode == SCROLL_INIT)
8161   {
8162     player->actual_frame_counter = FrameCounter;
8163     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8164
8165     if (Feld[last_jx][last_jy] == EL_EMPTY)
8166       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
8167
8168 #if 0
8169     DrawPlayer(player);
8170 #endif
8171
8172     return;
8173   }
8174   else if (!FrameReached(&player->actual_frame_counter, 1))
8175     return;
8176
8177   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
8178   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
8179
8180   if (!player->block_last_field &&
8181       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8182     Feld[last_jx][last_jy] = EL_EMPTY;
8183
8184   /* before DrawPlayer() to draw correct player graphic for this case */
8185   if (player->MovPos == 0)
8186     CheckGravityMovement(player);
8187
8188 #if 0
8189   DrawPlayer(player);   /* needed here only to cleanup last field */
8190 #endif
8191
8192   if (player->MovPos == 0)      /* player reached destination field */
8193   {
8194 #if 1
8195     if (player->move_delay_reset_counter > 0)
8196     {
8197       player->move_delay_reset_counter--;
8198
8199       if (player->move_delay_reset_counter == 0)
8200       {
8201         /* continue with normal speed after quickly moving through gate */
8202         HALVE_PLAYER_SPEED(player);
8203
8204         /* be able to make the next move without delay */
8205         player->move_delay = 0;
8206       }
8207     }
8208 #else
8209     if (IS_PASSABLE(Feld[last_jx][last_jy]))
8210     {
8211       /* continue with normal speed after quickly moving through gate */
8212       HALVE_PLAYER_SPEED(player);
8213
8214       /* be able to make the next move without delay */
8215       player->move_delay = 0;
8216     }
8217 #endif
8218
8219     if (player->block_last_field &&
8220         Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
8221       Feld[last_jx][last_jy] = EL_EMPTY;
8222
8223     player->last_jx = jx;
8224     player->last_jy = jy;
8225
8226     if (Feld[jx][jy] == EL_EXIT_OPEN ||
8227         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
8228         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
8229     {
8230       DrawPlayer(player);       /* needed here only to cleanup last field */
8231       RemoveHero(player);
8232
8233       if (local_player->friends_still_needed == 0 ||
8234           IS_SP_ELEMENT(Feld[jx][jy]))
8235         player->LevelSolved = player->GameOver = TRUE;
8236     }
8237
8238     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8239     {
8240       TestIfHeroTouchesBadThing(jx, jy);
8241       TestIfPlayerTouchesCustomElement(jx, jy);
8242 #if 1
8243       TestIfElementTouchesCustomElement(jx, jy);        /* for empty space */
8244 #endif
8245
8246       if (!player->active)
8247         RemoveHero(player);
8248     }
8249
8250     if (tape.single_step && tape.recording && !tape.pausing &&
8251         !player->programmed_action)
8252       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8253   }
8254 }
8255
8256 void ScrollScreen(struct PlayerInfo *player, int mode)
8257 {
8258   static unsigned long screen_frame_counter = 0;
8259
8260   if (mode == SCROLL_INIT)
8261   {
8262     /* set scrolling step size according to actual player's moving speed */
8263     ScrollStepSize = TILEX / player->move_delay_value;
8264
8265     screen_frame_counter = FrameCounter;
8266     ScreenMovDir = player->MovDir;
8267     ScreenMovPos = player->MovPos;
8268     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8269     return;
8270   }
8271   else if (!FrameReached(&screen_frame_counter, 1))
8272     return;
8273
8274   if (ScreenMovPos)
8275   {
8276     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
8277     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
8278     redraw_mask |= REDRAW_FIELD;
8279   }
8280   else
8281     ScreenMovDir = MV_NO_MOVING;
8282 }
8283
8284 void TestIfPlayerTouchesCustomElement(int x, int y)
8285 {
8286   static int xy[4][2] =
8287   {
8288     { 0, -1 },
8289     { -1, 0 },
8290     { +1, 0 },
8291     { 0, +1 }
8292   };
8293   static int change_sides[4][2] =
8294   {
8295     /* center side       border side */
8296     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8297     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8298     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8299     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8300   };
8301   static int touch_dir[4] =
8302   {
8303     MV_LEFT | MV_RIGHT,
8304     MV_UP   | MV_DOWN,
8305     MV_UP   | MV_DOWN,
8306     MV_LEFT | MV_RIGHT
8307   };
8308   int center_element = Feld[x][y];      /* should always be non-moving! */
8309   int i;
8310
8311   for (i = 0; i < 4; i++)
8312   {
8313     int xx = x + xy[i][0];
8314     int yy = y + xy[i][1];
8315     int center_side = change_sides[i][0];
8316     int border_side = change_sides[i][1];
8317     int border_element;
8318
8319     if (!IN_LEV_FIELD(xx, yy))
8320       continue;
8321
8322     if (IS_PLAYER(x, y))
8323     {
8324       if (game.engine_version < VERSION_IDENT(3,0,7,0))
8325         border_element = Feld[xx][yy];          /* may be moving! */
8326       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8327         border_element = Feld[xx][yy];
8328       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
8329         border_element = MovingOrBlocked2Element(xx, yy);
8330       else
8331         continue;               /* center and border element do not touch */
8332
8333       CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
8334                                       CE_OTHER_GETS_TOUCHED);
8335       CheckElementSideChange(xx, yy, border_element, border_side,
8336                              CE_TOUCHED_BY_PLAYER, -1);
8337     }
8338     else if (IS_PLAYER(xx, yy))
8339     {
8340       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8341       {
8342         struct PlayerInfo *player = PLAYERINFO(xx, yy);
8343
8344         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8345           continue;             /* center and border element do not touch */
8346       }
8347
8348       CheckTriggeredElementSideChange(x, y, center_element, center_side,
8349                                       CE_OTHER_GETS_TOUCHED);
8350       CheckElementSideChange(x, y, center_element, center_side,
8351                              CE_TOUCHED_BY_PLAYER, -1);
8352
8353       break;
8354     }
8355   }
8356 }
8357
8358 void TestIfElementTouchesCustomElement(int x, int y)
8359 {
8360   static int xy[4][2] =
8361   {
8362     { 0, -1 },
8363     { -1, 0 },
8364     { +1, 0 },
8365     { 0, +1 }
8366   };
8367   static int change_sides[4][2] =
8368   {
8369     /* center side      border side */
8370     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
8371     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
8372     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
8373     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
8374   };
8375   static int touch_dir[4] =
8376   {
8377     MV_LEFT | MV_RIGHT,
8378     MV_UP   | MV_DOWN,
8379     MV_UP   | MV_DOWN,
8380     MV_LEFT | MV_RIGHT
8381   };
8382   boolean change_center_element = FALSE;
8383   int center_element_change_page = 0;
8384   int center_element = Feld[x][y];      /* should always be non-moving! */
8385   int i, j;
8386
8387   for (i = 0; i < 4; i++)
8388   {
8389     int xx = x + xy[i][0];
8390     int yy = y + xy[i][1];
8391     int center_side = change_sides[i][0];
8392     int border_side = change_sides[i][1];
8393     int border_element;
8394
8395     if (!IN_LEV_FIELD(xx, yy))
8396       continue;
8397
8398     if (game.engine_version < VERSION_IDENT(3,0,7,0))
8399       border_element = Feld[xx][yy];    /* may be moving! */
8400     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
8401       border_element = Feld[xx][yy];
8402     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
8403       border_element = MovingOrBlocked2Element(xx, yy);
8404     else
8405       continue;                 /* center and border element do not touch */
8406
8407     /* check for change of center element (but change it only once) */
8408     if (IS_CUSTOM_ELEMENT(center_element) &&
8409         HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
8410         !change_center_element)
8411     {
8412       for (j = 0; j < element_info[center_element].num_change_pages; j++)
8413       {
8414         struct ElementChangeInfo *change =
8415           &element_info[center_element].change_page[j];
8416
8417         if (change->can_change &&
8418             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8419             change->sides & border_side &&
8420 #if 1
8421             IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
8422 #else
8423             change->trigger_element == border_element
8424 #endif
8425             )
8426         {
8427           change_center_element = TRUE;
8428           center_element_change_page = j;
8429
8430           break;
8431         }
8432       }
8433     }
8434
8435     /* check for change of border element */
8436     if (IS_CUSTOM_ELEMENT(border_element) &&
8437         HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
8438     {
8439       for (j = 0; j < element_info[border_element].num_change_pages; j++)
8440       {
8441         struct ElementChangeInfo *change =
8442           &element_info[border_element].change_page[j];
8443
8444         if (change->can_change &&
8445             change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
8446             change->sides & center_side &&
8447 #if 1
8448             IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
8449 #else
8450             change->trigger_element == center_element
8451 #endif
8452             )
8453         {
8454           CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
8455                                  CE_OTHER_IS_TOUCHING, j);
8456           break;
8457         }
8458       }
8459     }
8460   }
8461
8462   if (change_center_element)
8463     CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
8464                            CE_OTHER_IS_TOUCHING, center_element_change_page);
8465 }
8466
8467 void TestIfElementHitsCustomElement(int x, int y, int direction)
8468 {
8469   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8470   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8471   int hitx = x + dx, hity = y + dy;
8472   int hitting_element = Feld[x][y];
8473 #if 0
8474   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
8475                         !IS_FREE(hitx, hity) &&
8476                         (!IS_MOVING(hitx, hity) ||
8477                          MovDir[hitx][hity] != direction ||
8478                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
8479 #endif
8480
8481   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
8482     return;
8483
8484 #if 0
8485   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
8486     return;
8487 #endif
8488
8489   CheckElementSideChange(x, y, hitting_element,
8490                          direction, CE_HITTING_SOMETHING, -1);
8491
8492   if (IN_LEV_FIELD(hitx, hity))
8493   {
8494     int opposite_direction = MV_DIR_OPPOSITE(direction);
8495     int hitting_side = direction;
8496     int touched_side = opposite_direction;
8497     int touched_element = MovingOrBlocked2Element(hitx, hity);
8498 #if 1
8499     boolean object_hit = (!IS_MOVING(hitx, hity) ||
8500                           MovDir[hitx][hity] != direction ||
8501                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
8502
8503     object_hit = TRUE;
8504 #endif
8505
8506     if (object_hit)
8507     {
8508       int i;
8509
8510       CheckElementSideChange(hitx, hity, touched_element,
8511                              opposite_direction, CE_HIT_BY_SOMETHING, -1);
8512
8513       if (IS_CUSTOM_ELEMENT(hitting_element) &&
8514           HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
8515       {
8516         for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
8517         {
8518           struct ElementChangeInfo *change =
8519             &element_info[hitting_element].change_page[i];
8520
8521           if (change->can_change &&
8522               change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
8523               change->sides & touched_side &&
8524           
8525 #if 1
8526               IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
8527 #else
8528               change->trigger_element == touched_element
8529 #endif
8530               )
8531           {
8532             CheckElementSideChange(x, y, hitting_element,
8533                                    CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
8534             break;
8535           }
8536         }
8537       }
8538
8539       if (IS_CUSTOM_ELEMENT(touched_element) &&
8540           HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_HIT))
8541       {
8542         for (i = 0; i < element_info[touched_element].num_change_pages; i++)
8543         {
8544           struct ElementChangeInfo *change =
8545             &element_info[touched_element].change_page[i];
8546
8547           if (change->can_change &&
8548               change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
8549               change->sides & hitting_side &&
8550 #if 1
8551               IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
8552 #else
8553               change->trigger_element == hitting_element
8554 #endif
8555               )
8556           {
8557             CheckElementSideChange(hitx, hity, touched_element,
8558                                    CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
8559             break;
8560           }
8561         }
8562       }
8563     }
8564   }
8565 }
8566
8567 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
8568 {
8569   int i, kill_x = -1, kill_y = -1;
8570   static int test_xy[4][2] =
8571   {
8572     { 0, -1 },
8573     { -1, 0 },
8574     { +1, 0 },
8575     { 0, +1 }
8576   };
8577   static int test_dir[4] =
8578   {
8579     MV_UP,
8580     MV_LEFT,
8581     MV_RIGHT,
8582     MV_DOWN
8583   };
8584
8585   for (i = 0; i < 4; i++)
8586   {
8587     int test_x, test_y, test_move_dir, test_element;
8588
8589     test_x = good_x + test_xy[i][0];
8590     test_y = good_y + test_xy[i][1];
8591     if (!IN_LEV_FIELD(test_x, test_y))
8592       continue;
8593
8594     test_move_dir =
8595       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8596
8597 #if 0
8598     test_element = Feld[test_x][test_y];
8599 #else
8600     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
8601 #endif
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(test_element) && good_move_dir == test_dir[i]) ||
8607         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
8608     {
8609       kill_x = test_x;
8610       kill_y = test_y;
8611       break;
8612     }
8613   }
8614
8615   if (kill_x != -1 || kill_y != -1)
8616   {
8617     if (IS_PLAYER(good_x, good_y))
8618     {
8619       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
8620
8621       if (player->shield_deadly_time_left > 0)
8622         Bang(kill_x, kill_y);
8623       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
8624         KillHero(player);
8625     }
8626     else
8627       Bang(good_x, good_y);
8628   }
8629 }
8630
8631 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
8632 {
8633   int i, kill_x = -1, kill_y = -1;
8634   int bad_element = Feld[bad_x][bad_y];
8635   static int test_xy[4][2] =
8636   {
8637     { 0, -1 },
8638     { -1, 0 },
8639     { +1, 0 },
8640     { 0, +1 }
8641   };
8642   static int touch_dir[4] =
8643   {
8644     MV_LEFT | MV_RIGHT,
8645     MV_UP   | MV_DOWN,
8646     MV_UP   | MV_DOWN,
8647     MV_LEFT | MV_RIGHT
8648   };
8649   static int test_dir[4] =
8650   {
8651     MV_UP,
8652     MV_LEFT,
8653     MV_RIGHT,
8654     MV_DOWN
8655   };
8656
8657   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
8658     return;
8659
8660   for (i = 0; i < 4; i++)
8661   {
8662     int test_x, test_y, test_move_dir, test_element;
8663
8664     test_x = bad_x + test_xy[i][0];
8665     test_y = bad_y + test_xy[i][1];
8666     if (!IN_LEV_FIELD(test_x, test_y))
8667       continue;
8668
8669     test_move_dir =
8670       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
8671
8672     test_element = Feld[test_x][test_y];
8673
8674     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
8675        2nd case: DONT_TOUCH style bad thing does not move away from good thing
8676     */
8677     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
8678         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
8679     {
8680       /* good thing is player or penguin that does not move away */
8681       if (IS_PLAYER(test_x, test_y))
8682       {
8683         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
8684
8685         if (bad_element == EL_ROBOT && player->is_moving)
8686           continue;     /* robot does not kill player if he is moving */
8687
8688         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
8689         {
8690           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
8691             continue;           /* center and border element do not touch */
8692         }
8693
8694         kill_x = test_x;
8695         kill_y = test_y;
8696         break;
8697       }
8698       else if (test_element == EL_PENGUIN)
8699       {
8700         kill_x = test_x;
8701         kill_y = test_y;
8702         break;
8703       }
8704     }
8705   }
8706
8707   if (kill_x != -1 || kill_y != -1)
8708   {
8709     if (IS_PLAYER(kill_x, kill_y))
8710     {
8711       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
8712
8713       if (player->shield_deadly_time_left > 0)
8714         Bang(bad_x, bad_y);
8715       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
8716         KillHero(player);
8717     }
8718     else
8719       Bang(kill_x, kill_y);
8720   }
8721 }
8722
8723 void TestIfHeroTouchesBadThing(int x, int y)
8724 {
8725   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8726 }
8727
8728 void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
8729 {
8730   TestIfGoodThingHitsBadThing(x, y, move_dir);
8731 }
8732
8733 void TestIfBadThingTouchesHero(int x, int y)
8734 {
8735   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8736 }
8737
8738 void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
8739 {
8740   TestIfBadThingHitsGoodThing(x, y, move_dir);
8741 }
8742
8743 void TestIfFriendTouchesBadThing(int x, int y)
8744 {
8745   TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
8746 }
8747
8748 void TestIfBadThingTouchesFriend(int x, int y)
8749 {
8750   TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
8751 }
8752
8753 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
8754 {
8755   int i, kill_x = bad_x, kill_y = bad_y;
8756   static int xy[4][2] =
8757   {
8758     { 0, -1 },
8759     { -1, 0 },
8760     { +1, 0 },
8761     { 0, +1 }
8762   };
8763
8764   for (i = 0; i < 4; i++)
8765   {
8766     int x, y, element;
8767
8768     x = bad_x + xy[i][0];
8769     y = bad_y + xy[i][1];
8770     if (!IN_LEV_FIELD(x, y))
8771       continue;
8772
8773     element = Feld[x][y];
8774     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
8775         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
8776     {
8777       kill_x = x;
8778       kill_y = y;
8779       break;
8780     }
8781   }
8782
8783   if (kill_x != bad_x || kill_y != bad_y)
8784     Bang(bad_x, bad_y);
8785 }
8786
8787 void KillHero(struct PlayerInfo *player)
8788 {
8789   int jx = player->jx, jy = player->jy;
8790
8791   if (!player->active)
8792     return;
8793
8794   /* remove accessible field at the player's position */
8795   Feld[jx][jy] = EL_EMPTY;
8796
8797   /* deactivate shield (else Bang()/Explode() would not work right) */
8798   player->shield_normal_time_left = 0;
8799   player->shield_deadly_time_left = 0;
8800
8801   Bang(jx, jy);
8802   BuryHero(player);
8803 }
8804
8805 static void KillHeroUnlessEnemyProtected(int x, int y)
8806 {
8807   if (!PLAYER_ENEMY_PROTECTED(x, y))
8808     KillHero(PLAYERINFO(x, y));
8809 }
8810
8811 static void KillHeroUnlessExplosionProtected(int x, int y)
8812 {
8813   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
8814     KillHero(PLAYERINFO(x, y));
8815 }
8816
8817 void BuryHero(struct PlayerInfo *player)
8818 {
8819   int jx = player->jx, jy = player->jy;
8820
8821   if (!player->active)
8822     return;
8823
8824 #if 1
8825   PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
8826 #else
8827   PlayLevelSound(jx, jy, SND_CLASS_PLAYER_DYING);
8828 #endif
8829   PlayLevelSound(jx, jy, SND_GAME_LOSING);
8830
8831   player->GameOver = TRUE;
8832   RemoveHero(player);
8833 }
8834
8835 void RemoveHero(struct PlayerInfo *player)
8836 {
8837   int jx = player->jx, jy = player->jy;
8838   int i, found = FALSE;
8839
8840   player->present = FALSE;
8841   player->active = FALSE;
8842
8843   if (!ExplodeField[jx][jy])
8844     StorePlayer[jx][jy] = 0;
8845
8846   for (i = 0; i < MAX_PLAYERS; i++)
8847     if (stored_player[i].active)
8848       found = TRUE;
8849
8850   if (!found)
8851     AllPlayersGone = TRUE;
8852
8853   ExitX = ZX = jx;
8854   ExitY = ZY = jy;
8855 }
8856
8857 /*
8858   =============================================================================
8859   checkDiagonalPushing()
8860   -----------------------------------------------------------------------------
8861   check if diagonal input device direction results in pushing of object
8862   (by checking if the alternative direction is walkable, diggable, ...)
8863   =============================================================================
8864 */
8865
8866 static boolean checkDiagonalPushing(struct PlayerInfo *player,
8867                                     int x, int y, int real_dx, int real_dy)
8868 {
8869   int jx, jy, dx, dy, xx, yy;
8870
8871   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
8872     return TRUE;
8873
8874   /* diagonal direction: check alternative direction */
8875   jx = player->jx;
8876   jy = player->jy;
8877   dx = x - jx;
8878   dy = y - jy;
8879   xx = jx + (dx == 0 ? real_dx : 0);
8880   yy = jy + (dy == 0 ? real_dy : 0);
8881
8882   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
8883 }
8884
8885 /*
8886   =============================================================================
8887   DigField()
8888   -----------------------------------------------------------------------------
8889   x, y:                 field next to player (non-diagonal) to try to dig to
8890   real_dx, real_dy:     direction as read from input device (can be diagonal)
8891   =============================================================================
8892 */
8893
8894 int DigField(struct PlayerInfo *player,
8895              int x, int y, int real_dx, int real_dy, int mode)
8896 {
8897   static int change_sides[4] =
8898   {
8899     CH_SIDE_RIGHT,      /* moving left  */
8900     CH_SIDE_LEFT,       /* moving right */
8901     CH_SIDE_BOTTOM,     /* moving up    */
8902     CH_SIDE_TOP,        /* moving down  */
8903   };
8904   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0,0));
8905   int jx = player->jx, jy = player->jy;
8906   int dx = x - jx, dy = y - jy;
8907   int nextx = x + dx, nexty = y + dy;
8908   int move_direction = (dx == -1 ? MV_LEFT :
8909                         dx == +1 ? MV_RIGHT :
8910                         dy == -1 ? MV_UP :
8911                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
8912   int dig_side = change_sides[MV_DIR_BIT(move_direction)];
8913   int element;
8914
8915   if (player->MovPos == 0)
8916   {
8917     player->is_digging = FALSE;
8918     player->is_collecting = FALSE;
8919   }
8920
8921   if (player->MovPos == 0)      /* last pushing move finished */
8922     player->is_pushing = FALSE;
8923
8924   if (mode == DF_NO_PUSH)       /* player just stopped pushing */
8925   {
8926     player->is_switching = FALSE;
8927     player->push_delay = 0;
8928
8929     return MF_NO_ACTION;
8930   }
8931
8932   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
8933     return MF_NO_ACTION;
8934
8935 #if 0
8936   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
8937 #else
8938   if (IS_TUBE(Feld[jx][jy]) ||
8939       (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0)))
8940 #endif
8941   {
8942     int i = 0;
8943     int tube_element = (IS_TUBE(Feld[jx][jy]) ? Feld[jx][jy] : Back[jx][jy]);
8944     int tube_leave_directions[][2] =
8945     {
8946       { EL_TUBE_ANY,                    MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
8947       { EL_TUBE_VERTICAL,                                    MV_UP | MV_DOWN },
8948       { EL_TUBE_HORIZONTAL,             MV_LEFT | MV_RIGHT                   },
8949       { EL_TUBE_VERTICAL_LEFT,          MV_LEFT |            MV_UP | MV_DOWN },
8950       { EL_TUBE_VERTICAL_RIGHT,                   MV_RIGHT | MV_UP | MV_DOWN },
8951       { EL_TUBE_HORIZONTAL_UP,          MV_LEFT | MV_RIGHT | MV_UP           },
8952       { EL_TUBE_HORIZONTAL_DOWN,        MV_LEFT | MV_RIGHT |         MV_DOWN },
8953       { EL_TUBE_LEFT_UP,                MV_LEFT |            MV_UP           },
8954       { EL_TUBE_LEFT_DOWN,              MV_LEFT |                    MV_DOWN },
8955       { EL_TUBE_RIGHT_UP,                         MV_RIGHT | MV_UP           },
8956       { EL_TUBE_RIGHT_DOWN,                       MV_RIGHT |         MV_DOWN },
8957       { -1,                             MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN }
8958     };
8959
8960     while (tube_leave_directions[i][0] != tube_element)
8961     {
8962       i++;
8963       if (tube_leave_directions[i][0] == -1)    /* should not happen */
8964         break;
8965     }
8966
8967     if (!(tube_leave_directions[i][1] & move_direction))
8968       return MF_NO_ACTION;      /* tube has no opening in this direction */
8969   }
8970
8971   element = Feld[x][y];
8972
8973   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
8974       game.engine_version >= VERSION_IDENT(2,2,0,0))
8975     return MF_NO_ACTION;
8976
8977   switch (element)
8978   {
8979     case EL_SP_PORT_LEFT:
8980     case EL_SP_PORT_RIGHT:
8981     case EL_SP_PORT_UP:
8982     case EL_SP_PORT_DOWN:
8983     case EL_SP_PORT_HORIZONTAL:
8984     case EL_SP_PORT_VERTICAL:
8985     case EL_SP_PORT_ANY:
8986     case EL_SP_GRAVITY_PORT_LEFT:
8987     case EL_SP_GRAVITY_PORT_RIGHT:
8988     case EL_SP_GRAVITY_PORT_UP:
8989     case EL_SP_GRAVITY_PORT_DOWN:
8990 #if 1
8991       if (!canEnterSupaplexPort(x, y, dx, dy))
8992         return MF_NO_ACTION;
8993 #else
8994       if ((dx == -1 &&
8995            element != EL_SP_PORT_LEFT &&
8996            element != EL_SP_GRAVITY_PORT_LEFT &&
8997            element != EL_SP_PORT_HORIZONTAL &&
8998            element != EL_SP_PORT_ANY) ||
8999           (dx == +1 &&
9000            element != EL_SP_PORT_RIGHT &&
9001            element != EL_SP_GRAVITY_PORT_RIGHT &&
9002            element != EL_SP_PORT_HORIZONTAL &&
9003            element != EL_SP_PORT_ANY) ||
9004           (dy == -1 &&
9005            element != EL_SP_PORT_UP &&
9006            element != EL_SP_GRAVITY_PORT_UP &&
9007            element != EL_SP_PORT_VERTICAL &&
9008            element != EL_SP_PORT_ANY) ||
9009           (dy == +1 &&
9010            element != EL_SP_PORT_DOWN &&
9011            element != EL_SP_GRAVITY_PORT_DOWN &&
9012            element != EL_SP_PORT_VERTICAL &&
9013            element != EL_SP_PORT_ANY) ||
9014           !IN_LEV_FIELD(nextx, nexty) ||
9015           !IS_FREE(nextx, nexty))
9016         return MF_NO_ACTION;
9017 #endif
9018
9019       if (element == EL_SP_GRAVITY_PORT_LEFT ||
9020           element == EL_SP_GRAVITY_PORT_RIGHT ||
9021           element == EL_SP_GRAVITY_PORT_UP ||
9022           element == EL_SP_GRAVITY_PORT_DOWN)
9023         game.gravity = !game.gravity;
9024
9025       /* automatically move to the next field with double speed */
9026       player->programmed_action = move_direction;
9027 #if 1
9028       if (player->move_delay_reset_counter == 0)
9029       {
9030         player->move_delay_reset_counter = 2;   /* two double speed steps */
9031
9032         DOUBLE_PLAYER_SPEED(player);
9033       }
9034 #else
9035       player->move_delay_reset_counter = 2;
9036
9037       DOUBLE_PLAYER_SPEED(player);
9038 #endif
9039
9040       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
9041       break;
9042
9043     case EL_TUBE_ANY:
9044     case EL_TUBE_VERTICAL:
9045     case EL_TUBE_HORIZONTAL:
9046     case EL_TUBE_VERTICAL_LEFT:
9047     case EL_TUBE_VERTICAL_RIGHT:
9048     case EL_TUBE_HORIZONTAL_UP:
9049     case EL_TUBE_HORIZONTAL_DOWN:
9050     case EL_TUBE_LEFT_UP:
9051     case EL_TUBE_LEFT_DOWN:
9052     case EL_TUBE_RIGHT_UP:
9053     case EL_TUBE_RIGHT_DOWN:
9054       {
9055         int i = 0;
9056         int tube_enter_directions[][2] =
9057         {
9058           { EL_TUBE_ANY,                MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
9059           { EL_TUBE_VERTICAL,                                MV_UP | MV_DOWN },
9060           { EL_TUBE_HORIZONTAL,         MV_LEFT | MV_RIGHT                   },
9061           { EL_TUBE_VERTICAL_LEFT,                MV_RIGHT | MV_UP | MV_DOWN },
9062           { EL_TUBE_VERTICAL_RIGHT,     MV_LEFT            | MV_UP | MV_DOWN },
9063           { EL_TUBE_HORIZONTAL_UP,      MV_LEFT | MV_RIGHT |         MV_DOWN },
9064           { EL_TUBE_HORIZONTAL_DOWN,    MV_LEFT | MV_RIGHT | MV_UP           },
9065           { EL_TUBE_LEFT_UP,                      MV_RIGHT |         MV_DOWN },
9066           { EL_TUBE_LEFT_DOWN,                    MV_RIGHT | MV_UP           },
9067           { EL_TUBE_RIGHT_UP,           MV_LEFT |                    MV_DOWN },
9068           { EL_TUBE_RIGHT_DOWN,         MV_LEFT |            MV_UP           },
9069           { -1,                         MV_NO_MOVING                         }
9070         };
9071
9072         while (tube_enter_directions[i][0] != element)
9073         {
9074           i++;
9075           if (tube_enter_directions[i][0] == -1)        /* should not happen */
9076             break;
9077         }
9078
9079         if (!(tube_enter_directions[i][1] & move_direction))
9080           return MF_NO_ACTION;  /* tube has no opening in this direction */
9081
9082         PlayLevelSound(x, y, SND_CLASS_TUBE_WALKING);
9083       }
9084       break;
9085
9086     default:
9087
9088       if (IS_WALKABLE(element))
9089       {
9090         int sound_action = ACTION_WALKING;
9091
9092         if (element >= EL_GATE_1 && element <= EL_GATE_4)
9093         {
9094           if (!player->key[element - EL_GATE_1])
9095             return MF_NO_ACTION;
9096         }
9097         else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
9098         {
9099           if (!player->key[element - EL_GATE_1_GRAY])
9100             return MF_NO_ACTION;
9101         }
9102         else if (element == EL_EXIT_OPEN ||
9103                  element == EL_SP_EXIT_OPEN ||
9104                  element == EL_SP_EXIT_OPENING)
9105         {
9106           sound_action = ACTION_PASSING;        /* player is passing exit */
9107         }
9108         else if (element == EL_EMPTY)
9109         {
9110           sound_action = ACTION_MOVING;         /* nothing to walk on */
9111         }
9112
9113         /* play sound from background or player, whatever is available */
9114         if (element_info[element].sound[sound_action] != SND_UNDEFINED)
9115           PlayLevelSoundElementAction(x, y, element, sound_action);
9116         else
9117           PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
9118
9119         break;
9120       }
9121       else if (IS_PASSABLE(element))
9122       {
9123         if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
9124           return MF_NO_ACTION;
9125
9126 #if 1
9127         if (CAN_MOVE(element))  /* only fixed elements can be passed! */
9128           return MF_NO_ACTION;
9129 #endif
9130
9131         if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
9132         {
9133           if (!player->key[element - EL_EM_GATE_1])
9134             return MF_NO_ACTION;
9135         }
9136         else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
9137         {
9138           if (!player->key[element - EL_EM_GATE_1_GRAY])
9139             return MF_NO_ACTION;
9140         }
9141
9142         /* automatically move to the next field with double speed */
9143         player->programmed_action = move_direction;
9144 #if 1
9145         if (player->move_delay_reset_counter == 0)
9146         {
9147           player->move_delay_reset_counter = 2; /* two double speed steps */
9148
9149           DOUBLE_PLAYER_SPEED(player);
9150         }
9151 #else
9152         player->move_delay_reset_counter = 2;
9153
9154         DOUBLE_PLAYER_SPEED(player);
9155 #endif
9156
9157         PlayLevelSoundAction(x, y, ACTION_PASSING);
9158
9159         break;
9160       }
9161       else if (IS_DIGGABLE(element))
9162       {
9163         RemoveField(x, y);
9164
9165         if (mode != DF_SNAP)
9166         {
9167 #if 1
9168           GfxElement[x][y] = GFX_ELEMENT(element);
9169 #else
9170           GfxElement[x][y] =
9171             (GFX_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
9172 #endif
9173           player->is_digging = TRUE;
9174         }
9175
9176         PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
9177
9178         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
9179
9180 #if 1
9181         if (mode == DF_SNAP)
9182           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9183 #endif
9184
9185         break;
9186       }
9187       else if (IS_COLLECTIBLE(element))
9188       {
9189         RemoveField(x, y);
9190
9191         if (mode != DF_SNAP)
9192         {
9193           GfxElement[x][y] = element;
9194           player->is_collecting = TRUE;
9195         }
9196
9197         if (element == EL_SPEED_PILL)
9198           player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
9199         else if (element == EL_EXTRA_TIME && level.time > 0)
9200         {
9201           TimeLeft += 10;
9202           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9203         }
9204         else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
9205         {
9206           player->shield_normal_time_left += 10;
9207           if (element == EL_SHIELD_DEADLY)
9208             player->shield_deadly_time_left += 10;
9209         }
9210         else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
9211         {
9212           if (player->inventory_size < MAX_INVENTORY_SIZE)
9213             player->inventory_element[player->inventory_size++] = element;
9214
9215           DrawText(DX_DYNAMITE, DY_DYNAMITE,
9216                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9217         }
9218         else if (element == EL_DYNABOMB_INCREASE_NUMBER)
9219         {
9220           player->dynabomb_count++;
9221           player->dynabombs_left++;
9222         }
9223         else if (element == EL_DYNABOMB_INCREASE_SIZE)
9224         {
9225           player->dynabomb_size++;
9226         }
9227         else if (element == EL_DYNABOMB_INCREASE_POWER)
9228         {
9229           player->dynabomb_xl = TRUE;
9230         }
9231         else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
9232                  (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
9233         {
9234           int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
9235                         element - EL_KEY_1 : element - EL_EM_KEY_1);
9236
9237           player->key[key_nr] = TRUE;
9238
9239           DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
9240                              el2edimg(EL_KEY_1 + key_nr));
9241           redraw_mask |= REDRAW_DOOR_1;
9242         }
9243         else if (IS_ENVELOPE(element))
9244         {
9245 #if 1
9246           player->show_envelope = element;
9247 #else
9248           ShowEnvelope(element - EL_ENVELOPE_1);
9249 #endif
9250         }
9251         else if (IS_DROPPABLE(element)) /* can be collected and dropped */
9252         {
9253           int i;
9254
9255           for (i = 0; i < element_info[element].collect_count; i++)
9256             if (player->inventory_size < MAX_INVENTORY_SIZE)
9257               player->inventory_element[player->inventory_size++] = element;
9258
9259           DrawText(DX_DYNAMITE, DY_DYNAMITE,
9260                    int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9261         }
9262         else if (element_info[element].collect_count > 0)
9263         {
9264           local_player->gems_still_needed -=
9265             element_info[element].collect_count;
9266           if (local_player->gems_still_needed < 0)
9267             local_player->gems_still_needed = 0;
9268
9269           DrawText(DX_EMERALDS, DY_EMERALDS,
9270                    int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
9271         }
9272
9273         RaiseScoreElement(element);
9274         PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
9275
9276         CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
9277
9278 #if 1
9279         if (mode == DF_SNAP)
9280           TestIfElementTouchesCustomElement(x, y);      /* for empty space */
9281 #endif
9282
9283         break;
9284       }
9285       else if (IS_PUSHABLE(element))
9286       {
9287         if (mode == DF_SNAP && element != EL_BD_ROCK)
9288           return MF_NO_ACTION;
9289
9290         if (CAN_FALL(element) && dy)
9291           return MF_NO_ACTION;
9292
9293         if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
9294             !(element == EL_SPRING && use_spring_bug))
9295           return MF_NO_ACTION;
9296
9297 #if 1
9298         if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
9299             ((move_direction & MV_VERTICAL &&
9300               ((element_info[element].move_pattern & MV_LEFT &&
9301                 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
9302                (element_info[element].move_pattern & MV_RIGHT &&
9303                 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
9304              (move_direction & MV_HORIZONTAL &&
9305               ((element_info[element].move_pattern & MV_UP &&
9306                 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
9307                (element_info[element].move_pattern & MV_DOWN &&
9308                 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
9309           return MF_NO_ACTION;
9310 #endif
9311
9312 #if 1
9313         /* do not push elements already moving away faster than player */
9314         if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
9315             ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
9316           return MF_NO_ACTION;
9317 #else
9318         if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
9319           return MF_NO_ACTION;
9320 #endif
9321
9322 #if 1
9323         if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9324         {
9325           if (player->push_delay_value == -1)
9326             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9327         }
9328         else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
9329         {
9330           if (!player->is_pushing)
9331             player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9332         }
9333
9334         /*
9335         if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
9336             (game.engine_version < VERSION_IDENT(3,0,7,1) ||
9337              !player_is_pushing))
9338           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9339         */
9340 #else
9341         if (!player->is_pushing &&
9342             game.engine_version >= VERSION_IDENT(2,2,0,7))
9343           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9344 #endif
9345
9346 #if 0
9347         printf("::: push delay: %ld [%d, %d] [%d]\n",
9348                player->push_delay_value, FrameCounter, game.engine_version,
9349                player->is_pushing);
9350 #endif
9351
9352         player->is_pushing = TRUE;
9353
9354         if (!(IN_LEV_FIELD(nextx, nexty) &&
9355               (IS_FREE(nextx, nexty) ||
9356                (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
9357                 IS_SB_ELEMENT(element)))))
9358           return MF_NO_ACTION;
9359
9360         if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
9361           return MF_NO_ACTION;
9362
9363         if (player->push_delay == 0)    /* new pushing; restart delay */
9364           player->push_delay = FrameCounter;
9365
9366         if (!FrameReached(&player->push_delay, player->push_delay_value) &&
9367             !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
9368             element != EL_SPRING && element != EL_BALLOON)
9369         {
9370           /* make sure that there is no move delay before next try to push */
9371           if (game.engine_version >= VERSION_IDENT(3,0,7,1))
9372             player->move_delay = INITIAL_MOVE_DELAY_OFF;
9373
9374           return MF_NO_ACTION;
9375         }
9376
9377 #if 0
9378         printf("::: NOW PUSHING... [%d]\n", FrameCounter);
9379 #endif
9380
9381         if (IS_SB_ELEMENT(element))
9382         {
9383           if (element == EL_SOKOBAN_FIELD_FULL)
9384           {
9385             Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
9386             local_player->sokobanfields_still_needed++;
9387           }
9388
9389           if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
9390           {
9391             Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
9392             local_player->sokobanfields_still_needed--;
9393           }
9394
9395           Feld[x][y] = EL_SOKOBAN_OBJECT;
9396
9397           if (Back[x][y] == Back[nextx][nexty])
9398             PlayLevelSoundAction(x, y, ACTION_PUSHING);
9399           else if (Back[x][y] != 0)
9400             PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
9401                                         ACTION_EMPTYING);
9402           else
9403             PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
9404                                         ACTION_FILLING);
9405
9406           if (local_player->sokobanfields_still_needed == 0 &&
9407               game.emulation == EMU_SOKOBAN)
9408           {
9409             player->LevelSolved = player->GameOver = TRUE;
9410             PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
9411           }
9412         }
9413         else
9414           PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
9415
9416         InitMovingField(x, y, move_direction);
9417         GfxAction[x][y] = ACTION_PUSHING;
9418
9419         if (mode == DF_SNAP)
9420           ContinueMoving(x, y);
9421         else
9422           MovPos[x][y] = (dx != 0 ? dx : dy);
9423
9424         Pushed[x][y] = TRUE;
9425         Pushed[nextx][nexty] = TRUE;
9426
9427         if (game.engine_version < VERSION_IDENT(2,2,0,7))
9428           player->push_delay_value = GET_NEW_PUSH_DELAY(element);
9429         else
9430           player->push_delay_value = -1;        /* get new value later */
9431
9432         CheckTriggeredElementSideChange(x, y, element, dig_side,
9433                                         CE_OTHER_GETS_PUSHED);
9434         CheckElementSideChange(x, y, element, dig_side,
9435                                CE_PUSHED_BY_PLAYER, -1);
9436
9437         break;
9438       }
9439       else if (IS_SWITCHABLE(element))
9440       {
9441         if (PLAYER_SWITCHING(player, x, y))
9442           return MF_ACTION;
9443
9444         player->is_switching = TRUE;
9445         player->switch_x = x;
9446         player->switch_y = y;
9447
9448         PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
9449
9450         if (element == EL_ROBOT_WHEEL)
9451         {
9452           Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
9453           ZX = x;
9454           ZY = y;
9455
9456           DrawLevelField(x, y);
9457         }
9458         else if (element == EL_SP_TERMINAL)
9459         {
9460           int xx, yy;
9461
9462           for (yy = 0; yy < lev_fieldy; yy++) for (xx=0; xx < lev_fieldx; xx++)
9463           {
9464             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
9465               Bang(xx, yy);
9466             else if (Feld[xx][yy] == EL_SP_TERMINAL)
9467               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
9468           }
9469         }
9470         else if (IS_BELT_SWITCH(element))
9471         {
9472           ToggleBeltSwitch(x, y);
9473         }
9474         else if (element == EL_SWITCHGATE_SWITCH_UP ||
9475                  element == EL_SWITCHGATE_SWITCH_DOWN)
9476         {
9477           ToggleSwitchgateSwitch(x, y);
9478         }
9479         else if (element == EL_LIGHT_SWITCH ||
9480                  element == EL_LIGHT_SWITCH_ACTIVE)
9481         {
9482           ToggleLightSwitch(x, y);
9483
9484 #if 0
9485           PlayLevelSound(x, y, element == EL_LIGHT_SWITCH ?
9486                          SND_LIGHT_SWITCH_ACTIVATING :
9487                          SND_LIGHT_SWITCH_DEACTIVATING);
9488 #endif
9489         }
9490         else if (element == EL_TIMEGATE_SWITCH)
9491         {
9492           ActivateTimegateSwitch(x, y);
9493         }
9494         else if (element == EL_BALLOON_SWITCH_LEFT ||
9495                  element == EL_BALLOON_SWITCH_RIGHT ||
9496                  element == EL_BALLOON_SWITCH_UP ||
9497                  element == EL_BALLOON_SWITCH_DOWN ||
9498                  element == EL_BALLOON_SWITCH_ANY)
9499         {
9500           if (element == EL_BALLOON_SWITCH_ANY)
9501             game.balloon_dir = move_direction;
9502           else
9503             game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
9504                                 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
9505                                 element == EL_BALLOON_SWITCH_UP    ? MV_UP :
9506                                 element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
9507                                 MV_NO_MOVING);
9508         }
9509         else if (element == EL_LAMP)
9510         {
9511           Feld[x][y] = EL_LAMP_ACTIVE;
9512           local_player->lights_still_needed--;
9513
9514           DrawLevelField(x, y);
9515         }
9516         else if (element == EL_TIME_ORB_FULL)
9517         {
9518           Feld[x][y] = EL_TIME_ORB_EMPTY;
9519           TimeLeft += 10;
9520           DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
9521
9522           DrawLevelField(x, y);
9523
9524 #if 0
9525           PlaySoundStereo(SND_TIME_ORB_FULL_COLLECTING, SOUND_MIDDLE);
9526 #endif
9527         }
9528
9529         return MF_ACTION;
9530       }
9531       else
9532       {
9533         if (!PLAYER_SWITCHING(player, x, y))
9534         {
9535           player->is_switching = TRUE;
9536           player->switch_x = x;
9537           player->switch_y = y;
9538
9539           CheckTriggeredElementSideChange(x, y, element, dig_side,
9540                                           CE_OTHER_IS_SWITCHING);
9541           CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
9542         }
9543
9544         CheckTriggeredElementSideChange(x, y, element, dig_side,
9545                                         CE_OTHER_GETS_PRESSED);
9546         CheckElementSideChange(x, y, element, dig_side,
9547                                CE_PRESSED_BY_PLAYER, -1);
9548       }
9549
9550       return MF_NO_ACTION;
9551   }
9552
9553   player->push_delay = 0;
9554
9555   if (Feld[x][y] != element)            /* really digged/collected something */
9556     player->is_collecting = !player->is_digging;
9557
9558   return MF_MOVING;
9559 }
9560
9561 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
9562 {
9563   int jx = player->jx, jy = player->jy;
9564   int x = jx + dx, y = jy + dy;
9565   int snap_direction = (dx == -1 ? MV_LEFT :
9566                         dx == +1 ? MV_RIGHT :
9567                         dy == -1 ? MV_UP :
9568                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
9569
9570   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
9571     return FALSE;
9572
9573   if (!player->active || !IN_LEV_FIELD(x, y))
9574     return FALSE;
9575
9576   if (dx && dy)
9577     return FALSE;
9578
9579   if (!dx && !dy)
9580   {
9581     if (player->MovPos == 0)
9582       player->is_pushing = FALSE;
9583
9584     player->is_snapping = FALSE;
9585
9586     if (player->MovPos == 0)
9587     {
9588       player->is_moving = FALSE;
9589       player->is_digging = FALSE;
9590       player->is_collecting = FALSE;
9591     }
9592
9593     return FALSE;
9594   }
9595
9596   if (player->is_snapping)
9597     return FALSE;
9598
9599   player->MovDir = snap_direction;
9600
9601 #if 1
9602   if (player->MovPos == 0)
9603 #endif
9604   {
9605     player->is_moving = FALSE;
9606     player->is_digging = FALSE;
9607     player->is_collecting = FALSE;
9608   }
9609
9610   player->is_dropping = FALSE;
9611
9612   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
9613     return FALSE;
9614
9615   player->is_snapping = TRUE;
9616
9617 #if 1
9618   if (player->MovPos == 0)
9619 #endif
9620   {
9621     player->is_moving = FALSE;
9622     player->is_digging = FALSE;
9623     player->is_collecting = FALSE;
9624   }
9625
9626   DrawLevelField(x, y);
9627   BackToFront();
9628
9629   return TRUE;
9630 }
9631
9632 boolean DropElement(struct PlayerInfo *player)
9633 {
9634   int jx = player->jx, jy = player->jy;
9635   int old_element = Feld[jx][jy];
9636   int new_element;
9637
9638   /* check if player is active, not moving and ready to drop */
9639   if (!player->active || player->MovPos || player->drop_delay > 0)
9640     return FALSE;
9641
9642   /* check if player has anything that can be dropped */
9643   if (player->inventory_size == 0 && player->dynabombs_left == 0)
9644     return FALSE;
9645
9646   /* check if anything can be dropped at the current position */
9647   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
9648     return FALSE;
9649
9650   /* collected custom elements can only be dropped on empty fields */
9651   if (player->inventory_size > 0 &&
9652       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
9653       && old_element != EL_EMPTY)
9654     return FALSE;
9655
9656   if (old_element != EL_EMPTY)
9657     Back[jx][jy] = old_element;         /* store old element on this field */
9658
9659   ResetGfxAnimation(jx, jy);
9660   ResetRandomAnimationValue(jx, jy);
9661
9662   if (player->inventory_size > 0)
9663   {
9664     player->inventory_size--;
9665     new_element = player->inventory_element[player->inventory_size];
9666
9667     if (new_element == EL_DYNAMITE)
9668       new_element = EL_DYNAMITE_ACTIVE;
9669     else if (new_element == EL_SP_DISK_RED)
9670       new_element = EL_SP_DISK_RED_ACTIVE;
9671
9672     Feld[jx][jy] = new_element;
9673
9674     DrawText(DX_DYNAMITE, DY_DYNAMITE,
9675              int2str(local_player->inventory_size, 3), FONT_TEXT_2);
9676
9677     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9678       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9679
9680     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9681
9682 #if 1
9683     /* needed if previous element just changed to "empty" in the last frame */
9684     Changed[jx][jy] = 0;                /* allow another change */
9685 #endif
9686
9687     CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
9688     CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
9689
9690     TestIfElementTouchesCustomElement(jx, jy);
9691   }
9692   else          /* player is dropping a dyna bomb */
9693   {
9694     player->dynabombs_left--;
9695     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
9696
9697     Feld[jx][jy] = new_element;
9698
9699     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
9700       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
9701
9702     PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
9703   }
9704
9705
9706
9707 #if 1
9708
9709   if (Feld[jx][jy] == new_element)      /* uninitialized unless CE change */
9710   {
9711     InitField(jx, jy, FALSE);
9712     if (CAN_MOVE(Feld[jx][jy]))
9713       InitMovDir(jx, jy);
9714   }
9715
9716   new_element = Feld[jx][jy];
9717
9718   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
9719       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
9720   {
9721     int move_stepsize = element_info[new_element].move_stepsize;
9722     int direction, dx, dy, nextx, nexty;
9723
9724     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
9725       MovDir[jx][jy] = player->MovDir;
9726
9727     direction = MovDir[jx][jy];
9728     dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
9729     dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
9730     nextx = jx + dx;
9731     nexty = jy + dy;
9732
9733     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
9734     {
9735 #if 0
9736       WasJustMoving[jx][jy] = 3;
9737 #else
9738       InitMovingField(jx, jy, direction);
9739       ContinueMoving(jx, jy);
9740 #endif
9741     }
9742     else
9743     {
9744       Changed[jx][jy] = 0;              /* allow another change */
9745
9746 #if 1
9747       TestIfElementHitsCustomElement(jx, jy, direction);
9748 #else
9749       CheckElementSideChange(jx, jy, new_element,
9750                              direction, CE_HITTING_SOMETHING, -1);
9751 #endif
9752     }
9753
9754     player->drop_delay = 2 * TILEX / move_stepsize + 1;
9755   }
9756
9757 #if 0
9758   player->drop_delay = 8 + 8 + 8;
9759 #endif
9760
9761 #endif
9762
9763   player->is_dropping = TRUE;
9764
9765
9766   return TRUE;
9767 }
9768
9769 /* ------------------------------------------------------------------------- */
9770 /* game sound playing functions                                              */
9771 /* ------------------------------------------------------------------------- */
9772
9773 static int *loop_sound_frame = NULL;
9774 static int *loop_sound_volume = NULL;
9775
9776 void InitPlayLevelSound()
9777 {
9778   int num_sounds = getSoundListSize();
9779
9780   checked_free(loop_sound_frame);
9781   checked_free(loop_sound_volume);
9782
9783   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
9784   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
9785 }
9786
9787 static void PlayLevelSound(int x, int y, int nr)
9788 {
9789   int sx = SCREENX(x), sy = SCREENY(y);
9790   int volume, stereo_position;
9791   int max_distance = 8;
9792   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
9793
9794   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
9795       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
9796     return;
9797
9798   if (!IN_LEV_FIELD(x, y) ||
9799       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
9800       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
9801     return;
9802
9803   volume = SOUND_MAX_VOLUME;
9804
9805   if (!IN_SCR_FIELD(sx, sy))
9806   {
9807     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
9808     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
9809
9810     volume -= volume * (dx > dy ? dx : dy) / max_distance;
9811   }
9812
9813   stereo_position = (SOUND_MAX_LEFT +
9814                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
9815                      (SCR_FIELDX + 2 * max_distance));
9816
9817   if (IS_LOOP_SOUND(nr))
9818   {
9819     /* This assures that quieter loop sounds do not overwrite louder ones,
9820        while restarting sound volume comparison with each new game frame. */
9821
9822     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
9823       return;
9824
9825     loop_sound_volume[nr] = volume;
9826     loop_sound_frame[nr] = FrameCounter;
9827   }
9828
9829   PlaySoundExt(nr, volume, stereo_position, type);
9830 }
9831
9832 static void PlayLevelSoundNearest(int x, int y, int sound_action)
9833 {
9834   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
9835                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
9836                  y < LEVELY(BY1) ? LEVELY(BY1) :
9837                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
9838                  sound_action);
9839 }
9840
9841 static void PlayLevelSoundAction(int x, int y, int action)
9842 {
9843   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
9844 }
9845
9846 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
9847 {
9848   int sound_effect = element_info[element].sound[action];
9849
9850   if (sound_effect != SND_UNDEFINED)
9851     PlayLevelSound(x, y, sound_effect);
9852 }
9853
9854 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
9855                                               int action)
9856 {
9857   int sound_effect = element_info[element].sound[action];
9858
9859   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9860     PlayLevelSound(x, y, sound_effect);
9861 }
9862
9863 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
9864 {
9865   int sound_effect = element_info[Feld[x][y]].sound[action];
9866
9867   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9868     PlayLevelSound(x, y, sound_effect);
9869 }
9870
9871 static void StopLevelSoundActionIfLoop(int x, int y, int action)
9872 {
9873   int sound_effect = element_info[Feld[x][y]].sound[action];
9874
9875   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
9876     StopSound(sound_effect);
9877 }
9878
9879 static void PlayLevelMusic()
9880 {
9881   if (levelset.music[level_nr] != MUS_UNDEFINED)
9882     PlayMusic(levelset.music[level_nr]);        /* from config file */
9883   else
9884     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
9885 }
9886
9887 void RaiseScore(int value)
9888 {
9889   local_player->score += value;
9890   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5), FONT_TEXT_2);
9891 }
9892
9893 void RaiseScoreElement(int element)
9894 {
9895   switch(element)
9896   {
9897     case EL_EMERALD:
9898     case EL_BD_DIAMOND:
9899     case EL_EMERALD_YELLOW:
9900     case EL_EMERALD_RED:
9901     case EL_EMERALD_PURPLE:
9902     case EL_SP_INFOTRON:
9903       RaiseScore(level.score[SC_EMERALD]);
9904       break;
9905     case EL_DIAMOND:
9906       RaiseScore(level.score[SC_DIAMOND]);
9907       break;
9908     case EL_CRYSTAL:
9909       RaiseScore(level.score[SC_CRYSTAL]);
9910       break;
9911     case EL_PEARL:
9912       RaiseScore(level.score[SC_PEARL]);
9913       break;
9914     case EL_BUG:
9915     case EL_BD_BUTTERFLY:
9916     case EL_SP_ELECTRON:
9917       RaiseScore(level.score[SC_BUG]);
9918       break;
9919     case EL_SPACESHIP:
9920     case EL_BD_FIREFLY:
9921     case EL_SP_SNIKSNAK:
9922       RaiseScore(level.score[SC_SPACESHIP]);
9923       break;
9924     case EL_YAMYAM:
9925     case EL_DARK_YAMYAM:
9926       RaiseScore(level.score[SC_YAMYAM]);
9927       break;
9928     case EL_ROBOT:
9929       RaiseScore(level.score[SC_ROBOT]);
9930       break;
9931     case EL_PACMAN:
9932       RaiseScore(level.score[SC_PACMAN]);
9933       break;
9934     case EL_NUT:
9935       RaiseScore(level.score[SC_NUT]);
9936       break;
9937     case EL_DYNAMITE:
9938     case EL_SP_DISK_RED:
9939     case EL_DYNABOMB_INCREASE_NUMBER:
9940     case EL_DYNABOMB_INCREASE_SIZE:
9941     case EL_DYNABOMB_INCREASE_POWER:
9942       RaiseScore(level.score[SC_DYNAMITE]);
9943       break;
9944     case EL_SHIELD_NORMAL:
9945     case EL_SHIELD_DEADLY:
9946       RaiseScore(level.score[SC_SHIELD]);
9947       break;
9948     case EL_EXTRA_TIME:
9949       RaiseScore(level.score[SC_TIME_BONUS]);
9950       break;
9951     case EL_KEY_1:
9952     case EL_KEY_2:
9953     case EL_KEY_3:
9954     case EL_KEY_4:
9955       RaiseScore(level.score[SC_KEY]);
9956       break;
9957     default:
9958       RaiseScore(element_info[element].collect_score);
9959       break;
9960   }
9961 }
9962
9963 void RequestQuitGame(boolean ask_if_really_quit)
9964 {
9965   if (AllPlayersGone ||
9966       !ask_if_really_quit ||
9967       level_editor_test_game ||
9968       Request("Do you really want to quit the game ?",
9969               REQ_ASK | REQ_STAY_CLOSED))
9970   {
9971 #if defined(PLATFORM_UNIX)
9972     if (options.network)
9973       SendToServer_StopPlaying();
9974     else
9975 #endif
9976     {
9977       game_status = GAME_MODE_MAIN;
9978       DrawMainMenu();
9979     }
9980   }
9981   else
9982   {
9983     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
9984   }
9985 }
9986
9987
9988 /* ---------- new game button stuff ---------------------------------------- */
9989
9990 /* graphic position values for game buttons */
9991 #define GAME_BUTTON_XSIZE       30
9992 #define GAME_BUTTON_YSIZE       30
9993 #define GAME_BUTTON_XPOS        5
9994 #define GAME_BUTTON_YPOS        215
9995 #define SOUND_BUTTON_XPOS       5
9996 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
9997
9998 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
9999 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10000 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10001 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
10002 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
10003 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
10004
10005 static struct
10006 {
10007   int x, y;
10008   int gadget_id;
10009   char *infotext;
10010 } gamebutton_info[NUM_GAME_BUTTONS] =
10011 {
10012   {
10013     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
10014     GAME_CTRL_ID_STOP,
10015     "stop game"
10016   },
10017   {
10018     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
10019     GAME_CTRL_ID_PAUSE,
10020     "pause game"
10021   },
10022   {
10023     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
10024     GAME_CTRL_ID_PLAY,
10025     "play game"
10026   },
10027   {
10028     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
10029     SOUND_CTRL_ID_MUSIC,
10030     "background music on/off"
10031   },
10032   {
10033     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
10034     SOUND_CTRL_ID_LOOPS,
10035     "sound loops on/off"
10036   },
10037   {
10038     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
10039     SOUND_CTRL_ID_SIMPLE,
10040     "normal sounds on/off"
10041   }
10042 };
10043
10044 void CreateGameButtons()
10045 {
10046   int i;
10047
10048   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10049   {
10050     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
10051     struct GadgetInfo *gi;
10052     int button_type;
10053     boolean checked;
10054     unsigned long event_mask;
10055     int gd_xoffset, gd_yoffset;
10056     int gd_x1, gd_x2, gd_y1, gd_y2;
10057     int id = i;
10058
10059     gd_xoffset = gamebutton_info[i].x;
10060     gd_yoffset = gamebutton_info[i].y;
10061     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
10062     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
10063
10064     if (id == GAME_CTRL_ID_STOP ||
10065         id == GAME_CTRL_ID_PAUSE ||
10066         id == GAME_CTRL_ID_PLAY)
10067     {
10068       button_type = GD_TYPE_NORMAL_BUTTON;
10069       checked = FALSE;
10070       event_mask = GD_EVENT_RELEASED;
10071       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10072       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10073     }
10074     else
10075     {
10076       button_type = GD_TYPE_CHECK_BUTTON;
10077       checked =
10078         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
10079          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
10080          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
10081       event_mask = GD_EVENT_PRESSED;
10082       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
10083       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
10084     }
10085
10086     gi = CreateGadget(GDI_CUSTOM_ID, id,
10087                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
10088                       GDI_X, DX + gd_xoffset,
10089                       GDI_Y, DY + gd_yoffset,
10090                       GDI_WIDTH, GAME_BUTTON_XSIZE,
10091                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
10092                       GDI_TYPE, button_type,
10093                       GDI_STATE, GD_BUTTON_UNPRESSED,
10094                       GDI_CHECKED, checked,
10095                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
10096                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
10097                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
10098                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
10099                       GDI_EVENT_MASK, event_mask,
10100                       GDI_CALLBACK_ACTION, HandleGameButtons,
10101                       GDI_END);
10102
10103     if (gi == NULL)
10104       Error(ERR_EXIT, "cannot create gadget");
10105
10106     game_gadget[id] = gi;
10107   }
10108 }
10109
10110 void FreeGameButtons()
10111 {
10112   int i;
10113
10114   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10115     FreeGadget(game_gadget[i]);
10116 }
10117
10118 static void MapGameButtons()
10119 {
10120   int i;
10121
10122   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10123     MapGadget(game_gadget[i]);
10124 }
10125
10126 void UnmapGameButtons()
10127 {
10128   int i;
10129
10130   for (i = 0; i < NUM_GAME_BUTTONS; i++)
10131     UnmapGadget(game_gadget[i]);
10132 }
10133
10134 static void HandleGameButtons(struct GadgetInfo *gi)
10135 {
10136   int id = gi->custom_id;
10137
10138   if (game_status != GAME_MODE_PLAYING)
10139     return;
10140
10141   switch (id)
10142   {
10143     case GAME_CTRL_ID_STOP:
10144       RequestQuitGame(TRUE);
10145       break;
10146
10147     case GAME_CTRL_ID_PAUSE:
10148       if (options.network)
10149       {
10150 #if defined(PLATFORM_UNIX)
10151         if (tape.pausing)
10152           SendToServer_ContinuePlaying();
10153         else
10154           SendToServer_PausePlaying();
10155 #endif
10156       }
10157       else
10158         TapeTogglePause(TAPE_TOGGLE_MANUAL);
10159       break;
10160
10161     case GAME_CTRL_ID_PLAY:
10162       if (tape.pausing)
10163       {
10164 #if defined(PLATFORM_UNIX)
10165         if (options.network)
10166           SendToServer_ContinuePlaying();
10167         else
10168 #endif
10169         {
10170           tape.pausing = FALSE;
10171           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
10172         }
10173       }
10174       break;
10175
10176     case SOUND_CTRL_ID_MUSIC:
10177       if (setup.sound_music)
10178       { 
10179         setup.sound_music = FALSE;
10180         FadeMusic();
10181       }
10182       else if (audio.music_available)
10183       { 
10184         setup.sound = setup.sound_music = TRUE;
10185
10186         SetAudioMode(setup.sound);
10187
10188         PlayLevelMusic();
10189       }
10190       break;
10191
10192     case SOUND_CTRL_ID_LOOPS:
10193       if (setup.sound_loops)
10194         setup.sound_loops = FALSE;
10195       else if (audio.loops_available)
10196       {
10197         setup.sound = setup.sound_loops = TRUE;
10198         SetAudioMode(setup.sound);
10199       }
10200       break;
10201
10202     case SOUND_CTRL_ID_SIMPLE:
10203       if (setup.sound_simple)
10204         setup.sound_simple = FALSE;
10205       else if (audio.sound_available)
10206       {
10207         setup.sound = setup.sound_simple = TRUE;
10208         SetAudioMode(setup.sound);
10209       }
10210       break;
10211
10212     default:
10213       break;
10214   }
10215 }