unit GameMode.StarGate;

interface

uses
  QuadEngine, GameMode.Custom, Vec2f, Math, Generics.Collections, windows, BASS, Collision, GlobalTypes;

type
  TGameModeStarGate = class(TGameModeCustom)
  type
    TGateMode = (gmOff, gmInit, gmDialing, gmEstablish, gmKawoosh, gmOpened, gmClosing);
  private
  const
    FIRST = 219;
    LAST = 254;
    LightsCoord: array[FIRST..LAST] of TVec2f = ((X: 509 - 445; Y: 57 - 444),  (X: 556 - 445; Y: 67 - 444),  (X: 603 - 445; Y: 84 - 444),  (X: 645 - 445; Y: 106 - 444),
                                                 (X: 741 - 445; Y: 190 - 444), (X: 770 - 445; Y: 226 - 444), (X: 794 - 445; Y: 271 - 444), (X: 814 - 445; Y: 315 - 444),
                                                 (X: 837 - 445; Y: 441 - 444), (X: 833 - 445; Y: 490 - 444), (X: 825 - 445; Y: 538 - 444), (X: 812 - 445; Y: 581 - 444),
                                                 (X: 748 - 445; Y: 693 - 444), (X: 720 - 445; Y: 731 - 444), (X: 681 - 445; Y: 765 - 444), (X: 639 - 445; Y: 787 - 444),
                                                 (X: 518 - 445; Y: 830 - 444), (X: 469 - 445; Y: 836 - 444), (X: 421 - 445; Y: 837 - 444), (X: 373 - 445; Y: 830 - 444),
                                                 (X: 256 - 445; Y: 783 - 444), (X: 213 - 445; Y: 758 - 444), (X: 178 - 445; Y: 725 - 444), (X: 141 - 445; Y: 693 - 444),
                                                 (X: 80 - 445; Y: 580 - 444),  (X: 64 - 445; Y: 535 - 444),  (X: 54 - 445; Y: 487 - 444),  (X: 55 - 445; Y: 440 - 444),
                                                 (X: 75 - 445; Y: 314 - 444),  (X: 93 - 445; Y: 269 - 444),  (X: 119 - 445; Y: 225 - 444), (X: 145 - 445; Y: 188 - 444),
                                                 (X: 249 - 445; Y: 110 - 444), (X: 289 - 445; Y: 87 - 444),  (X: 335 - 445; Y: 67 - 444),  (X: 380 - 445; Y: 58 - 444)
                                                );
  var
    FRollSound: HSAMPLE;
    FGateMode: TGateMode;
    FGateAngle: Single;
    FTargetAngle: Single;
    FTime: Double;
    FLight: Single;
    FSleepTimer: Single;
    FGateOpenTime: Single;
    FNextPoint: Byte;
    FActiveLights: TList<Byte>;
    FKawooshFrame: Double;
    FLastChevron: Byte;

    FStatus: TGateModeStatus;
    procedure DrawLights;
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure Process(const Delta: Double); override;
    procedure DrawGBuffer;
    procedure Draw; override;
    procedure Restart;
  end;

implementation

uses
  Resources, GameOverlay;

{ TGameModeStarGate }

function Animate(A, B, Time: Double): Double;
begin
  Result := (B - A) * (Time * Time * (3 - 2 * Time)) + A;
end;

constructor TGameModeStarGate.Create;
begin
  inherited Create;
  TGameOverlay.SetEnabled(False);
  FLight := 255;
  TShaders.Lights.BindVariableToPS(0, @FLight, 1);

  FGateOpenTime := 0;
  TShaders.GateOpen.BindVariableToPS(0, @FGateOpenTime, 1);

  FTime := 0;
  FActiveLights := TList<Byte>.Create;

  FGateMode := gmOff;
  FGateAngle := 0;
  FSleepTimer := 0;
  FLastChevron := 0;
  FStatus := gmsIntro;
  TGameOverlay.AddDialog(1, 'Раш, где мы очутись?', lEarth);
  TGameOverlay.AddDialog(0, 'Перенеслись на корабль древних, далеко от Земли', lEarth);
  TGameOverlay.AddDialog(1, 'Как нам вернуться назад?', lEarth);
  TGameOverlay.AddDialog(0, 'Думаю мы найдем ответы тут. Вон какая-то консоль, глянем там.', lEarth);
  TGameOverlay.AddDialog(0, 'Тут навигация. Сейчас мы находимся в FTL полёте.', lEarth);
end;

procedure TGameModeStarGate.Restart;
begin
  FLight := 255;
  FGateOpenTime := 0;
  FTime := 0;
  FActiveLights.Clear;
  FGateMode := gmInit;
  FGateAngle := 0;
  FSleepTimer := 0;
  FLastChevron := 0;
  FStatus := gmsGo;
end;

procedure TGameModeStarGate.DrawLights;
var
  i: Integer;
  X, Y: Single;
  ind: Byte;
begin
  if FGateMode = gmEstablish then
  begin
    TTextures.QuadGBuffer.DrawLight(tvec3f.Create((512 - TGlobals.QuadCameraGate.GetPosition.X) / TGlobals.GWidth, (512 - TGlobals.QuadCameraGate.GetPosition.Y) / TGlobals.GHeight, 0.75 - FGateOpenTime * 0.5 + 0.35), 2.0 - FGateOpenTime * 0.5, $FFDDEEFF);
    TTextures.QuadGBuffer.DrawLight(tvec3f.Create((512 - TGlobals.QuadCameraGate.GetPosition.X) / TGlobals.GWidth, (512 - TGlobals.QuadCameraGate.GetPosition.Y) / TGlobals.GHeight, 0.75 - FGateOpenTime * 0.5 + 0.35), 2.0 - FGateOpenTime * 0.5, $FFDDEEFF);
  end;


  if (FGateMode = gmOpened) or ((FGateMode = gmKawoosh) and (FKawooshFrame > 15)) then
  begin
    TTextures.QuadGBuffer.DrawLight(tvec3f.Create((512 - TGlobals.QuadCameraGate.GetPosition.X) / TGlobals.GWidth, (512 - TGlobals.QuadCameraGate.GetPosition.Y) / TGlobals.GHeight, 1.0), 1.0, $FFDDEEFF);
  end;


  if (FGateMode = gmDialing) and (FSleepTimer > 0.7) then
    TTextures.QuadGBuffer.DrawLight(tvec3f.Create((512 - TGlobals.QuadCameraGate.GetPosition.X) / TGlobals.GWidth, (- TGlobals.QuadCameraGate.GetPosition.Y) / TGlobals.GHeight, 0.10), 1.0, $D0FFFFDD);


  TTextures.QuadGBuffer.DrawLight(tvec3f.Create((128 - TGlobals.QuadCameraGate.GetPosition.X) / TGlobals.GWidth, (- TGlobals.QuadCameraGate.GetPosition.Y) / TGlobals.GHeight, 0.17), 4.0, $D0FFEEDD);
  TTextures.QuadGBuffer.DrawLight(tvec3f.Create((896 - TGlobals.QuadCameraGate.GetPosition.X) / TGlobals.GWidth, (- TGlobals.QuadCameraGate.GetPosition.Y) / TGlobals.GHeight, 0.17), 4.0, $D0FFEEDD);

  // лампочки
  if FGateMode in [gmInit, gmDialing, gmEstablish, gmKawoosh, gmOpened] then
  begin
    for i := 0 to 8 do
    begin
      X := (512 + 420 * sin(degtorad(FGateAngle + i * 40)));
      Y := (512 - 420 * cos(degtorad(FGateAngle + i * 40)));
      TTextures.QuadGBuffer.DrawLight(tvec3f.Create((X - TGlobals.QuadCameraGate.GetPosition.X) / TGlobals.GWidth, (Y - TGlobals.QuadCameraGate.GetPosition.Y) / TGlobals.GHeight, 0.04), 0.4, $806699FF);
    end;

    TGlobals.QuadCameraGate.Enable;
    FLight := 1.0;
    TShaders.Lights.SetShaderState(True);
    TTextures.FLight.DrawRot(TVec2f.Create(512, 512), FGateAngle, 1.0);
    TShaders.Lights.SetShaderState(False);
    TGlobals.QuadCameraGate.Disable;
  end;

  for i := 0 to FActiveLights.Count - 1 do
  begin
    ind := FIRST + LAST - FActiveLights[i];
    // 66.5 - позиция спрайта - магическое число пока что
    X := (LightsCoord[ind].X * cos(degtorad(FGateAngle)) - LightsCoord[ind].Y * sin(degtorad(FGateAngle))) + 512;
    Y := (LightsCoord[ind].X * sin(degtorad(FGateAngle)) + LightsCoord[ind].Y * cos(degtorad(FGateAngle))) + 512;
    TGlobals.QuadRender.SetBlendMode(qbmNone);
    TTextures.QuadGBuffer.DrawLight(tvec3f.Create((X - TGlobals.QuadCameraGate.GetPosition.X) / TGlobals.GWidth, (Y - TGlobals.QuadCameraGate.GetPosition.Y) / TGlobals.GHeight, 0.001), 0.15, $88FFFFFF);
  end;

  TGlobals.QuadCameraGate.Enable;
  for i := 0 to FActiveLights.Count - 1 do
  begin
    FLight := FActiveLights[i] / 255;
    TShaders.Lights.SetShaderState(True);
    TTextures.FLight.DrawRot(TVec2f.Create(512, 512), FGateAngle, 1.0);
    TShaders.Lights.SetShaderState(False);
  end;
  TGlobals.QuadCameraGate.Disable;
end;

procedure TGameModeStarGate.Process(const Delta: Double);
var
  ang: Single;
begin
  FTime := FTime + Delta;
  FLight := FLight + Delta / 10;
  if FLight > 1 then
    FLight := 219 / 255;

  if FGateMode = gmDialing then
  begin
    if (FNextPoint = 0) and (FSleepTimer <= 0) then
    begin
      repeat
        FNextPoint := random(LAST - FIRST) + FIRST;
        ang := LightsCoord[FNextPoint].Angle;
      until not FActiveLights.Contains(FNextPoint) and (abs(FLastChevron - FNextPoint) > 9);
      FLastChevron := FNextPoint;
      FTargetAngle := ang + 90;

      if FTargetAngle > 360 then
        FTargetAngle := FTargetAngle - 360;

      if FTargetAngle < -360 then
        FTargetAngle := FTargetAngle + 360;

      FRollSound := TSounds.Play(TSounds.Gate.Roll);
    end;

    if (abs(FTargetAngle - FGateAngle) < 1) and (FSleepTimer <= 0) then
    begin
      FActiveLights.Add(FNextPoint);
      if (FActiveLights.Count = 7) and (FSleepTimer <= 0) then
      begin
        FGateMode := gmEstablish;
        TSounds.Play(TSounds.Gate.Open);
      end
      else
      begin
        FNextPoint := 0;
        FSleepTimer := 1.0;
      end;

      TSounds.Stop(FRollSound);
      TSounds.Play(TSounds.Gate.Chevron);
    end
    else
    begin
      if FSleepTimer > 0 then
        FSleepTimer := FSleepTimer - Delta
      else
      begin
        if FTargetAngle > FGateAngle then
          FGateAngle := FGateAngle + Delta * 100
        else
          FGateAngle := FGateAngle - Delta * 100;
      end;

      if FGateAngle > 360 then
        FGateAngle := FGateAngle - 360;

      if FGateAngle < -360 then
        FGateAngle := FGateAngle + 360;
    end;
  end;

  if FGateMode = gmEstablish then
  begin
    if FGateOpenTime < 1.5 then
      FGateOpenTime := FGateOpenTime + Delta * 2.0
    else
      FGateMode := gmKawoosh;
  end;

  if (FStatus = gmsIntro) and (FGateMode = gmOpened) then
  begin
    TGameOverlay.SetEnabled(True);
    if TGameOverlay.GetDialogCount = 0 then
      TGlobals.GameModeManager.SetActiveGameMode(TGlobals.GameModes.Computer);
  end;

  if TGlobals.QuadInput.IsKeyPress(ord('X')) then
    FGateMode := gmOpened;

  if TGlobals.QuadInput.IsKeyPress(VK_SPACE) or (TGlobals.QuadInput.IsMouseClick(mbLeft) and TCollision.PointCircle(TGlobals.QuadInput.GetMousePosition, T2DCircle.Create(TVec2f.Create(512, 550), 350))) then
  begin
    if (FStatus = gmsIntro) and (FGateMode = gmOff) then
      begin
        FGateMode := gmInit;
        FSleepTimer := 1.2;
        TSounds.Play(TSounds.Gate.BeginRoll);
      end;

    if (FStatus = gmsGo) and (FGateMode = gmOpened) then
      TGlobals.GameModeManager.SetActiveGameMode(TGlobals.GameModes.FeesSquad);
  end;

  if FGateMode = gmInit then
  begin
    FSleepTimer := FSleepTimer - Delta;

    if FSleepTimer <= 0 then
      FGateMode := gmDialing;
  end;

  if FGateMode = gmKawoosh then
  begin
    if FKawooshFrame < 29 then
      FKawooshFrame := FKawooshFrame + Delta * 20
    else
      FGateMode := gmOpened;
  end;
end;

destructor TGameModeStarGate.Destroy;
begin
  FActiveLights.Free;
  inherited;
end;

procedure TGameModeStarGate.Draw;
var
  i: Integer;
  color: Byte;
begin
  TGlobals.QuadRender.SetBlendMode(qbmNone);
  TTextures.FRoom.DrawRot(TVec2f.Create(535, 490), 0, 1.635);

  if FGateMode in [gmKawoosh, gmEstablish] then
  begin
    TGlobals.QuadRender.SetBlendMode(qbmSrcAlphaAdd);
    color := max(205 - Trunc(FGateOpenTime * 120), 0);
    TGlobals.QuadRender.Rectangle(TVec2f.Zero, TVec2f.Create(TGlobals.GWidth, TGlobals.GHeight), color shl 24 + $FFFFFF);


    TGlobals.QuadRender.SetBlendMode(qbmSrcAlpha);
    TShaders.GateOpen.SetShaderState(True);
    TTextures.GateOpen.DrawRot(TVec2f.Create(512, 512), 0, 1.7, $FFDDEEFF);
    TShaders.GateOpen.SetShaderState(False);
  end;

  DrawGBuffer;

  TTextures.QuadGBuffer.DiffuseMap.Draw(TVec2f.Zero, $FF111111);

  TGlobals.QuadCameraGate.Enable;

  TGlobals.QuadRender.SetBlendMode(qbmMul);
  TTextures.FOcclusion.DrawRot(TVec2f.Create(512, 512), FGateAngle, 1.0);

  TGlobals.QuadRender.SetBlendMode(qbmAdd);
  TGlobals.QuadCameraGate.Disable;

  DrawLights;

  TGlobals.QuadCameraGate.Enable;
  TGlobals.QuadRender.SetBlendMode(qbmSrcAlpha);

  if FGateMode = gmKawoosh then
  begin
    TTextures.Kawoosh.DrawRotFrame(TVec2f.Create(512, 550), 0, Animate(2, 9.0, FKawooshFrame / 20), Trunc(FKawooshFrame));
    TTextures.Kawoosh.DrawRotFrame(TVec2f.Create(512, 550), 10, Animate(2, 8.0, FKawooshFrame / 20), Trunc(FKawooshFrame));
  end;

  TGlobals.QuadCameraGate.Disable;
end;

procedure TGameModeStarGate.DrawGBuffer;
begin
  TGlobals.QuadCameraGate.Enable;
  TGlobals.QuadRender.RenderToGBuffer(True, TTextures.QuadGBuffer);

  TGlobals.QuadRender.Clear($00333333);

  TGlobals.QuadRender.SetBlendMode(qbmSrcAlpha);

  if (FGateMode = gmOpened) or ((FGateMode = gmKawoosh) and (FKawooshFrame > 15)) then
    TTextures.FEvent.DrawRot(TVec2f.Create(512, 512), 0, 1.5, $88AACCFF);

  TGlobals.QuadRender.SetBlendMode(qbmSrcAlpha);
  TTextures.FGate.DrawRot(TVec2f.Create(512, 512), FGateAngle, 1.0);

  TGlobals.QuadRender.RenderToGBuffer(False);
  TGlobals.QuadCameraGate.Disable;
end;

end.
