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