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