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