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