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