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