unit UGame;

interface

uses
  Squall,
  JEN_Header,
  JEN_Math,
  UPlayer,
  UWeapon,
  UEnemy;

const
  WorldSize = 1120;

type
  TGameStatus = (gsGame, gsOver, gsWin);
  TGame = class(TInterfacedObject, IGame)
  constructor Create;
  private
    FChannel      : LongInt;
    FStatus       : TGameStatus;
    FBackground   : ITexture;
    FScrMul       : ITexture;
    FLifeTex      : ITexture;
    FBombTex      : ITexture;
    FBonusTex     : array[TBonusType] of ITexture;
    FScores       : Integer;
    FTimeScores   : Integer;
    FFont         : IFont;

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

    FScoreLabel : array[1..60] of record
      CreateTime : LongInt;
      Count      : LongInt;
      Pos        : TVec2f;
    end;
  public
    Camera        : TVec2f;
    Player        : TPlayer;
    EnemyEng      : TEnemyEng;
    WeaponEng     : TWeaponEng;
    GameStartTime : LongInt;
    GameTime      : LongInt;
    procedure LoadContent; stdcall;
    procedure AddScore(Count: LongInt; Pos: TVec2f);
    procedure AddBonus(Pos: TVec2f);
    procedure OnUpdate(dt: LongInt); stdcall;
    procedure OnRender; stdcall;
    procedure Close; stdcall;
    procedure Fail;
    procedure Boom;
    procedure Win;
    procedure Restart;
  end;

implementation

uses
  UMain;

constructor TGame.Create;
begin
end;

procedure TGame.LoadContent;
var
  Res : IShaderResource;
begin
  FChannel := SQUALL_Sample_Play(SQUALL_Sample_LoadFile('Media\Mefistolhl - Zybex (Mefi Remix).mp3', 1, 0), 1, 0, 0);

  ResMan.Load('Media\Background.dds', FBackground);
  ResMan.Load('Media\Scr.dds', FScrMul);
  ResMan.Load('Media\ArialFont.jfi', FFont);
  ResMan.Load('Media\Life.dds', FLifeTex);
  ResMan.Load('Media\Bomb.dds', FBombTex);

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

  Player := TPlayer.Create;
  EnemyEng := TEnemyEng.Create;
  WeaponEng := TWeaponEng.Create;
  Restart;
end;

procedure TGame.Restart;
var
  i : Integer;
begin
  SQUALL_Channel_SetPlayPositionMs(FChannel, 0);
  SQUALL_Channel_Start(FChannel);

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

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

  Player.Restart;
  EnemyEng.Restart;
  WeaponEng.Restart;

  GameStartTime := Utils.Time;
  FStatus       := gsGame;
  FScores       := 0;
  FTimeScores   := 0;
  Camera        := Vec2f((WorldSize-Screen_Width)/2, 0);
  GameTime      := 0;
end;

procedure TGame.Fail;
begin
  if FStatus = gsGame then
  FStatus := gsOver;
end;

procedure TGame.Win;
begin
  if FStatus = gsGame then
  FStatus := gsWin;
end;

procedure TGame.Boom;
var
  i : Integer;
begin
  with EnemyEng do
  for i := 1 to High(Enemies) do
    if (Enemies[i].Kind <> ekNone) and (Enemies[i].Dead <> True) then
      Hit(i, wtBFG);
end;

procedure TGame.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 := Utils.Time;
      Exit;
    end;
end;

procedure TGame.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(100) of
      0..60: FBonus[i].BonusType := btPowerUp;
      61..85: FBonus[i].BonusType := btHeals;
      86..95: FBonus[i].BonusType := btUtimate;
      end;

      Exit;
    end;
end;

procedure TGame.OnUpdate(dt: LongInt);
var
  i, j : Integer;
  Scores : Integer;
  Pos1  :  TVec2f;
  Pos2  :  TVec2f;
begin
  GameTime := Utils.Time - GameStartTime;
  if FStatus = gsGame then
  begin
    FTimeScores := GameTime div 30;
  end;

  if FStatus = gsGame then
  Player.OnUpdate(dt);
  EnemyEng.OnUpdate(dt);
  WeaponEng.OnUpdate(dt);

  if FStatus = gsGame then
  with EnemyEng do
  for J := 1 to High(Enemies) do
    if (Enemies[j].Kind <> ekNone) and (Enemies[j].Dead <> True) then
      if CircleVsCircle(Player.Pos, Player.Size/2, Enemies[j].Pos, EnemyParam[Enemies[j].Kind].Size/2) then
      begin
      Player.Coll;
      EnemyEng.Coll(j);
      Break;
      end;

  for I := 1 to High(FScoreLabel) do
  with FScoreLabel[i] do
    if (Count <> -1) and (Utils.Time - CreateTime> 2000)then
      Count := -1;

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

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

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

    end;

  Pos1 := Player.Pos- Vec2f(Screen_Width, Screen_Height)*0.5;// +(Vec2f(Input.Mouse.Pos.x, Input.Mouse.Pos.y) - Vec2f(Screen_Width, Screen_Height)*0.5 )*0.2 - Vec2f(Screen_Width, Screen_Height)*0.5;
  Pos1 := Pos1.Min(Vec2f(WorldSize-Screen_Width+Player.Size/2, 0)).Max(Vec2f(-Player.Size/2,0));
  Camera := Camera.Lerp(Pos1, dt/500).Min(Vec2f(Player.Pos.x-Player.Size/2,0)).Max(Vec2f(Player.Pos.x+Player.Size/2-Screen_Width,0));

  if FStatus <> gsGame then
  begin
    if Input.Hit[ikMouseL] then
      Restart;
  end;

 //.Max(Vec2f(Player.Pos).Min(Vec2f(Player.Pos.x+Player.Size/2+Screen_Height,0));
end;

procedure TGame.OnRender;
var
  i,j    : Integer;
  Scores : Integer;
  Move   : Single;
  P      :  TVec2f;
begin
  Render.Clear(True,False,False);

  for I := -1 to Round(WorldSize/256) do
    for j := -1 to Round(Screen_Height/256) do
      Render2d.DrawSprite(FBackground, I*256- Camera.x/2, J*256 +(GameTime div 15) mod 256, 256, 256, Vec4f(0.05, 0.15, 0.05, 1));

  Render.BlendType := btAdd;
   for I := -1 to Round(WorldSize/450) do
    for j := -1 to Round(Screen_Height/450)do
      Render2d.DrawSprite(FBackground, I*450- Camera.x/20+14, J*450 +(GameTime div 25) mod 450, 450,450, Vec4f(0.03, 0.09, 0.03, 1));

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

  Render.BlendType := btMult;
  Render2d.DrawSprite(FScrMul, 0, 0, Screen_Width, Screen_Height, clWhite);

  Render.BlendType := btAdd;
  Player.OnRender;
  EnemyEng.OnRender;
  WeaponEng.OnRender;

  for I := 1 to High(FScoreLabel) do
  with FScoreLabel[i] do
    if (Count <> -1) then
    begin
      FFont.Scale := 0.02;
      FFont.EdgeSmooth := 2;
      FFont.OutlineSize := 0;
      FFont.Color := Vec4f(0.3,0.9,0.3, Min(1,(2000 - (Utils.Time - CreateTime))/1000) );
      P := Pos - Vec2f(Camera.x+FFont.GetTextWidth(Utils.IntToStr(Count))/2, (Utils.Time - CreateTime)/2000*50);
      FFont.Print(Utils.IntToStr(Count), P.x, P.y);
    end;

  for I := 0 to Player.RestartCount-1 do
    Render2d.DrawSprite(FLifeTex, I*32 + 10, 80, 24, 24, Vec4f(1, 0.2, 0.2, 1));

  for I := 0 to Player.UltimateCount-1 do
    Render2d.DrawSprite(FBombTex, I*32 + 100, 80, 24, 24, Vec4f(0.2, 0.2, 1, 1));

  Render.BlendType := btNormal;
  FFont.Scale := 0.04;
  FFont.EdgeSmooth := 1;
  FFont.SetGradColors(Vec4f(0.2,0.7,0.2,1),Vec4f(0.1,0.2,0.1,1));
  FFont.OutlineColor := Vec3f(0.08,0.18,0.08);
  FFont.OutlineSize := 1.5;
  FFont.Print('Scores:'+Utils.IntToStr(FTimeScores + FScores), 0, 0);
  FFont.OutlineSize := 1.9;
  FFont.Scale := 0.08;
  FFont.Print('Health:'+Utils.IntToStr(Round(Player.Health)), 0, 20);

  if FStatus = gsOver then
  begin
    FFont.OutlineColor := Vec3f(0.17,0.00,0.0);
    FFont.SetGradColors(Vec4f(1,0.1,0.1,1),Vec4f(0.3,0.0,0.0,1));
    FFont.Scale := 0.07;
    FFont.OutlineSize := 1.9;
    FFont.Print('Click to continue', (Screen_Width - FFont.GetTextWidth('Click to continue'))/2, 420);

    FFont.OutlineSize := 3;
    FFont.Scale := 0.2;
    FFont.Print('Game over', (Screen_Width - FFont.GetTextWidth('Game over'))/2, 300);
  end;

  if FStatus = gsWin then
  begin
    FFont.OutlineColor := Vec3f(0.58,0.2,0.0);
    FFont.SetGradColors(Vec4f(1,0.8,0.4,1),Vec4f(0.7,0.4,0.0,1));
    FFont.Scale := 0.07;
    FFont.OutlineSize := 1.5;
    FFont.Print('Level 1 complete', (Screen_Width - FFont.GetTextWidth('Level 1 complete'))/2, 270);
    FFont.Print('А больше уровней и нет', (Screen_Width - FFont.GetTextWidth('А больше уровней и нет'))/2, 420);

    FFont.OutlineSize := 3;
    FFont.Scale := 0.2;
    FFont.Print('Scores:'+Utils.IntToStr(FTimeScores+FScores), (Screen_Width - FFont.GetTextWidth('Scores:'+Utils.IntToStr(FTimeScores+FScores)))/2, 300);
  end;


            {
  for I := 0 to Round(WorldSize/64) do
    for j := 0 to Round(WorldSize/64) do
      Render2d.DrawSprite(FGroundTex, I*64 -Camera.x, J*64-Camera.y, 64, 64, clWhite);

  Worm.OnRenderTrace;

  for I := 1 to RockCount do
    Render2d.DrawSprite(FRockTex, Rock[I].Pos.x-Camera.x-10, Rock[I].Pos.y-Camera.y-10, 20, 20, clWhite, Rock[i].Angle);

  for I := 1 to FoodCount do
    Render2d.DrawSprite(FFoodTex[Food[i].Kind], Food[I].Pos.x-Camera.x-8, Food[I].Pos.y-Camera.y-8, 16, 16, clWhite);

  Worm.OnRender;
              {
  Scores := Round(FCounter);
  for I := 5 downto 0 do
  begin
    Move := Frac(FCounter / Pow(10, 6-I));
    Scores := Scores div 10;
    Render2d.DrawSpriteAdv(FCouterShader, FCounterTex, nil, nil, I*32, 0, 32, 26,Vec4f((Scores mod 10)/10 + SmootherStep(0.9, 1.0, Move)/10, 0, 0 ,0), clWhite, clWhite, clWhite);
  end;
                    }
 // Render2d.Print(FFont, 'qts'+Utils.IntToStr(Round(FCounter)), 0, 0 , 1 , clWhite );
 { if FStatus = gsOver then
    Render2d.DrawSprite(FGameOverTex, 256, 320, 512, 128, clWhite);

      FFont.Scale:=0.03;
  FFont.Print('|!\{$Йqwer', 0, 0  );

  FFont.Scale:=0.3;
  FFont.Print('|!\{$Йqwer', 0,300 );     }

end;

procedure TGame.Close;
begin
  Player.Free;
  EnemyEng.Free;
  WeaponEng.Free;
  Display  := nil;
  Utils    := nil;
  Input    := nil;
  Render   := nil;
  Render2d := nil;
  ResMan   := nil;
end;

end.
