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