unit UGameScene;

interface

uses
  SysUtils,
  Squall,
  JEN_Header,
  JEN_Math,
  Main,
  UDirector,
  USpriteSheet,
  UPlayer,
  UWeapon,
  UEnemy,
  UCollide;

const
  CLOUD_COUNT = 24;

type
  TGameStatus = (gsGame, gsOver, gsWin);
  TGameScene = class(TScene)
    FSample         : LongInt;
    FChannel        : LongInt;
    FVolume         : Single;
    FStatus         : TGameStatus;
    FBackGround     : ITexture;
    FNoiseShader    : IShaderProgram;
    FTimeUniform    : IShaderUniform;
    FAbstractShader : IShaderProgram;
    FNoise          : ITexture;
    FFrame          : ITextureFrame;
    FLife           : ITexture;
    FLifeOffset     : Single;
    FAbstract       : TSpriteSheet;
    FClouds         : array[0..CLOUD_COUNT] of TVec3f;
    FBonusTex       : array[TBonusType] of ITexture;
    FAnonce         : string;
    FAnonceTime     : LongInt;

    FBonus : array[1..5] of record
      BonusType : TBonusType;
      Pos       : TVec2f;
    end;

    FScoreLabel : array[1..120] of record
      CreateTime : LongInt;
      Count      : LongInt;
      Pos        : TVec2f;
      Dir        : TVec2f;
    end;
    function GetName: String; override;
    procedure Load; override;
    procedure UnLoad; override;
    procedure Update(dt: LongInt); override;
    procedure Draw; override;
    procedure AddScore(Count: LongInt; Pos: TVec2f);
    procedure AddBonus(Pos: TVec2f);
    procedure Anonce(const Text: string);
    procedure Fail;
    procedure Win;
  end;


implementation


function TGameScene.GetName: String;
begin
  Result := 'Game';
end;

procedure TGameScene.Load;
var
  i: integer;
  sr : IShaderResource;
begin
  FVolume := 0;
  FSample := SQUALL_Sample_LoadFile('Media\DST-AngryMod.mp3', 1, 0);
  FChannel := SQUALL_Sample_Play(FSample, 1, 0, 1);
  SQUALL_Channel_SetVolume(FChannel, 0);

  ResMan.Load('Media\Heals.dds', FBonusTex[btHeals]);
  ResMan.Load('Media\Power.dds', FBonusTex[btPowerUp]);
  ResMan.Load('Media\Super.dds', FBonusTex[btUtimate]);

  FAbstract := TSpriteSheet.Create('Media\Abstract.plist');
  ResMan.Load('Media\BackGround.dds', FBackGround);
  ResMan.Load('Media\Noise.dds', FNoise);
  FNoise.Clamp := false;

  ResMan.Load('Media\Life.dds', FLife);
  ResMan.Load('Media\NoiseShader.xml', sr);
  sr.Compile(FNoiseShader);
  FTimeUniform := FNoiseShader.Uniform('Time', utVec1);

  ResMan.Load('Media\AbstractShader.xml', sr);
  sr.Compile(FAbstractShader);

  FFrame := TTextureFrame.Create('', FNoise, Rectf(0.0,0.0,Game.ScreenWidth/128.0, Game.ScreenHeight/128.0));

  Input.SetCapture(True);
  Display.Cursor := False;

  FStatus := gsGame;
  FAnonce := '';
  Game.Score := 0;
  Game.GameTime := 0;
  Game.Player := TPlayer.Create;
  Game.WeaponEng := TWeaponEng.Create;
  Game.EnemyEng := TEnemyEng.Create;

   for I := 1 to High(FScoreLabel) do
    FScoreLabel[i].Count := -1;

  for i := 0 to CLOUD_COUNT-1 do
    FClouds[i] := Vec3f(Random, Random, Random);

  for I := 1 to High(FBonus) do
    FBonus[i].BonusType := btNone;
  Anonce('Start');
end;

procedure TGameScene.UnLoad;
begin
  SQUALL_Sample_Stop(FSample);
  SQUALL_Sample_Unload(FSample);
  FBackGround := nil;
  FLife := nil;
  FAbstract.Free;
  Game.Cash := Game.Cash + Round(Game.Score/10);
  Game.Player.Free;
  Game.WeaponEng.Free;
  Game.EnemyEng.Free;

  Input.SetCapture(False);
  Display.Cursor := True;
end;

procedure TGameScene.Update;
var
  i: Integer;
begin
  FVolume := Min(FVolume + dt*0.0005, 0.9);
  SQUALL_Channel_SetVolume(FChannel, Round(FVolume*100));
  if Input.Hit[ikEsc] then
    Game.SceneDirector.LoadScene('Menu');
    
  if Input.Hit[ikMouseL] and (FStatus = gsOver) then
    Game.SceneDirector.LoadScene('Menu');
  
  Game.Player.OnUpdate(dt);
  Game.WeaponEng.OnUpdate(dt);
  Game.EnemyEng.OnUpdate(dt);
  Inc(Game.GameTime, dt);

  for I := 1 to High(FScoreLabel) do
  with FScoreLabel[i] do
    if (Count <> -1) then
    begin
      Dir := Dir -  Dir * dt * 0.008;
      Pos := Pos + (Dir + Vec2f(0, 1)) * dt*0.6 ;

      if CircleVsCircle(Game.Player.Pos, Game.Player.Size/2, Pos, 16) then
      begin
        Game.AddScore(Count);
        Count := -1;
      end;

      if Pos.y > Game.ScreenHeight + 40 then
        Count := -1;
    end;



  for I := 1 to High(FBonus) do
  with FBonus[i] do
    if BonusType <> btNone then
    begin
      Pos.y := Pos.y + 0.05*dt;

      if Pos.y>Game.ScreenHeight+32 then
        BonusType := btNone;

      if FStatus <> gsOver then
      if CircleVsCircle(Game.Player.Pos, Game.Player.Size/2, Pos, 16) then
      begin
        Game.Player.GetBonus(BonusType);
        BonusType := btNone;
      end;

    end;

  for i := 0 to CLOUD_COUNT-1 do
  begin
    FClouds[i].y := FClouds[i].y + 0.00003 * dt * (1-FClouds[i].z);
    if FClouds[i].y > 1 then
      FClouds[i] := Vec3f(Random, 0, Random);
  end;
  FLifeOffset := FLifeOffset + (Game.Player.RestartCount*40*0.5 - FLifeOffset) * 0.003 * dt ;
end;

procedure TGameScene.Draw;
var
  str   : string;
  time  : Single;
  i     : Integer;
  s     : Single;
  color : TVec3f;
  p     : TVec2f;
begin
  Render2d.DrawSprite(FBackGround.Frame, 0, 0, Game.ScreenWidth, Game.ScreenHeight, clWhite);
  time := Helpers.Time*0.04;

  Render.BlendType := btNormal;
  Render2d.BatchBegin;
  Render2d.BeginDraw(FAbstractShader, FAbstract.FrameI[Random(0)]);
  for i := 0 to CLOUD_COUNT-1 do
  begin
    s := 100 + 1000 * FClouds[i].z;
    color := Vec3f(0.2, 0.4, 0.7)  * (1-FClouds[i].z)*2.0;
    Render2d.SetData(Vec4f(color,(FClouds[i].z+0.07)*0.3), Vec4f(1, 1, 1, Min( 1.1-FClouds[i].z, 1.0) * 0.1 ), clWhite, clWhite);
    Render2d.DrawQuad(FClouds[i].x * (Game.ScreenWidth + s) - s, FClouds[i].y * (Game.ScreenHeight+s) - s, s, s, 0);
  end;
  Render2d.EndDraw;
  Render2d.BatchEnd;


  Render.BlendType := btMult;
  Render2d.BeginDraw(FNoiseShader, FFrame);
  FTimeUniform.Value(time);
  Render2d.DrawQuad(0, 0, Game.ScreenWidth, Game.ScreenHeight, 0);
  Render2d.EndDraw;

  Render.BlendType := btAdd;

  if Helpers.Time - FAnonceTime < 3000 then
  begin
    Game.Font.Scale := 0.3;
    Game.Font.EdgeSmooth := 1;
    Game.Font.OutlineSize := 0;
    Game.Font.Color := Vec4f(1, 1, 1, 1 - (Helpers.Time - FAnonceTime)/3000 );
    Game.Font.Print(PWideChar(FAnonce), (Game.ScreenWidth - Game.Font.GetTextWidth(PWideChar(FAnonce))) * 0.5, Game.ScreenHeight  * 0.5 -100);
  end;


  for I := 1 to High(FScoreLabel) do
  with FScoreLabel[i] do
    if (Count <> -1) then
    begin
      Game.Font.Scale := 0.03;
      Game.Font.EdgeSmooth := 1;
      Game.Font.OutlineSize := 0;
      Game.Font.Color := Vec4f(0.9,0.9,0.3, 0.4);
      str := IntToStr(Count) + '$';
      P := Pos - Vec2f(Game.Font.GetTextWidth(PWideChar(str))/2, 0);
      Game.Font.Print(PWideChar(str), P.x + Game.OffSet.x, P.y+ Game.OffSet.y);
    end;

  Render2d.BatchBegin;
  for I := 1 to High(FBonus) do
  with FBonus[i] do
    if FBonus[i].BonusType <> btNone then
    begin
      Render2d.DrawSprite(FBonusTex[FBonus[i].BonusType].Frame, FBonus[i].Pos.x - 16 + Game.OffSet.x, FBonus[i].Pos.y - 16 + Game.OffSet.y, 32, 32, clWhite);
    end;
  Render2d.BatchEnd;

  Render.BlendType := btNormal;


  Game.Player.OnRender;
  Game.WeaponEng.OnRender;
  Game.EnemyEng.OnRender;

  str := IntToStr(Game.Score);
  Game.Font.Scale := 0.08;
  Game.Font.Color := Vec4f(0.5,0.8,1.0, 1.0);
  Game.Font.Print(PWideChar(str), (Game.ScreenWidth - Game.Font.GetTextWidth(PWideChar(str))) * 0.5, 0);

  Render2d.BatchBegin;
  for I := 0 to Game.Player.RestartCount-1 do
    Render2d.DrawSprite(FLife.Frame, Game.ScreenWidth * 0.5 + i*40 - FLifeOffset, Game.ScreenHeight - 64, 32, 32, clWhite);

  Render2d.BatchEnd;

end;

procedure TGameScene.AddScore(Count: LongInt; Pos: TVec2f);
var
  i : Integer;
begin
//  Inc(FScores, Count);
  for I := 1 to High(FScoreLabel) do
    if FScoreLabel[i].Count = -1 then
    begin
      FScoreLabel[i].Pos := Pos;
      FScoreLabel[i].Count := Count;
      FScoreLabel[i].CreateTime := Helpers.Time;
      FScoreLabel[i].Dir := Vec2f(Random*2 - 1, -Random).Normal * 3.0;
      Exit;
    end;
end;

procedure TGameScene.AddBonus(Pos: TVec2f);
var
  i : Integer;
begin
  for I := 1 to High(FBonus) do
    if FBonus[i].BonusType = btNone then
    begin
      FBonus[i].Pos := Pos;
      case Random(83) of
      0..60: FBonus[i].BonusType := btPowerUp;
      61..85: FBonus[i].BonusType := btHeals;
      86..95: FBonus[i].BonusType := btUtimate;
      end;
      Exit;
    end;
end;

procedure TGameScene.Anonce(const Text: string);
begin
  FAnonceTime := Helpers.Time;
  FAnonce := Text;
end;

procedure TGameScene.Fail;
begin
  if FStatus <> gsGame then
    Exit;

  FStatus := gsOver;
  Anonce('Game over');
end;

procedure TGameScene.Win;
begin
  if FStatus <> gsGame then
    Exit;

  FStatus := gsWin;
  Game.SaveHighScore;
  Anonce('You won!');
end;


end.
