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