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