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