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