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