rnd-19981229-2
[rocksndiamonds.git] / src / game.c
index ec95ff6dcf71d38bed26507ac7ee088a04a5e998..b88998efb9b0f61b1f09e178c53fb5e80483d40f 100644 (file)
@@ -176,6 +176,7 @@ static void InitField(int x, int y, boolean init_game)
   switch (Feld[x][y])
   {
     case EL_SPIELFIGUR:
+    case EL_SP_MURPHY:
       if (init_game)
        Feld[x][y] = EL_SPIELER1;
       /* no break! */
@@ -260,6 +261,8 @@ static void InitField(int x, int y, boolean init_game)
     case EL_MAMPFER2:
     case EL_ROBOT:
     case EL_PACMAN:
+    case EL_SP_SNIKSNAK:
+    case EL_SP_ELECTRON:
       InitMovDir(x, y);
       break;
 
@@ -298,6 +301,10 @@ static void InitField(int x, int y, boolean init_game)
       MovDir[x][y] = 1 << RND(4);
       break;
 
+    case EL_SP_EMPTY:
+      Feld[x][y] = EL_LEERRAUM;
+      break;
+
     default:
       break;
   }
@@ -389,7 +396,20 @@ void InitGame()
   ScreenMovPos = 0;
   ScreenGfxPos = 0;
 
-  AllPlayersGone = SiebAktiv = FALSE;
+  if (level.high_speed)
+  {
+    MoveSpeed = 4;
+    ScrollStepSize = TILEX/4;
+  }
+  else
+  {
+    MoveSpeed = 8;
+    ScrollStepSize = TILEX/8;
+  }
+
+  AllPlayersGone = FALSE;
+  SiebAktiv = FALSE;
+  SiebCount = 0;
 
   for (i=0; i<MAX_NUM_AMOEBA; i++)
     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
@@ -635,7 +655,9 @@ void InitMovDir(int x, int y)
       if (element != EL_KAEFER &&
          element != EL_FLIEGER &&
          element != EL_BUTTERFLY &&
-         element != EL_FIREFLY)
+         element != EL_FIREFLY &&
+         element != EL_SP_SNIKSNAK &&
+         element != EL_SP_ELECTRON)
        break;
 
       for (i=0; i<4; i++)
@@ -650,7 +672,8 @@ void InitMovDir(int x, int y)
            MovDir[x][y] = direction[0][i];
            break;
          }
-         else if (element == EL_FLIEGER || element == EL_FIREFLY)
+         else if (element == EL_FLIEGER || element == EL_FIREFLY ||
+                  element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
          {
            MovDir[x][y] = direction[1][i];
            break;
@@ -1267,7 +1290,8 @@ void Impact(int x, int y)
     return;
   }
 
-  if (element == EL_BOMBE && (lastline || object_hit)) /* element is bomb */
+  if ((element == EL_BOMBE || element == EL_SP_DISK_ORANGE) &&
+      (lastline || object_hit))        /* element is bomb */
   {
     Bang(x, y);
     return;
@@ -1290,8 +1314,22 @@ void Impact(int x, int y)
   if (!lastline && object_hit)         /* check which object was hit */
   {
     if (CAN_CHANGE(element) && 
-       (smashed == EL_SIEB_LEER || smashed == EL_SIEB2_LEER) && !SiebAktiv)
-      SiebAktiv = level.dauer_sieb * FRAMES_PER_SECOND;
+       (smashed == EL_SIEB_INAKTIV || smashed == EL_SIEB2_INAKTIV))
+    {
+      int x, y;
+      int activated_magic_wall =
+       (smashed == EL_SIEB_INAKTIV ? EL_SIEB_LEER : EL_SIEB2_LEER);
+
+      /* activate magic wall / mill */
+
+      for (y=0; y<lev_fieldy; y++)
+       for (x=0; x<lev_fieldx; x++)
+         if (Feld[x][y] == smashed)
+           Feld[x][y] = activated_magic_wall;
+
+      SiebCount = level.dauer_sieb * FRAMES_PER_SECOND;
+      SiebAktiv = TRUE;
+    }
 
     if (IS_PLAYER(x, y+1))
     {
@@ -1313,8 +1351,9 @@ void Impact(int x, int y)
     }
     else if (element == EL_FELSBROCKEN)
     {
-      if (IS_ENEMY(smashed) || smashed == EL_BOMBE || smashed == EL_SONDE ||
-         smashed == EL_SCHWEIN || smashed == EL_DRACHE)
+      if (IS_ENEMY(smashed) ||
+         smashed == EL_BOMBE || smashed == EL_SP_DISK_ORANGE ||
+         smashed == EL_SONDE || smashed == EL_SCHWEIN || smashed == EL_DRACHE)
       {
        Bang(x, y+1);
        return;
@@ -1453,7 +1492,8 @@ void TurnRound(int x, int y)
     else if (element == EL_BUTTERFLY)  /* && MovDir[x][y] == left_dir) */
       MovDelay[x][y] = 1;
   }
-  else if (element == EL_FLIEGER || element == EL_FIREFLY)
+  else if (element == EL_FLIEGER || element == EL_FIREFLY ||
+          element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
   {
     TestIfBadThingHitsOtherBadThing(x, y);
 
@@ -1464,7 +1504,9 @@ void TurnRound(int x, int y)
             !IS_FREE_OR_PLAYER(move_x, move_y))
       MovDir[x][y] = right_dir;
 
-    if (element == EL_FLIEGER && MovDir[x][y] != old_move_dir)
+    if ((element == EL_FLIEGER ||
+        element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
+       && MovDir[x][y] != old_move_dir)
       MovDelay[x][y] = 9;
     else if (element == EL_FIREFLY)    /* && MovDir[x][y] == right_dir) */
       MovDelay[x][y] = 1;
@@ -1884,7 +1926,7 @@ void StartMoving(int x, int y)
        Store2[x][y] = 0;
       }
     }
-    else if (SiebAktiv && CAN_CHANGE(element) &&
+    else if (CAN_CHANGE(element) &&
             (Feld[x][y+1] == EL_SIEB_LEER || Feld[x][y+1] == EL_SIEB2_LEER))
     {
       InitMovingField(x, y, MV_DOWN);
@@ -1942,7 +1984,9 @@ void StartMoving(int x, int y)
       if (element!=EL_MAMPFER && element!=EL_MAMPFER2 && element!=EL_PACMAN)
       {
        TurnRound(x, y);
-       if (MovDelay[x][y] && (element == EL_KAEFER || element == EL_FLIEGER))
+       if (MovDelay[x][y] && (element == EL_KAEFER || element == EL_FLIEGER ||
+                              element == EL_SP_SNIKSNAK ||
+                              element == EL_SP_ELECTRON))
          DrawLevelField(x, y);
       }
     }
@@ -2187,7 +2231,8 @@ void StartMoving(int x, int y)
 
       TurnRound(x, y);
 
-      if (element == EL_KAEFER || element == EL_FLIEGER)
+      if (element == EL_KAEFER || element == EL_FLIEGER ||
+         element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
        DrawLevelField(x, y);
       else if (element == EL_BUTTERFLY || element == EL_FIREFLY)
        DrawGraphicAnimation(x, y, el2gfx(element), 2, 4, ANIM_NORMAL);
@@ -2217,7 +2262,7 @@ void ContinueMoving(int x, int y)
   int newx = x + dx, newy = y + dy;
   int step = (horiz_move ? dx : dy) * TILEX/8;
 
-  if (CAN_FALL(element) && horiz_move)
+  if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
     step*=2;
   else if (element == EL_TROPFEN)
     step/=2;
@@ -2580,7 +2625,7 @@ void AmoebeAbleger(int ax, int ay)
 
     if (newax == ax && neway == ay)            /* amoeba cannot grow */
     {
-      if (i == 4 && !waiting_for_player)
+      if (i == 4 && (!waiting_for_player || game_emulation == EMU_BOULDERDASH))
       {
        Feld[ax][ay] = EL_AMOEBE_TOT;
        DrawLevelField(ax, ay);
@@ -2794,10 +2839,9 @@ void NussKnacken(int x, int y)
 
 void SiebAktivieren(int x, int y, int typ)
 {
-  if (!(SiebAktiv % 4) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
-    DrawGraphic(SCREENX(x), SCREENY(y),
-               (typ == 1 ? GFX_SIEB_VOLL :
-                GFX_SIEB2_VOLL) + 3 - (SiebAktiv % 16) / 4);
+  int graphic = (typ == 1 ? GFX_SIEB_VOLL : GFX_SIEB2_VOLL) + 3;
+
+  DrawGraphicAnimation(x, y, graphic, 4, 4, ANIM_REVERSE);
 }
 
 void AusgangstuerPruefen(int x, int y)
@@ -3446,29 +3490,35 @@ void GameActions()
 
   if (SiebAktiv)
   {
-    if (!(SiebAktiv%4))
+    if (!(SiebCount % 4))
       PlaySoundLevel(sieb_x, sieb_y, SND_MIEP);
-    SiebAktiv--;
-    if (!SiebAktiv)
+
+    if (SiebCount > 0)
     {
-      for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+      SiebCount--;
+      if (!SiebCount)
       {
-       element = Feld[x][y];
-       if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL)
-       {
-         Feld[x][y] = EL_SIEB_TOT;
-         DrawLevelField(x, y);
-       }
-       else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL)
+       for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
        {
-         Feld[x][y] = EL_SIEB2_TOT;
-         DrawLevelField(x, y);
+         element = Feld[x][y];
+         if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL)
+         {
+           Feld[x][y] = EL_SIEB_TOT;
+           DrawLevelField(x, y);
+         }
+         else if (element == EL_SIEB2_LEER || element == EL_SIEB2_VOLL)
+         {
+           Feld[x][y] = EL_SIEB2_TOT;
+           DrawLevelField(x, y);
+         }
        }
+
+       SiebAktiv = FALSE;
       }
     }
   }
 
-  if (TimeLeft>0 && TimeFrames>=(1000/GameFrameDelay) && !tape.pausing)
+  if (TimeLeft > 0 && TimeFrames >= (1000 / GameFrameDelay) && !tape.pausing)
   {
     TimeFrames = 0;
     TimeLeft--;
@@ -3610,7 +3660,7 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
   jy = player->jy = new_jy;
   StorePlayer[jx][jy] = player->element_nr;
 
-  player->MovPos = (dx > 0 || dy > 0 ? -1 : 1) * 7*TILEX/8;
+  player->MovPos = (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / MoveSpeed);
 
   ScrollFigure(player, SCROLL_INIT);
 
@@ -3634,10 +3684,15 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
     /* should only happen if pre-1.2 tape recordings are played */
     /* this is only for backward compatibility */
 
+    int old_move_speed = MoveSpeed;
+
 #if DEBUG
     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n");
 #endif
 
+    /* scroll remaining steps with finest movement resolution */
+    MoveSpeed = 8;
+
     while (player->MovPos)
     {
       ScrollFigure(player, SCROLL_GO_ON);
@@ -3646,6 +3701,8 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
       DrawAllPlayers();
       BackToFront();
     }
+
+    MoveSpeed = old_move_speed;
   }
 
   if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
@@ -3780,7 +3837,7 @@ void ScrollFigure(struct PlayerInfo *player, int mode)
   else if (!FrameReached(&player->actual_frame_counter, 1))
     return;
 
-  player->MovPos += (player->MovPos > 0 ? -1 : 1) * TILEX/8;
+  player->MovPos += (player->MovPos > 0 ? -1 : 1) * TILEX / MoveSpeed;
   player->GfxPos = ScrollStepSize * (player->MovPos / ScrollStepSize);
 
   if (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
@@ -3820,7 +3877,7 @@ void ScrollScreen(struct PlayerInfo *player, int mode)
 
   if (ScreenMovPos)
   {
-    ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * TILEX/8;
+    ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * TILEX / MoveSpeed;
     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
     redraw_mask |= REDRAW_FIELD;
   }
@@ -4081,7 +4138,15 @@ int DigField(struct PlayerInfo *player,
       PlaySoundLevel(x, y, SND_PONG);
       break;
 
+    case EL_SPEED_PILL:
+      RemoveField(x, y);
+      MoveSpeed = 4;
+      ScrollStepSize = TILEX/4;
+      PlaySoundLevel(x, y, SND_PONG);
+      break;
+
     case EL_DYNAMIT_AUS:
+    case EL_SP_DISK_RED:
       RemoveField(x, y);
       player->dynamite++;
       RaiseScoreElement(EL_DYNAMIT);
@@ -4141,10 +4206,35 @@ int DigField(struct PlayerInfo *player,
       return MF_ACTION;
       break;
 
+    case EL_SP_TERMINAL:
+      {
+       int xx, yy;
+
+       for (yy=0; yy<lev_fieldy; yy++)
+         for (xx=0; xx<lev_fieldx; xx++)
+           if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
+             Bang(xx, yy);
+
+       Feld[x][y] = EL_SP_TERMINAL_ACTIVE;
+       DrawLevelField(x, y);
+       return MF_ACTION;
+      }
+      break;
+
+    case EL_SP_EXIT:
+      if (local_player->gems_still_needed > 0)
+       return MF_NO_ACTION;
+
+      player->LevelSolved = player->GameOver = TRUE;
+      PlaySoundLevel(x, y, SND_BUING);
+      break;
+
     case EL_FELSBROCKEN:
     case EL_BOMBE:
     case EL_KOKOSNUSS:
     case EL_ZEIT_LEER:
+    case EL_SP_ZONK:
+    case EL_SP_DISK_ORANGE:
       if (dy || mode == DF_SNAP)
        return MF_NO_ACTION;
 
@@ -4195,6 +4285,42 @@ int DigField(struct PlayerInfo *player,
        return MF_NO_ACTION;
       break;
 
+    case EL_SP_PORT1_LEFT:
+    case EL_SP_PORT2_LEFT:
+    case EL_SP_PORT1_RIGHT:
+    case EL_SP_PORT2_RIGHT:
+    case EL_SP_PORT1_UP:
+    case EL_SP_PORT2_UP:
+    case EL_SP_PORT1_DOWN:
+    case EL_SP_PORT2_DOWN:
+    case EL_SP_PORT_X:
+    case EL_SP_PORT_Y:
+    case EL_SP_PORT_XY:
+      if ((dx == -1 &&
+          element != EL_SP_PORT1_LEFT &&
+          element != EL_SP_PORT2_LEFT &&
+          element != EL_SP_PORT_X &&
+          element != EL_SP_PORT_XY) ||
+         (dx == +1 &&
+          element != EL_SP_PORT1_RIGHT &&
+          element != EL_SP_PORT2_RIGHT &&
+          element != EL_SP_PORT_X &&
+          element != EL_SP_PORT_XY) ||
+         (dy == -1 &&
+          element != EL_SP_PORT1_UP &&
+          element != EL_SP_PORT2_UP &&
+          element != EL_SP_PORT_Y &&
+          element != EL_SP_PORT_XY) ||
+         (dy == +1 &&
+          element != EL_SP_PORT1_DOWN &&
+          element != EL_SP_PORT2_DOWN &&
+          element != EL_SP_PORT_Y &&
+          element != EL_SP_PORT_XY) ||
+         !IN_LEV_FIELD(x + dx, y + dy) ||
+         !IS_FREE(x + dx, y + dy))
+       return MF_NO_ACTION;
+      break;
+
     case EL_AUSGANG_ZU:
     case EL_AUSGANG_ACT:
       /* door is not (yet) open */
@@ -4240,6 +4366,7 @@ int DigField(struct PlayerInfo *player,
     case EL_SOKOBAN_FELD_VOLL:
     case EL_SOKOBAN_OBJEKT:
     case EL_SONDE:
+    case EL_SP_DISK_YELLOW:
       if (mode == DF_SNAP)
        return MF_NO_ACTION;