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