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