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