rnd-19990222-1-src
[rocksndiamonds.git] / src / game.c
index dc992f2573674d3a11831b92827c8434729b6167..b4e3fc970304b40ee27dc3be0748b7bbff1b055c 100644 (file)
                                 (s)==SND_TYGER || (s)==SND_VOYAGER || \
                                 (s)==SND_TWILIGHT)
 
-/* score for elements */
-#define SC_EDELSTEIN           0
-#define SC_DIAMANT             1
-#define SC_KAEFER              2
-#define SC_FLIEGER             3
-#define SC_MAMPFER             4
-#define SC_ROBOT               5
-#define SC_PACMAN              6
-#define SC_KOKOSNUSS           7
-#define SC_DYNAMIT             8
-#define SC_SCHLUESSEL          9
-#define SC_ZEITBONUS           10
+/* values for player movement speed (which is in fact a delay value) */
+#define MOVE_DELAY_NORMAL_SPEED        8
+#define MOVE_DELAY_HIGH_SPEED  4
 
-/* values for game_emulation */
-#define EMU_NONE               0
-#define EMU_BOULDERDASH                1
-#define EMU_SOKOBAN            2
-#define EMU_SUPAPLEX           3
+#define DOUBLE_MOVE_DELAY(x)   (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
+#define HALVE_MOVE_DELAY(x)    (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
+#define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
+#define HALVE_PLAYER_SPEED(p)  (DOUBLE_MOVE_DELAY((p)->move_delay_value))
 
-/* to control special behaviour of certain game elements */
-int game_emulation = EMU_NONE;
+/* game button identifiers */
+#define GAME_CTRL_ID_STOP              0
+#define GAME_CTRL_ID_PAUSE             1
+#define GAME_CTRL_ID_PLAY              2
+#define SOUND_CTRL_ID_MUSIC            3
+#define SOUND_CTRL_ID_LOOPS            4
+#define SOUND_CTRL_ID_SIMPLE           5
 
+#define NUM_GAME_BUTTONS               6
+
+/* forward declaration for internal use */
+static void CheckGravityMovement(struct PlayerInfo *);
+
+static void MapGameButtons();
+static void HandleGameButtons(struct GadgetInfo *);
+
+static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
 
 
 
@@ -192,10 +196,6 @@ static void InitField(int x, int y, boolean init_game)
 
        player->present = TRUE;
 
-       /*
-       if (!network_playing || player->connected)
-       */
-
        if (!options.network || player->connected)
        {
          player->active = TRUE;
@@ -306,6 +306,19 @@ static void InitField(int x, int y, boolean init_game)
       Feld[x][y] = EL_LEERRAUM;
       break;
 
+    case EL_EM_KEY_1_FILE:
+      Feld[x][y] = EL_EM_KEY_1;
+      break;
+    case EL_EM_KEY_2_FILE:
+      Feld[x][y] = EL_EM_KEY_2;
+      break;
+    case EL_EM_KEY_3_FILE:
+      Feld[x][y] = EL_EM_KEY_3;
+      break;
+    case EL_EM_KEY_4_FILE:
+      Feld[x][y] = EL_EM_KEY_4;
+      break;
+
     default:
       break;
   }
@@ -333,9 +346,10 @@ void InitGame()
 
     player->action = 0;
     player->effective_action = 0;
+    player->programmed_action = 0;
 
     player->score = 0;
-    player->gems_still_needed = level.edelsteine;
+    player->gems_still_needed = level.gems_needed;
     player->sokobanfields_still_needed = 0;
     player->lights_still_needed = 0;
     player->friends_still_needed = 0;
@@ -365,9 +379,10 @@ void InitGame()
     player->move_delay = 0;
     player->last_move_dir = MV_NO_MOVING;
 
-    player->snapped = FALSE;
+    player->move_delay_value =
+      (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
 
-    player->gone = FALSE;
+    player->snapped = FALSE;
 
     player->last_jx = player->last_jy = 0;
     player->jx = player->jy = 0;
@@ -389,29 +404,21 @@ void InitGame()
 
   ZX = ZY = -1;
 
-  MampferNr = 0;
+  game.yam_content_nr = 0;
   FrameCounter = 0;
   TimeFrames = 0;
+  TimePlayed = 0;
   TimeLeft = level.time;
 
   ScreenMovDir = MV_NO_MOVING;
   ScreenMovPos = 0;
   ScreenGfxPos = 0;
 
-  if (level.high_speed)
-  {
-    MoveSpeed = 4;
-    ScrollStepSize = TILEX/4;
-  }
-  else
-  {
-    MoveSpeed = 8;
-    ScrollStepSize = TILEX/8;
-  }
+  ScrollStepSize = 0;  /* will be correctly initialized by ScrollScreen() */
 
   AllPlayersGone = FALSE;
-  SiebAktiv = FALSE;
-  SiebCount = 0;
+  game.magic_wall_active = FALSE;
+  game.magic_wall_time_left = 0;
 
   for (i=0; i<MAX_NUM_AMOEBA; i++)
     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
@@ -539,19 +546,41 @@ void InitGame()
     }
   }
 
-  game_emulation = (emulate_bd ? EMU_BOULDERDASH :
+  game.emulation = (emulate_bd ? EMU_BOULDERDASH :
                    emulate_sb ? EMU_SOKOBAN :
                    emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
 
-  scroll_x = scroll_y = -1;
-  if (local_player->jx >= MIDPOSX-1)
-    scroll_x = (local_player->jx <= lev_fieldx-MIDPOSX ?
+  if (BorderElement == EL_LEERRAUM)
+  {
+    SBX_Left = 0;
+    SBX_Right = lev_fieldx - SCR_FIELDX;
+    SBY_Upper = 0;
+    SBY_Lower = lev_fieldy - SCR_FIELDY;
+  }
+  else
+  {
+    SBX_Left = -1;
+    SBX_Right = lev_fieldx - SCR_FIELDX + 1;
+    SBY_Upper = -1;
+    SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
+  }
+
+  if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
+    SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
+
+  if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
+    SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+
+  scroll_x = SBX_Left;
+  scroll_y = SBY_Upper;
+  if (local_player->jx >= SBX_Left + MIDPOSX)
+    scroll_x = (local_player->jx <= SBX_Right + MIDPOSX ?
                local_player->jx - MIDPOSX :
-               lev_fieldx - SCR_FIELDX + 1);
-  if (local_player->jy >= MIDPOSY-1)
-    scroll_y = (local_player->jy <= lev_fieldy-MIDPOSY ?
+               SBX_Right);
+  if (local_player->jy >= SBY_Upper + MIDPOSY)
+    scroll_y = (local_player->jy <= SBY_Lower + MIDPOSY ?
                local_player->jy - MIDPOSY :
-               lev_fieldy - SCR_FIELDY + 1);
+               SBY_Lower);
 
   CloseDoor(DOOR_CLOSE_1);
 
@@ -559,38 +588,50 @@ void InitGame()
   DrawAllPlayers();
   FadeToFront();
 
-  XCopyArea(display, pix[PIX_DOOR], pix[PIX_DB_DOOR], gc,
-           DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE,
-           DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
-  DrawTextExt(pix[PIX_DB_DOOR], gc,
-             DOOR_GFX_PAGEX1 + XX_LEVEL, DOOR_GFX_PAGEY1 + YY_LEVEL,
-             int2str(level_nr, 2), FS_SMALL, FC_YELLOW);
-  DrawTextExt(pix[PIX_DB_DOOR], gc,
-             DOOR_GFX_PAGEX1 + XX_EMERALDS, DOOR_GFX_PAGEY1 + YY_EMERALDS,
-             int2str(local_player->gems_still_needed,3), FS_SMALL, FC_YELLOW);
-  DrawTextExt(pix[PIX_DB_DOOR], gc,
-             DOOR_GFX_PAGEX1 + XX_DYNAMITE, DOOR_GFX_PAGEY1 + YY_DYNAMITE,
-             int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
-  DrawTextExt(pix[PIX_DB_DOOR], gc,
-             DOOR_GFX_PAGEX1 + XX_SCORE, DOOR_GFX_PAGEY1 + YY_SCORE,
-             int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
-  DrawTextExt(pix[PIX_DB_DOOR], gc,
-             DOOR_GFX_PAGEX1 + XX_TIME, DOOR_GFX_PAGEY1 + YY_TIME,
-             int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
-
-  DrawGameButton(BUTTON_GAME_STOP);
-  DrawGameButton(BUTTON_GAME_PAUSE);
-  DrawGameButton(BUTTON_GAME_PLAY);
-  DrawSoundDisplay(BUTTON_SOUND_MUSIC  | (setup.sound_music  ? BUTTON_ON : 0));
-  DrawSoundDisplay(BUTTON_SOUND_LOOPS  | (setup.sound_loops  ? BUTTON_ON : 0));
-  DrawSoundDisplay(BUTTON_SOUND_SIMPLE | (setup.sound_simple ? BUTTON_ON : 0));
+  if (setup.soft_scrolling)
+    XCopyArea(display, fieldbuffer, backbuffer, gc,
+             FX, FY, SXSIZE, SYSIZE, SX, SY);
+
+  redraw_mask |= REDRAW_FROM_BACKBUFFER;
+
+  /* copy default game door content to main double buffer */
+  XCopyArea(display, pix[PIX_DOOR], drawto, gc,
+           DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+
+  if (level_nr < 100)
+    DrawText(DX + XX_LEVEL, DY + YY_LEVEL,
+            int2str(level_nr, 2), FS_SMALL, FC_YELLOW);
+  else
+  {
+    DrawTextExt(drawto, gc, DX + XX_EMERALDS, DY + YY_EMERALDS,
+               int2str(level_nr, 3), FS_SMALL, FC_SPECIAL3);
+    XCopyArea(display, drawto, drawto, gc,
+             DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
+             FONT5_XSIZE * 3, FONT5_YSIZE - 1,
+             DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
+  }
+
+  DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
+          int2str(local_player->gems_still_needed, 3), FS_SMALL, FC_YELLOW);
+  DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
+          int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
+  DrawText(DX + XX_SCORE, DY + YY_SCORE,
+          int2str(local_player->score, 5), FS_SMALL, FC_YELLOW);
+  DrawText(DX + XX_TIME, DY + YY_TIME,
+          int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+
+  UnmapGameButtons();
+  game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
+  game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
+  game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
+  MapGameButtons();
+  MapTapeButtons();
+
+  /* copy actual game door content to door double buffer for OpenDoor() */
   XCopyArea(display, drawto, pix[PIX_DB_DOOR], gc,
-           DX + GAME_CONTROL_XPOS, DY + GAME_CONTROL_YPOS,
-           GAME_CONTROL_XSIZE, 2 * GAME_CONTROL_YSIZE,
-           DOOR_GFX_PAGEX1 + GAME_CONTROL_XPOS,
-           DOOR_GFX_PAGEY1 + GAME_CONTROL_YPOS);
+           DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
 
-  OpenDoor(DOOR_OPEN_1);
+  OpenDoor(DOOR_OPEN_ALL);
 
   if (setup.sound_music)
     PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
@@ -600,8 +641,8 @@ void InitGame()
   if (options.verbose)
   {
     for (i=0; i<4; i++)
-      printf("Spieler %d %saktiv.\n",
-            i+1, (stored_player[i].active ? "" : "nicht "));
+      printf("Player %d %sactive.\n",
+            i + 1, (stored_player[i].active ? "" : "not "));
   }
 }
 
@@ -744,7 +785,7 @@ void GameWon()
     {
       if (!setup.sound_loops)
        PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
-      if (TimeLeft && !(TimeLeft % 10))
+      if (TimeLeft > 0 && !(TimeLeft % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
       if (TimeLeft > 100 && !(TimeLeft % 10))
        TimeLeft -= 10;
@@ -758,6 +799,29 @@ void GameWon()
     if (setup.sound_loops)
       StopSound(SND_SIRR);
   }
+  else if (level.time == 0)            /* level without time limit */
+  {
+    if (setup.sound_loops)
+      PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
+
+    while(TimePlayed < 999)
+    {
+      if (!setup.sound_loops)
+       PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
+      if (TimePlayed < 999 && !(TimePlayed % 10))
+       RaiseScore(level.score[SC_ZEITBONUS]);
+      if (TimePlayed < 900 && !(TimePlayed % 10))
+       TimePlayed += 10;
+      else
+       TimePlayed++;
+      DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
+      BackToFront();
+      Delay(10);
+    }
+
+    if (setup.sound_loops)
+      StopSound(SND_SIRR);
+  }
 
   FadeSounds();
 
@@ -794,7 +858,7 @@ void GameWon()
   BackToFront();
 }
 
-boolean NewHiScore()
+int NewHiScore()
 {
   int k, l;
   int position = -1;
@@ -907,6 +971,29 @@ int MovingOrBlocked2Element(int x, int y)
     return element;
 }
 
+static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
+{
+  /* like MovingOrBlocked2Element(), but if element is moving
+     and (x,y) is the field the moving element is just leaving,
+     return EL_BLOCKED instead of the element value */
+  int element = Feld[x][y];
+
+  if (IS_MOVING(x, y))
+  {
+    if (element == EL_BLOCKED)
+    {
+      int oldx, oldy;
+
+      Blocked2Moving(x, y, &oldx, &oldy);
+      return Feld[oldx][oldy];
+    }
+    else
+      return EL_BLOCKED;
+  }
+  else
+    return element;
+}
+
 static void RemoveField(int x, int y)
 {
   Feld[x][y] = EL_LEERRAUM;
@@ -978,7 +1065,9 @@ void DrawDynamite(int x, int y)
       phase = 7 - phase;
   }
 
-  if (Store[x][y])
+  if (game.emulation == EMU_SUPAPLEX)
+    DrawGraphic(sx, sy, GFX_SP_DISK_RED);
+  else if (Store[x][y])
     DrawGraphicThruMask(sx, sy, graphic + phase);
   else
     DrawGraphic(sx, sy, graphic + phase);
@@ -1013,6 +1102,7 @@ void Explode(int ex, int ey, int phase, int mode)
   int num_phase = 9, delay = 2;
   int last_phase = num_phase * delay;
   int half_phase = (num_phase / 2) * delay;
+  int first_phase_after_start = EX_PHASE_START + 1;
 
   if (phase == EX_PHASE_START)         /* initialize 'Store[][]' field */
   {
@@ -1034,7 +1124,7 @@ void Explode(int ex, int ey, int phase, int mode)
        RemoveMovingField(x, y);
       }
 
-      if (!IN_LEV_FIELD(x, y) || IS_MASSIV(element) || element == EL_BURNING)
+      if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(element) || element == EL_BURNING)
        continue;
 
       if ((mode!=EX_NORMAL || center_element == EL_AMOEBA2DIAM) &&
@@ -1063,7 +1153,7 @@ void Explode(int ex, int ey, int phase, int mode)
            break;
        }
 
-       if (game_emulation == EMU_SUPAPLEX)
+       if (game.emulation == EMU_SUPAPLEX)
          Store[x][y] = EL_LEERRAUM;
       }
       else if (center_element == EL_MAULWURF)
@@ -1077,9 +1167,9 @@ void Explode(int ex, int ey, int phase, int mode)
       else if (center_element == EL_SP_ELECTRON)
        Store[x][y] = EL_SP_INFOTRON;
       else if (center_element == EL_MAMPFER)
-       Store[x][y] = level.mampfer_inhalt[MampferNr][x-ex+1][y-ey+1];
+       Store[x][y] = level.yam_content[game.yam_content_nr][x-ex+1][y-ey+1];
       else if (center_element == EL_AMOEBA2DIAM)
-       Store[x][y] = level.amoebe_inhalt;
+       Store[x][y] = level.amoeba_content;
       else if (element == EL_ERZ_EDEL)
        Store[x][y] = EL_EDELSTEIN;
       else if (element == EL_ERZ_DIAM)
@@ -1116,7 +1206,7 @@ void Explode(int ex, int ey, int phase, int mode)
     }
 
     if (center_element == EL_MAMPFER)
-      MampferNr = (MampferNr + 1) % MampferMax;
+      game.yam_content_nr = (game.yam_content_nr + 1) % level.num_yam_contents;
 
     return;
   }
@@ -1127,9 +1217,20 @@ void Explode(int ex, int ey, int phase, int mode)
   x = ex;
   y = ey;
 
-  Frame[x][y] = (phase<last_phase ? phase+1 : 0);
+  Frame[x][y] = (phase < last_phase ? phase + 1 : 0);
 
-  if (phase == half_phase)
+  if (phase == first_phase_after_start)
+  {
+    int element = Store2[x][y];
+
+    if (element == EL_BLACK_ORB)
+    {
+      Feld[x][y] = Store2[x][y];
+      Store2[x][y] = 0;
+      Bang(x, y);
+    }
+  }
+  else if (phase == half_phase)
   {
     int element = Store2[x][y];
 
@@ -1161,7 +1262,7 @@ void Explode(int ex, int ey, int phase, int mode)
   {
     int graphic = GFX_EXPLOSION;
 
-    if (game_emulation == EMU_SUPAPLEX)
+    if (game.emulation == EMU_SUPAPLEX)
       graphic = (Store[x][y] == EL_SP_INFOTRON ?
                 GFX_SP_EXPLODE_INFOTRON :
                 GFX_SP_EXPLODE_EMPTY);
@@ -1197,7 +1298,7 @@ void DynaExplode(int ex, int ey)
       int y = ey+j*xy[i%4][1];
       int element;
 
-      if (!IN_LEV_FIELD(x, y) || IS_MASSIV(Feld[x][y]))
+      if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(Feld[x][y]))
        break;
 
       element = Feld[x][y];
@@ -1218,7 +1319,10 @@ void Bang(int x, int y)
 {
   int element = Feld[x][y];
 
-  PlaySoundLevel(x, y, SND_ROAAAR);
+  if (game.emulation == EMU_SUPAPLEX)
+    PlaySoundLevel(x, y, SND_SP_BOOOM);
+  else
+    PlaySoundLevel(x, y, SND_ROAAAR);
 
   if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
     element = EL_LEERRAUM;
@@ -1358,8 +1462,8 @@ void Impact(int x, int y)
          if (Feld[x][y] == smashed)
            Feld[x][y] = activated_magic_wall;
 
-      SiebCount = level.dauer_sieb * FRAMES_PER_SECOND;
-      SiebAktiv = TRUE;
+      game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
+      game.magic_wall_active = TRUE;
     }
 
     if (IS_PLAYER(x, y+1))
@@ -1434,6 +1538,7 @@ void Impact(int x, int y)
       case EL_EDELSTEIN_ROT:
       case EL_EDELSTEIN_LILA:
       case EL_DIAMANT:
+      case EL_SP_INFOTRON:
         sound = SND_PLING;
        break;
       case EL_KOKOSNUSS:
@@ -1442,11 +1547,18 @@ void Impact(int x, int y)
       case EL_FELSBROCKEN:
        sound = SND_KLOPF;
        break;
+      case EL_SP_ZONK:
+       sound = SND_SP_ZONKDOWN;
+       break;
       case EL_SCHLUESSEL:
       case EL_SCHLUESSEL1:
       case EL_SCHLUESSEL2:
       case EL_SCHLUESSEL3:
       case EL_SCHLUESSEL4:
+      case EL_EM_KEY_1:
+      case EL_EM_KEY_2:
+      case EL_EM_KEY_3:
+      case EL_EM_KEY_4:
        sound = SND_KINK;
        break;
       case EL_ZEIT_VOLL:
@@ -1734,7 +1846,7 @@ void TurnRound(int x, int y)
        struct PlayerInfo *player = &stored_player[i];
        int jx = player->jx, jy = player->jy;
 
-       if (!player->active || player->gone)
+       if (!player->active)
          continue;
 
        if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
@@ -1849,8 +1961,7 @@ static boolean JustBeingPushed(int x, int y)
   {
     struct PlayerInfo *player = &stored_player[i];
 
-    if (player->active && !player->gone &&
-       player->Pushing && player->MovPos)
+    if (player->active && player->Pushing && player->MovPos)
     {
       int next_jx = player->jx + (player->jx - player->last_jx);
       int next_jy = player->jy + (player->jy - player->last_jy);
@@ -1993,7 +2104,7 @@ void StartMoving(int x, int y)
 
       if (left || right)
       {
-       if (left && right && game_emulation != EMU_BOULDERDASH)
+       if (left && right && game.emulation != EMU_BOULDERDASH)
          left = !(right = RND(2));
 
        InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT);
@@ -2325,22 +2436,24 @@ void ContinueMoving(int x, int y)
     else if (Store[x][y] == EL_SIEB_VOLL)
     {
       Store[x][y] = 0;
-      element = Feld[newx][newy] = (SiebAktiv ? EL_SIEB_VOLL : EL_SIEB_TOT);
+      element = Feld[newx][newy] =
+       (game.magic_wall_active ? EL_SIEB_VOLL : EL_SIEB_TOT);
     }
     else if (Store[x][y] == EL_SIEB_LEER)
     {
       Store[x][y] = Store2[x][y] = 0;
-      Feld[x][y] = (SiebAktiv ? EL_SIEB_LEER : EL_SIEB_TOT);
+      Feld[x][y] = (game.magic_wall_active ? EL_SIEB_LEER : EL_SIEB_TOT);
     }
     else if (Store[x][y] == EL_SIEB2_VOLL)
     {
       Store[x][y] = 0;
-      element = Feld[newx][newy] = (SiebAktiv ? EL_SIEB2_VOLL : EL_SIEB2_TOT);
+      element = Feld[newx][newy] =
+       (game.magic_wall_active ? EL_SIEB2_VOLL : EL_SIEB2_TOT);
     }
     else if (Store[x][y] == EL_SIEB2_LEER)
     {
       Store[x][y] = Store2[x][y] = 0;
-      Feld[x][y] = (SiebAktiv ? EL_SIEB2_LEER : EL_SIEB2_TOT);
+      Feld[x][y] = (game.magic_wall_active ? EL_SIEB2_LEER : EL_SIEB2_TOT);
     }
     else if (Store[x][y] == EL_SALZSAEURE)
     {
@@ -2597,7 +2710,7 @@ void AmoebeAbleger(int ax, int ay)
     { 0, +1 }
   };
 
-  if (!level.tempo_amoebe)
+  if (!level.amoeba_speed)
   {
     Feld[ax][ay] = EL_AMOEBE_TOT;
     DrawLevelField(ax, ay);
@@ -2605,7 +2718,7 @@ void AmoebeAbleger(int ax, int ay)
   }
 
   if (!MovDelay[ax][ay])       /* start making new amoeba field */
-    MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.tempo_amoebe));
+    MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
 
   if (MovDelay[ax][ay])                /* wait some time before making new amoeba */
   {
@@ -2660,7 +2773,7 @@ void AmoebeAbleger(int ax, int ay)
 
     if (newax == ax && neway == ay)            /* amoeba cannot grow */
     {
-      if (i == 4 && (!waiting_for_player || game_emulation == EMU_BOULDERDASH))
+      if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
       {
        Feld[ax][ay] = EL_AMOEBE_TOT;
        DrawLevelField(ax, ay);
@@ -2671,7 +2784,7 @@ void AmoebeAbleger(int ax, int ay)
          if (element == EL_AMOEBE_VOLL)
            AmoebeUmwandeln(ax, ay);
          else if (element == EL_AMOEBE_BD)
-           AmoebeUmwandelnBD(ax, ay, level.amoebe_inhalt);
+           AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
        }
       }
       return;
@@ -2795,7 +2908,7 @@ void Life(int ax, int ay)
 void Ablenk(int x, int y)
 {
   if (!MovDelay[x][y])         /* next animation frame */
-    MovDelay[x][y] = level.dauer_ablenk * FRAMES_PER_SECOND;
+    MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
 
   if (MovDelay[x][y])          /* wait some time before next frame */
   {
@@ -3157,6 +3270,68 @@ void CheckForDragon(int x, int y)
   }
 }
 
+static void CheckBuggyBase(int x, int y)
+{
+  int element = Feld[x][y];
+
+  if (element == EL_SP_BUG)
+  {
+    if (!MovDelay[x][y])       /* start activating buggy base */
+      MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
+
+    if (MovDelay[x][y])                /* wait some time before activating base */
+    {
+      MovDelay[x][y]--;
+      if (MovDelay[x][y] < 5 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+       DrawGraphic(SCREENX(x), SCREENY(y), GFX_SP_BUG_WARNING);
+      if (MovDelay[x][y])
+       return;
+
+      Feld[x][y] = EL_SP_BUG_ACTIVE;
+    }
+  }
+  else if (element == EL_SP_BUG_ACTIVE)
+  {
+    if (!MovDelay[x][y])       /* start activating buggy base */
+      MovDelay[x][y] = 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND);
+
+    if (MovDelay[x][y])                /* wait some time before activating base */
+    {
+      MovDelay[x][y]--;
+      if (MovDelay[x][y])
+      {
+       int i;
+       static int xy[4][2] =
+       {
+         { 0, -1 },
+         { -1, 0 },
+         { +1, 0 },
+         { 0, +1 }
+       };
+
+       if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+         DrawGraphic(SCREENX(x),SCREENY(y), GFX_SP_BUG_ACTIVE + SimpleRND(4));
+
+       for (i=0; i<4; i++)
+       {
+         int xx = x + xy[i][0], yy = y + xy[i][1];
+
+         if (IS_PLAYER(xx, yy))
+         {
+           PlaySoundLevel(x, y, SND_SP_BUG);
+           break;
+         }
+       }
+
+       return;
+      }
+
+      Feld[x][y] = EL_SP_BUG;
+      DrawLevelField(x, y);
+    }
+  }
+}
+
 static void PlayerActions(struct PlayerInfo *player, byte player_action)
 {
   static byte stored_player_action[MAX_PLAYERS];
@@ -3176,7 +3351,7 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
   stored_player_action[player->index_nr] = 0;
   num_stored_actions++;
 
-  if (!player->active || player->gone || tape.pausing)
+  if (!player->active || tape.pausing)
     return;
 
   if (player_action)
@@ -3199,25 +3374,19 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
        player_action &= JOY_BUTTON;
 
       stored_player_action[player->index_nr] = player_action;
-
-#if 0
-      /* this allows cycled sequences of PlayerActions() */
-      if (num_stored_actions >= MAX_PLAYERS)
-      {
-       TapeRecordAction(stored_player_action);
-       num_stored_actions = 0;
-      }
-#endif
-
     }
     else if (tape.playing && snapped)
       SnapField(player, 0, 0);                 /* stop snapping */
   }
   else
   {
+    /* no actions for this player (no input at player's configured device) */
+
     DigField(player, 0, 0, 0, 0, DF_NO_PUSH);
     SnapField(player, 0, 0);
-    if (++player->frame_reset_delay > MoveSpeed)
+    CheckGravityMovement(player);
+
+    if (++player->frame_reset_delay > player->move_delay_value)
       player->Frame = 0;
   }
 
@@ -3270,40 +3439,9 @@ void GameActions()
   action_delay_value =
     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
 
-  /*
-  if (tape.playing && tape.fast_forward)
-  {
-    char buf[100];
+  /* ---------- main game synchronization point ---------- */
 
-    sprintf(buf, "FFWD: %ld ms", action_delay_value);
-    print_debug(buf);
-  }
-  */
-
-
-  /* main game synchronization point */
-
-
-
-
-#if 1
   WaitUntilDelayReached(&action_delay, action_delay_value);
-#else
-
-  while (!DelayReached(&action_delay, action_delay_value))
-  {
-    char buf[100];
-
-    sprintf(buf, "%ld %ld %ld",
-           Counter(), action_delay, action_delay_value);
-    print_debug(buf);
-  }
-  print_debug("done");
-
-#endif
-
-
-
 
   if (network_playing && !network_player_action_received)
   {
@@ -3332,14 +3470,6 @@ void GameActions()
     }
   }
 
-
-  /*
-  if (tape.pausing || (tape.playing && !TapePlayDelay()))
-    return;
-  else if (tape.recording)
-    TapeRecordDelay();
-  */
-
   if (tape.pausing)
     return;
 
@@ -3370,6 +3500,9 @@ void GameActions()
   {
     int actual_player_action = stored_player[i].effective_action;
 
+    if (stored_player[i].programmed_action)
+      actual_player_action = stored_player[i].programmed_action;
+
     if (recorded_player_action)
       actual_player_action = recorded_player_action[i];
 
@@ -3382,41 +3515,25 @@ void GameActions()
   ScrollScreen(NULL, SCROLL_GO_ON);
 
 
-  /*
-  if (tape.pausing || (tape.playing && !TapePlayDelay()))
-    return;
-  else if (tape.recording)
-    TapeRecordDelay();
-  */
-
-
-
-
 
 #ifdef DEBUG
-  /*
-  if (TimeFrames == 0 && !local_player->gone)
+#if 0
+  if (TimeFrames == 0 && local_player->active)
   {
     extern unsigned int last_RND();
 
     printf("DEBUG: %03d last RND was %d \t [state checksum is %d]\n",
-          level.time - TimeLeft,
-          last_RND(),
-          getStateCheckSum(level.time - TimeLeft));
+          TimePlayed, last_RND(), getStateCheckSum(TimePlayed));
   }
-  */
 #endif
-
-
+#endif
 
 #ifdef DEBUG
-  /*
+#if 0
   if (GameFrameDelay >= 500)
     printf("FrameCounter == %d\n", FrameCounter);
-  */
 #endif
-
-
+#endif
 
 
 
@@ -3437,7 +3554,7 @@ void GameActions()
       Blocked2Moving(x, y, &oldx, &oldy);
       if (!IS_MOVING(oldx, oldy))
       {
-       printf("GameActions(): (BLOCKED=>MOVING) context corrupted!\n");
+       printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
        printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
        printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
        printf("GameActions(): This should never happen!\n");
@@ -3495,12 +3612,14 @@ void GameActions()
       MauerAbleger(x, y);
     else if (element == EL_BURNING)
       CheckForDragon(x, y);
+    else if (element == EL_SP_BUG || element == EL_SP_BUG_ACTIVE)
+      CheckBuggyBase(x, y);
     else if (element == EL_SP_TERMINAL)
-      DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL, 7, 10, ANIM_NORMAL);
+      DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL, 7, 12, ANIM_NORMAL);
     else if (element == EL_SP_TERMINAL_ACTIVE)
-      DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL_ACTIVE, 7, 2, ANIM_NORMAL);
+      DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL_ACTIVE, 7, 4, ANIM_NORMAL);
 
-    if (SiebAktiv)
+    if (game.magic_wall_active)
     {
       boolean sieb = FALSE;
       int jx = local_player->jx, jy = local_player->jy;
@@ -3527,15 +3646,15 @@ void GameActions()
     }
   }
 
-  if (SiebAktiv)
+  if (game.magic_wall_active)
   {
-    if (!(SiebCount % 4))
+    if (!(game.magic_wall_time_left % 4))
       PlaySoundLevel(sieb_x, sieb_y, SND_MIEP);
 
-    if (SiebCount > 0)
+    if (game.magic_wall_time_left > 0)
     {
-      SiebCount--;
-      if (!SiebCount)
+      game.magic_wall_time_left--;
+      if (!game.magic_wall_time_left)
       {
        for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
        {
@@ -3552,27 +3671,34 @@ void GameActions()
          }
        }
 
-       SiebAktiv = FALSE;
+       game.magic_wall_active = FALSE;
       }
     }
   }
 
-  if (TimeLeft > 0 && TimeFrames >= (1000 / GameFrameDelay) && !tape.pausing)
+  if (TimeFrames >= (1000 / GameFrameDelay) && !tape.pausing)
   {
     TimeFrames = 0;
-    TimeLeft--;
+    TimePlayed++;
 
     if (tape.recording || tape.playing)
-      DrawVideoDisplay(VIDEO_STATE_TIME_ON, level.time-TimeLeft);
+      DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
 
-    if (TimeLeft<=10)
-      PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+    if (TimeLeft > 0)
+    {
+      TimeLeft--;
 
-    DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+      if (TimeLeft <= 10)
+       PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
 
-    if (!TimeLeft)
-      for (i=0; i<MAX_PLAYERS; i++)
-       KillHero(&stored_player[i]);
+      DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+
+      if (!TimeLeft)
+       for (i=0; i<MAX_PLAYERS; i++)
+         KillHero(&stored_player[i]);
+    }
+    else if (level.time == 0)          /* level without time limit */
+      DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
   }
 
   DrawAllPlayers();
@@ -3587,8 +3713,7 @@ static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
   {
     int jx = stored_player[i].jx, jy = stored_player[i].jy;
 
-    if (!stored_player[i].active || stored_player[i].gone ||
-       &stored_player[i] == player)
+    if (!stored_player[i].active || &stored_player[i] == player)
       continue;
 
     min_x = MIN(min_x, jx);
@@ -3608,7 +3733,7 @@ static boolean AllPlayersInVisibleScreen()
   {
     int jx = stored_player[i].jx, jy = stored_player[i].jy;
 
-    if (!stored_player[i].active || stored_player[i].gone)
+    if (!stored_player[i].active)
       continue;
 
     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
@@ -3647,6 +3772,32 @@ void ScrollLevel(int dx, int dy)
   redraw_mask |= REDRAW_FIELD;
 }
 
+static void CheckGravityMovement(struct PlayerInfo *player)
+{
+  if (level.gravity && !player->programmed_action)
+  {
+    int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
+    int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
+    int move_dir =
+      (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
+       (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
+       (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
+    int jx = player->jx, jy = player->jy;
+    int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
+    int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
+    int new_jx = jx + dx, new_jy = jy + dy;
+    boolean field_under_player_is_free =
+      (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
+    boolean player_is_moving_to_valid_field =
+      (IN_LEV_FIELD(new_jx, new_jy) &&
+       (Feld[new_jx][new_jy] == EL_SP_BASE ||
+       Feld[new_jx][new_jy] == EL_ERDREICH));
+
+    if (field_under_player_is_free && !player_is_moving_to_valid_field)
+      player->programmed_action = MV_DOWN;
+  }
+}
+
 boolean MoveFigureOneStep(struct PlayerInfo *player,
                          int dx, int dy, int real_dx, int real_dy)
 {
@@ -3655,7 +3806,7 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
   int element;
   int can_move;
 
-  if (player->gone || (!dx && !dy))
+  if (!player->active || (!dx && !dy))
     return MF_NO_ACTION;
 
   player->MovDir = (dx < 0 ? MV_LEFT :
@@ -3669,7 +3820,11 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
     return MF_NO_ACTION;
 
+#if 0
   element = MovingOrBlocked2Element(new_jx, new_jy);
+#else
+  element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
+#endif
 
   if (DONT_GO_TO(element))
   {
@@ -3699,7 +3854,8 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
   jy = player->jy = new_jy;
   StorePlayer[jx][jy] = player->element_nr;
 
-  player->MovPos = (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / MoveSpeed);
+  player->MovPos =
+    (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
 
   ScrollFigure(player, SCROLL_INIT);
 
@@ -3712,25 +3868,29 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
   int old_jx = jx, old_jy = jy;
   int moved = MF_NO_ACTION;
 
-  if (player->gone || (!dx && !dy))
+  if (!player->active || (!dx && !dy))
     return FALSE;
 
-  if (!FrameReached(&player->move_delay, MoveSpeed) && !tape.playing)
+  if (!FrameReached(&player->move_delay, player->move_delay_value) &&
+      !tape.playing)
     return FALSE;
 
+  /* remove the last programmed player action */
+  player->programmed_action = 0;
+
   if (player->MovPos)
   {
     /* should only happen if pre-1.2 tape recordings are played */
     /* this is only for backward compatibility */
 
-    int old_move_speed = MoveSpeed;
+    int original_move_delay_value = player->move_delay_value;
 
 #if DEBUG
     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n");
 #endif
 
     /* scroll remaining steps with finest movement resolution */
-    MoveSpeed = 8;
+    player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
 
     while (player->MovPos)
     {
@@ -3741,7 +3901,7 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
       BackToFront();
     }
 
-    MoveSpeed = old_move_speed;
+    player->move_delay_value = original_move_delay_value;
   }
 
   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
@@ -3776,13 +3936,13 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
     {
       if (jx != old_jx)                /* player has moved horizontally */
       {
-       if ((player->MovDir == MV_LEFT && scroll_x > jx-MIDPOSX+offset) ||
-           (player->MovDir == MV_RIGHT && scroll_x < jx-MIDPOSX-offset))
+       if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
+           (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
          scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
 
        /* don't scroll over playfield boundaries */
-       if (scroll_x < -1 || scroll_x > lev_fieldx - SCR_FIELDX + 1)
-         scroll_x = (scroll_x < -1 ? -1 : lev_fieldx - SCR_FIELDX + 1);
+       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
+         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
 
        /* don't scroll more than one field at a time */
        scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
@@ -3794,13 +3954,13 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
       }
       else                     /* player has moved vertically */
       {
-       if ((player->MovDir == MV_UP && scroll_y > jy-MIDPOSY+offset) ||
-           (player->MovDir == MV_DOWN && scroll_y < jy-MIDPOSY-offset))
+       if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
+           (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
          scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
 
        /* don't scroll over playfield boundaries */
-       if (scroll_y < -1 || scroll_y > lev_fieldy - SCR_FIELDY + 1)
-         scroll_y = (scroll_y < -1 ? -1 : lev_fieldy - SCR_FIELDY + 1);
+       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
+         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
 
        /* don't scroll more than one field at a time */
        scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
@@ -3844,11 +4004,15 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
     player->last_move_dir = player->MovDir;
   }
   else
+  {
+    CheckGravityMovement(player);
+
     player->last_move_dir = MV_NO_MOVING;
+  }
 
   TestIfHeroHitsBadThing(jx, jy);
 
-  if (player->gone)
+  if (!player->active)
     RemoveHero(player);
 
   return moved;
@@ -3858,14 +4022,15 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
 {
   int jx = player->jx, jy = player->jy;
   int last_jx = player->last_jx, last_jy = player->last_jy;
+  int move_stepsize = TILEX / player->move_delay_value;
 
-  if (!player->active || player->gone || !player->MovPos)
+  if (!player->active || !player->MovPos)
     return;
 
   if (mode == SCROLL_INIT)
   {
     player->actual_frame_counter = FrameCounter;
-    player->GfxPos = ScrollStepSize * (player->MovPos / ScrollStepSize);
+    player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
     if (Feld[last_jx][last_jy] == EL_LEERRAUM)
       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
@@ -3876,16 +4041,29 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
   else if (!FrameReached(&player->actual_frame_counter, 1))
     return;
 
-  player->MovPos += (player->MovPos > 0 ? -1 : 1) * TILEX / MoveSpeed;
-  player->GfxPos = ScrollStepSize * (player->MovPos / ScrollStepSize);
+  player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
+  player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
     Feld[last_jx][last_jy] = EL_LEERRAUM;
 
+  /* before DrawPlayer() to draw correct player graphic for this case */
+  if (player->MovPos == 0)
+    CheckGravityMovement(player);
+
   DrawPlayer(player);
 
-  if (!player->MovPos)
+  if (player->MovPos == 0)
   {
+    if (IS_QUICK_GATE(Feld[last_jx][last_jy]))
+    {
+      /* continue with normal speed after quickly moving through gate */
+      HALVE_PLAYER_SPEED(player);
+
+      /* be able to make the next move without delay */
+      player->move_delay = 0;
+    }
+
     player->last_jx = jx;
     player->last_jy = jy;
 
@@ -3905,6 +4083,9 @@ void ScrollScreen(struct PlayerInfo *player, int mode)
 
   if (mode == SCROLL_INIT)
   {
+    /* set scrolling step size according to actual player's moving speed */
+    ScrollStepSize = TILEX / player->move_delay_value;
+
     screen_frame_counter = FrameCounter;
     ScreenMovDir = player->MovDir;
     ScreenMovPos = player->MovPos;
@@ -3916,7 +4097,7 @@ void ScrollScreen(struct PlayerInfo *player, int mode)
 
   if (ScreenMovPos)
   {
-    ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * TILEX / MoveSpeed;
+    ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
     redraw_mask |= REDRAW_FIELD;
   }
@@ -3951,7 +4132,11 @@ void TestIfGoodThingHitsBadThing(int goodx, int goody)
     if (!IN_LEV_FIELD(x, y))
       continue;
 
+#if 0
     element = Feld[x][y];
+#else
+    element = MovingOrBlocked2ElementIfNotLeaving(x, y);
+#endif
 
     if (DONT_TOUCH(element))
     {
@@ -4086,7 +4271,7 @@ void KillHero(struct PlayerInfo *player)
 {
   int jx = player->jx, jy = player->jy;
 
-  if (player->gone)
+  if (!player->active)
     return;
 
   if (IS_PFORTE(Feld[jx][jy]))
@@ -4100,7 +4285,7 @@ void BuryHero(struct PlayerInfo *player)
 {
   int jx = player->jx, jy = player->jy;
 
-  if (player->gone)
+  if (!player->active)
     return;
 
   PlaySoundLevel(jx, jy, SND_AUTSCH);
@@ -4115,11 +4300,13 @@ void RemoveHero(struct PlayerInfo *player)
   int jx = player->jx, jy = player->jy;
   int i, found = FALSE;
 
-  player->gone = TRUE;
+  player->present = FALSE;
+  player->active = FALSE;
+
   StorePlayer[jx][jy] = 0;
 
   for (i=0; i<MAX_PLAYERS; i++)
-    if (stored_player[i].active && !stored_player[i].gone)
+    if (stored_player[i].active)
       found = TRUE;
 
   if (!found)
@@ -4134,6 +4321,10 @@ int DigField(struct PlayerInfo *player,
 {
   int jx = player->jx, jy = player->jy;
   int dx = x - jx, dy = y - jy;
+  int move_direction = (dx == -1 ? MV_LEFT :
+                       dx == +1 ? MV_RIGHT :
+                       dy == -1 ? MV_UP :
+                       dy == +1 ? MV_DOWN : MV_NO_MOVING);
   int element;
 
   if (!player->MovPos)
@@ -4153,10 +4344,18 @@ int DigField(struct PlayerInfo *player,
   switch(element)
   {
     case EL_LEERRAUM:
+      PlaySoundLevel(x, y, SND_EMPTY);
       break;
 
     case EL_ERDREICH:
       Feld[x][y] = EL_LEERRAUM;
+      PlaySoundLevel(x, y, SND_SCHLURF);
+      break;
+
+    case EL_SP_BASE:
+    case EL_SP_BUG:
+      Feld[x][y] = EL_LEERRAUM;
+      PlaySoundLevel(x, y, SND_SP_BASE);
       break;
 
     case EL_EDELSTEIN:
@@ -4174,13 +4373,15 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_EMERALDS, DY_EMERALDS,
               int2str(local_player->gems_still_needed, 3),
               FS_SMALL, FC_YELLOW);
-      PlaySoundLevel(x, y, SND_PONG);
+      if (element == EL_SP_INFOTRON)
+       PlaySoundLevel(x, y, SND_SP_INFOTRON);
+      else
+       PlaySoundLevel(x, y, SND_PONG);
       break;
 
     case EL_SPEED_PILL:
       RemoveField(x, y);
-      MoveSpeed = 4;
-      ScrollStepSize = TILEX/4;
+      player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
       PlaySoundLevel(x, y, SND_PONG);
       break;
 
@@ -4192,7 +4393,10 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_DYNAMITE, DY_DYNAMITE,
               int2str(local_player->dynamite, 3),
               FS_SMALL, FC_YELLOW);
-      PlaySoundLevel(x, y, SND_PONG);
+      if (element == EL_SP_DISK_RED)
+       PlaySoundLevel(x, y, SND_SP_INFOTRON);
+      else
+       PlaySoundLevel(x, y, SND_PONG);
       break;
 
     case EL_DYNABOMB_NR:
@@ -4222,7 +4426,27 @@ int DigField(struct PlayerInfo *player,
     case EL_SCHLUESSEL3:
     case EL_SCHLUESSEL4:
     {
-      int key_nr = element-EL_SCHLUESSEL1;
+      int key_nr = element - EL_SCHLUESSEL1;
+
+      RemoveField(x, y);
+      player->key[key_nr] = TRUE;
+      RaiseScoreElement(EL_SCHLUESSEL);
+      DrawMiniGraphicExt(drawto, gc,
+                        DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
+                        GFX_SCHLUESSEL1+key_nr);
+      DrawMiniGraphicExt(window, gc,
+                        DX_KEYS+key_nr*MINI_TILEX, DY_KEYS,
+                        GFX_SCHLUESSEL1+key_nr);
+      PlaySoundLevel(x, y, SND_PONG);
+      break;
+    }
+
+    case EL_EM_KEY_1:
+    case EL_EM_KEY_2:
+    case EL_EM_KEY_3:
+    case EL_EM_KEY_4:
+    {
+      int key_nr = element - EL_EM_KEY_1;
 
       RemoveField(x, y);
       player->key[key_nr] = TRUE;
@@ -4269,7 +4493,7 @@ int DigField(struct PlayerInfo *player,
        return MF_NO_ACTION;
 
       player->LevelSolved = player->GameOver = TRUE;
-      PlaySoundLevel(x, y, SND_BUING);
+      PlaySoundStereo(SND_SP_EXIT, PSND_MAX_RIGHT);
       break;
 
     case EL_FELSBROCKEN:
@@ -4308,6 +4532,8 @@ int DigField(struct PlayerInfo *player,
        PlaySoundLevel(x+dx, y+dy, SND_PUSCH);
       else if (element == EL_KOKOSNUSS)
        PlaySoundLevel(x+dx, y+dy, SND_KNURK);
+      else if (IS_SP_ELEMENT(element))
+       PlaySoundLevel(x+dx, y+dy, SND_SP_ZONKPUSH);
       else
        PlaySoundLevel(x+dx, y+dy, SND_KLOPF);
       break;
@@ -4316,7 +4542,7 @@ int DigField(struct PlayerInfo *player,
     case EL_PFORTE2:
     case EL_PFORTE3:
     case EL_PFORTE4:
-      if (!player->key[element-EL_PFORTE1])
+      if (!player->key[element - EL_PFORTE1])
        return MF_NO_ACTION;
       break;
 
@@ -4324,8 +4550,38 @@ int DigField(struct PlayerInfo *player,
     case EL_PFORTE2X:
     case EL_PFORTE3X:
     case EL_PFORTE4X:
-      if (!player->key[element-EL_PFORTE1X])
+      if (!player->key[element - EL_PFORTE1X])
+       return MF_NO_ACTION;
+      break;
+
+    case EL_EM_GATE_1:
+    case EL_EM_GATE_2:
+    case EL_EM_GATE_3:
+    case EL_EM_GATE_4:
+      if (!player->key[element - EL_EM_GATE_1])
+       return MF_NO_ACTION;
+      if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
        return MF_NO_ACTION;
+
+      /* automatically move to the next field with double speed */
+      player->programmed_action = move_direction;
+      DOUBLE_PLAYER_SPEED(player);
+
+      break;
+
+    case EL_EM_GATE_1X:
+    case EL_EM_GATE_2X:
+    case EL_EM_GATE_3X:
+    case EL_EM_GATE_4X:
+      if (!player->key[element - EL_EM_GATE_1X])
+       return MF_NO_ACTION;
+      if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
+       return MF_NO_ACTION;
+
+      /* automatically move to the next field with double speed */
+      player->programmed_action = move_direction;
+      DOUBLE_PLAYER_SPEED(player);
+
       break;
 
     case EL_SP_PORT1_LEFT:
@@ -4362,6 +4618,11 @@ int DigField(struct PlayerInfo *player,
          !IN_LEV_FIELD(x + dx, y + dy) ||
          !IS_FREE(x + dx, y + dy))
        return MF_NO_ACTION;
+
+      /* automatically move to the next field with double speed */
+      player->programmed_action = move_direction;
+      DOUBLE_PLAYER_SPEED(player);
+
       break;
 
     case EL_AUSGANG_ZU:
@@ -4376,14 +4637,6 @@ int DigField(struct PlayerInfo *player,
 
       PlaySoundLevel(x, y, SND_BUING);
 
-      /*
-      player->gone = TRUE;
-      PlaySoundLevel(x, y, SND_BUING);
-
-      if (!local_player->friends_still_needed)
-       player->LevelSolved = player->GameOver = TRUE;
-      */
-
       break;
 
     case EL_BIRNE_AUS:
@@ -4472,7 +4725,7 @@ int DigField(struct PlayerInfo *player,
 
       if (IS_SB_ELEMENT(element) &&
          local_player->sokobanfields_still_needed == 0 &&
-         game_emulation == EMU_SOKOBAN)
+         game.emulation == EMU_SOKOBAN)
       {
        player->LevelSolved = player->GameOver = TRUE;
        PlaySoundLevel(x, y, SND_BUING);
@@ -4487,12 +4740,7 @@ int DigField(struct PlayerInfo *player,
       break;
 
     default:
-      if (IS_EATABLE(element))         /* other kinds of 'dirt' */
-       Feld[x][y] = EL_LEERRAUM;
-      else
-       return MF_NO_ACTION;
-
-      break;
+      return MF_NO_ACTION;
   }
 
   player->push_delay = 0;
@@ -4505,7 +4753,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
   int jx = player->jx, jy = player->jy;
   int x = jx + dx, y = jy + dy;
 
-  if (player->gone || !IN_LEV_FIELD(x, y))
+  if (!player->active || !IN_LEV_FIELD(x, y))
     return FALSE;
 
   if (dx && dy)
@@ -4540,7 +4788,7 @@ boolean PlaceBomb(struct PlayerInfo *player)
   int jx = player->jx, jy = player->jy;
   int element;
 
-  if (player->gone || player->MovPos)
+  if (!player->active || player->MovPos)
     return FALSE;
 
   element = Feld[jx][jy];
@@ -4561,7 +4809,12 @@ boolean PlaceBomb(struct PlayerInfo *player)
     DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
             FS_SMALL, FC_YELLOW);
     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
-      DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNAMIT);
+    {
+      if (game.emulation == EMU_SUPAPLEX)
+       DrawGraphic(SCREENX(jx), SCREENY(jy), GFX_SP_DISK_RED);
+      else
+       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), GFX_DYNAMIT);
+    }
   }
   else
   {
@@ -4594,19 +4847,21 @@ void PlaySoundLevel(int x, int y, int sound_nr)
   volume = PSND_MAX_VOLUME;
 
 #ifndef MSDOS
-  stereo = (sx-SCR_FIELDX/2)*12;
+  stereo = (sx - SCR_FIELDX/2) * 12;
 #else
-  stereo = PSND_MIDDLE+(2*sx-(SCR_FIELDX-1))*5;
-  if(stereo > PSND_MAX_RIGHT) stereo = PSND_MAX_RIGHT;
-  if(stereo < PSND_MAX_LEFT) stereo = PSND_MAX_LEFT;
+  stereo = PSND_MIDDLE + (2 * sx - (SCR_FIELDX - 1)) * 5;
+  if (stereo > PSND_MAX_RIGHT)
+    stereo = PSND_MAX_RIGHT;
+  if (stereo < PSND_MAX_LEFT)
+    stereo = PSND_MAX_LEFT;
 #endif
 
   if (!IN_SCR_FIELD(sx, sy))
   {
-    int dx = ABS(sx-SCR_FIELDX/2)-SCR_FIELDX/2;
-    int dy = ABS(sy-SCR_FIELDY/2)-SCR_FIELDY/2;
+    int dx = ABS(sx - SCR_FIELDX/2) - SCR_FIELDX/2;
+    int dy = ABS(sy - SCR_FIELDY/2) - SCR_FIELDY/2;
 
-    volume -= volume*(dx > dy ? dx : dy)/silence_distance;
+    volume -= volume * (dx > dy ? dx : dy) / silence_distance;
   }
 
   PlaySoundExt(sound_nr, volume, stereo, PSND_NO_LOOP);
@@ -4664,3 +4919,238 @@ void RaiseScoreElement(int element)
       break;
   }
 }
+
+/* ---------- new game button stuff ---------------------------------------- */
+
+/* graphic position values for game buttons */
+#define GAME_BUTTON_XSIZE      30
+#define GAME_BUTTON_YSIZE      30
+#define GAME_BUTTON_XPOS       5
+#define GAME_BUTTON_YPOS       215
+#define SOUND_BUTTON_XPOS      5
+#define SOUND_BUTTON_YPOS      (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
+
+#define GAME_BUTTON_STOP_XPOS  (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
+#define GAME_BUTTON_PLAY_XPOS  (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
+#define SOUND_BUTTON_MUSIC_XPOS        (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
+#define SOUND_BUTTON_LOOPS_XPOS        (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
+#define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
+
+static struct
+{
+  int x, y;
+  int gadget_id;
+  char *infotext;
+} gamebutton_info[NUM_GAME_BUTTONS] =
+{
+  {
+    GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_STOP,
+    "stop game"
+  },
+  {
+    GAME_BUTTON_PAUSE_XPOS,    GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_PAUSE,
+    "pause game"
+  },
+  {
+    GAME_BUTTON_PLAY_XPOS,     GAME_BUTTON_YPOS,
+    GAME_CTRL_ID_PLAY,
+    "play game"
+  },
+  {
+    SOUND_BUTTON_MUSIC_XPOS,   SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_MUSIC,
+    "background music on/off"
+  },
+  {
+    SOUND_BUTTON_LOOPS_XPOS,   SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_LOOPS,
+    "sound loops on/off"
+  },
+  {
+    SOUND_BUTTON_SIMPLE_XPOS,  SOUND_BUTTON_YPOS,
+    SOUND_CTRL_ID_SIMPLE,
+    "normal sounds on/off"
+  }
+};
+
+void CreateGameButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_GAME_BUTTONS; i++)
+  {
+    Pixmap gd_pixmap = pix[PIX_DOOR];
+    struct GadgetInfo *gi;
+    int button_type;
+    boolean checked;
+    unsigned long event_mask;
+    int gd_xoffset, gd_yoffset;
+    int gd_x1, gd_x2, gd_y1, gd_y2;
+    int id = i;
+
+    gd_xoffset = gamebutton_info[i].x;
+    gd_yoffset = gamebutton_info[i].y;
+    gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
+    gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
+
+    if (id == GAME_CTRL_ID_STOP ||
+       id == GAME_CTRL_ID_PAUSE ||
+       id == GAME_CTRL_ID_PLAY)
+    {
+      button_type = GD_TYPE_NORMAL_BUTTON;
+      checked = FALSE;
+      event_mask = GD_EVENT_RELEASED;
+      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
+      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
+    }
+    else
+    {
+      button_type = GD_TYPE_CHECK_BUTTON;
+      checked =
+       ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
+        (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
+        (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
+      event_mask = GD_EVENT_PRESSED;
+      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
+      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
+    }
+
+    gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_INFO_TEXT, gamebutton_info[i].infotext,
+                     GDI_X, DX + gd_xoffset,
+                     GDI_Y, DY + gd_yoffset,
+                     GDI_WIDTH, GAME_BUTTON_XSIZE,
+                     GDI_HEIGHT, GAME_BUTTON_YSIZE,
+                     GDI_TYPE, button_type,
+                     GDI_STATE, GD_BUTTON_UNPRESSED,
+                     GDI_CHECKED, checked,
+                     GDI_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y1,
+                     GDI_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y1,
+                     GDI_ALT_DESIGN_UNPRESSED, gd_pixmap, gd_x1, gd_y2,
+                     GDI_ALT_DESIGN_PRESSED, gd_pixmap, gd_x2, gd_y2,
+                     GDI_EVENT_MASK, event_mask,
+                     GDI_CALLBACK_ACTION, HandleGameButtons,
+                     GDI_END);
+
+    if (gi == NULL)
+      Error(ERR_EXIT, "cannot create gadget");
+
+    game_gadget[id] = gi;
+  }
+}
+
+static void MapGameButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_GAME_BUTTONS; i++)
+    MapGadget(game_gadget[i]);
+}
+
+void UnmapGameButtons()
+{
+  int i;
+
+  for (i=0; i<NUM_GAME_BUTTONS; i++)
+    UnmapGadget(game_gadget[i]);
+}
+
+static void HandleGameButtons(struct GadgetInfo *gi)
+{
+  int id = gi->custom_id;
+
+  if (game_status != PLAYING)
+    return;
+
+  switch (id)
+  {
+    case GAME_CTRL_ID_STOP:
+      if (AllPlayersGone)
+      {
+       CloseDoor(DOOR_CLOSE_1);
+       game_status = MAINMENU;
+       DrawMainMenu();
+       break;
+      }
+
+      if (level_editor_test_game ||
+         Request("Do you really want to quit the game ?",
+                 REQ_ASK | REQ_STAY_CLOSED))
+      { 
+#ifndef MSDOS
+       if (options.network)
+         SendToServer_StopPlaying();
+       else
+#endif
+       {
+         game_status = MAINMENU;
+         DrawMainMenu();
+       }
+      }
+      else
+       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+      break;
+
+    case GAME_CTRL_ID_PAUSE:
+      if (options.network)
+      {
+#ifndef MSDOS
+       if (tape.pausing)
+         SendToServer_ContinuePlaying();
+       else
+         SendToServer_PausePlaying();
+#endif
+      }
+      else
+       TapeTogglePause();
+      break;
+
+    case GAME_CTRL_ID_PLAY:
+      if (tape.pausing)
+      {
+#ifndef MSDOS
+       if (options.network)
+         SendToServer_ContinuePlaying();
+       else
+#endif
+       {
+         tape.pausing = FALSE;
+         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+       }
+      }
+      break;
+
+    case SOUND_CTRL_ID_MUSIC:
+      if (setup.sound_music)
+      { 
+       setup.sound_music = FALSE;
+       FadeSound(background_loop[level_nr % num_bg_loops]);
+      }
+      else if (sound_loops_allowed)
+      { 
+       setup.sound = setup.sound_music = TRUE;
+       PlaySoundLoop(background_loop[level_nr % num_bg_loops]);
+      }
+      break;
+
+    case SOUND_CTRL_ID_LOOPS:
+      if (setup.sound_loops)
+       setup.sound_loops = FALSE;
+      else if (sound_loops_allowed)
+       setup.sound = setup.sound_loops = TRUE;
+      break;
+
+    case SOUND_CTRL_ID_SIMPLE:
+      if (setup.sound_simple)
+       setup.sound_simple = FALSE;
+      else if (sound_status==SOUND_AVAILABLE)
+       setup.sound = setup.sound_simple = TRUE;
+      break;
+
+    default:
+      break;
+  }
+}