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