rnd-19990216-2
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  game.c                                                  *
12 ***********************************************************/
13
14 #include "game.h"
15 #include "misc.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "sound.h"
19 #include "init.h"
20 #include "buttons.h"
21 #include "files.h"
22 #include "tape.h"
23 #include "joystick.h"
24 #include "network.h"
25
26 /* for DigField() */
27 #define DF_NO_PUSH              0
28 #define DF_DIG                  1
29 #define DF_SNAP                 2
30
31 /* for MoveFigure() */
32 #define MF_NO_ACTION            0
33 #define MF_MOVING               1
34 #define MF_ACTION               2
35
36 /* for ScrollFigure() */
37 #define SCROLL_INIT             0
38 #define SCROLL_GO_ON            1
39
40 /* for Explode() */
41 #define EX_PHASE_START          0
42 #define EX_NORMAL               0
43 #define EX_CENTER               1
44 #define EX_BORDER               2
45
46 /* special positions in the game control window (relative to control window) */
47 #define XX_LEVEL                37
48 #define YY_LEVEL                20
49 #define XX_EMERALDS             29
50 #define YY_EMERALDS             54
51 #define XX_DYNAMITE             29
52 #define YY_DYNAMITE             89
53 #define XX_KEYS                 18
54 #define YY_KEYS                 123
55 #define XX_SCORE                15
56 #define YY_SCORE                159
57 #define XX_TIME                 29
58 #define YY_TIME                 194
59
60 /* special positions in the game control window (relative to main window) */
61 #define DX_LEVEL                (DX + XX_LEVEL)
62 #define DY_LEVEL                (DY + YY_LEVEL)
63 #define DX_EMERALDS             (DX + XX_EMERALDS)
64 #define DY_EMERALDS             (DY + YY_EMERALDS)
65 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
66 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
67 #define DX_KEYS                 (DX + XX_KEYS)
68 #define DY_KEYS                 (DY + YY_KEYS)
69 #define DX_SCORE                (DX + XX_SCORE)
70 #define DY_SCORE                (DY + YY_SCORE)
71 #define DX_TIME                 (DX + XX_TIME)
72 #define DY_TIME                 (DY + YY_TIME)
73
74 #define IS_LOOP_SOUND(s)        ((s)==SND_KLAPPER || (s)==SND_ROEHR ||  \
75                                  (s)==SND_NJAM || (s)==SND_MIEP)
76 #define IS_MUSIC_SOUND(s)       ((s)==SND_ALCHEMY || (s)==SND_CHASE || \
77                                  (s)==SND_NETWORK || (s)==SND_CZARDASZ || \
78                                  (s)==SND_TYGER || (s)==SND_VOYAGER || \
79                                  (s)==SND_TWILIGHT)
80
81 /* values for player movement speed (which is in fact a delay value) */
82 #define MOVE_DELAY_NORMAL_SPEED 8
83 #define MOVE_DELAY_HIGH_SPEED   4
84
85 #define DOUBLE_MOVE_DELAY(x)    (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
86 #define HALVE_MOVE_DELAY(x)     (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
87 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY((p)->move_delay_value))
88 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
89
90 /* game button identifiers */
91 #define GAME_CTRL_ID_STOP               0
92 #define GAME_CTRL_ID_PAUSE              1
93 #define GAME_CTRL_ID_PLAY               2
94 #define SOUND_CTRL_ID_MUSIC             3
95 #define SOUND_CTRL_ID_LOOPS             4
96 #define SOUND_CTRL_ID_SIMPLE            5
97
98 #define NUM_GAME_BUTTONS                6
99
100 /* forward declaration for internal use */
101 static void CheckGravityMovement(struct PlayerInfo *);
102
103 static void MapGameButtons();
104 static void HandleGameButtons(struct GadgetInfo *);
105
106 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
107
108
109
110 #ifdef DEBUG
111 #if 0
112 static unsigned int getStateCheckSum(int counter)
113 {
114   int x, y;
115   unsigned int mult = 1;
116   unsigned int checksum = 0;
117   /*
118   static short lastFeld[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
119   */
120   static boolean first_game = TRUE;
121
122   for (y=0; y<lev_fieldy; y++) for(x=0; x<lev_fieldx; x++)
123   {
124     /*
125     if (counter == 3)
126     {
127       if (first_game)
128         lastFeld[x][y] = Feld[x][y];
129       else if (lastFeld[x][y] != Feld[x][y])
130         printf("DIFF: [%d][%d]: lastFeld == %d != %d == Feld\n",
131                x, y, lastFeld[x][y], Feld[x][y]);
132     }
133     */
134
135     checksum += mult++ * Ur[x][y];
136     checksum += mult++ * Feld[x][y];
137
138     /*
139     checksum += mult++ * MovPos[x][y];
140     checksum += mult++ * MovDir[x][y];
141     checksum += mult++ * MovDelay[x][y];
142     checksum += mult++ * Store[x][y];
143     checksum += mult++ * Store2[x][y];
144     checksum += mult++ * StorePlayer[x][y];
145     checksum += mult++ * Frame[x][y];
146     checksum += mult++ * AmoebaNr[x][y];
147     checksum += mult++ * JustHit[x][y];
148     checksum += mult++ * Stop[x][y];
149     */
150   }
151
152   if (counter == 3 && first_game)
153     first_game = FALSE;
154
155   return checksum;
156 }
157 #endif
158 #endif
159
160
161
162
163 void GetPlayerConfig()
164 {
165   if (sound_status == SOUND_OFF)
166     setup.sound = FALSE;
167
168   if (!sound_loops_allowed)
169   {
170     setup.sound_loops = FALSE;
171     setup.sound_music = FALSE;
172   }
173
174   setup.sound_simple = setup.sound;
175
176   InitJoysticks();
177 }
178
179 static void InitField(int x, int y, boolean init_game)
180 {
181   switch (Feld[x][y])
182   {
183     case EL_SPIELFIGUR:
184     case EL_SP_MURPHY:
185       if (init_game)
186         Feld[x][y] = EL_SPIELER1;
187       /* no break! */
188     case EL_SPIELER1:
189     case EL_SPIELER2:
190     case EL_SPIELER3:
191     case EL_SPIELER4:
192       if (init_game)
193       {
194         struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_SPIELER1];
195         int jx = player->jx, jy = player->jy;
196
197         player->present = TRUE;
198
199         if (!options.network || player->connected)
200         {
201           player->active = TRUE;
202
203           /* remove potentially duplicate players */
204           if (StorePlayer[jx][jy] == Feld[x][y])
205             StorePlayer[jx][jy] = 0;
206
207           StorePlayer[x][y] = Feld[x][y];
208
209           if (options.verbose)
210           {
211             printf("Player %d activated.\n", player->element_nr);
212             printf("[Local player is %d and currently %s.]\n",
213                    local_player->element_nr,
214                    local_player->active ? "active" : "not active");
215           }
216         }
217
218         Feld[x][y] = EL_LEERRAUM;
219         player->jx = player->last_jx = x;
220         player->jy = player->last_jy = y;
221       }
222       break;
223
224     case EL_BADEWANNE:
225       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_SALZSAEURE)
226         Feld[x][y] = EL_BADEWANNE1;
227       else if (x > 0 && Feld[x-1][y] == EL_SALZSAEURE)
228         Feld[x][y] = EL_BADEWANNE2;
229       else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE1)
230         Feld[x][y] = EL_BADEWANNE3;
231       else if (y > 0 && Feld[x][y-1] == EL_SALZSAEURE)
232         Feld[x][y] = EL_BADEWANNE4;
233       else if (y > 0 && Feld[x][y-1] == EL_BADEWANNE2)
234         Feld[x][y] = EL_BADEWANNE5;
235       break;
236
237     case EL_KAEFER_R:
238     case EL_KAEFER_O:
239     case EL_KAEFER_L:
240     case EL_KAEFER_U:
241     case EL_KAEFER:
242     case EL_FLIEGER_R:
243     case EL_FLIEGER_O:
244     case EL_FLIEGER_L:
245     case EL_FLIEGER_U:
246     case EL_FLIEGER:
247     case EL_BUTTERFLY_R:
248     case EL_BUTTERFLY_O:
249     case EL_BUTTERFLY_L:
250     case EL_BUTTERFLY_U:
251     case EL_BUTTERFLY:
252     case EL_FIREFLY_R:
253     case EL_FIREFLY_O:
254     case EL_FIREFLY_L:
255     case EL_FIREFLY_U:
256     case EL_FIREFLY:
257     case EL_PACMAN_R:
258     case EL_PACMAN_O:
259     case EL_PACMAN_L:
260     case EL_PACMAN_U:
261     case EL_MAMPFER:
262     case EL_MAMPFER2:
263     case EL_ROBOT:
264     case EL_PACMAN:
265     case EL_SP_SNIKSNAK:
266     case EL_SP_ELECTRON:
267       InitMovDir(x, y);
268       break;
269
270     case EL_AMOEBE_VOLL:
271     case EL_AMOEBE_BD:
272       InitAmoebaNr(x, y);
273       break;
274
275     case EL_TROPFEN:
276       if (y == lev_fieldy - 1)
277       {
278         Feld[x][y] = EL_AMOEBING;
279         Store[x][y] = EL_AMOEBE_NASS;
280       }
281       break;
282
283     case EL_DYNAMIT:
284       MovDelay[x][y] = 96;
285       break;
286
287     case EL_BIRNE_AUS:
288       local_player->lights_still_needed++;
289       break;
290
291     case EL_SOKOBAN_FELD_LEER:
292       local_player->sokobanfields_still_needed++;
293       break;
294
295     case EL_MAULWURF:
296     case EL_PINGUIN:
297       local_player->friends_still_needed++;
298       break;
299
300     case EL_SCHWEIN:
301     case EL_DRACHE:
302       MovDir[x][y] = 1 << RND(4);
303       break;
304
305     case EL_SP_EMPTY:
306       Feld[x][y] = EL_LEERRAUM;
307       break;
308
309     case EL_EM_KEY_1_FILE:
310       Feld[x][y] = EL_EM_KEY_1;
311       break;
312     case EL_EM_KEY_2_FILE:
313       Feld[x][y] = EL_EM_KEY_2;
314       break;
315     case EL_EM_KEY_3_FILE:
316       Feld[x][y] = EL_EM_KEY_3;
317       break;
318     case EL_EM_KEY_4_FILE:
319       Feld[x][y] = EL_EM_KEY_4;
320       break;
321
322     default:
323       break;
324   }
325 }
326
327 void InitGame()
328 {
329   int i, j, x, y;
330   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
331   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
332   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
333
334   /* don't play tapes over network */
335   network_playing = (options.network && !tape.playing);
336
337   for (i=0; i<MAX_PLAYERS; i++)
338   {
339     struct PlayerInfo *player = &stored_player[i];
340
341     player->index_nr = i;
342     player->element_nr = EL_SPIELER1 + i;
343
344     player->present = FALSE;
345     player->active = FALSE;
346
347     player->action = 0;
348     player->effective_action = 0;
349     player->programmed_action = 0;
350
351     player->score = 0;
352     player->gems_still_needed = level.edelsteine;
353     player->sokobanfields_still_needed = 0;
354     player->lights_still_needed = 0;
355     player->friends_still_needed = 0;
356
357     for (j=0; j<4; j++)
358       player->key[j] = FALSE;
359
360     player->dynamite = 0;
361     player->dynabomb_count = 0;
362     player->dynabomb_size = 0;
363     player->dynabombs_left = 0;
364     player->dynabomb_xl = FALSE;
365
366     player->MovDir = MV_NO_MOVING;
367     player->MovPos = 0;
368     player->Pushing = FALSE;
369     player->GfxPos = 0;
370     player->Frame = 0;
371
372     player->actual_frame_counter = 0;
373
374     player->frame_reset_delay = 0;
375
376     player->push_delay = 0;
377     player->push_delay_value = 5;
378
379     player->move_delay = 0;
380     player->last_move_dir = MV_NO_MOVING;
381
382     player->move_delay_value =
383       (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
384
385     player->snapped = FALSE;
386
387     player->gone = FALSE;
388
389     player->last_jx = player->last_jy = 0;
390     player->jx = player->jy = 0;
391
392     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
393     SnapField(player, 0, 0);
394
395     player->LevelSolved = FALSE;
396     player->GameOver = FALSE;
397   }
398
399   network_player_action_received = FALSE;
400
401 #ifndef MSDOS
402   /* initial null action */
403   if (network_playing)
404     SendToServer_MovePlayer(MV_NO_MOVING);
405 #endif
406
407   ZX = ZY = -1;
408
409   MampferNr = 0;
410   FrameCounter = 0;
411   TimeFrames = 0;
412   TimePlayed = 0;
413   TimeLeft = level.time;
414
415   ScreenMovDir = MV_NO_MOVING;
416   ScreenMovPos = 0;
417   ScreenGfxPos = 0;
418
419   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
420
421   AllPlayersGone = FALSE;
422   SiebAktiv = FALSE;
423   SiebCount = 0;
424
425   for (i=0; i<MAX_NUM_AMOEBA; i++)
426     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
427
428   for (x=0; x<lev_fieldx; x++)
429   {
430     for (y=0; y<lev_fieldy; y++)
431     {
432       Feld[x][y] = Ur[x][y];
433       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
434       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = 0;
435       Frame[x][y] = 0;
436       AmoebaNr[x][y] = 0;
437       JustHit[x][y] = 0;
438       Stop[x][y] = FALSE;
439     }
440   }
441
442   for(y=0; y<lev_fieldy; y++)
443   {
444     for(x=0; x<lev_fieldx; x++)
445     {
446       if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
447         emulate_bd = FALSE;
448       if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
449         emulate_sb = FALSE;
450       if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
451         emulate_sp = FALSE;
452
453       InitField(x, y, TRUE);
454     }
455   }
456
457   /* check if any connected player was not found in playfield */
458   for (i=0; i<MAX_PLAYERS; i++)
459   {
460     struct PlayerInfo *player = &stored_player[i];
461
462     if (player->connected && !player->present)
463     {
464       for (j=0; j<MAX_PLAYERS; j++)
465       {
466         struct PlayerInfo *some_player = &stored_player[j];
467         int jx = some_player->jx, jy = some_player->jy;
468
469         /* assign first free player found that is present in the playfield */
470         if (some_player->present && !some_player->connected)
471         {
472           player->present = TRUE;
473           player->active = TRUE;
474           some_player->present = FALSE;
475
476           StorePlayer[jx][jy] = player->element_nr;
477           player->jx = player->last_jx = jx;
478           player->jy = player->last_jy = jy;
479
480           break;
481         }
482       }
483     }
484   }
485
486   if (tape.playing)
487   {
488     /* when playing a tape, eliminate all players who do not participate */
489
490     for (i=0; i<MAX_PLAYERS; i++)
491     {
492       if (stored_player[i].active && !tape.player_participates[i])
493       {
494         struct PlayerInfo *player = &stored_player[i];
495         int jx = player->jx, jy = player->jy;
496
497         player->active = FALSE;
498         StorePlayer[jx][jy] = 0;
499         Feld[jx][jy] = EL_LEERRAUM;
500       }
501     }
502   }
503   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
504   {
505     /* when in single player mode, eliminate all but the first active player */
506
507     for (i=0; i<MAX_PLAYERS; i++)
508     {
509       if (stored_player[i].active)
510       {
511         for (j=i+1; j<MAX_PLAYERS; j++)
512         {
513           if (stored_player[j].active)
514           {
515             struct PlayerInfo *player = &stored_player[j];
516             int jx = player->jx, jy = player->jy;
517
518             player->active = FALSE;
519             StorePlayer[jx][jy] = 0;
520             Feld[jx][jy] = EL_LEERRAUM;
521           }
522         }
523       }
524     }
525   }
526
527   /* when recording the game, store which players take part in the game */
528   if (tape.recording)
529   {
530     for (i=0; i<MAX_PLAYERS; i++)
531       if (stored_player[i].active)
532         tape.player_participates[i] = TRUE;
533   }
534
535   if (options.verbose)
536   {
537     for (i=0; i<MAX_PLAYERS; i++)
538     {
539       struct PlayerInfo *player = &stored_player[i];
540
541       printf("Player %d: present == %d, connected == %d, active == %d.\n",
542              i+1,
543              player->present,
544              player->connected,
545              player->active);
546       if (local_player == player)
547         printf("Player  %d is local player.\n", i+1);
548     }
549   }
550
551   game_emulation = (emulate_bd ? EMU_BOULDERDASH :
552                     emulate_sb ? EMU_SOKOBAN :
553                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
554
555   /* determine border element for this level */
556   SetBorderElement();
557
558   if (BorderElement == EL_LEERRAUM)
559   {
560     SBX_Left = 0;
561     SBX_Right = lev_fieldx - SCR_FIELDX;
562     SBY_Upper = 0;
563     SBY_Lower = lev_fieldy - SCR_FIELDY;
564   }
565   else
566   {
567     SBX_Left = -1;
568     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
569     SBY_Upper = -1;
570     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
571   }
572
573   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
574     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
575
576   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
577     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
578
579   scroll_x = SBX_Left;
580   scroll_y = SBY_Upper;
581   if (local_player->jx >= SBX_Left + MIDPOSX)
582     scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
583                 local_player->jx - MIDPOSX :
584                 SBX_Right);
585   if (local_player->jy >= SBY_Upper + MIDPOSY)
586     scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
587                 local_player->jy - MIDPOSY :
588                 SBY_Lower);
589
590   CloseDoor(DOOR_CLOSE_1);
591
592   DrawLevel();
593   DrawAllPlayers();
594   FadeToFront();
595
596   /* copy default game door content to main double buffer */
597   XCopyArea(display, pix[PIX_DOOR], drawto, gc,
598             DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
599
600   DrawText(DX + XX_LEVEL, DY + YY_LEVEL,
601            int2str(level_nr, 2), FS_SMALL, FC_YELLOW);
602   DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
603            int2str(local_player->gems_still_needed,3), FS_SMALL, FC_YELLOW);
604   DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
605            int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
606   DrawText(DX + XX_SCORE, DY + YY_SCORE,
607            int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
608   DrawText(DX + XX_TIME, DY + YY_TIME,
609            int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
610
611   UnmapGameButtons();
612   game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
613   game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
614   game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
615   MapGameButtons();
616   MapTapeButtons();
617
618   /* copy actual game door content to door double buffer for OpenDoor() */
619   XCopyArea(display, drawto, pix[PIX_DB_DOOR], gc,
620             DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
621
622   OpenDoor(DOOR_OPEN_ALL);
623
624   if (setup.sound_music)
625     PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
626
627   XAutoRepeatOff(display);
628
629   if (options.verbose)
630   {
631     for (i=0; i<4; i++)
632       printf("Player %d %sactive.\n",
633              i + 1, (stored_player[i].active ? "" : "not "));
634   }
635 }
636
637 void InitMovDir(int x, int y)
638 {
639   int i, element = Feld[x][y];
640   static int xy[4][2] =
641   {
642     {  0, +1 },
643     { +1,  0 },
644     {  0, -1 },
645     { -1,  0 }
646   };
647   static int direction[2][4] =
648   {
649     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
650     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP }
651   };
652
653   switch(element)
654   {
655     case EL_KAEFER_R:
656     case EL_KAEFER_O:
657     case EL_KAEFER_L:
658     case EL_KAEFER_U:
659       Feld[x][y] = EL_KAEFER;
660       MovDir[x][y] = direction[0][element - EL_KAEFER_R];
661       break;
662
663     case EL_FLIEGER_R:
664     case EL_FLIEGER_O:
665     case EL_FLIEGER_L:
666     case EL_FLIEGER_U:
667       Feld[x][y] = EL_FLIEGER;
668       MovDir[x][y] = direction[0][element - EL_FLIEGER_R];
669       break;
670
671     case EL_BUTTERFLY_R:
672     case EL_BUTTERFLY_O:
673     case EL_BUTTERFLY_L:
674     case EL_BUTTERFLY_U:
675       Feld[x][y] = EL_BUTTERFLY;
676       MovDir[x][y] = direction[0][element - EL_BUTTERFLY_R];
677       break;
678
679     case EL_FIREFLY_R:
680     case EL_FIREFLY_O:
681     case EL_FIREFLY_L:
682     case EL_FIREFLY_U:
683       Feld[x][y] = EL_FIREFLY;
684       MovDir[x][y] = direction[0][element - EL_FIREFLY_R];
685       break;
686
687     case EL_PACMAN_R:
688     case EL_PACMAN_O:
689     case EL_PACMAN_L:
690     case EL_PACMAN_U:
691       Feld[x][y] = EL_PACMAN;
692       MovDir[x][y] = direction[0][element - EL_PACMAN_R];
693       break;
694
695     case EL_SP_SNIKSNAK:
696       MovDir[x][y] = MV_UP;
697       break;
698
699     case EL_SP_ELECTRON:
700       MovDir[x][y] = MV_LEFT;
701       break;
702
703     default:
704       MovDir[x][y] = 1 << RND(4);
705       if (element != EL_KAEFER &&
706           element != EL_FLIEGER &&
707           element != EL_BUTTERFLY &&
708           element != EL_FIREFLY)
709         break;
710
711       for (i=0; i<4; i++)
712       {
713         int x1 = x + xy[i][0];
714         int y1 = y + xy[i][1];
715
716         if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
717         {
718           if (element == EL_KAEFER || element == EL_BUTTERFLY)
719           {
720             MovDir[x][y] = direction[0][i];
721             break;
722           }
723           else if (element == EL_FLIEGER || element == EL_FIREFLY ||
724                    element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
725           {
726             MovDir[x][y] = direction[1][i];
727             break;
728           }
729         }
730       }
731       break;
732   }
733 }
734
735 void InitAmoebaNr(int x, int y)
736 {
737   int i;
738   int group_nr = AmoebeNachbarNr(x, y);
739
740   if (group_nr == 0)
741   {
742     for (i=1; i<MAX_NUM_AMOEBA; i++)
743     {
744       if (AmoebaCnt[i] == 0)
745       {
746         group_nr = i;
747         break;
748       }
749     }
750   }
751
752   AmoebaNr[x][y] = group_nr;
753   AmoebaCnt[group_nr]++;
754   AmoebaCnt2[group_nr]++;
755 }
756
757 void GameWon()
758 {
759   int hi_pos;
760   int bumplevel = FALSE;
761
762   if (local_player->MovPos)
763     return;
764
765   local_player->LevelSolved = FALSE;
766
767   if (TimeLeft)
768   {
769     if (setup.sound_loops)
770       PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
771
772     while(TimeLeft > 0)
773     {
774       if (!setup.sound_loops)
775         PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
776       if (TimeLeft > 0 && !(TimeLeft % 10))
777         RaiseScore(level.score[SC_ZEITBONUS]);
778       if (TimeLeft > 100 && !(TimeLeft % 10))
779         TimeLeft -= 10;
780       else
781         TimeLeft--;
782       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
783       BackToFront();
784       Delay(10);
785     }
786
787     if (setup.sound_loops)
788       StopSound(SND_SIRR);
789   }
790   else if (level.time == 0)             /* level without time limit */
791   {
792     if (setup.sound_loops)
793       PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
794
795     while(TimePlayed < 999)
796     {
797       if (!setup.sound_loops)
798         PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
799       if (TimePlayed < 999 && !(TimePlayed % 10))
800         RaiseScore(level.score[SC_ZEITBONUS]);
801       if (TimePlayed < 900 && !(TimePlayed % 10))
802         TimePlayed += 10;
803       else
804         TimePlayed++;
805       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
806       BackToFront();
807       Delay(10);
808     }
809
810     if (setup.sound_loops)
811       StopSound(SND_SIRR);
812   }
813
814   FadeSounds();
815
816   /* Hero disappears */
817   DrawLevelField(ExitX, ExitY);
818   BackToFront();
819
820   if (tape.playing)
821     return;
822
823   CloseDoor(DOOR_CLOSE_1);
824
825   if (tape.recording)
826   {
827     TapeStop();
828     SaveTape(tape.level_nr);            /* Ask to save tape */
829   }
830
831   if ((hi_pos = NewHiScore()) >= 0) 
832   {
833     game_status = HALLOFFAME;
834     DrawHallOfFame(hi_pos);
835     if (bumplevel && TAPE_IS_EMPTY(tape))
836       level_nr++;
837   }
838   else
839   {
840     game_status = MAINMENU;
841     if (bumplevel && TAPE_IS_EMPTY(tape))
842       level_nr++;
843     DrawMainMenu();
844   }
845
846   BackToFront();
847 }
848
849 int NewHiScore()
850 {
851   int k, l;
852   int position = -1;
853
854   LoadScore(level_nr);
855
856   if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
857       local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
858     return -1;
859
860   for (k=0; k<MAX_SCORE_ENTRIES; k++) 
861   {
862     if (local_player->score > highscore[k].Score)
863     {
864       /* player has made it to the hall of fame */
865
866       if (k < MAX_SCORE_ENTRIES - 1)
867       {
868         int m = MAX_SCORE_ENTRIES - 1;
869
870 #ifdef ONE_PER_NAME
871         for (l=k; l<MAX_SCORE_ENTRIES; l++)
872           if (!strcmp(setup.player_name, highscore[l].Name))
873             m = l;
874         if (m == k)     /* player's new highscore overwrites his old one */
875           goto put_into_list;
876 #endif
877
878         for (l=m; l>k; l--)
879         {
880           strcpy(highscore[l].Name, highscore[l - 1].Name);
881           highscore[l].Score = highscore[l - 1].Score;
882         }
883       }
884
885 #ifdef ONE_PER_NAME
886       put_into_list:
887 #endif
888       strncpy(highscore[k].Name, setup.player_name, MAX_NAMELEN - 1);
889       highscore[k].Name[MAX_NAMELEN - 1] = '\0';
890       highscore[k].Score = local_player->score; 
891       position = k;
892       break;
893     }
894
895 #ifdef ONE_PER_NAME
896     else if (!strncmp(setup.player_name, highscore[k].Name, MAX_NAMELEN - 1))
897       break;    /* player already there with a higher score */
898 #endif
899
900   }
901
902   if (position >= 0) 
903     SaveScore(level_nr);
904
905   return position;
906 }
907
908 void InitMovingField(int x, int y, int direction)
909 {
910   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
911   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
912
913   MovDir[x][y] = direction;
914   MovDir[newx][newy] = direction;
915   if (Feld[newx][newy] == EL_LEERRAUM)
916     Feld[newx][newy] = EL_BLOCKED;
917 }
918
919 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
920 {
921   int direction = MovDir[x][y];
922   int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
923   int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
924
925   *goes_to_x = newx;
926   *goes_to_y = newy;
927 }
928
929 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
930 {
931   int oldx = x, oldy = y;
932   int direction = MovDir[x][y];
933
934   if (direction == MV_LEFT)
935     oldx++;
936   else if (direction == MV_RIGHT)
937     oldx--;
938   else if (direction == MV_UP)
939     oldy++;
940   else if (direction == MV_DOWN)
941     oldy--;
942
943   *comes_from_x = oldx;
944   *comes_from_y = oldy;
945 }
946
947 int MovingOrBlocked2Element(int x, int y)
948 {
949   int element = Feld[x][y];
950
951   if (element == EL_BLOCKED)
952   {
953     int oldx, oldy;
954
955     Blocked2Moving(x, y, &oldx, &oldy);
956     return Feld[oldx][oldy];
957   }
958   else
959     return element;
960 }
961
962 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
963 {
964   /* like MovingOrBlocked2Element(), but if element is moving
965      and (x,y) is the field the moving element is just leaving,
966      return EL_BLOCKED instead of the element value */
967   int element = Feld[x][y];
968
969   if (IS_MOVING(x, y))
970   {
971     if (element == EL_BLOCKED)
972     {
973       int oldx, oldy;
974
975       Blocked2Moving(x, y, &oldx, &oldy);
976       return Feld[oldx][oldy];
977     }
978     else
979       return EL_BLOCKED;
980   }
981   else
982     return element;
983 }
984
985 static void RemoveField(int x, int y)
986 {
987   Feld[x][y] = EL_LEERRAUM;
988   MovPos[x][y] = 0;
989   MovDir[x][y] = 0;
990   MovDelay[x][y] = 0;
991 }
992
993 void RemoveMovingField(int x, int y)
994 {
995   int oldx = x, oldy = y, newx = x, newy = y;
996
997   if (Feld[x][y] != EL_BLOCKED && !IS_MOVING(x, y))
998     return;
999
1000   if (IS_MOVING(x, y))
1001   {
1002     Moving2Blocked(x, y, &newx, &newy);
1003     if (Feld[newx][newy] != EL_BLOCKED)
1004       return;
1005   }
1006   else if (Feld[x][y] == EL_BLOCKED)
1007   {
1008     Blocked2Moving(x, y, &oldx, &oldy);
1009     if (!IS_MOVING(oldx, oldy))
1010       return;
1011   }
1012
1013   if (Feld[x][y] == EL_BLOCKED &&
1014       (Store[oldx][oldy] == EL_MORAST_LEER ||
1015        Store[oldx][oldy] == EL_SIEB_LEER ||
1016        Store[oldx][oldy] == EL_SIEB2_LEER ||
1017        Store[oldx][oldy] == EL_AMOEBE_NASS))
1018   {
1019     Feld[oldx][oldy] = Store[oldx][oldy];
1020     Store[oldx][oldy] = Store2[oldx][oldy] = 0;
1021   }
1022   else
1023     Feld[oldx][oldy] = EL_LEERRAUM;
1024
1025   Feld[newx][newy] = EL_LEERRAUM;
1026   MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
1027   MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
1028
1029   DrawLevelField(oldx, oldy);
1030   DrawLevelField(newx, newy);
1031 }
1032
1033 void DrawDynamite(int x, int y)
1034 {
1035   int sx = SCREENX(x), sy = SCREENY(y);
1036   int graphic = el2gfx(Feld[x][y]);
1037   int phase;
1038
1039   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
1040     return;
1041
1042   if (Store[x][y])
1043     DrawGraphic(sx, sy, el2gfx(Store[x][y]));
1044
1045   if (Feld[x][y] == EL_DYNAMIT)
1046   {
1047     if ((phase = (96 - MovDelay[x][y]) / 12) > 6)
1048       phase = 6;
1049   }
1050   else
1051   {
1052     if ((phase = ((96 - MovDelay[x][y]) / 6) % 8) > 3)
1053       phase = 7 - phase;
1054   }
1055
1056   if (game_emulation == EMU_SUPAPLEX)
1057     DrawGraphic(sx, sy, GFX_SP_DISK_RED);
1058   else if (Store[x][y])
1059     DrawGraphicThruMask(sx, sy, graphic + phase);
1060   else
1061     DrawGraphic(sx, sy, graphic + phase);
1062 }
1063
1064 void CheckDynamite(int x, int y)
1065 {
1066   if (MovDelay[x][y])           /* dynamite is still waiting to explode */
1067   {
1068     MovDelay[x][y]--;
1069     if (MovDelay[x][y])
1070     {
1071       if (!(MovDelay[x][y] % 12))
1072         PlaySoundLevel(x, y, SND_ZISCH);
1073
1074       if (Feld[x][y] == EL_DYNAMIT && !(MovDelay[x][y] % 12))
1075         DrawDynamite(x, y);
1076       else if (Feld[x][y] == EL_DYNABOMB && !(MovDelay[x][y] % 6))
1077         DrawDynamite(x, y);
1078
1079       return;
1080     }
1081   }
1082
1083   StopSound(SND_ZISCH);
1084   Bang(x, y);
1085 }
1086
1087 void Explode(int ex, int ey, int phase, int mode)
1088 {
1089   int x, y;
1090   int num_phase = 9, delay = 2;
1091   int last_phase = num_phase * delay;
1092   int half_phase = (num_phase / 2) * delay;
1093   int first_phase_after_start = EX_PHASE_START + 1;
1094
1095   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
1096   {
1097     int center_element = Feld[ex][ey];
1098
1099     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
1100     {
1101       center_element = MovingOrBlocked2Element(ex, ey);
1102       RemoveMovingField(ex, ey);
1103     }
1104
1105     for (y=ey-1; y<ey+2; y++) for(x=ex-1; x<ex+2; x++)
1106     {
1107       int element = Feld[x][y];
1108
1109       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
1110       {
1111         element = MovingOrBlocked2Element(x, y);
1112         RemoveMovingField(x, y);
1113       }
1114
1115       if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(element) || element == EL_BURNING)
1116         continue;
1117
1118       if ((mode!=EX_NORMAL || center_element == EL_AMOEBA2DIAM) &&
1119           (x!=ex || y!=ey))
1120         continue;
1121
1122       if (element == EL_EXPLODING)
1123         element = Store2[x][y];
1124
1125       if (IS_PLAYER(ex, ey))
1126       {
1127         switch(StorePlayer[ex][ey])
1128         {
1129           case EL_SPIELER2:
1130             Store[x][y] = EL_EDELSTEIN_ROT;
1131             break;
1132           case EL_SPIELER3:
1133             Store[x][y] = EL_EDELSTEIN;
1134             break;
1135           case EL_SPIELER4:
1136             Store[x][y] = EL_EDELSTEIN_LILA;
1137             break;
1138           case EL_SPIELER1:
1139           default:
1140             Store[x][y] = EL_EDELSTEIN_GELB;
1141             break;
1142         }
1143
1144         if (game_emulation == EMU_SUPAPLEX)
1145           Store[x][y] = EL_LEERRAUM;
1146       }
1147       else if (center_element == EL_MAULWURF)
1148         Store[x][y] = EL_EDELSTEIN_ROT;
1149       else if (center_element == EL_PINGUIN)
1150         Store[x][y] = EL_EDELSTEIN_LILA;
1151       else if (center_element == EL_KAEFER)
1152         Store[x][y] = ((x == ex && y == ey) ? EL_DIAMANT : EL_EDELSTEIN);
1153       else if (center_element == EL_BUTTERFLY)
1154         Store[x][y] = EL_EDELSTEIN_BD;
1155       else if (center_element == EL_SP_ELECTRON)
1156         Store[x][y] = EL_SP_INFOTRON;
1157       else if (center_element == EL_MAMPFER)
1158         Store[x][y] = level.mampfer_inhalt[MampferNr][x-ex+1][y-ey+1];
1159       else if (center_element == EL_AMOEBA2DIAM)
1160         Store[x][y] = level.amoebe_inhalt;
1161       else if (element == EL_ERZ_EDEL)
1162         Store[x][y] = EL_EDELSTEIN;
1163       else if (element == EL_ERZ_DIAM)
1164         Store[x][y] = EL_DIAMANT;
1165       else if (element == EL_ERZ_EDEL_BD)
1166         Store[x][y] = EL_EDELSTEIN_BD;
1167       else if (element == EL_ERZ_EDEL_GELB)
1168         Store[x][y] = EL_EDELSTEIN_GELB;
1169       else if (element == EL_ERZ_EDEL_ROT)
1170         Store[x][y] = EL_EDELSTEIN_ROT;
1171       else if (element == EL_ERZ_EDEL_LILA)
1172         Store[x][y] = EL_EDELSTEIN_LILA;
1173       else if (!IS_PFORTE(Store[x][y]))
1174         Store[x][y] = EL_LEERRAUM;
1175
1176       if (x != ex || y != ey ||
1177           center_element == EL_AMOEBA2DIAM || mode == EX_BORDER)
1178         Store2[x][y] = element;
1179
1180       if (AmoebaNr[x][y] &&
1181           (element == EL_AMOEBE_VOLL ||
1182            element == EL_AMOEBE_BD ||
1183            element == EL_AMOEBING))
1184       {
1185         AmoebaCnt[AmoebaNr[x][y]]--;
1186         AmoebaCnt2[AmoebaNr[x][y]]--;
1187       }
1188
1189       Feld[x][y] = EL_EXPLODING;
1190       MovDir[x][y] = MovPos[x][y] = 0;
1191       AmoebaNr[x][y] = 0;
1192       Frame[x][y] = 1;
1193       Stop[x][y] = TRUE;
1194     }
1195
1196     if (center_element == EL_MAMPFER)
1197       MampferNr = (MampferNr + 1) % MampferMax;
1198
1199     return;
1200   }
1201
1202   if (Stop[ex][ey])
1203     return;
1204
1205   x = ex;
1206   y = ey;
1207
1208   Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
1209
1210   if (phase == first_phase_after_start)
1211   {
1212     int element = Store2[x][y];
1213
1214     if (element == EL_BLACK_ORB)
1215     {
1216       Feld[x][y] = Store2[x][y];
1217       Store2[x][y] = 0;
1218       Bang(x, y);
1219     }
1220   }
1221   else if (phase == half_phase)
1222   {
1223     int element = Store2[x][y];
1224
1225     if (IS_PLAYER(x, y))
1226       KillHero(PLAYERINFO(x, y));
1227     else if (IS_EXPLOSIVE(element))
1228     {
1229       Feld[x][y] = Store2[x][y];
1230       Store2[x][y] = 0;
1231       Bang(x, y);
1232     }
1233     else if (element == EL_AMOEBA2DIAM)
1234       AmoebeUmwandeln(x, y);
1235   }
1236
1237   if (phase == last_phase)
1238   {
1239     int element;
1240
1241     element = Feld[x][y] = Store[x][y];
1242     Store[x][y] = Store2[x][y] = 0;
1243     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
1244     InitField(x, y, FALSE);
1245     if (CAN_MOVE(element) || COULD_MOVE(element))
1246       InitMovDir(x, y);
1247     DrawLevelField(x, y);
1248   }
1249   else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1250   {
1251     int graphic = GFX_EXPLOSION;
1252
1253     if (game_emulation == EMU_SUPAPLEX)
1254       graphic = (Store[x][y] == EL_SP_INFOTRON ?
1255                  GFX_SP_EXPLODE_INFOTRON :
1256                  GFX_SP_EXPLODE_EMPTY);
1257
1258     if (phase == delay)
1259       ErdreichAnbroeckeln(SCREENX(x), SCREENY(y));
1260
1261     DrawGraphic(SCREENX(x), SCREENY(y), graphic + (phase / delay - 1));
1262   }
1263 }
1264
1265 void DynaExplode(int ex, int ey)
1266 {
1267   int i, j;
1268   struct PlayerInfo *player = &stored_player[Store2[ex][ey] - EL_SPIELER1];
1269   static int xy[4][2] =
1270   {
1271     { 0, -1 },
1272     { -1, 0 },
1273     { +1, 0 },
1274     { 0, +1 }
1275   };
1276
1277   Store2[ex][ey] = 0;   /* delete player information */
1278
1279   Explode(ex, ey, EX_PHASE_START, EX_CENTER);
1280
1281   for (i=0; i<4; i++)
1282   {
1283     for (j=1; j<=player->dynabomb_size; j++)
1284     {
1285       int x = ex+j*xy[i%4][0];
1286       int y = ey+j*xy[i%4][1];
1287       int element;
1288
1289       if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(Feld[x][y]))
1290         break;
1291
1292       element = Feld[x][y];
1293       Explode(x, y, EX_PHASE_START, EX_BORDER);
1294
1295       if (element != EL_LEERRAUM &&
1296           element != EL_ERDREICH &&
1297           element != EL_EXPLODING &&
1298           !player->dynabomb_xl)
1299         break;
1300     }
1301   }
1302
1303   player->dynabombs_left++;
1304 }
1305
1306 void Bang(int x, int y)
1307 {
1308   int element = Feld[x][y];
1309
1310   if (game_emulation == EMU_SUPAPLEX)
1311     PlaySoundLevel(x, y, SND_SP_BOOOM);
1312   else
1313     PlaySoundLevel(x, y, SND_ROAAAR);
1314
1315   if (IS_PLAYER(x, y))  /* remove objects that might cause smaller explosion */
1316     element = EL_LEERRAUM;
1317
1318   switch(element)
1319   {
1320     case EL_KAEFER:
1321     case EL_FLIEGER:
1322     case EL_BUTTERFLY:
1323     case EL_FIREFLY:
1324     case EL_MAMPFER:
1325     case EL_MAMPFER2:
1326     case EL_ROBOT:
1327     case EL_PACMAN:
1328       RaiseScoreElement(element);
1329       Explode(x, y, EX_PHASE_START, EX_NORMAL);
1330       break;
1331     case EL_DYNABOMB:
1332     case EL_DYNABOMB_NR:
1333     case EL_DYNABOMB_SZ:
1334     case EL_DYNABOMB_XL:
1335       DynaExplode(x, y);
1336       break;
1337     case EL_MAULWURF:
1338     case EL_PINGUIN:
1339     case EL_BIRNE_AUS:
1340     case EL_BIRNE_EIN:
1341       Explode(x, y, EX_PHASE_START, EX_CENTER);
1342       break;
1343     default:
1344       Explode(x, y, EX_PHASE_START, EX_NORMAL);
1345       break;
1346   }
1347 }
1348
1349 void Blurb(int x, int y)
1350 {
1351   int element = Feld[x][y];
1352
1353   if (element != EL_BLURB_LEFT && element != EL_BLURB_RIGHT)    /* start */
1354   {
1355     PlaySoundLevel(x, y, SND_BLURB);
1356     if (IN_LEV_FIELD(x-1, y) && IS_FREE(x-1, y) &&
1357         (!IN_LEV_FIELD(x-1, y-1) ||
1358          !CAN_FALL(MovingOrBlocked2Element(x-1, y-1))))
1359     {
1360       Feld[x-1][y] = EL_BLURB_LEFT;
1361     }
1362     if (IN_LEV_FIELD(x+1, y) && IS_FREE(x+1, y) &&
1363         (!IN_LEV_FIELD(x+1, y-1) ||
1364          !CAN_FALL(MovingOrBlocked2Element(x+1, y-1))))
1365     {
1366       Feld[x+1][y] = EL_BLURB_RIGHT;
1367     }
1368   }
1369   else                                                          /* go on */
1370   {
1371     int graphic = (element==EL_BLURB_LEFT ? GFX_BLURB_LEFT : GFX_BLURB_RIGHT);
1372
1373     if (!MovDelay[x][y])        /* initialize animation counter */
1374       MovDelay[x][y] = 9;
1375
1376     if (MovDelay[x][y])         /* continue animation */
1377     {
1378       MovDelay[x][y]--;
1379       if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1380         DrawGraphic(SCREENX(x), SCREENY(y), graphic+4-MovDelay[x][y]/2);
1381
1382       if (!MovDelay[x][y])
1383       {
1384         Feld[x][y] = EL_LEERRAUM;
1385         DrawLevelField(x, y);
1386       }
1387     }
1388   }
1389 }
1390
1391 void Impact(int x, int y)
1392 {
1393   boolean lastline = (y == lev_fieldy-1);
1394   boolean object_hit = FALSE;
1395   int element = Feld[x][y];
1396   int smashed = 0;
1397
1398   if (!lastline)        /* check if element below was hit */
1399   {
1400     if (Feld[x][y+1] == EL_PLAYER_IS_LEAVING)
1401       return;
1402
1403     object_hit = (!IS_FREE(x, y+1) && (!IS_MOVING(x, y+1) ||
1404                                       MovDir[x][y+1]!=MV_DOWN ||
1405                                       MovPos[x][y+1]<=TILEY/2));
1406     if (object_hit)
1407       smashed = MovingOrBlocked2Element(x, y+1);
1408   }
1409
1410   if (!lastline && smashed == EL_SALZSAEURE)    /* element falls into acid */
1411   {
1412     Blurb(x, y);
1413     return;
1414   }
1415
1416   if ((element == EL_BOMBE || element == EL_SP_DISK_ORANGE) &&
1417       (lastline || object_hit)) /* element is bomb */
1418   {
1419     Bang(x, y);
1420     return;
1421   }
1422
1423   if (element == EL_TROPFEN && (lastline || object_hit))        /* acid drop */
1424   {
1425     if (object_hit && IS_PLAYER(x, y+1))
1426       KillHero(PLAYERINFO(x, y+1));
1427     else if (object_hit && (smashed == EL_MAULWURF || smashed == EL_PINGUIN))
1428       Bang(x, y+1);
1429     else
1430     {
1431       Feld[x][y] = EL_AMOEBING;
1432       Store[x][y] = EL_AMOEBE_NASS;
1433     }
1434     return;
1435   }
1436
1437   if (!lastline && object_hit)          /* check which object was hit */
1438   {
1439     if (CAN_CHANGE(element) && 
1440         (smashed == EL_SIEB_INAKTIV || smashed == EL_SIEB2_INAKTIV))
1441     {
1442       int x, y;
1443       int activated_magic_wall =
1444         (smashed == EL_SIEB_INAKTIV ? EL_SIEB_LEER : EL_SIEB2_LEER);
1445
1446       /* activate magic wall / mill */
1447
1448       for (y=0; y<lev_fieldy; y++)
1449         for (x=0; x<lev_fieldx; x++)
1450           if (Feld[x][y] == smashed)
1451             Feld[x][y] = activated_magic_wall;
1452
1453       SiebCount = level.dauer_sieb * FRAMES_PER_SECOND;
1454       SiebAktiv = TRUE;
1455     }
1456
1457     if (IS_PLAYER(x, y+1))
1458     {
1459       KillHero(PLAYERINFO(x, y+1));
1460       return;
1461     }
1462     else if (smashed == EL_MAULWURF || smashed == EL_PINGUIN)
1463     {
1464       Bang(x, y+1);
1465       return;
1466     }
1467     else if (element == EL_EDELSTEIN_BD)
1468     {
1469       if (IS_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
1470       {
1471         Bang(x, y+1);
1472         return;
1473       }
1474     }
1475     else if (element == EL_FELSBROCKEN || element == EL_SP_ZONK)
1476     {
1477       if (IS_ENEMY(smashed) ||
1478           smashed == EL_BOMBE || smashed == EL_SP_DISK_ORANGE ||
1479           smashed == EL_SONDE || smashed == EL_SCHWEIN || smashed == EL_DRACHE)
1480       {
1481         Bang(x, y+1);
1482         return;
1483       }
1484       else if (!IS_MOVING(x, y+1))
1485       {
1486         if (smashed == EL_BIRNE_AUS || smashed == EL_BIRNE_EIN)
1487         {
1488           Bang(x, y+1);
1489           return;
1490         }
1491         else if (smashed == EL_KOKOSNUSS)
1492         {
1493           Feld[x][y+1] = EL_CRACKINGNUT;
1494           PlaySoundLevel(x, y, SND_KNACK);
1495           RaiseScoreElement(EL_KOKOSNUSS);
1496           return;
1497         }
1498         else if (smashed == EL_DIAMANT)
1499         {
1500           Feld[x][y+1] = EL_LEERRAUM;
1501           PlaySoundLevel(x, y, SND_QUIRK);
1502           return;
1503         }
1504       }
1505     }
1506   }
1507
1508   /* play sound of magic wall / mill */
1509   if (!lastline &&
1510       (Feld[x][y+1] == EL_SIEB_LEER || Feld[x][y+1] == EL_SIEB2_LEER))
1511   {
1512     PlaySoundLevel(x, y, SND_QUIRK);
1513     return;
1514   }
1515
1516   /* play sound of object that hits the ground */
1517   if (lastline || object_hit)
1518   {
1519     int sound;
1520
1521     switch(element)
1522     {
1523       case EL_EDELSTEIN:
1524       case EL_EDELSTEIN_BD:
1525       case EL_EDELSTEIN_GELB:
1526       case EL_EDELSTEIN_ROT:
1527       case EL_EDELSTEIN_LILA:
1528       case EL_DIAMANT:
1529       case EL_SP_INFOTRON:
1530         sound = SND_PLING;
1531         break;
1532       case EL_KOKOSNUSS:
1533         sound = SND_KLUMPF;
1534         break;
1535       case EL_FELSBROCKEN:
1536         sound = SND_KLOPF;
1537         break;
1538       case EL_SP_ZONK:
1539         sound = SND_SP_ZONKDOWN;
1540         break;
1541       case EL_SCHLUESSEL:
1542       case EL_SCHLUESSEL1:
1543       case EL_SCHLUESSEL2:
1544       case EL_SCHLUESSEL3:
1545       case EL_SCHLUESSEL4:
1546       case EL_EM_KEY_1:
1547       case EL_EM_KEY_2:
1548       case EL_EM_KEY_3:
1549       case EL_EM_KEY_4:
1550         sound = SND_KINK;
1551         break;
1552       case EL_ZEIT_VOLL:
1553       case EL_ZEIT_LEER:
1554         sound = SND_DENG;
1555         break;
1556       default:
1557         sound = -1;
1558         break;
1559     }
1560
1561     if (sound>=0)
1562       PlaySoundLevel(x, y, sound);
1563   }
1564 }
1565
1566 void TurnRound(int x, int y)
1567 {
1568   static struct
1569   {
1570     int x, y;
1571   } move_xy[] =
1572   {
1573     { 0, 0 },
1574     {-1, 0 },
1575     {+1, 0 },
1576     { 0, 0 },
1577     { 0, -1 },
1578     { 0, 0 }, { 0, 0 }, { 0, 0 },
1579     { 0, +1 }
1580   };
1581   static struct
1582   {
1583     int left, right, back;
1584   } turn[] =
1585   {
1586     { 0,        0,              0 },
1587     { MV_DOWN,  MV_UP,          MV_RIGHT },
1588     { MV_UP,    MV_DOWN,        MV_LEFT },
1589     { 0,        0,              0 },
1590     { MV_LEFT,  MV_RIGHT,       MV_DOWN },
1591     { 0,0,0 },  { 0,0,0 },      { 0,0,0 },
1592     { MV_RIGHT, MV_LEFT,        MV_UP }
1593   };
1594
1595   int element = Feld[x][y];
1596   int old_move_dir = MovDir[x][y];
1597   int left_dir = turn[old_move_dir].left;
1598   int right_dir = turn[old_move_dir].right;
1599   int back_dir = turn[old_move_dir].back;
1600
1601   int left_dx = move_xy[left_dir].x, left_dy = move_xy[left_dir].y;
1602   int right_dx = move_xy[right_dir].x, right_dy = move_xy[right_dir].y;
1603   int move_dx = move_xy[old_move_dir].x, move_dy = move_xy[old_move_dir].y;
1604   int back_dx = move_xy[back_dir].x, back_dy = move_xy[back_dir].y;
1605
1606   int left_x = x+left_dx, left_y = y+left_dy;
1607   int right_x = x+right_dx, right_y = y+right_dy;
1608   int move_x = x+move_dx, move_y = y+move_dy;
1609
1610   if (element == EL_KAEFER || element == EL_BUTTERFLY)
1611   {
1612     TestIfBadThingHitsOtherBadThing(x, y);
1613
1614     if (IN_LEV_FIELD(right_x, right_y) &&
1615         IS_FREE_OR_PLAYER(right_x, right_y))
1616       MovDir[x][y] = right_dir;
1617     else if (!IN_LEV_FIELD(move_x, move_y) ||
1618              !IS_FREE_OR_PLAYER(move_x, move_y))
1619       MovDir[x][y] = left_dir;
1620
1621     if (element == EL_KAEFER && MovDir[x][y] != old_move_dir)
1622       MovDelay[x][y] = 9;
1623     else if (element == EL_BUTTERFLY)   /* && MovDir[x][y] == left_dir) */
1624       MovDelay[x][y] = 1;
1625   }
1626   else if (element == EL_FLIEGER || element == EL_FIREFLY ||
1627            element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1628   {
1629     TestIfBadThingHitsOtherBadThing(x, y);
1630
1631     if (IN_LEV_FIELD(left_x, left_y) &&
1632         IS_FREE_OR_PLAYER(left_x, left_y))
1633       MovDir[x][y] = left_dir;
1634     else if (!IN_LEV_FIELD(move_x, move_y) ||
1635              !IS_FREE_OR_PLAYER(move_x, move_y))
1636       MovDir[x][y] = right_dir;
1637
1638     if ((element == EL_FLIEGER ||
1639          element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
1640         && MovDir[x][y] != old_move_dir)
1641       MovDelay[x][y] = 9;
1642     else if (element == EL_FIREFLY)     /* && MovDir[x][y] == right_dir) */
1643       MovDelay[x][y] = 1;
1644   }
1645   else if (element == EL_MAMPFER)
1646   {
1647     boolean can_turn_left = FALSE, can_turn_right = FALSE;
1648
1649     if (IN_LEV_FIELD(left_x, left_y) &&
1650         (IS_FREE_OR_PLAYER(left_x, left_y) ||
1651          Feld[left_x][left_y] == EL_DIAMANT))
1652       can_turn_left = TRUE;
1653     if (IN_LEV_FIELD(right_x, right_y) &&
1654         (IS_FREE_OR_PLAYER(right_x, right_y) ||
1655          Feld[right_x][right_y] == EL_DIAMANT))
1656       can_turn_right = TRUE;
1657
1658     if (can_turn_left && can_turn_right)
1659       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
1660     else if (can_turn_left)
1661       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
1662     else if (can_turn_right)
1663       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
1664     else
1665       MovDir[x][y] = back_dir;
1666
1667     MovDelay[x][y] = 16+16*RND(3);
1668   }
1669   else if (element == EL_MAMPFER2)
1670   {
1671     boolean can_turn_left = FALSE, can_turn_right = FALSE;
1672
1673     if (IN_LEV_FIELD(left_x, left_y) &&
1674         (IS_FREE_OR_PLAYER(left_x, left_y) ||
1675          IS_MAMPF2(Feld[left_x][left_y])))
1676       can_turn_left = TRUE;
1677     if (IN_LEV_FIELD(right_x, right_y) &&
1678         (IS_FREE_OR_PLAYER(right_x, right_y) ||
1679          IS_MAMPF2(Feld[right_x][right_y])))
1680       can_turn_right = TRUE;
1681
1682     if (can_turn_left && can_turn_right)
1683       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
1684     else if (can_turn_left)
1685       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
1686     else if (can_turn_right)
1687       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
1688     else
1689       MovDir[x][y] = back_dir;
1690
1691     MovDelay[x][y] = 16+16*RND(3);
1692   }
1693   else if (element == EL_PACMAN)
1694   {
1695     boolean can_turn_left = FALSE, can_turn_right = FALSE;
1696
1697     if (IN_LEV_FIELD(left_x, left_y) &&
1698         (IS_FREE_OR_PLAYER(left_x, left_y) ||
1699          IS_AMOEBOID(Feld[left_x][left_y])))
1700       can_turn_left = TRUE;
1701     if (IN_LEV_FIELD(right_x, right_y) &&
1702         (IS_FREE_OR_PLAYER(right_x, right_y) ||
1703          IS_AMOEBOID(Feld[right_x][right_y])))
1704       can_turn_right = TRUE;
1705
1706     if (can_turn_left && can_turn_right)
1707       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
1708     else if (can_turn_left)
1709       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
1710     else if (can_turn_right)
1711       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
1712     else
1713       MovDir[x][y] = back_dir;
1714
1715     MovDelay[x][y] = 6+RND(40);
1716   }
1717   else if (element == EL_SCHWEIN)
1718   {
1719     boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
1720     boolean should_turn_left = FALSE, should_turn_right = FALSE;
1721     boolean should_move_on = FALSE;
1722     int rnd_value = 24;
1723     int rnd = RND(rnd_value);
1724
1725     if (IN_LEV_FIELD(left_x, left_y) &&
1726         (IS_FREE(left_x, left_y) || IS_GEM(Feld[left_x][left_y])))
1727       can_turn_left = TRUE;
1728     if (IN_LEV_FIELD(right_x, right_y) &&
1729         (IS_FREE(right_x, right_y) || IS_GEM(Feld[right_x][right_y])))
1730       can_turn_right = TRUE;
1731     if (IN_LEV_FIELD(move_x, move_y) &&
1732         (IS_FREE(move_x, move_y) || IS_GEM(Feld[move_x][move_y])))
1733       can_move_on = TRUE;
1734
1735     if (can_turn_left &&
1736         (!can_move_on ||
1737          (IN_LEV_FIELD(x+back_dx+left_dx, y+back_dy+left_dy) &&
1738           !IS_FREE(x+back_dx+left_dx, y+back_dy+left_dy))))
1739       should_turn_left = TRUE;
1740     if (can_turn_right &&
1741         (!can_move_on ||
1742          (IN_LEV_FIELD(x+back_dx+right_dx, y+back_dy+right_dy) &&
1743           !IS_FREE(x+back_dx+right_dx, y+back_dy+right_dy))))
1744       should_turn_right = TRUE;
1745     if (can_move_on &&
1746         (!can_turn_left || !can_turn_right ||
1747          (IN_LEV_FIELD(x+move_dx+left_dx, y+move_dy+left_dy) &&
1748           !IS_FREE(x+move_dx+left_dx, y+move_dy+left_dy)) ||
1749          (IN_LEV_FIELD(x+move_dx+right_dx, y+move_dy+right_dy) &&
1750           !IS_FREE(x+move_dx+right_dx, y+move_dy+right_dy))))
1751       should_move_on = TRUE;
1752
1753     if (should_turn_left || should_turn_right || should_move_on)
1754     {
1755       if (should_turn_left && should_turn_right && should_move_on)
1756         MovDir[x][y] = (rnd < rnd_value/3 ? left_dir :
1757                         rnd < 2*rnd_value/3 ? right_dir :
1758                         old_move_dir);
1759       else if (should_turn_left && should_turn_right)
1760         MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
1761       else if (should_turn_left && should_move_on)
1762         MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : old_move_dir);
1763       else if (should_turn_right && should_move_on)
1764         MovDir[x][y] = (rnd < rnd_value/2 ? right_dir : old_move_dir);
1765       else if (should_turn_left)
1766         MovDir[x][y] = left_dir;
1767       else if (should_turn_right)
1768         MovDir[x][y] = right_dir;
1769       else if (should_move_on)
1770         MovDir[x][y] = old_move_dir;
1771     }
1772     else if (can_move_on && rnd > rnd_value/8)
1773       MovDir[x][y] = old_move_dir;
1774     else if (can_turn_left && can_turn_right)
1775       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
1776     else if (can_turn_left && rnd > rnd_value/8)
1777       MovDir[x][y] = left_dir;
1778     else if (can_turn_right && rnd > rnd_value/8)
1779       MovDir[x][y] = right_dir;
1780     else
1781       MovDir[x][y] = back_dir;
1782
1783     if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) &&
1784         !IS_GEM(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
1785       MovDir[x][y] = old_move_dir;
1786
1787     MovDelay[x][y] = 0;
1788   }
1789   else if (element == EL_DRACHE)
1790   {
1791     boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
1792     int rnd_value = 24;
1793     int rnd = RND(rnd_value);
1794
1795     if (IN_LEV_FIELD(left_x, left_y) && IS_FREE(left_x, left_y))
1796       can_turn_left = TRUE;
1797     if (IN_LEV_FIELD(right_x, right_y) && IS_FREE(right_x, right_y))
1798       can_turn_right = TRUE;
1799     if (IN_LEV_FIELD(move_x, move_y) && IS_FREE(move_x, move_y))
1800       can_move_on = TRUE;
1801
1802     if (can_move_on && rnd > rnd_value/8)
1803       MovDir[x][y] = old_move_dir;
1804     else if (can_turn_left && can_turn_right)
1805       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
1806     else if (can_turn_left && rnd > rnd_value/8)
1807       MovDir[x][y] = left_dir;
1808     else if (can_turn_right && rnd > rnd_value/8)
1809       MovDir[x][y] = right_dir;
1810     else
1811       MovDir[x][y] = back_dir;
1812
1813     if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y))
1814       MovDir[x][y] = old_move_dir;
1815
1816     MovDelay[x][y] = 0;
1817   }
1818   else if (element == EL_ROBOT || element == EL_SONDE ||
1819            element == EL_MAULWURF || element == EL_PINGUIN)
1820   {
1821     int attr_x = -1, attr_y = -1;
1822
1823     if (AllPlayersGone)
1824     {
1825       attr_x = ExitX;
1826       attr_y = ExitY;
1827     }
1828     else
1829     {
1830       int i;
1831
1832       for (i=0; i<MAX_PLAYERS; i++)
1833       {
1834         struct PlayerInfo *player = &stored_player[i];
1835         int jx = player->jx, jy = player->jy;
1836
1837         if (!player->active || player->gone)
1838           continue;
1839
1840         if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
1841         {
1842           attr_x = jx;
1843           attr_y = jy;
1844         }
1845       }
1846     }
1847
1848     if (element == EL_ROBOT && ZX>=0 && ZY>=0)
1849     {
1850       attr_x = ZX;
1851       attr_y = ZY;
1852     }
1853
1854     if (element == EL_MAULWURF || element == EL_PINGUIN)
1855     {
1856       int i;
1857       static int xy[4][2] =
1858       {
1859         { 0, -1 },
1860         { -1, 0 },
1861         { +1, 0 },
1862         { 0, +1 }
1863       };
1864
1865       for (i=0; i<4; i++)
1866       {
1867         int ex = x + xy[i%4][0];
1868         int ey = y + xy[i%4][1];
1869
1870         if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_AUSGANG_AUF)
1871         {
1872           attr_x = ex;
1873           attr_y = ey;
1874           break;
1875         }
1876       }
1877     }
1878
1879     MovDir[x][y] = MV_NO_MOVING;
1880     if (attr_x<x)
1881       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
1882     else if (attr_x>x)
1883       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
1884     if (attr_y<y)
1885       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
1886     else if (attr_y>y)
1887       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
1888
1889     if (element == EL_ROBOT)
1890     {
1891       int newx, newy;
1892
1893       if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
1894         MovDir[x][y] &= (RND(2) ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
1895       Moving2Blocked(x, y, &newx, &newy);
1896
1897       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
1898         MovDelay[x][y] = 8+8*!RND(3);
1899       else
1900         MovDelay[x][y] = 16;
1901     }
1902     else
1903     {
1904       int newx, newy;
1905
1906       MovDelay[x][y] = 1;
1907
1908       if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
1909       {
1910         boolean first_horiz = RND(2);
1911         int new_move_dir = MovDir[x][y];
1912
1913         MovDir[x][y] =
1914           new_move_dir & (first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
1915         Moving2Blocked(x, y, &newx, &newy);
1916
1917         if (IN_LEV_FIELD(newx, newy) &&
1918             (IS_FREE(newx, newy) ||
1919              Feld[newx][newy] == EL_SALZSAEURE ||
1920              ((element == EL_MAULWURF || element == EL_PINGUIN) &&
1921               (Feld[newx][newy] == EL_AUSGANG_AUF ||
1922                IS_MAMPF3(Feld[newx][newy])))))
1923           return;
1924
1925         MovDir[x][y] =
1926           new_move_dir & (!first_horiz ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
1927         Moving2Blocked(x, y, &newx, &newy);
1928
1929         if (IN_LEV_FIELD(newx, newy) &&
1930             (IS_FREE(newx, newy) ||
1931              Feld[newx][newy] == EL_SALZSAEURE ||
1932              ((element == EL_MAULWURF || element == EL_PINGUIN) &&
1933               (Feld[newx][newy] == EL_AUSGANG_AUF ||
1934                IS_MAMPF3(Feld[newx][newy])))))
1935           return;
1936
1937         MovDir[x][y] = old_move_dir;
1938         return;
1939       }
1940     }
1941   }
1942 }
1943
1944 static boolean JustBeingPushed(int x, int y)
1945 {
1946   int i;
1947
1948   for (i=0; i<MAX_PLAYERS; i++)
1949   {
1950     struct PlayerInfo *player = &stored_player[i];
1951
1952     if (player->active && !player->gone &&
1953         player->Pushing && player->MovPos)
1954     {
1955       int next_jx = player->jx + (player->jx - player->last_jx);
1956       int next_jy = player->jy + (player->jy - player->last_jy);
1957
1958       if (x == next_jx && y == next_jy)
1959         return TRUE;
1960     }
1961   }
1962
1963   return FALSE;
1964 }
1965
1966 void StartMoving(int x, int y)
1967 {
1968   int element = Feld[x][y];
1969
1970   if (Stop[x][y])
1971     return;
1972
1973   if (CAN_FALL(element) && y<lev_fieldy-1)
1974   {
1975     if ((x>0 && IS_PLAYER(x-1, y)) || (x<lev_fieldx-1 && IS_PLAYER(x+1, y)))
1976       if (JustBeingPushed(x, y))
1977         return;
1978
1979     if (element == EL_MORAST_VOLL)
1980     {
1981       if (IS_FREE(x, y+1))
1982       {
1983         InitMovingField(x, y, MV_DOWN);
1984         Feld[x][y] = EL_FELSBROCKEN;
1985         Store[x][y] = EL_MORAST_LEER;
1986       }
1987       else if (Feld[x][y+1] == EL_MORAST_LEER)
1988       {
1989         if (!MovDelay[x][y])
1990           MovDelay[x][y] = TILEY + 1;
1991
1992         if (MovDelay[x][y])
1993         {
1994           MovDelay[x][y]--;
1995           if (MovDelay[x][y])
1996             return;
1997         }
1998
1999         Feld[x][y] = EL_MORAST_LEER;
2000         Feld[x][y+1] = EL_MORAST_VOLL;
2001       }
2002     }
2003     else if (element == EL_FELSBROCKEN && Feld[x][y+1] == EL_MORAST_LEER)
2004     {
2005       InitMovingField(x, y, MV_DOWN);
2006       Store[x][y] = EL_MORAST_VOLL;
2007     }
2008     else if (element == EL_SIEB_VOLL)
2009     {
2010       if (IS_FREE(x, y+1))
2011       {
2012         InitMovingField(x, y, MV_DOWN);
2013         Feld[x][y] = EL_CHANGED(Store2[x][y]);
2014         Store[x][y] = EL_SIEB_LEER;
2015       }
2016       else if (Feld[x][y+1] == EL_SIEB_LEER)
2017       {
2018         if (!MovDelay[x][y])
2019           MovDelay[x][y] = TILEY/4 + 1;
2020
2021         if (MovDelay[x][y])
2022         {
2023           MovDelay[x][y]--;
2024           if (MovDelay[x][y])
2025             return;
2026         }
2027
2028         Feld[x][y] = EL_SIEB_LEER;
2029         Feld[x][y+1] = EL_SIEB_VOLL;
2030         Store2[x][y+1] = EL_CHANGED(Store2[x][y]);
2031         Store2[x][y] = 0;
2032       }
2033     }
2034     else if (element == EL_SIEB2_VOLL)
2035     {
2036       if (IS_FREE(x, y+1))
2037       {
2038         InitMovingField(x, y, MV_DOWN);
2039         Feld[x][y] = EL_CHANGED2(Store2[x][y]);
2040         Store[x][y] = EL_SIEB2_LEER;
2041       }
2042       else if (Feld[x][y+1] == EL_SIEB2_LEER)
2043       {
2044         if (!MovDelay[x][y])
2045           MovDelay[x][y] = TILEY/4 + 1;
2046
2047         if (MovDelay[x][y])
2048         {
2049           MovDelay[x][y]--;
2050           if (MovDelay[x][y])
2051             return;
2052         }
2053
2054         Feld[x][y] = EL_SIEB2_LEER;
2055         Feld[x][y+1] = EL_SIEB2_VOLL;
2056         Store2[x][y+1] = EL_CHANGED2(Store2[x][y]);
2057         Store2[x][y] = 0;
2058       }
2059     }
2060     else if (CAN_CHANGE(element) &&
2061              (Feld[x][y+1] == EL_SIEB_LEER || Feld[x][y+1] == EL_SIEB2_LEER))
2062     {
2063       InitMovingField(x, y, MV_DOWN);
2064       Store[x][y] =
2065         (Feld[x][y+1] == EL_SIEB_LEER ? EL_SIEB_VOLL : EL_SIEB2_VOLL);
2066       Store2[x][y+1] = element;
2067     }
2068     else if (CAN_SMASH(element) && Feld[x][y+1] == EL_SALZSAEURE)
2069     {
2070       Blurb(x, y);
2071       InitMovingField(x, y, MV_DOWN);
2072       Store[x][y] = EL_SALZSAEURE;
2073     }
2074     else if (CAN_SMASH(element) && Feld[x][y+1] == EL_BLOCKED && JustHit[x][y])
2075     {
2076       Impact(x, y);
2077     }
2078     else if (IS_FREE(x, y+1))
2079     {
2080       InitMovingField(x, y, MV_DOWN);
2081     }
2082     else if (element == EL_TROPFEN)
2083     {
2084       Feld[x][y] = EL_AMOEBING;
2085       Store[x][y] = EL_AMOEBE_NASS;
2086     }
2087     else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1])
2088     {
2089       boolean left  = (x>0 && IS_FREE(x-1, y) &&
2090                        (IS_FREE(x-1, y+1) || Feld[x-1][y+1] == EL_SALZSAEURE));
2091       boolean right = (x<lev_fieldx-1 && IS_FREE(x+1, y) &&
2092                        (IS_FREE(x+1, y+1) || Feld[x+1][y+1] == EL_SALZSAEURE));
2093
2094       if (left || right)
2095       {
2096         if (left && right && game_emulation != EMU_BOULDERDASH)
2097           left = !(right = RND(2));
2098
2099         InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT);
2100       }
2101     }
2102   }
2103   else if (CAN_MOVE(element))
2104   {
2105     int newx, newy;
2106
2107     if (element == EL_SONDE && JustBeingPushed(x, y))
2108       return;
2109
2110     if (!MovDelay[x][y])        /* start new movement phase */
2111     {
2112       /* all objects that can change their move direction after each step */
2113       /* (MAMPFER, MAMPFER2 and PACMAN go straight until they hit a wall  */
2114
2115       if (element!=EL_MAMPFER && element!=EL_MAMPFER2 && element!=EL_PACMAN)
2116       {
2117         TurnRound(x, y);
2118         if (MovDelay[x][y] && (element == EL_KAEFER || element == EL_FLIEGER ||
2119                                element == EL_SP_SNIKSNAK ||
2120                                element == EL_SP_ELECTRON))
2121           DrawLevelField(x, y);
2122       }
2123     }
2124
2125     if (MovDelay[x][y])         /* wait some time before next movement */
2126     {
2127       MovDelay[x][y]--;
2128
2129       if (element == EL_ROBOT ||
2130           element == EL_MAMPFER || element == EL_MAMPFER2)
2131       {
2132         int phase = MovDelay[x][y] % 8;
2133
2134         if (phase>3)
2135           phase = 7-phase;
2136
2137         if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2138           DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(element)+phase);
2139
2140         if ((element == EL_MAMPFER || element == EL_MAMPFER2)
2141             && MovDelay[x][y]%4 == 3)
2142           PlaySoundLevel(x, y, SND_NJAM);
2143       }
2144       else if (element == EL_SP_ELECTRON)
2145         DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
2146       else if (element == EL_DRACHE)
2147       {
2148         int i;
2149         int dir = MovDir[x][y];
2150         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2151         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
2152         int graphic = (dir == MV_LEFT   ? GFX_FLAMMEN_LEFT :
2153                        dir == MV_RIGHT  ? GFX_FLAMMEN_RIGHT :
2154                        dir == MV_UP     ? GFX_FLAMMEN_UP :
2155                        dir == MV_DOWN   ? GFX_FLAMMEN_DOWN : GFX_LEERRAUM);
2156         int phase = FrameCounter % 2;
2157
2158         for (i=1; i<=3; i++)
2159         {
2160           int xx = x + i*dx, yy = y + i*dy;
2161           int sx = SCREENX(xx), sy = SCREENY(yy);
2162
2163           if (!IN_LEV_FIELD(xx, yy) ||
2164               IS_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLODING)
2165             break;
2166
2167           if (MovDelay[x][y])
2168           {
2169             int flamed = MovingOrBlocked2Element(xx, yy);
2170
2171             if (IS_ENEMY(flamed) || IS_EXPLOSIVE(flamed))
2172               Bang(xx, yy);
2173             else
2174               RemoveMovingField(xx, yy);
2175
2176             Feld[xx][yy] = EL_BURNING;
2177             if (IN_SCR_FIELD(sx, sy))
2178               DrawGraphic(sx, sy, graphic + phase*3 + i-1);
2179           }
2180           else
2181           {
2182             if (Feld[xx][yy] == EL_BURNING)
2183               Feld[xx][yy] = EL_LEERRAUM;
2184             DrawLevelField(xx, yy);
2185           }
2186         }
2187       }
2188
2189       if (MovDelay[x][y])
2190         return;
2191     }
2192
2193     if (element == EL_KAEFER || element == EL_BUTTERFLY)
2194     {
2195       PlaySoundLevel(x, y, SND_KLAPPER);
2196     }
2197     else if (element == EL_FLIEGER || element == EL_FIREFLY)
2198     {
2199       PlaySoundLevel(x, y, SND_ROEHR);
2200     }
2201
2202     /* now make next step */
2203
2204     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
2205
2206     if (IS_ENEMY(element) && IS_PLAYER(newx, newy))
2207     {
2208       /* enemy got the player */
2209       MovDir[x][y] = 0;
2210       KillHero(PLAYERINFO(newx, newy));
2211       return;
2212     }
2213     else if ((element == EL_MAULWURF || element == EL_PINGUIN ||
2214               element == EL_ROBOT || element == EL_SONDE) &&
2215              IN_LEV_FIELD(newx, newy) &&
2216              MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_SALZSAEURE)
2217     {
2218       Blurb(x, y);
2219       Store[x][y] = EL_SALZSAEURE;
2220     }
2221     else if ((element == EL_MAULWURF || element == EL_PINGUIN) &&
2222              IN_LEV_FIELD(newx, newy))
2223     {
2224       if (Feld[newx][newy] == EL_AUSGANG_AUF)
2225       {
2226         Feld[x][y] = EL_LEERRAUM;
2227         DrawLevelField(x, y);
2228
2229         PlaySoundLevel(newx, newy, SND_BUING);
2230         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2231           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2gfx(element));
2232
2233         local_player->friends_still_needed--;
2234         if (!local_player->friends_still_needed &&
2235             !local_player->GameOver && AllPlayersGone)
2236           local_player->LevelSolved = local_player->GameOver = TRUE;
2237
2238         return;
2239       }
2240       else if (IS_MAMPF3(Feld[newx][newy]))
2241       {
2242         if (DigField(local_player, newx, newy, 0, 0, DF_DIG) == MF_MOVING)
2243           DrawLevelField(newx, newy);
2244         else
2245           MovDir[x][y] = MV_NO_MOVING;
2246       }
2247       else if (!IS_FREE(newx, newy))
2248       {
2249         if (IS_PLAYER(x, y))
2250           DrawPlayerField(x, y);
2251         else
2252           DrawLevelField(x, y);
2253         return;
2254       }
2255     }
2256     else if (element == EL_SCHWEIN && IN_LEV_FIELD(newx, newy))
2257     {
2258       if (IS_GEM(Feld[newx][newy]))
2259       {
2260         if (IS_MOVING(newx, newy))
2261           RemoveMovingField(newx, newy);
2262         else
2263         {
2264           Feld[newx][newy] = EL_LEERRAUM;
2265           DrawLevelField(newx, newy);
2266         }
2267       }
2268       else if (!IS_FREE(newx, newy))
2269       {
2270         if (IS_PLAYER(x, y))
2271           DrawPlayerField(x, y);
2272         else
2273           DrawLevelField(x, y);
2274         return;
2275       }
2276     }
2277     else if (element == EL_DRACHE && IN_LEV_FIELD(newx, newy))
2278     {
2279       if (!IS_FREE(newx, newy))
2280       {
2281         if (IS_PLAYER(x, y))
2282           DrawPlayerField(x, y);
2283         else
2284           DrawLevelField(x, y);
2285         return;
2286       }
2287       else
2288       {
2289         boolean wanna_flame = !RND(10);
2290         int dx = newx - x, dy = newy - y;
2291         int newx1 = newx+1*dx, newy1 = newy+1*dy;
2292         int newx2 = newx+2*dx, newy2 = newy+2*dy;
2293         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
2294                         MovingOrBlocked2Element(newx1, newy1) : EL_BETON);
2295         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
2296                         MovingOrBlocked2Element(newx2, newy2) : EL_BETON);
2297
2298         if ((wanna_flame || IS_ENEMY(element1) || IS_ENEMY(element2)) &&
2299             element1 != EL_DRACHE && element2 != EL_DRACHE &&
2300             element1 != EL_BURNING && element2 != EL_BURNING)
2301         {
2302           if (IS_PLAYER(x, y))
2303             DrawPlayerField(x, y);
2304           else
2305             DrawLevelField(x, y);
2306
2307           MovDelay[x][y] = 50;
2308           Feld[newx][newy] = EL_BURNING;
2309           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_LEERRAUM)
2310             Feld[newx1][newy1] = EL_BURNING;
2311           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_LEERRAUM)
2312             Feld[newx2][newy2] = EL_BURNING;
2313           return;
2314         }
2315       }
2316     }
2317     else if (element == EL_MAMPFER && IN_LEV_FIELD(newx, newy) &&
2318              Feld[newx][newy] == EL_DIAMANT)
2319     {
2320       if (IS_MOVING(newx, newy))
2321         RemoveMovingField(newx, newy);
2322       else
2323       {
2324         Feld[newx][newy] = EL_LEERRAUM;
2325         DrawLevelField(newx, newy);
2326       }
2327     }
2328     else if (element == EL_MAMPFER2 && IN_LEV_FIELD(newx, newy) &&
2329              IS_MAMPF2(Feld[newx][newy]))
2330     {
2331       if (AmoebaNr[newx][newy])
2332       {
2333         AmoebaCnt2[AmoebaNr[newx][newy]]--;
2334         if (Feld[newx][newy] == EL_AMOEBE_VOLL ||
2335             Feld[newx][newy] == EL_AMOEBE_BD)
2336           AmoebaCnt[AmoebaNr[newx][newy]]--;
2337       }
2338
2339       if (IS_MOVING(newx, newy))
2340         RemoveMovingField(newx, newy);
2341       else
2342       {
2343         Feld[newx][newy] = EL_LEERRAUM;
2344         DrawLevelField(newx, newy);
2345       }
2346     }
2347     else if (element == EL_PACMAN && IN_LEV_FIELD(newx, newy) &&
2348              IS_AMOEBOID(Feld[newx][newy]))
2349     {
2350       if (AmoebaNr[newx][newy])
2351       {
2352         AmoebaCnt2[AmoebaNr[newx][newy]]--;
2353         if (Feld[newx][newy] == EL_AMOEBE_VOLL ||
2354             Feld[newx][newy] == EL_AMOEBE_BD)
2355           AmoebaCnt[AmoebaNr[newx][newy]]--;
2356       }
2357
2358       Feld[newx][newy] = EL_LEERRAUM;
2359       DrawLevelField(newx, newy);
2360     }
2361     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
2362     {
2363       /* object was running against a wall */
2364
2365       TurnRound(x, y);
2366
2367       if (element == EL_KAEFER || element == EL_FLIEGER ||
2368           element == EL_SP_SNIKSNAK)
2369         DrawLevelField(x, y);
2370       else if (element == EL_BUTTERFLY || element == EL_FIREFLY)
2371         DrawGraphicAnimation(x, y, el2gfx(element), 2, 4, ANIM_NORMAL);
2372       else if (element == EL_SONDE)
2373         DrawGraphicAnimation(x, y, GFX_SONDE_START, 8, 2, ANIM_NORMAL);
2374       else if (element == EL_SP_ELECTRON)
2375         DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
2376
2377       return;
2378     }
2379
2380     if (element == EL_ROBOT && IN_SCR_FIELD(x, y))
2381       PlaySoundLevel(x, y, SND_SCHLURF);
2382
2383     InitMovingField(x, y, MovDir[x][y]);
2384   }
2385
2386   if (MovDir[x][y])
2387     ContinueMoving(x, y);
2388 }
2389
2390 void ContinueMoving(int x, int y)
2391 {
2392   int element = Feld[x][y];
2393   int direction = MovDir[x][y];
2394   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
2395   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
2396   int horiz_move = (dx!=0);
2397   int newx = x + dx, newy = y + dy;
2398   int step = (horiz_move ? dx : dy) * TILEX/8;
2399
2400   if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
2401     step*=2;
2402   else if (element == EL_TROPFEN)
2403     step/=2;
2404   else if (Store[x][y] == EL_MORAST_VOLL || Store[x][y] == EL_MORAST_LEER)
2405     step/=4;
2406
2407   MovPos[x][y] += step;
2408
2409   if (ABS(MovPos[x][y])>=TILEX)         /* object reached its destination */
2410   {
2411     Feld[x][y] = EL_LEERRAUM;
2412     Feld[newx][newy] = element;
2413
2414     if (Store[x][y] == EL_MORAST_VOLL)
2415     {
2416       Store[x][y] = 0;
2417       Feld[newx][newy] = EL_MORAST_VOLL;
2418       element = EL_MORAST_VOLL;
2419     }
2420     else if (Store[x][y] == EL_MORAST_LEER)
2421     {
2422       Store[x][y] = 0;
2423       Feld[x][y] = EL_MORAST_LEER;
2424     }
2425     else if (Store[x][y] == EL_SIEB_VOLL)
2426     {
2427       Store[x][y] = 0;
2428       element = Feld[newx][newy] = (SiebAktiv ? EL_SIEB_VOLL : EL_SIEB_TOT);
2429     }
2430     else if (Store[x][y] == EL_SIEB_LEER)
2431     {
2432       Store[x][y] = Store2[x][y] = 0;
2433       Feld[x][y] = (SiebAktiv ? EL_SIEB_LEER : EL_SIEB_TOT);
2434     }
2435     else if (Store[x][y] == EL_SIEB2_VOLL)
2436     {
2437       Store[x][y] = 0;
2438       element = Feld[newx][newy] = (SiebAktiv ? EL_SIEB2_VOLL : EL_SIEB2_TOT);
2439     }
2440     else if (Store[x][y] == EL_SIEB2_LEER)
2441     {
2442       Store[x][y] = Store2[x][y] = 0;
2443       Feld[x][y] = (SiebAktiv ? EL_SIEB2_LEER : EL_SIEB2_TOT);
2444     }
2445     else if (Store[x][y] == EL_SALZSAEURE)
2446     {
2447       Store[x][y] = 0;
2448       Feld[newx][newy] = EL_SALZSAEURE;
2449       element = EL_SALZSAEURE;
2450     }
2451     else if (Store[x][y] == EL_AMOEBE_NASS)
2452     {
2453       Store[x][y] = 0;
2454       Feld[x][y] = EL_AMOEBE_NASS;
2455     }
2456
2457     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
2458     MovDelay[newx][newy] = 0;
2459
2460     if (!CAN_MOVE(element))
2461       MovDir[newx][newy] = 0;
2462
2463     DrawLevelField(x, y);
2464     DrawLevelField(newx, newy);
2465
2466     Stop[newx][newy] = TRUE;
2467     JustHit[x][newy] = 3;
2468
2469     if (DONT_TOUCH(element))    /* object may be nasty to player or others */
2470     {
2471       TestIfBadThingHitsHero(newx, newy);
2472       TestIfBadThingHitsFriend(newx, newy);
2473       TestIfBadThingHitsOtherBadThing(newx, newy);
2474     }
2475     else if (element == EL_PINGUIN)
2476       TestIfFriendHitsBadThing(newx, newy);
2477
2478     if (CAN_SMASH(element) && direction == MV_DOWN &&
2479         (newy == lev_fieldy-1 || !IS_FREE(x, newy+1)))
2480       Impact(x, newy);
2481   }
2482   else                          /* still moving on */
2483     DrawLevelField(x, y);
2484 }
2485
2486 int AmoebeNachbarNr(int ax, int ay)
2487 {
2488   int i;
2489   int element = Feld[ax][ay];
2490   int group_nr = 0;
2491   static int xy[4][2] =
2492   {
2493     { 0, -1 },
2494     { -1, 0 },
2495     { +1, 0 },
2496     { 0, +1 }
2497   };
2498
2499   for (i=0; i<4; i++)
2500   {
2501     int x = ax + xy[i][0];
2502     int y = ay + xy[i][1];
2503
2504     if (!IN_LEV_FIELD(x, y))
2505       continue;
2506
2507     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
2508       group_nr = AmoebaNr[x][y];
2509   }
2510
2511   return group_nr;
2512 }
2513
2514 void AmoebenVereinigen(int ax, int ay)
2515 {
2516   int i, x, y, xx, yy;
2517   int new_group_nr = AmoebaNr[ax][ay];
2518   static int xy[4][2] =
2519   {
2520     { 0, -1 },
2521     { -1, 0 },
2522     { +1, 0 },
2523     { 0, +1 }
2524   };
2525
2526   if (new_group_nr == 0)
2527     return;
2528
2529   for (i=0; i<4; i++)
2530   {
2531     x = ax + xy[i][0];
2532     y = ay + xy[i][1];
2533
2534     if (!IN_LEV_FIELD(x, y))
2535       continue;
2536
2537     if ((Feld[x][y] == EL_AMOEBE_VOLL ||
2538          Feld[x][y] == EL_AMOEBE_BD ||
2539          Feld[x][y] == EL_AMOEBE_TOT) &&
2540         AmoebaNr[x][y] != new_group_nr)
2541     {
2542       int old_group_nr = AmoebaNr[x][y];
2543
2544       if (old_group_nr == 0)
2545         return;
2546
2547       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
2548       AmoebaCnt[old_group_nr] = 0;
2549       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
2550       AmoebaCnt2[old_group_nr] = 0;
2551
2552       for (yy=0; yy<lev_fieldy; yy++)
2553       {
2554         for (xx=0; xx<lev_fieldx; xx++)
2555         {
2556           if (AmoebaNr[xx][yy] == old_group_nr)
2557             AmoebaNr[xx][yy] = new_group_nr;
2558         }
2559       }
2560     }
2561   }
2562 }
2563
2564 void AmoebeUmwandeln(int ax, int ay)
2565 {
2566   int i, x, y;
2567
2568   if (Feld[ax][ay] == EL_AMOEBE_TOT)
2569   {
2570     int group_nr = AmoebaNr[ax][ay];
2571
2572 #ifdef DEBUG
2573     if (group_nr == 0)
2574     {
2575       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
2576       printf("AmoebeUmwandeln(): This should never happen!\n");
2577       return;
2578     }
2579 #endif
2580
2581     for (y=0; y<lev_fieldy; y++)
2582     {
2583       for (x=0; x<lev_fieldx; x++)
2584       {
2585         if (Feld[x][y] == EL_AMOEBE_TOT && AmoebaNr[x][y] == group_nr)
2586         {
2587           AmoebaNr[x][y] = 0;
2588           Feld[x][y] = EL_AMOEBA2DIAM;
2589         }
2590       }
2591     }
2592     Bang(ax, ay);
2593   }
2594   else
2595   {
2596     static int xy[4][2] =
2597     {
2598       { 0, -1 },
2599       { -1, 0 },
2600       { +1, 0 },
2601       { 0, +1 }
2602     };
2603
2604     for (i=0; i<4; i++)
2605     {
2606       x = ax + xy[i][0];
2607       y = ay + xy[i][1];
2608
2609       if (!IN_LEV_FIELD(x, y))
2610         continue;
2611
2612       if (Feld[x][y] == EL_AMOEBA2DIAM)
2613         Bang(x, y);
2614     }
2615   }
2616 }
2617
2618 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
2619 {
2620   int x, y;
2621   int group_nr = AmoebaNr[ax][ay];
2622   boolean done = FALSE;
2623
2624 #ifdef DEBUG
2625   if (group_nr == 0)
2626   {
2627     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
2628     printf("AmoebeUmwandelnBD(): This should never happen!\n");
2629     return;
2630   }
2631 #endif
2632
2633   for (y=0; y<lev_fieldy; y++)
2634   {
2635     for (x=0; x<lev_fieldx; x++)
2636     {
2637       if (AmoebaNr[x][y] == group_nr &&
2638           (Feld[x][y] == EL_AMOEBE_TOT ||
2639            Feld[x][y] == EL_AMOEBE_BD ||
2640            Feld[x][y] == EL_AMOEBING))
2641       {
2642         AmoebaNr[x][y] = 0;
2643         Feld[x][y] = new_element;
2644         InitField(x, y, FALSE);
2645         DrawLevelField(x, y);
2646         done = TRUE;
2647       }
2648     }
2649   }
2650
2651   if (done)
2652     PlaySoundLevel(ax, ay,
2653                    (new_element == EL_FELSBROCKEN ? SND_KLOPF : SND_PLING));
2654 }
2655
2656 void AmoebeWaechst(int x, int y)
2657 {
2658   static unsigned long sound_delay = 0;
2659   static unsigned long sound_delay_value = 0;
2660
2661   if (!MovDelay[x][y])          /* start new growing cycle */
2662   {
2663     MovDelay[x][y] = 7;
2664
2665     if (DelayReached(&sound_delay, sound_delay_value))
2666     {
2667       PlaySoundLevel(x, y, SND_AMOEBE);
2668       sound_delay_value = 30;
2669     }
2670   }
2671
2672   if (MovDelay[x][y])           /* wait some time before growing bigger */
2673   {
2674     MovDelay[x][y]--;
2675     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2676       DrawGraphic(SCREENX(x), SCREENY(y), GFX_AMOEBING + 3 - MovDelay[x][y]/2);
2677
2678     if (!MovDelay[x][y])
2679     {
2680       Feld[x][y] = Store[x][y];
2681       Store[x][y] = 0;
2682       DrawLevelField(x, y);
2683     }
2684   }
2685 }
2686
2687 void AmoebeAbleger(int ax, int ay)
2688 {
2689   int i;
2690   int element = Feld[ax][ay];
2691   int newax = ax, neway = ay;
2692   static int xy[4][2] =
2693   {
2694     { 0, -1 },
2695     { -1, 0 },
2696     { +1, 0 },
2697     { 0, +1 }
2698   };
2699
2700   if (!level.tempo_amoebe)
2701   {
2702     Feld[ax][ay] = EL_AMOEBE_TOT;
2703     DrawLevelField(ax, ay);
2704     return;
2705   }
2706
2707   if (!MovDelay[ax][ay])        /* start making new amoeba field */
2708     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.tempo_amoebe));
2709
2710   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
2711   {
2712     MovDelay[ax][ay]--;
2713     if (MovDelay[ax][ay])
2714       return;
2715   }
2716
2717   if (element == EL_AMOEBE_NASS)        /* object is an acid / amoeba drop */
2718   {
2719     int start = RND(4);
2720     int x = ax + xy[start][0];
2721     int y = ay + xy[start][1];
2722
2723     if (!IN_LEV_FIELD(x, y))
2724       return;
2725
2726     if (IS_FREE(x, y) ||
2727         Feld[x][y] == EL_ERDREICH || Feld[x][y] == EL_MORAST_LEER)
2728     {
2729       newax = x;
2730       neway = y;
2731     }
2732
2733     if (newax == ax && neway == ay)
2734       return;
2735   }
2736   else                          /* normal or "filled" (BD style) amoeba */
2737   {
2738     int start = RND(4);
2739     boolean waiting_for_player = FALSE;
2740
2741     for (i=0; i<4; i++)
2742     {
2743       int j = (start + i) % 4;
2744       int x = ax + xy[j][0];
2745       int y = ay + xy[j][1];
2746
2747       if (!IN_LEV_FIELD(x, y))
2748         continue;
2749
2750       if (IS_FREE(x, y) ||
2751           Feld[x][y] == EL_ERDREICH || Feld[x][y] == EL_MORAST_LEER)
2752       {
2753         newax = x;
2754         neway = y;
2755         break;
2756       }
2757       else if (IS_PLAYER(x, y))
2758         waiting_for_player = TRUE;
2759     }
2760
2761     if (newax == ax && neway == ay)             /* amoeba cannot grow */
2762     {
2763       if (i == 4 && (!waiting_for_player || game_emulation == EMU_BOULDERDASH))
2764       {
2765         Feld[ax][ay] = EL_AMOEBE_TOT;
2766         DrawLevelField(ax, ay);
2767         AmoebaCnt[AmoebaNr[ax][ay]]--;
2768
2769         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
2770         {
2771           if (element == EL_AMOEBE_VOLL)
2772             AmoebeUmwandeln(ax, ay);
2773           else if (element == EL_AMOEBE_BD)
2774             AmoebeUmwandelnBD(ax, ay, level.amoebe_inhalt);
2775         }
2776       }
2777       return;
2778     }
2779     else if (element == EL_AMOEBE_VOLL || element == EL_AMOEBE_BD)
2780     {
2781       /* amoeba gets larger by growing in some direction */
2782
2783       int new_group_nr = AmoebaNr[ax][ay];
2784
2785 #ifdef DEBUG
2786   if (new_group_nr == 0)
2787   {
2788     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
2789     printf("AmoebeAbleger(): This should never happen!\n");
2790     return;
2791   }
2792 #endif
2793
2794       AmoebaNr[newax][neway] = new_group_nr;
2795       AmoebaCnt[new_group_nr]++;
2796       AmoebaCnt2[new_group_nr]++;
2797
2798       /* if amoeba touches other amoeba(s) after growing, unify them */
2799       AmoebenVereinigen(newax, neway);
2800
2801       if (element == EL_AMOEBE_BD && AmoebaCnt2[new_group_nr] >= 200)
2802       {
2803         AmoebeUmwandelnBD(newax, neway, EL_FELSBROCKEN);
2804         return;
2805       }
2806     }
2807   }
2808
2809   if (element != EL_AMOEBE_NASS || neway < ay || !IS_FREE(newax, neway) ||
2810       (neway == lev_fieldy - 1 && newax != ax))
2811   {
2812     Feld[newax][neway] = EL_AMOEBING;
2813     Store[newax][neway] = element;
2814   }
2815   else if (neway == ay)
2816     Feld[newax][neway] = EL_TROPFEN;
2817   else
2818   {
2819     InitMovingField(ax, ay, MV_DOWN);
2820     Feld[ax][ay] = EL_TROPFEN;
2821     Store[ax][ay] = EL_AMOEBE_NASS;
2822     ContinueMoving(ax, ay);
2823     return;
2824   }
2825
2826   DrawLevelField(newax, neway);
2827 }
2828
2829 void Life(int ax, int ay)
2830 {
2831   int x1, y1, x2, y2;
2832   static int life[4] = { 2, 3, 3, 3 };  /* parameters for "game of life" */
2833   int life_time = 40;
2834   int element = Feld[ax][ay];
2835
2836   if (Stop[ax][ay])
2837     return;
2838
2839   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
2840     MovDelay[ax][ay] = life_time;
2841
2842   if (MovDelay[ax][ay])         /* wait some time before next cycle */
2843   {
2844     MovDelay[ax][ay]--;
2845     if (MovDelay[ax][ay])
2846       return;
2847   }
2848
2849   for (y1=-1; y1<2; y1++) for(x1=-1; x1<2; x1++)
2850   {
2851     int xx = ax+x1, yy = ay+y1;
2852     int nachbarn = 0;
2853
2854     if (!IN_LEV_FIELD(xx, yy))
2855       continue;
2856
2857     for (y2=-1; y2<2; y2++) for (x2=-1; x2<2; x2++)
2858     {
2859       int x = xx+x2, y = yy+y2;
2860
2861       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
2862         continue;
2863
2864       if (((Feld[x][y] == element ||
2865             (element == EL_LIFE && IS_PLAYER(x, y))) &&
2866            !Stop[x][y]) ||
2867           (IS_FREE(x, y) && Stop[x][y]))
2868         nachbarn++;
2869     }
2870
2871     if (xx == ax && yy == ay)           /* field in the middle */
2872     {
2873       if (nachbarn<life[0] || nachbarn>life[1])
2874       {
2875         Feld[xx][yy] = EL_LEERRAUM;
2876         if (!Stop[xx][yy])
2877           DrawLevelField(xx, yy);
2878         Stop[xx][yy] = TRUE;
2879       }
2880     }
2881     else if (IS_FREE(xx, yy) || Feld[xx][yy] == EL_ERDREICH)
2882     {                                   /* free border field */
2883       if (nachbarn>=life[2] && nachbarn<=life[3])
2884       {
2885         Feld[xx][yy] = element;
2886         MovDelay[xx][yy] = (element == EL_LIFE ? 0 : life_time-1);
2887         if (!Stop[xx][yy])
2888           DrawLevelField(xx, yy);
2889         Stop[xx][yy] = TRUE;
2890       }
2891     }
2892   }
2893 }
2894
2895 void Ablenk(int x, int y)
2896 {
2897   if (!MovDelay[x][y])          /* next animation frame */
2898     MovDelay[x][y] = level.dauer_ablenk * FRAMES_PER_SECOND;
2899
2900   if (MovDelay[x][y])           /* wait some time before next frame */
2901   {
2902     MovDelay[x][y]--;
2903     if (MovDelay[x][y])
2904     {
2905       if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2906         DrawGraphic(SCREENX(x), SCREENY(y), GFX_ABLENK+MovDelay[x][y]%4);
2907       if (!(MovDelay[x][y]%4))
2908         PlaySoundLevel(x, y, SND_MIEP);
2909       return;
2910     }
2911   }
2912
2913   Feld[x][y] = EL_ABLENK_AUS;
2914   DrawLevelField(x, y);
2915   if (ZX == x && ZY == y)
2916     ZX = ZY = -1;
2917 }
2918
2919 void Birne(int x, int y)
2920 {
2921   if (!MovDelay[x][y])          /* next animation frame */
2922     MovDelay[x][y] = 800;
2923
2924   if (MovDelay[x][y])           /* wait some time before next frame */
2925   {
2926     MovDelay[x][y]--;
2927     if (MovDelay[x][y])
2928     {
2929       if (!(MovDelay[x][y]%5))
2930       {
2931         if (!(MovDelay[x][y]%10))
2932           Feld[x][y]=EL_ABLENK_EIN;
2933         else
2934           Feld[x][y]=EL_ABLENK_AUS;
2935         DrawLevelField(x, y);
2936         Feld[x][y]=EL_ABLENK_EIN;
2937       }
2938       return;
2939     }
2940   }
2941
2942   Feld[x][y]=EL_ABLENK_AUS;
2943   DrawLevelField(x, y);
2944   if (ZX == x && ZY == y)
2945     ZX=ZY=-1;
2946 }
2947
2948 void Blubber(int x, int y)
2949 {
2950   if (y > 0 && IS_MOVING(x, y-1) && MovDir[x][y-1] == MV_DOWN)
2951     DrawLevelField(x, y-1);
2952   else
2953     DrawGraphicAnimation(x, y, GFX_GEBLUBBER, 4, 10, ANIM_NORMAL);
2954 }
2955
2956 void NussKnacken(int x, int y)
2957 {
2958   if (!MovDelay[x][y])          /* next animation frame */
2959     MovDelay[x][y] = 7;
2960
2961   if (MovDelay[x][y])           /* wait some time before next frame */
2962   {
2963     MovDelay[x][y]--;
2964     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2965       DrawGraphic(SCREENX(x), SCREENY(y), GFX_CRACKINGNUT+3-MovDelay[x][y]/2);
2966
2967     if (!MovDelay[x][y])
2968     {
2969       Feld[x][y] = EL_EDELSTEIN;
2970       DrawLevelField(x, y);
2971     }
2972   }
2973 }
2974
2975 void SiebAktivieren(int x, int y, int typ)
2976 {
2977   int graphic = (typ == 1 ? GFX_SIEB_VOLL : GFX_SIEB2_VOLL) + 3;
2978
2979   DrawGraphicAnimation(x, y, graphic, 4, 4, ANIM_REVERSE);
2980 }
2981
2982 void AusgangstuerPruefen(int x, int y)
2983 {
2984   if (!local_player->gems_still_needed &&
2985       !local_player->sokobanfields_still_needed &&
2986       !local_player->lights_still_needed)
2987   {
2988     Feld[x][y] = EL_AUSGANG_ACT;
2989
2990     PlaySoundLevel(x < LEVELX(BX1) ? LEVELX(BX1) :
2991                    (x > LEVELX(BX2) ? LEVELX(BX2) : x),
2992                    y < LEVELY(BY1) ? LEVELY(BY1) :
2993                    (y > LEVELY(BY2) ? LEVELY(BY2) : y),
2994                    SND_OEFFNEN);
2995   }
2996 }
2997
2998 void AusgangstuerOeffnen(int x, int y)
2999 {
3000   int delay = 6;
3001
3002   if (!MovDelay[x][y])          /* next animation frame */
3003     MovDelay[x][y] = 5*delay;
3004
3005   if (MovDelay[x][y])           /* wait some time before next frame */
3006   {
3007     int tuer;
3008
3009     MovDelay[x][y]--;
3010     tuer = MovDelay[x][y]/delay;
3011     if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3012       DrawGraphic(SCREENX(x), SCREENY(y), GFX_AUSGANG_AUF-tuer);
3013
3014     if (!MovDelay[x][y])
3015     {
3016       Feld[x][y] = EL_AUSGANG_AUF;
3017       DrawLevelField(x, y);
3018     }
3019   }
3020 }
3021
3022 void AusgangstuerBlinken(int x, int y)
3023 {
3024   DrawGraphicAnimation(x, y, GFX_AUSGANG_AUF, 4, 4, ANIM_OSCILLATE);
3025 }
3026
3027 void EdelsteinFunkeln(int x, int y)
3028 {
3029   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
3030     return;
3031
3032   if (Feld[x][y] == EL_EDELSTEIN_BD)
3033     DrawGraphicAnimation(x, y, GFX_EDELSTEIN_BD, 4, 4, ANIM_REVERSE);
3034   else
3035   {
3036     if (!MovDelay[x][y])        /* next animation frame */
3037       MovDelay[x][y] = 11 * !SimpleRND(500);
3038
3039     if (MovDelay[x][y])         /* wait some time before next frame */
3040     {
3041       MovDelay[x][y]--;
3042
3043       if (setup.direct_draw && MovDelay[x][y])
3044         SetDrawtoField(DRAW_BUFFERED);
3045
3046       DrawGraphic(SCREENX(x), SCREENY(y), el2gfx(Feld[x][y]));
3047
3048       if (MovDelay[x][y])
3049       {
3050         int phase = (MovDelay[x][y]-1)/2;
3051
3052         if (phase > 2)
3053           phase = 4-phase;
3054
3055         DrawGraphicThruMask(SCREENX(x), SCREENY(y), GFX_FUNKELN_WEISS + phase);
3056
3057         if (setup.direct_draw)
3058         {
3059           int dest_x, dest_y;
3060
3061           dest_x = FX + SCREENX(x)*TILEX;
3062           dest_y = FY + SCREENY(y)*TILEY;
3063
3064           XCopyArea(display, drawto_field, window, gc,
3065                     dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
3066           SetDrawtoField(DRAW_DIRECT);
3067         }
3068       }
3069     }
3070   }
3071 }
3072
3073 void MauerWaechst(int x, int y)
3074 {
3075   int delay = 6;
3076
3077   if (!MovDelay[x][y])          /* next animation frame */
3078     MovDelay[x][y] = 3*delay;
3079
3080   if (MovDelay[x][y])           /* wait some time before next frame */
3081   {
3082     int phase;
3083
3084     MovDelay[x][y]--;
3085     phase = 2-MovDelay[x][y]/delay;
3086     if (!(MovDelay[x][y]%delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3087       DrawGraphic(SCREENX(x), SCREENY(y),
3088                   (MovDir[x][y] == MV_LEFT  ? GFX_MAUER_LEFT  :
3089                    MovDir[x][y] == MV_RIGHT ? GFX_MAUER_RIGHT :
3090                    MovDir[x][y] == MV_UP    ? GFX_MAUER_UP    :
3091                                               GFX_MAUER_DOWN  ) + phase);
3092
3093     if (!MovDelay[x][y])
3094     {
3095       if (MovDir[x][y] == MV_LEFT)
3096       {
3097         if (IN_LEV_FIELD(x-1, y) && IS_MAUER(Feld[x-1][y]))
3098           DrawLevelField(x-1, y);
3099       }
3100       else if (MovDir[x][y] == MV_RIGHT)
3101       {
3102         if (IN_LEV_FIELD(x+1, y) && IS_MAUER(Feld[x+1][y]))
3103           DrawLevelField(x+1, y);
3104       }
3105       else if (MovDir[x][y] == MV_UP)
3106       {
3107         if (IN_LEV_FIELD(x, y-1) && IS_MAUER(Feld[x][y-1]))
3108           DrawLevelField(x, y-1);
3109       }
3110       else
3111       {
3112         if (IN_LEV_FIELD(x, y+1) && IS_MAUER(Feld[x][y+1]))
3113           DrawLevelField(x, y+1);
3114       }
3115
3116       Feld[x][y] = Store[x][y];
3117       Store[x][y] = 0;
3118       MovDir[x][y] = MV_NO_MOVING;
3119       DrawLevelField(x, y);
3120     }
3121   }
3122 }
3123
3124 void MauerAbleger(int ax, int ay)
3125 {
3126   int element = Feld[ax][ay];
3127   boolean oben_frei = FALSE, unten_frei = FALSE;
3128   boolean links_frei = FALSE, rechts_frei = FALSE;
3129   boolean oben_massiv = FALSE, unten_massiv = FALSE;
3130   boolean links_massiv = FALSE, rechts_massiv = FALSE;
3131
3132   if (!MovDelay[ax][ay])        /* start building new wall */
3133     MovDelay[ax][ay] = 6;
3134
3135   if (MovDelay[ax][ay])         /* wait some time before building new wall */
3136   {
3137     MovDelay[ax][ay]--;
3138     if (MovDelay[ax][ay])
3139       return;
3140   }
3141
3142   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
3143     oben_frei = TRUE;
3144   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
3145     unten_frei = TRUE;
3146   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
3147     links_frei = TRUE;
3148   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
3149     rechts_frei = TRUE;
3150
3151   if (element == EL_MAUER_Y || element == EL_MAUER_XY)
3152   {
3153     if (oben_frei)
3154     {
3155       Feld[ax][ay-1] = EL_MAUERND;
3156       Store[ax][ay-1] = element;
3157       MovDir[ax][ay-1] = MV_UP;
3158       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
3159         DrawGraphic(SCREENX(ax), SCREENY(ay-1), GFX_MAUER_UP);
3160     }
3161     if (unten_frei)
3162     {
3163       Feld[ax][ay+1] = EL_MAUERND;
3164       Store[ax][ay+1] = element;
3165       MovDir[ax][ay+1] = MV_DOWN;
3166       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
3167         DrawGraphic(SCREENX(ax), SCREENY(ay+1), GFX_MAUER_DOWN);
3168     }
3169   }
3170
3171   if (element == EL_MAUER_X || element == EL_MAUER_XY ||
3172       element == EL_MAUER_LEBT)
3173   {
3174     if (links_frei)
3175     {
3176       Feld[ax-1][ay] = EL_MAUERND;
3177       Store[ax-1][ay] = element;
3178       MovDir[ax-1][ay] = MV_LEFT;
3179       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
3180         DrawGraphic(SCREENX(ax-1), SCREENY(ay), GFX_MAUER_LEFT);
3181     }
3182     if (rechts_frei)
3183     {
3184       Feld[ax+1][ay] = EL_MAUERND;
3185       Store[ax+1][ay] = element;
3186       MovDir[ax+1][ay] = MV_RIGHT;
3187       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
3188         DrawGraphic(SCREENX(ax+1), SCREENY(ay), GFX_MAUER_RIGHT);
3189     }
3190   }
3191
3192   if (element == EL_MAUER_LEBT && (links_frei || rechts_frei))
3193     DrawLevelField(ax, ay);
3194
3195   if (!IN_LEV_FIELD(ax, ay-1) || IS_MAUER(Feld[ax][ay-1]))
3196     oben_massiv = TRUE;
3197   if (!IN_LEV_FIELD(ax, ay+1) || IS_MAUER(Feld[ax][ay+1]))
3198     unten_massiv = TRUE;
3199   if (!IN_LEV_FIELD(ax-1, ay) || IS_MAUER(Feld[ax-1][ay]))
3200     links_massiv = TRUE;
3201   if (!IN_LEV_FIELD(ax+1, ay) || IS_MAUER(Feld[ax+1][ay]))
3202     rechts_massiv = TRUE;
3203
3204   if (((oben_massiv && unten_massiv) ||
3205        element == EL_MAUER_X || element == EL_MAUER_LEBT) &&
3206       ((links_massiv && rechts_massiv) ||
3207        element == EL_MAUER_Y))
3208     Feld[ax][ay] = EL_MAUERWERK;
3209 }
3210
3211 void CheckForDragon(int x, int y)
3212 {
3213   int i, j;
3214   boolean dragon_found = FALSE;
3215   static int xy[4][2] =
3216   {
3217     { 0, -1 },
3218     { -1, 0 },
3219     { +1, 0 },
3220     { 0, +1 }
3221   };
3222
3223   for (i=0; i<4; i++)
3224   {
3225     for (j=0; j<4; j++)
3226     {
3227       int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
3228
3229       if (IN_LEV_FIELD(xx, yy) &&
3230           (Feld[xx][yy] == EL_BURNING || Feld[xx][yy] == EL_DRACHE))
3231       {
3232         if (Feld[xx][yy] == EL_DRACHE)
3233           dragon_found = TRUE;
3234       }
3235       else
3236         break;
3237     }
3238   }
3239
3240   if (!dragon_found)
3241   {
3242     for (i=0; i<4; i++)
3243     {
3244       for (j=0; j<3; j++)
3245       {
3246         int xx = x + j*xy[i][0], yy = y + j*xy[i][1];
3247   
3248         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_BURNING)
3249         {
3250           Feld[xx][yy] = EL_LEERRAUM;
3251           DrawLevelField(xx, yy);
3252         }
3253         else
3254           break;
3255       }
3256     }
3257   }
3258 }
3259
3260 static void CheckBuggyBase(int x, int y)
3261 {
3262   int element = Feld[x][y];
3263
3264   if (element == EL_SP_BUG)
3265   {
3266     if (!MovDelay[x][y])        /* start activating buggy base */
3267       MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
3268
3269     if (MovDelay[x][y])         /* wait some time before activating base */
3270     {
3271       MovDelay[x][y]--;
3272       if (MovDelay[x][y] < 5 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3273         DrawGraphic(SCREENX(x), SCREENY(y), GFX_SP_BUG_WARNING);
3274       if (MovDelay[x][y])
3275         return;
3276
3277       Feld[x][y] = EL_SP_BUG_ACTIVE;
3278     }
3279   }
3280   else if (element == EL_SP_BUG_ACTIVE)
3281   {
3282     if (!MovDelay[x][y])        /* start activating buggy base */
3283       MovDelay[x][y] = 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND);
3284
3285     if (MovDelay[x][y])         /* wait some time before activating base */
3286     {
3287       MovDelay[x][y]--;
3288       if (MovDelay[x][y])
3289       {
3290         int i;
3291         static int xy[4][2] =
3292         {
3293           { 0, -1 },
3294           { -1, 0 },
3295           { +1, 0 },
3296           { 0, +1 }
3297         };
3298
3299         if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
3300           DrawGraphic(SCREENX(x),SCREENY(y), GFX_SP_BUG_ACTIVE + SimpleRND(4));
3301
3302         for (i=0; i<4; i++)
3303         {
3304           int xx = x + xy[i][0], yy = y + xy[i][1];
3305
3306           if (IS_PLAYER(xx, yy))
3307           {
3308             PlaySoundLevel(x, y, SND_SP_BUG);
3309             break;
3310           }
3311         }
3312
3313         return;
3314       }
3315
3316       Feld[x][y] = EL_SP_BUG;
3317       DrawLevelField(x, y);
3318     }
3319   }
3320 }
3321
3322 static void PlayerActions(struct PlayerInfo *player, byte player_action)
3323 {
3324   static byte stored_player_action[MAX_PLAYERS];
3325   static int num_stored_actions = 0;
3326   static boolean save_tape_entry = FALSE;
3327   boolean moved = FALSE, snapped = FALSE, bombed = FALSE;
3328   int jx = player->jx, jy = player->jy;
3329   int left      = player_action & JOY_LEFT;
3330   int right     = player_action & JOY_RIGHT;
3331   int up        = player_action & JOY_UP;
3332   int down      = player_action & JOY_DOWN;
3333   int button1   = player_action & JOY_BUTTON_1;
3334   int button2   = player_action & JOY_BUTTON_2;
3335   int dx        = (left ? -1    : right ? 1     : 0);
3336   int dy        = (up   ? -1    : down  ? 1     : 0);
3337
3338   stored_player_action[player->index_nr] = 0;
3339   num_stored_actions++;
3340
3341   if (!player->active || player->gone || tape.pausing)
3342     return;
3343
3344   if (player_action)
3345   {
3346     save_tape_entry = TRUE;
3347     player->frame_reset_delay = 0;
3348
3349     if (button1)
3350       snapped = SnapField(player, dx, dy);
3351     else
3352     {
3353       if (button2)
3354         bombed = PlaceBomb(player);
3355       moved = MoveFigure(player, dx, dy);
3356     }
3357
3358     if (tape.recording && (moved || snapped || bombed))
3359     {
3360       if (bombed && !moved)
3361         player_action &= JOY_BUTTON;
3362
3363       stored_player_action[player->index_nr] = player_action;
3364     }
3365     else if (tape.playing && snapped)
3366       SnapField(player, 0, 0);                  /* stop snapping */
3367   }
3368   else
3369   {
3370     /* no actions for this player (no input at player's configured device) */
3371
3372     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
3373     SnapField(player, 0, 0);
3374     CheckGravityMovement(player);
3375
3376     if (++player->frame_reset_delay > player->move_delay_value)
3377       player->Frame = 0;
3378   }
3379
3380   if (tape.recording && num_stored_actions >= MAX_PLAYERS && save_tape_entry)
3381   {
3382     TapeRecordAction(stored_player_action);
3383     num_stored_actions = 0;
3384     save_tape_entry = FALSE;
3385   }
3386
3387   if (tape.playing && !tape.pausing && !player_action &&
3388       tape.counter < tape.length)
3389   {
3390     int next_joy =
3391       tape.pos[tape.counter].action[player->index_nr] & (JOY_LEFT|JOY_RIGHT);
3392
3393     if ((next_joy == JOY_LEFT || next_joy == JOY_RIGHT) &&
3394         (player->MovDir != JOY_UP && player->MovDir != JOY_DOWN))
3395     {
3396       int dx = (next_joy == JOY_LEFT ? -1 : +1);
3397
3398       if (IN_LEV_FIELD(jx+dx, jy) && IS_PUSHABLE(Feld[jx+dx][jy]))
3399       {
3400         int el = Feld[jx+dx][jy];
3401         int push_delay = (IS_SB_ELEMENT(el) || el == EL_SONDE ? 2 : 10);
3402
3403         if (tape.delay_played + push_delay >= tape.pos[tape.counter].delay)
3404         {
3405           player->MovDir = next_joy;
3406           player->Frame = FrameCounter % 4;
3407           player->Pushing = TRUE;
3408         }
3409       }
3410     }
3411   }
3412 }
3413
3414 void GameActions()
3415 {
3416   static unsigned long action_delay = 0;
3417   unsigned long action_delay_value;
3418   int sieb_x = 0, sieb_y = 0;
3419   int i, x, y, element;
3420   byte *recorded_player_action;
3421   byte summarized_player_action = 0;
3422
3423   if (game_status != PLAYING)
3424     return;
3425
3426   action_delay_value =
3427     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
3428
3429   /* ---------- main game synchronization point ---------- */
3430
3431   WaitUntilDelayReached(&action_delay, action_delay_value);
3432
3433   if (network_playing && !network_player_action_received)
3434   {
3435     /*
3436 #ifdef DEBUG
3437     printf("DEBUG: try to get network player actions in time\n");
3438 #endif
3439     */
3440
3441 #ifndef MSDOS
3442     /* last chance to get network player actions without main loop delay */
3443     HandleNetworking();
3444 #endif
3445
3446     if (game_status != PLAYING)
3447       return;
3448
3449     if (!network_player_action_received)
3450     {
3451       /*
3452 #ifdef DEBUG
3453       printf("DEBUG: failed to get network player actions in time\n");
3454 #endif
3455       */
3456       return;
3457     }
3458   }
3459
3460   if (tape.pausing)
3461     return;
3462
3463   if (tape.playing)
3464     TapePlayDelay();
3465   else if (tape.recording)
3466     TapeRecordDelay();
3467
3468   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
3469
3470   for (i=0; i<MAX_PLAYERS; i++)
3471   {
3472     summarized_player_action |= stored_player[i].action;
3473
3474     if (!network_playing)
3475       stored_player[i].effective_action = stored_player[i].action;
3476   }
3477
3478 #ifndef MSDOS
3479   if (network_playing)
3480     SendToServer_MovePlayer(summarized_player_action);
3481 #endif
3482
3483   if (!options.network && !setup.team_mode)
3484     local_player->effective_action = summarized_player_action;
3485
3486   for (i=0; i<MAX_PLAYERS; i++)
3487   {
3488     int actual_player_action = stored_player[i].effective_action;
3489
3490     if (stored_player[i].programmed_action)
3491       actual_player_action = stored_player[i].programmed_action;
3492
3493     if (recorded_player_action)
3494       actual_player_action = recorded_player_action[i];
3495
3496     PlayerActions(&stored_player[i], actual_player_action);
3497     ScrollFigure(&stored_player[i], SCROLL_GO_ON);
3498   }
3499
3500   network_player_action_received = FALSE;
3501
3502   ScrollScreen(NULL, SCROLL_GO_ON);
3503
3504
3505
3506 #ifdef DEBUG
3507 #if 0
3508   if (TimeFrames == 0 && !local_player->gone)
3509   {
3510     extern unsigned int last_RND();
3511
3512     printf("DEBUG: %03d last RND was %d \t [state checksum is %d]\n",
3513            TimePlayed, last_RND(), getStateCheckSum(TimePlayed));
3514   }
3515 #endif
3516 #endif
3517
3518 #ifdef DEBUG
3519 #if 0
3520   if (GameFrameDelay >= 500)
3521     printf("FrameCounter == %d\n", FrameCounter);
3522 #endif
3523 #endif
3524
3525
3526
3527   FrameCounter++;
3528   TimeFrames++;
3529
3530   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
3531   {
3532     Stop[x][y] = FALSE;
3533     if (JustHit[x][y]>0)
3534       JustHit[x][y]--;
3535
3536 #if DEBUG
3537     if (IS_BLOCKED(x, y))
3538     {
3539       int oldx, oldy;
3540
3541       Blocked2Moving(x, y, &oldx, &oldy);
3542       if (!IS_MOVING(oldx, oldy))
3543       {
3544         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
3545         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
3546         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
3547         printf("GameActions(): This should never happen!\n");
3548       }
3549     }
3550 #endif
3551   }
3552
3553   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
3554   {
3555     element = Feld[x][y];
3556
3557     if (IS_INACTIVE(element))
3558       continue;
3559
3560     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
3561     {
3562       StartMoving(x, y);
3563
3564       if (IS_GEM(element))
3565         EdelsteinFunkeln(x, y);
3566     }
3567     else if (IS_MOVING(x, y))
3568       ContinueMoving(x, y);
3569     else if (element == EL_DYNAMIT || element == EL_DYNABOMB)
3570       CheckDynamite(x, y);
3571     else if (element == EL_EXPLODING)
3572       Explode(x, y, Frame[x][y], EX_NORMAL);
3573     else if (element == EL_AMOEBING)
3574       AmoebeWaechst(x, y);
3575     else if (IS_AMOEBALIVE(element))
3576       AmoebeAbleger(x, y);
3577     else if (element == EL_LIFE || element == EL_LIFE_ASYNC)
3578       Life(x, y);
3579     else if (element == EL_ABLENK_EIN)
3580       Ablenk(x, y);
3581     else if (element == EL_SALZSAEURE)
3582       Blubber(x, y);
3583     else if (element == EL_BLURB_LEFT || element == EL_BLURB_RIGHT)
3584       Blurb(x, y);
3585     else if (element == EL_CRACKINGNUT)
3586       NussKnacken(x, y);
3587     else if (element == EL_AUSGANG_ZU)
3588       AusgangstuerPruefen(x, y);
3589     else if (element == EL_AUSGANG_ACT)
3590       AusgangstuerOeffnen(x, y);
3591     else if (element == EL_AUSGANG_AUF)
3592       AusgangstuerBlinken(x, y);
3593     else if (element == EL_MAUERND)
3594       MauerWaechst(x, y);
3595     else if (element == EL_MAUER_LEBT ||
3596              element == EL_MAUER_X ||
3597              element == EL_MAUER_Y ||
3598              element == EL_MAUER_XY)
3599       MauerAbleger(x, y);
3600     else if (element == EL_BURNING)
3601       CheckForDragon(x, y);
3602     else if (element == EL_SP_BUG || element == EL_SP_BUG_ACTIVE)
3603       CheckBuggyBase(x, y);
3604     else if (element == EL_SP_TERMINAL)
3605       DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL, 7, 12, ANIM_NORMAL);
3606     else if (element == EL_SP_TERMINAL_ACTIVE)
3607       DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL_ACTIVE, 7, 4, ANIM_NORMAL);
3608
3609     if (SiebAktiv)
3610     {
3611       boolean sieb = FALSE;
3612       int jx = local_player->jx, jy = local_player->jy;
3613
3614       if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL ||
3615           Store[x][y] == EL_SIEB_LEER)
3616       {
3617         SiebAktivieren(x, y, 1);
3618         sieb = TRUE;
3619       }
3620       else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL ||
3621                Store[x][y] == EL_SIEB2_LEER)
3622       {
3623         SiebAktivieren(x, y, 2);
3624         sieb = TRUE;
3625       }
3626
3627       /* play the element sound at the position nearest to the player */
3628       if (sieb && ABS(x-jx)+ABS(y-jy) < ABS(sieb_x-jx)+ABS(sieb_y-jy))
3629       {
3630         sieb_x = x;
3631         sieb_y = y;
3632       }
3633     }
3634   }
3635
3636   if (SiebAktiv)
3637   {
3638     if (!(SiebCount % 4))
3639       PlaySoundLevel(sieb_x, sieb_y, SND_MIEP);
3640
3641     if (SiebCount > 0)
3642     {
3643       SiebCount--;
3644       if (!SiebCount)
3645       {
3646         for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
3647         {
3648           element = Feld[x][y];
3649           if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL)
3650           {
3651             Feld[x][y] = EL_SIEB_TOT;
3652             DrawLevelField(x, y);
3653           }
3654           else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL)
3655           {
3656             Feld[x][y] = EL_SIEB2_TOT;
3657             DrawLevelField(x, y);
3658           }
3659         }
3660
3661         SiebAktiv = FALSE;
3662       }
3663     }
3664   }
3665
3666   if (TimeFrames >= (1000 / GameFrameDelay) && !tape.pausing)
3667   {
3668     TimeFrames = 0;
3669     TimePlayed++;
3670
3671     if (tape.recording || tape.playing)
3672       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
3673
3674     if (TimeLeft > 0)
3675     {
3676       TimeLeft--;
3677
3678       if (TimeLeft <= 10)
3679         PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
3680
3681       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
3682
3683       if (!TimeLeft)
3684         for (i=0; i<MAX_PLAYERS; i++)
3685           KillHero(&stored_player[i]);
3686     }
3687     else if (level.time == 0)           /* level without time limit */
3688       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
3689   }
3690
3691   DrawAllPlayers();
3692 }
3693
3694 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
3695 {
3696   int min_x = x, min_y = y, max_x = x, max_y = y;
3697   int i;
3698
3699   for (i=0; i<MAX_PLAYERS; i++)
3700   {
3701     int jx = stored_player[i].jx, jy = stored_player[i].jy;
3702
3703     if (!stored_player[i].active || stored_player[i].gone ||
3704         &stored_player[i] == player)
3705       continue;
3706
3707     min_x = MIN(min_x, jx);
3708     min_y = MIN(min_y, jy);
3709     max_x = MAX(max_x, jx);
3710     max_y = MAX(max_y, jy);
3711   }
3712
3713   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
3714 }
3715
3716 static boolean AllPlayersInVisibleScreen()
3717 {
3718   int i;
3719
3720   for (i=0; i<MAX_PLAYERS; i++)
3721   {
3722     int jx = stored_player[i].jx, jy = stored_player[i].jy;
3723
3724     if (!stored_player[i].active || stored_player[i].gone)
3725       continue;
3726
3727     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3728       return FALSE;
3729   }
3730
3731   return TRUE;
3732 }
3733
3734 void ScrollLevel(int dx, int dy)
3735 {
3736   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
3737   int x, y;
3738
3739   XCopyArea(display, drawto_field, drawto_field, gc,
3740             FX + TILEX*(dx == -1) - softscroll_offset,
3741             FY + TILEY*(dy == -1) - softscroll_offset,
3742             SXSIZE - TILEX*(dx!=0) + 2*softscroll_offset,
3743             SYSIZE - TILEY*(dy!=0) + 2*softscroll_offset,
3744             FX + TILEX*(dx == 1) - softscroll_offset,
3745             FY + TILEY*(dy == 1) - softscroll_offset);
3746
3747   if (dx)
3748   {
3749     x = (dx == 1 ? BX1 : BX2);
3750     for (y=BY1; y<=BY2; y++)
3751       DrawScreenField(x, y);
3752   }
3753   if (dy)
3754   {
3755     y = (dy == 1 ? BY1 : BY2);
3756     for (x=BX1; x<=BX2; x++)
3757       DrawScreenField(x, y);
3758   }
3759
3760   redraw_mask |= REDRAW_FIELD;
3761 }
3762
3763 static void CheckGravityMovement(struct PlayerInfo *player)
3764 {
3765   if (level.gravity && !player->programmed_action)
3766   {
3767     int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
3768     int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
3769     int move_dir =
3770       (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
3771        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
3772        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
3773     int jx = player->jx, jy = player->jy;
3774     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
3775     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
3776     int new_jx = jx + dx, new_jy = jy + dy;
3777     boolean field_under_player_is_free =
3778       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
3779     boolean player_is_moving_to_valid_field =
3780       (IN_LEV_FIELD(new_jx, new_jy) &&
3781        (Feld[new_jx][new_jy] == EL_SP_BASE ||
3782         Feld[new_jx][new_jy] == EL_ERDREICH));
3783
3784     if (field_under_player_is_free && !player_is_moving_to_valid_field)
3785       player->programmed_action = MV_DOWN;
3786   }
3787 }
3788
3789 boolean MoveFigureOneStep(struct PlayerInfo *player,
3790                           int dx, int dy, int real_dx, int real_dy)
3791 {
3792   int jx = player->jx, jy = player->jy;
3793   int new_jx = jx+dx, new_jy = jy+dy;
3794   int element;
3795   int can_move;
3796
3797   if (player->gone || (!dx && !dy))
3798     return MF_NO_ACTION;
3799
3800   player->MovDir = (dx < 0 ? MV_LEFT :
3801                     dx > 0 ? MV_RIGHT :
3802                     dy < 0 ? MV_UP :
3803                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
3804
3805   if (!IN_LEV_FIELD(new_jx, new_jy))
3806     return MF_NO_ACTION;
3807
3808   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
3809     return MF_NO_ACTION;
3810
3811 #if 0
3812   element = MovingOrBlocked2Element(new_jx, new_jy);
3813 #else
3814   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
3815 #endif
3816
3817   if (DONT_GO_TO(element))
3818   {
3819     if (element == EL_SALZSAEURE && dx == 0 && dy == 1)
3820     {
3821       Blurb(jx, jy);
3822       Feld[jx][jy] = EL_SPIELFIGUR;
3823       InitMovingField(jx, jy, MV_DOWN);
3824       Store[jx][jy] = EL_SALZSAEURE;
3825       ContinueMoving(jx, jy);
3826       BuryHero(player);
3827     }
3828     else
3829       KillHero(player);
3830
3831     return MF_MOVING;
3832   }
3833
3834   can_move = DigField(player, new_jx, new_jy, real_dx, real_dy, DF_DIG);
3835   if (can_move != MF_MOVING)
3836     return can_move;
3837
3838   StorePlayer[jx][jy] = 0;
3839   player->last_jx = jx;
3840   player->last_jy = jy;
3841   jx = player->jx = new_jx;
3842   jy = player->jy = new_jy;
3843   StorePlayer[jx][jy] = player->element_nr;
3844
3845   player->MovPos =
3846     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
3847
3848   ScrollFigure(player, SCROLL_INIT);
3849
3850   return MF_MOVING;
3851 }
3852
3853 boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
3854 {
3855   int jx = player->jx, jy = player->jy;
3856   int old_jx = jx, old_jy = jy;
3857   int moved = MF_NO_ACTION;
3858
3859   if (player->gone || (!dx && !dy))
3860     return FALSE;
3861
3862   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
3863       !tape.playing)
3864     return FALSE;
3865
3866   /* remove the last programmed player action */
3867   player->programmed_action = 0;
3868
3869   if (player->MovPos)
3870   {
3871     /* should only happen if pre-1.2 tape recordings are played */
3872     /* this is only for backward compatibility */
3873
3874     int original_move_delay_value = player->move_delay_value;
3875
3876 #if DEBUG
3877     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n");
3878 #endif
3879
3880     /* scroll remaining steps with finest movement resolution */
3881     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
3882
3883     while (player->MovPos)
3884     {
3885       ScrollFigure(player, SCROLL_GO_ON);
3886       ScrollScreen(NULL, SCROLL_GO_ON);
3887       FrameCounter++;
3888       DrawAllPlayers();
3889       BackToFront();
3890     }
3891
3892     player->move_delay_value = original_move_delay_value;
3893   }
3894
3895   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
3896   {
3897     if (!(moved |= MoveFigureOneStep(player, 0, dy, dx, dy)))
3898       moved |= MoveFigureOneStep(player, dx, 0, dx, dy);
3899   }
3900   else
3901   {
3902     if (!(moved |= MoveFigureOneStep(player, dx, 0, dx, dy)))
3903       moved |= MoveFigureOneStep(player, 0, dy, dx, dy);
3904   }
3905
3906   jx = player->jx;
3907   jy = player->jy;
3908
3909   if (moved & MF_MOVING && !ScreenMovPos &&
3910       (player == local_player || !options.network))
3911   {
3912     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
3913     int offset = (setup.scroll_delay ? 3 : 0);
3914
3915     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
3916     {
3917       /* actual player has left the screen -- scroll in that direction */
3918       if (jx != old_jx)         /* player has moved horizontally */
3919         scroll_x += (jx - old_jx);
3920       else                      /* player has moved vertically */
3921         scroll_y += (jy - old_jy);
3922     }
3923     else
3924     {
3925       if (jx != old_jx)         /* player has moved horizontally */
3926       {
3927         if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
3928             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
3929           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
3930
3931         /* don't scroll over playfield boundaries */
3932         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
3933           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
3934
3935         /* don't scroll more than one field at a time */
3936         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
3937
3938         /* don't scroll against the player's moving direction */
3939         if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
3940             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
3941           scroll_x = old_scroll_x;
3942       }
3943       else                      /* player has moved vertically */
3944       {
3945         if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
3946             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
3947           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
3948
3949         /* don't scroll over playfield boundaries */
3950         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
3951           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
3952
3953         /* don't scroll more than one field at a time */
3954         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
3955
3956         /* don't scroll against the player's moving direction */
3957         if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
3958             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
3959           scroll_y = old_scroll_y;
3960       }
3961     }
3962
3963     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
3964     {
3965       if (!options.network && !AllPlayersInVisibleScreen())
3966       {
3967         scroll_x = old_scroll_x;
3968         scroll_y = old_scroll_y;
3969       }
3970       else
3971       {
3972         ScrollScreen(player, SCROLL_INIT);
3973         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
3974       }
3975     }
3976   }
3977
3978   if (!(moved & MF_MOVING) && !player->Pushing)
3979     player->Frame = 0;
3980   else
3981     player->Frame = (player->Frame + 1) % 4;
3982
3983   if (moved & MF_MOVING)
3984   {
3985     if (old_jx != jx && old_jy == jy)
3986       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
3987     else if (old_jx == jx && old_jy != jy)
3988       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
3989
3990     DrawLevelField(jx, jy);     /* for "ErdreichAnbroeckeln()" */
3991
3992     player->last_move_dir = player->MovDir;
3993   }
3994   else
3995   {
3996     CheckGravityMovement(player);
3997
3998     player->last_move_dir = MV_NO_MOVING;
3999   }
4000
4001   TestIfHeroHitsBadThing(jx, jy);
4002
4003   if (player->gone)
4004     RemoveHero(player);
4005
4006   return moved;
4007 }
4008
4009 void ScrollFigure(struct PlayerInfo *player, int mode)
4010 {
4011   int jx = player->jx, jy = player->jy;
4012   int last_jx = player->last_jx, last_jy = player->last_jy;
4013   int move_stepsize = TILEX / player->move_delay_value;
4014
4015   if (!player->active || player->gone || !player->MovPos)
4016     return;
4017
4018   if (mode == SCROLL_INIT)
4019   {
4020     player->actual_frame_counter = FrameCounter;
4021     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
4022
4023     if (Feld[last_jx][last_jy] == EL_LEERRAUM)
4024       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
4025
4026     DrawPlayer(player);
4027     return;
4028   }
4029   else if (!FrameReached(&player->actual_frame_counter, 1))
4030     return;
4031
4032   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
4033   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
4034
4035   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
4036     Feld[last_jx][last_jy] = EL_LEERRAUM;
4037
4038   /* before DrawPlayer() to draw correct player graphic for this case */
4039   if (player->MovPos == 0)
4040     CheckGravityMovement(player);
4041
4042   DrawPlayer(player);
4043
4044   if (player->MovPos == 0)
4045   {
4046     if (IS_QUICK_GATE(Feld[last_jx][last_jy]))
4047     {
4048       /* continue with normal speed after quickly moving through gate */
4049       HALVE_PLAYER_SPEED(player);
4050
4051       /* be able to make the next move without delay */
4052       player->move_delay = 0;
4053     }
4054
4055     player->last_jx = jx;
4056     player->last_jy = jy;
4057
4058     if (Feld[jx][jy] == EL_AUSGANG_AUF)
4059     {
4060       RemoveHero(player);
4061
4062       if (!local_player->friends_still_needed)
4063         player->LevelSolved = player->GameOver = TRUE;
4064     }
4065   }
4066 }
4067
4068 void ScrollScreen(struct PlayerInfo *player, int mode)
4069 {
4070   static unsigned long screen_frame_counter = 0;
4071
4072   if (mode == SCROLL_INIT)
4073   {
4074     /* set scrolling step size according to actual player's moving speed */
4075     ScrollStepSize = TILEX / player->move_delay_value;
4076
4077     screen_frame_counter = FrameCounter;
4078     ScreenMovDir = player->MovDir;
4079     ScreenMovPos = player->MovPos;
4080     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
4081     return;
4082   }
4083   else if (!FrameReached(&screen_frame_counter, 1))
4084     return;
4085
4086   if (ScreenMovPos)
4087   {
4088     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
4089     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
4090     redraw_mask |= REDRAW_FIELD;
4091   }
4092   else
4093     ScreenMovDir = MV_NO_MOVING;
4094 }
4095
4096 void TestIfGoodThingHitsBadThing(int goodx, int goody)
4097 {
4098   int i, killx = goodx, killy = goody;
4099   static int xy[4][2] =
4100   {
4101     { 0, -1 },
4102     { -1, 0 },
4103     { +1, 0 },
4104     { 0, +1 }
4105   };
4106   static int harmless[4] =
4107   {
4108     MV_UP,
4109     MV_LEFT,
4110     MV_RIGHT,
4111     MV_DOWN
4112   };
4113
4114   for (i=0; i<4; i++)
4115   {
4116     int x, y, element;
4117
4118     x = goodx + xy[i][0];
4119     y = goody + xy[i][1];
4120     if (!IN_LEV_FIELD(x, y))
4121       continue;
4122
4123 #if 0
4124     element = Feld[x][y];
4125 #else
4126     element = MovingOrBlocked2ElementIfNotLeaving(x, y);
4127 #endif
4128
4129     if (DONT_TOUCH(element))
4130     {
4131       if (MovDir[x][y] == harmless[i])
4132         continue;
4133
4134       killx = x;
4135       killy = y;
4136       break;
4137     }
4138   }
4139
4140   if (killx != goodx || killy != goody)
4141   {
4142     if (IS_PLAYER(goodx, goody))
4143       KillHero(PLAYERINFO(goodx, goody));
4144     else
4145       Bang(goodx, goody);
4146   }
4147 }
4148
4149 void TestIfBadThingHitsGoodThing(int badx, int bady)
4150 {
4151   int i, killx = badx, killy = bady;
4152   static int xy[4][2] =
4153   {
4154     { 0, -1 },
4155     { -1, 0 },
4156     { +1, 0 },
4157     { 0, +1 }
4158   };
4159   static int harmless[4] =
4160   {
4161     MV_UP,
4162     MV_LEFT,
4163     MV_RIGHT,
4164     MV_DOWN
4165   };
4166
4167   for (i=0; i<4; i++)
4168   {
4169     int x, y, element;
4170
4171     x = badx + xy[i][0];
4172     y = bady + xy[i][1];
4173     if (!IN_LEV_FIELD(x, y))
4174       continue;
4175
4176     element = Feld[x][y];
4177
4178     if (IS_PLAYER(x, y))
4179     {
4180       killx = x;
4181       killy = y;
4182       break;
4183     }
4184     else if (element == EL_PINGUIN)
4185     {
4186       if (MovDir[x][y] == harmless[i] && IS_MOVING(x, y))
4187         continue;
4188
4189       killx = x;
4190       killy = y;
4191       break;
4192     }
4193   }
4194
4195   if (killx != badx || killy != bady)
4196   {
4197     if (IS_PLAYER(killx, killy))
4198       KillHero(PLAYERINFO(killx, killy));
4199     else
4200       Bang(killx, killy);
4201   }
4202 }
4203
4204 void TestIfHeroHitsBadThing(int x, int y)
4205 {
4206   TestIfGoodThingHitsBadThing(x, y);
4207 }
4208
4209 void TestIfBadThingHitsHero(int x, int y)
4210 {
4211   TestIfBadThingHitsGoodThing(x, y);
4212 }
4213
4214 void TestIfFriendHitsBadThing(int x, int y)
4215 {
4216   TestIfGoodThingHitsBadThing(x, y);
4217 }
4218
4219 void TestIfBadThingHitsFriend(int x, int y)
4220 {
4221   TestIfBadThingHitsGoodThing(x, y);
4222 }
4223
4224 void TestIfBadThingHitsOtherBadThing(int badx, int bady)
4225 {
4226   int i, killx = badx, killy = bady;
4227   static int xy[4][2] =
4228   {
4229     { 0, -1 },
4230     { -1, 0 },
4231     { +1, 0 },
4232     { 0, +1 }
4233   };
4234
4235   for (i=0; i<4; i++)
4236   {
4237     int x, y, element;
4238
4239     x=badx + xy[i][0];
4240     y=bady + xy[i][1];
4241     if (!IN_LEV_FIELD(x, y))
4242       continue;
4243
4244     element = Feld[x][y];
4245     if (IS_AMOEBOID(element) || element == EL_LIFE ||
4246         element == EL_AMOEBING || element == EL_TROPFEN)
4247     {
4248       killx = x;
4249       killy = y;
4250       break;
4251     }
4252   }
4253
4254   if (killx != badx || killy != bady)
4255     Bang(badx, bady);
4256 }
4257
4258 void KillHero(struct PlayerInfo *player)
4259 {
4260   int jx = player->jx, jy = player->jy;
4261
4262   if (player->gone)
4263     return;
4264
4265   if (IS_PFORTE(Feld[jx][jy]))
4266     Feld[jx][jy] = EL_LEERRAUM;
4267
4268   Bang(jx, jy);
4269   BuryHero(player);
4270 }
4271
4272 void BuryHero(struct PlayerInfo *player)
4273 {
4274   int jx = player->jx, jy = player->jy;
4275
4276   if (player->gone)
4277     return;
4278
4279   PlaySoundLevel(jx, jy, SND_AUTSCH);
4280   PlaySoundLevel(jx, jy, SND_LACHEN);
4281
4282   player->GameOver = TRUE;
4283   RemoveHero(player);
4284 }
4285
4286 void RemoveHero(struct PlayerInfo *player)
4287 {
4288   int jx = player->jx, jy = player->jy;
4289   int i, found = FALSE;
4290
4291   player->gone = TRUE;
4292   StorePlayer[jx][jy] = 0;
4293
4294   for (i=0; i<MAX_PLAYERS; i++)
4295     if (stored_player[i].active && !stored_player[i].gone)
4296       found = TRUE;
4297
4298   if (!found)
4299     AllPlayersGone = TRUE;
4300
4301   ExitX = ZX = jx;
4302   ExitY = ZY = jy;
4303 }
4304
4305 int DigField(struct PlayerInfo *player,
4306              int x, int y, int real_dx, int real_dy, int mode)
4307 {
4308   int jx = player->jx, jy = player->jy;
4309   int dx = x - jx, dy = y - jy;
4310   int move_direction = (dx == -1 ? MV_LEFT :
4311                         dx == +1 ? MV_RIGHT :
4312                         dy == -1 ? MV_UP :
4313                         dy == +1 ? MV_DOWN : MV_NO_MOVING);
4314   int element;
4315
4316   if (!player->MovPos)
4317     player->Pushing = FALSE;
4318
4319   if (mode == DF_NO_PUSH)
4320   {
4321     player->push_delay = 0;
4322     return MF_NO_ACTION;
4323   }
4324
4325   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
4326     return MF_NO_ACTION;
4327
4328   element = Feld[x][y];
4329
4330   switch(element)
4331   {
4332     case EL_LEERRAUM:
4333       PlaySoundLevel(x, y, SND_EMPTY);
4334       break;
4335
4336     case EL_ERDREICH:
4337       Feld[x][y] = EL_LEERRAUM;
4338       PlaySoundLevel(x, y, SND_SCHLURF);
4339       break;
4340
4341     case EL_SP_BASE:
4342     case EL_SP_BUG:
4343       Feld[x][y] = EL_LEERRAUM;
4344       PlaySoundLevel(x, y, SND_SP_BASE);
4345       break;
4346
4347     case EL_EDELSTEIN:
4348     case EL_EDELSTEIN_BD:
4349     case EL_EDELSTEIN_GELB:
4350     case EL_EDELSTEIN_ROT:
4351     case EL_EDELSTEIN_LILA:
4352     case EL_DIAMANT:
4353     case EL_SP_INFOTRON:
4354       RemoveField(x, y);
4355       local_player->gems_still_needed -= (element == EL_DIAMANT ? 3 : 1);
4356       if (local_player->gems_still_needed < 0)
4357         local_player->gems_still_needed = 0;
4358       RaiseScoreElement(element);
4359       DrawText(DX_EMERALDS, DY_EMERALDS,
4360                int2str(local_player->gems_still_needed, 3),
4361                FS_SMALL, FC_YELLOW);
4362       if (element == EL_SP_INFOTRON)
4363         PlaySoundLevel(x, y, SND_SP_INFOTRON);
4364       else
4365         PlaySoundLevel(x, y, SND_PONG);
4366       break;
4367
4368     case EL_SPEED_PILL:
4369       RemoveField(x, y);
4370       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
4371       PlaySoundLevel(x, y, SND_PONG);
4372       break;
4373
4374     case EL_DYNAMIT_AUS:
4375     case EL_SP_DISK_RED:
4376       RemoveField(x, y);
4377       player->dynamite++;
4378       RaiseScoreElement(EL_DYNAMIT);
4379       DrawText(DX_DYNAMITE, DY_DYNAMITE,
4380                int2str(local_player->dynamite, 3),
4381                FS_SMALL, FC_YELLOW);
4382       if (element == EL_SP_DISK_RED)
4383         PlaySoundLevel(x, y, SND_SP_INFOTRON);
4384       else
4385         PlaySoundLevel(x, y, SND_PONG);
4386       break;
4387
4388     case EL_DYNABOMB_NR:
4389       RemoveField(x, y);
4390       player->dynabomb_count++;
4391       player->dynabombs_left++;
4392       RaiseScoreElement(EL_DYNAMIT);
4393       PlaySoundLevel(x, y, SND_PONG);
4394       break;
4395
4396     case EL_DYNABOMB_SZ:
4397       RemoveField(x, y);
4398       player->dynabomb_size++;
4399       RaiseScoreElement(EL_DYNAMIT);
4400       PlaySoundLevel(x, y, SND_PONG);
4401       break;
4402
4403     case EL_DYNABOMB_XL:
4404       RemoveField(x, y);
4405       player->dynabomb_xl = TRUE;
4406       RaiseScoreElement(EL_DYNAMIT);
4407       PlaySoundLevel(x, y, SND_PONG);
4408       break;
4409
4410     case EL_SCHLUESSEL1:
4411     case EL_SCHLUESSEL2:
4412     case EL_SCHLUESSEL3:
4413     case EL_SCHLUESSEL4:
4414     {
4415       int key_nr = element - EL_SCHLUESSEL1;
4416
4417       RemoveField(x, y);
4418       player->key[key_nr] = TRUE;
4419       RaiseScoreElement(EL_SCHLUESSEL);
4420       DrawMiniGraphicExt(drawto, gc,
4421                          DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
4422                          GFX_SCHLUESSEL1+key_nr);
4423       DrawMiniGraphicExt(window, gc,
4424                          DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
4425                          GFX_SCHLUESSEL1+key_nr);
4426       PlaySoundLevel(x, y, SND_PONG);
4427       break;
4428     }
4429
4430     case EL_EM_KEY_1:
4431     case EL_EM_KEY_2:
4432     case EL_EM_KEY_3:
4433     case EL_EM_KEY_4:
4434     {
4435       int key_nr = element - EL_EM_KEY_1;
4436
4437       RemoveField(x, y);
4438       player->key[key_nr] = TRUE;
4439       RaiseScoreElement(EL_SCHLUESSEL);
4440       DrawMiniGraphicExt(drawto, gc,
4441                          DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
4442                          GFX_SCHLUESSEL1+key_nr);
4443       DrawMiniGraphicExt(window, gc,
4444                          DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
4445                          GFX_SCHLUESSEL1+key_nr);
4446       PlaySoundLevel(x, y, SND_PONG);
4447       break;
4448     }
4449
4450     case EL_ABLENK_AUS:
4451       Feld[x][y] = EL_ABLENK_EIN;
4452       ZX = x;
4453       ZY = y;
4454       DrawLevelField(x, y);
4455       return MF_ACTION;
4456       break;
4457
4458     case EL_SP_TERMINAL:
4459       {
4460         int xx, yy;
4461
4462         for (yy=0; yy<lev_fieldy; yy++)
4463         {
4464           for (xx=0; xx<lev_fieldx; xx++)
4465           {
4466             if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
4467               Bang(xx, yy);
4468             else if (Feld[xx][yy] == EL_SP_TERMINAL)
4469               Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
4470           }
4471         }
4472
4473         return MF_ACTION;
4474       }
4475       break;
4476
4477     case EL_SP_EXIT:
4478       if (local_player->gems_still_needed > 0)
4479         return MF_NO_ACTION;
4480
4481       player->LevelSolved = player->GameOver = TRUE;
4482       PlaySoundStereo(SND_SP_EXIT, PSND_MAX_RIGHT);
4483       break;
4484
4485     case EL_FELSBROCKEN:
4486     case EL_BOMBE:
4487     case EL_KOKOSNUSS:
4488     case EL_ZEIT_LEER:
4489     case EL_SP_ZONK:
4490     case EL_SP_DISK_ORANGE:
4491       if (dy || mode == DF_SNAP)
4492         return MF_NO_ACTION;
4493
4494       player->Pushing = TRUE;
4495
4496       if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
4497         return MF_NO_ACTION;
4498
4499       if (real_dy)
4500       {
4501         if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
4502           return MF_NO_ACTION;
4503       }
4504
4505       if (player->push_delay == 0)
4506         player->push_delay = FrameCounter;
4507       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
4508           !tape.playing)
4509         return MF_NO_ACTION;
4510
4511       RemoveField(x, y);
4512       Feld[x+dx][y+dy] = element;
4513
4514       player->push_delay_value = 2+RND(8);
4515
4516       DrawLevelField(x+dx, y+dy);
4517       if (element == EL_FELSBROCKEN)
4518         PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
4519       else if (element == EL_KOKOSNUSS)
4520         PlaySoundLevel(x+dx, y+dy, SND_KNURK);
4521       else if (IS_SP_ELEMENT(element))
4522         PlaySoundLevel(x+dx, y+dy, SND_SP_ZONKPUSH);
4523       else
4524         PlaySoundLevel(x+dx, y+dy, SND_KLOPF);
4525       break;
4526
4527     case EL_PFORTE1:
4528     case EL_PFORTE2:
4529     case EL_PFORTE3:
4530     case EL_PFORTE4:
4531       if (!player->key[element - EL_PFORTE1])
4532         return MF_NO_ACTION;
4533       break;
4534
4535     case EL_PFORTE1X:
4536     case EL_PFORTE2X:
4537     case EL_PFORTE3X:
4538     case EL_PFORTE4X:
4539       if (!player->key[element - EL_PFORTE1X])
4540         return MF_NO_ACTION;
4541       break;
4542
4543     case EL_EM_GATE_1:
4544     case EL_EM_GATE_2:
4545     case EL_EM_GATE_3:
4546     case EL_EM_GATE_4:
4547       if (!player->key[element - EL_EM_GATE_1])
4548         return MF_NO_ACTION;
4549       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
4550         return MF_NO_ACTION;
4551
4552       /* automatically move to the next field with double speed */
4553       player->programmed_action = move_direction;
4554       DOUBLE_PLAYER_SPEED(player);
4555
4556       break;
4557
4558     case EL_EM_GATE_1X:
4559     case EL_EM_GATE_2X:
4560     case EL_EM_GATE_3X:
4561     case EL_EM_GATE_4X:
4562       if (!player->key[element - EL_EM_GATE_1X])
4563         return MF_NO_ACTION;
4564       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
4565         return MF_NO_ACTION;
4566
4567       /* automatically move to the next field with double speed */
4568       player->programmed_action = move_direction;
4569       DOUBLE_PLAYER_SPEED(player);
4570
4571       break;
4572
4573     case EL_SP_PORT1_LEFT:
4574     case EL_SP_PORT2_LEFT:
4575     case EL_SP_PORT1_RIGHT:
4576     case EL_SP_PORT2_RIGHT:
4577     case EL_SP_PORT1_UP:
4578     case EL_SP_PORT2_UP:
4579     case EL_SP_PORT1_DOWN:
4580     case EL_SP_PORT2_DOWN:
4581     case EL_SP_PORT_X:
4582     case EL_SP_PORT_Y:
4583     case EL_SP_PORT_XY:
4584       if ((dx == -1 &&
4585            element != EL_SP_PORT1_LEFT &&
4586            element != EL_SP_PORT2_LEFT &&
4587            element != EL_SP_PORT_X &&
4588            element != EL_SP_PORT_XY) ||
4589           (dx == +1 &&
4590            element != EL_SP_PORT1_RIGHT &&
4591            element != EL_SP_PORT2_RIGHT &&
4592            element != EL_SP_PORT_X &&
4593            element != EL_SP_PORT_XY) ||
4594           (dy == -1 &&
4595            element != EL_SP_PORT1_UP &&
4596            element != EL_SP_PORT2_UP &&
4597            element != EL_SP_PORT_Y &&
4598            element != EL_SP_PORT_XY) ||
4599           (dy == +1 &&
4600            element != EL_SP_PORT1_DOWN &&
4601            element != EL_SP_PORT2_DOWN &&
4602            element != EL_SP_PORT_Y &&
4603            element != EL_SP_PORT_XY) ||
4604           !IN_LEV_FIELD(x + dx, y + dy) ||
4605           !IS_FREE(x + dx, y + dy))
4606         return MF_NO_ACTION;
4607
4608       /* automatically move to the next field with double speed */
4609       player->programmed_action = move_direction;
4610       DOUBLE_PLAYER_SPEED(player);
4611
4612       break;
4613
4614     case EL_AUSGANG_ZU:
4615     case EL_AUSGANG_ACT:
4616       /* door is not (yet) open */
4617       return MF_NO_ACTION;
4618       break;
4619
4620     case EL_AUSGANG_AUF:
4621       if (mode == DF_SNAP)
4622         return MF_NO_ACTION;
4623
4624       PlaySoundLevel(x, y, SND_BUING);
4625
4626       break;
4627
4628     case EL_BIRNE_AUS:
4629       Feld[x][y] = EL_BIRNE_EIN;
4630       local_player->lights_still_needed--;
4631       DrawLevelField(x, y);
4632       PlaySoundLevel(x, y, SND_DENG);
4633       return MF_ACTION;
4634       break;
4635
4636     case EL_ZEIT_VOLL:
4637       Feld[x][y] = EL_ZEIT_LEER;
4638       TimeLeft += 10;
4639       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
4640       DrawLevelField(x, y);
4641       PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
4642       return MF_ACTION;
4643       break;
4644
4645     case EL_SOKOBAN_FELD_LEER:
4646       break;
4647
4648     case EL_SOKOBAN_FELD_VOLL:
4649     case EL_SOKOBAN_OBJEKT:
4650     case EL_SONDE:
4651     case EL_SP_DISK_YELLOW:
4652       if (mode == DF_SNAP)
4653         return MF_NO_ACTION;
4654
4655       player->Pushing = TRUE;
4656
4657       if (!IN_LEV_FIELD(x+dx, y+dy)
4658           || (!IS_FREE(x+dx, y+dy)
4659               && (Feld[x+dx][y+dy] != EL_SOKOBAN_FELD_LEER
4660                   || !IS_SB_ELEMENT(element))))
4661         return MF_NO_ACTION;
4662
4663       if (dx && real_dy)
4664       {
4665         if (IN_LEV_FIELD(jx, jy+real_dy) && !IS_SOLID(Feld[jx][jy+real_dy]))
4666           return MF_NO_ACTION;
4667       }
4668       else if (dy && real_dx)
4669       {
4670         if (IN_LEV_FIELD(jx+real_dx, jy) && !IS_SOLID(Feld[jx+real_dx][jy]))
4671           return MF_NO_ACTION;
4672       }
4673
4674       if (player->push_delay == 0)
4675         player->push_delay = FrameCounter;
4676       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
4677           !tape.playing)
4678         return MF_NO_ACTION;
4679
4680       if (IS_SB_ELEMENT(element))
4681       {
4682         if (element == EL_SOKOBAN_FELD_VOLL)
4683         {
4684           Feld[x][y] = EL_SOKOBAN_FELD_LEER;
4685           local_player->sokobanfields_still_needed++;
4686         }
4687         else
4688           RemoveField(x, y);
4689
4690         if (Feld[x+dx][y+dy] == EL_SOKOBAN_FELD_LEER)
4691         {
4692           Feld[x+dx][y+dy] = EL_SOKOBAN_FELD_VOLL;
4693           local_player->sokobanfields_still_needed--;
4694           if (element == EL_SOKOBAN_OBJEKT)
4695             PlaySoundLevel(x, y, SND_DENG);
4696         }
4697         else
4698           Feld[x+dx][y+dy] = EL_SOKOBAN_OBJEKT;
4699       }
4700       else
4701       {
4702         RemoveField(x, y);
4703         Feld[x+dx][y+dy] = element;
4704       }
4705
4706       player->push_delay_value = 2;
4707
4708       DrawLevelField(x, y);
4709       DrawLevelField(x+dx, y+dy);
4710       PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
4711
4712       if (IS_SB_ELEMENT(element) &&
4713           local_player->sokobanfields_still_needed == 0 &&
4714           game_emulation == EMU_SOKOBAN)
4715       {
4716         player->LevelSolved = player->GameOver = TRUE;
4717         PlaySoundLevel(x, y, SND_BUING);
4718       }
4719
4720       break;
4721
4722     case EL_MAULWURF:
4723     case EL_PINGUIN:
4724     case EL_SCHWEIN:
4725     case EL_DRACHE:
4726       break;
4727
4728     default:
4729       return MF_NO_ACTION;
4730   }
4731
4732   player->push_delay = 0;
4733
4734   return MF_MOVING;
4735 }
4736
4737 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
4738 {
4739   int jx = player->jx, jy = player->jy;
4740   int x = jx + dx, y = jy + dy;
4741
4742   if (player->gone || !IN_LEV_FIELD(x, y))
4743     return FALSE;
4744
4745   if (dx && dy)
4746     return FALSE;
4747
4748   if (!dx && !dy)
4749   {
4750     player->snapped = FALSE;
4751     return FALSE;
4752   }
4753
4754   if (player->snapped)
4755     return FALSE;
4756
4757   player->MovDir = (dx < 0 ? MV_LEFT :
4758                     dx > 0 ? MV_RIGHT :
4759                     dy < 0 ? MV_UP :
4760                     dy > 0 ? MV_DOWN :  MV_NO_MOVING);
4761
4762   if (!DigField(player, x, y, 0, 0, DF_SNAP))
4763     return FALSE;
4764
4765   player->snapped = TRUE;
4766   DrawLevelField(x, y);
4767   BackToFront();
4768
4769   return TRUE;
4770 }
4771
4772 boolean PlaceBomb(struct PlayerInfo *player)
4773 {
4774   int jx = player->jx, jy = player->jy;
4775   int element;
4776
4777   if (player->gone || player->MovPos)
4778     return FALSE;
4779
4780   element = Feld[jx][jy];
4781
4782   if ((player->dynamite == 0 && player->dynabombs_left == 0) ||
4783       element == EL_DYNAMIT || element == EL_DYNABOMB ||
4784       element == EL_EXPLODING)
4785     return FALSE;
4786
4787   if (element != EL_LEERRAUM)
4788     Store[jx][jy] = element;
4789
4790   if (player->dynamite)
4791   {
4792     Feld[jx][jy] = EL_DYNAMIT;
4793     MovDelay[jx][jy] = 96;
4794     player->dynamite--;
4795     DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
4796              FS_SMALL, FC_YELLOW);
4797     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
4798     {
4799       if (game_emulation == EMU_SUPAPLEX)
4800         DrawGraphic(SCREENX(jx), SCREENY(jy), GFX_SP_DISK_RED);
4801       else
4802         DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNAMIT);
4803     }
4804   }
4805   else
4806   {
4807     Feld[jx][jy] = EL_DYNABOMB;
4808     Store2[jx][jy] = player->element_nr;        /* for DynaExplode() */
4809     MovDelay[jx][jy] = 96;
4810     player->dynabombs_left--;
4811     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
4812       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNABOMB);
4813   }
4814
4815   return TRUE;
4816 }
4817
4818 void PlaySoundLevel(int x, int y, int sound_nr)
4819 {
4820   int sx = SCREENX(x), sy = SCREENY(y);
4821   int volume, stereo;
4822   int silence_distance = 8;
4823
4824   if ((!setup.sound_simple && !IS_LOOP_SOUND(sound_nr)) ||
4825       (!setup.sound_loops && IS_LOOP_SOUND(sound_nr)))
4826     return;
4827
4828   if (!IN_LEV_FIELD(x, y) ||
4829       sx < -silence_distance || sx >= SCR_FIELDX+silence_distance ||
4830       sy < -silence_distance || sy >= SCR_FIELDY+silence_distance)
4831     return;
4832
4833   volume = PSND_MAX_VOLUME;
4834
4835 #ifndef MSDOS
4836   stereo = (sx - SCR_FIELDX/2) * 12;
4837 #else
4838   stereo = PSND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
4839   if (stereo > PSND_MAX_RIGHT)
4840     stereo = PSND_MAX_RIGHT;
4841   if (stereo < PSND_MAX_LEFT)
4842     stereo = PSND_MAX_LEFT;
4843 #endif
4844
4845   if (!IN_SCR_FIELD(sx, sy))
4846   {
4847     int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
4848     int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
4849
4850     volume -= volume * (dx > dy ? dx : dy) / silence_distance;
4851   }
4852
4853   PlaySoundExt(sound_nr, volume, stereo, PSND_NO_LOOP);
4854 }
4855
4856 void RaiseScore(int value)
4857 {
4858   local_player->score += value;
4859   DrawText(DX_SCORE, DY_SCORE, int2str(local_player->score, 5),
4860            FS_SMALL, FC_YELLOW);
4861 }
4862
4863 void RaiseScoreElement(int element)
4864 {
4865   switch(element)
4866   {
4867     case EL_EDELSTEIN:
4868     case EL_EDELSTEIN_BD:
4869     case EL_EDELSTEIN_GELB:
4870     case EL_EDELSTEIN_ROT:
4871     case EL_EDELSTEIN_LILA:
4872       RaiseScore(level.score[SC_EDELSTEIN]);
4873       break;
4874     case EL_DIAMANT:
4875       RaiseScore(level.score[SC_DIAMANT]);
4876       break;
4877     case EL_KAEFER:
4878     case EL_BUTTERFLY:
4879       RaiseScore(level.score[SC_KAEFER]);
4880       break;
4881     case EL_FLIEGER:
4882     case EL_FIREFLY:
4883       RaiseScore(level.score[SC_FLIEGER]);
4884       break;
4885     case EL_MAMPFER:
4886     case EL_MAMPFER2:
4887       RaiseScore(level.score[SC_MAMPFER]);
4888       break;
4889     case EL_ROBOT:
4890       RaiseScore(level.score[SC_ROBOT]);
4891       break;
4892     case EL_PACMAN:
4893       RaiseScore(level.score[SC_PACMAN]);
4894       break;
4895     case EL_KOKOSNUSS:
4896       RaiseScore(level.score[SC_KOKOSNUSS]);
4897       break;
4898     case EL_DYNAMIT:
4899       RaiseScore(level.score[SC_DYNAMIT]);
4900       break;
4901     case EL_SCHLUESSEL:
4902       RaiseScore(level.score[SC_SCHLUESSEL]);
4903       break;
4904     default:
4905       break;
4906   }
4907 }
4908
4909 /* ---------- new game button stuff ---------------------------------------- */
4910
4911 /* graphic position values for game buttons */
4912 #define GAME_BUTTON_XSIZE       30
4913 #define GAME_BUTTON_YSIZE       30
4914 #define GAME_BUTTON_XPOS        5
4915 #define GAME_BUTTON_YPOS        215
4916 #define SOUND_BUTTON_XPOS       5
4917 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
4918
4919 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
4920 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
4921 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
4922 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
4923 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
4924 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
4925
4926 static struct
4927 {
4928   int x, y;
4929   int gadget_id;
4930   char *infotext;
4931 } gamebutton_info[NUM_GAME_BUTTONS] =
4932 {
4933   {
4934     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
4935     GAME_CTRL_ID_STOP,
4936     "stop game"
4937   },
4938   {
4939     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
4940     GAME_CTRL_ID_PAUSE,
4941     "pause game"
4942   },
4943   {
4944     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
4945     GAME_CTRL_ID_PLAY,
4946     "play game"
4947   },
4948   {
4949     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
4950     SOUND_CTRL_ID_MUSIC,
4951     "background music on/off"
4952   },
4953   {
4954     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
4955     SOUND_CTRL_ID_LOOPS,
4956     "sound loops on/off"
4957   },
4958   {
4959     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
4960     SOUND_CTRL_ID_SIMPLE,
4961     "normal sounds on/off"
4962   }
4963 };
4964
4965 void CreateGameButtons()
4966 {
4967   int i;
4968
4969   for (i=0; i<NUM_GAME_BUTTONS; i++)
4970   {
4971     Pixmap gd_pixmap = pix[PIX_DOOR];
4972     struct GadgetInfo *gi;
4973     int button_type;
4974     boolean checked;
4975     unsigned long event_mask;
4976     int gd_xoffset, gd_yoffset;
4977     int gd_x1, gd_x2, gd_y1, gd_y2;
4978     int id = i;
4979
4980     gd_xoffset = gamebutton_info[i].x;
4981     gd_yoffset = gamebutton_info[i].y;
4982     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
4983     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
4984
4985     if (id == GAME_CTRL_ID_STOP ||
4986         id == GAME_CTRL_ID_PAUSE ||
4987         id == GAME_CTRL_ID_PLAY)
4988     {
4989       button_type = GD_TYPE_NORMAL_BUTTON;
4990       checked = FALSE;
4991       event_mask = GD_EVENT_RELEASED;
4992       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
4993       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
4994     }
4995     else
4996     {
4997       button_type = GD_TYPE_CHECK_BUTTON;
4998       checked =
4999         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
5000          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
5001          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
5002       event_mask = GD_EVENT_PRESSED;
5003       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
5004       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
5005     }
5006
5007     gi = CreateGadget(GDI_CUSTOM_ID, id,
5008                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
5009                       GDI_X, DX + gd_xoffset,
5010                       GDI_Y, DY + gd_yoffset,
5011                       GDI_WIDTH, GAME_BUTTON_XSIZE,
5012                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
5013                       GDI_TYPE, button_type,
5014                       GDI_STATE, GD_BUTTON_UNPRESSED,
5015                       GDI_CHECKED, checked,
5016                       GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
5017                       GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y1,
5018                       GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y2,
5019                       GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
5020                       GDI_EVENT_MASK, event_mask,
5021                       GDI_CALLBACK_ACTION, HandleGameButtons,
5022                       GDI_END);
5023
5024     if (gi == NULL)
5025       Error(ERR_EXIT, "cannot create gadget");
5026
5027     game_gadget[id] = gi;
5028   }
5029 }
5030
5031 static void MapGameButtons()
5032 {
5033   int i;
5034
5035   for (i=0; i<NUM_GAME_BUTTONS; i++)
5036     MapGadget(game_gadget[i]);
5037 }
5038
5039 void UnmapGameButtons()
5040 {
5041   int i;
5042
5043   for (i=0; i<NUM_GAME_BUTTONS; i++)
5044     UnmapGadget(game_gadget[i]);
5045 }
5046
5047 static void HandleGameButtons(struct GadgetInfo *gi)
5048 {
5049   int id = gi->custom_id;
5050
5051   if (game_status != PLAYING)
5052     return;
5053
5054   switch (id)
5055   {
5056     case GAME_CTRL_ID_STOP:
5057       if (AllPlayersGone)
5058       {
5059         CloseDoor(DOOR_CLOSE_1);
5060         game_status = MAINMENU;
5061         DrawMainMenu();
5062         break;
5063       }
5064
5065       if (level_editor_test_game ||
5066           Request("Do you really want to quit the game ?",
5067                   REQ_ASK | REQ_STAY_CLOSED))
5068       { 
5069 #ifndef MSDOS
5070         if (options.network)
5071           SendToServer_StopPlaying();
5072         else
5073 #endif
5074         {
5075           game_status = MAINMENU;
5076           DrawMainMenu();
5077         }
5078       }
5079       else
5080         OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
5081       break;
5082
5083     case GAME_CTRL_ID_PAUSE:
5084       if (options.network)
5085       {
5086 #ifndef MSDOS
5087         if (tape.pausing)
5088           SendToServer_ContinuePlaying();
5089         else
5090           SendToServer_PausePlaying();
5091 #endif
5092       }
5093       else
5094         TapeTogglePause();
5095       break;
5096
5097     case GAME_CTRL_ID_PLAY:
5098       if (tape.pausing)
5099       {
5100 #ifndef MSDOS
5101         if (options.network)
5102           SendToServer_ContinuePlaying();
5103         else
5104 #endif
5105         {
5106           tape.pausing = FALSE;
5107           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
5108         }
5109       }
5110       break;
5111
5112     case SOUND_CTRL_ID_MUSIC:
5113       if (setup.sound_music)
5114       { 
5115         setup.sound_music = FALSE;
5116         FadeSound(background_loop[level_nr % num_bg_loops]);
5117       }
5118       else if (sound_loops_allowed)
5119       { 
5120         setup.sound = setup.sound_music = TRUE;
5121         PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
5122       }
5123       break;
5124
5125     case SOUND_CTRL_ID_LOOPS:
5126       if (setup.sound_loops)
5127         setup.sound_loops = FALSE;
5128       else if (sound_loops_allowed)
5129         setup.sound = setup.sound_loops = TRUE;
5130       break;
5131
5132     case SOUND_CTRL_ID_SIMPLE:
5133       if (setup.sound_simple)
5134         setup.sound_simple = FALSE;
5135       else if (sound_status==SOUND_AVAILABLE)
5136         setup.sound = setup.sound_simple = TRUE;
5137       break;
5138
5139     default:
5140       break;
5141   }
5142 }