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