unit UEnemy;

interface

uses
  Squall,
  JEN_Header,
  JEN_Math,
  UWeapon;

type
  TEnemyKind = (ekNone, ekTriangle, ekStupidShip, ekCube, ekCruiser, ekFighter, ekBoss);
  TSpawnType = (stAll, stMidlle, st2Lines, stLeft, stRight, stCenter);
  TConfig    = (coNone, coBlock, coLine,  coFlight, coCircle);

const
  EnemyParam : array[TEnemyKind] of record
    Life  : Single;
    Size  : Single;
    Speed : Single;
    WeaponType: TWeaponType;
    BonusChance : Single;
    Scores: LongInt;
  end = (
         (Life: 0; Size: 0; Speed: 0; WeaponType: wtNone; Scores: 0 ),
         (Life: 40; Size: 32; Speed: 1; WeaponType: wtEnemyLaser1; Scores: 100),
         (Life: 60; Size: 40; Speed: 1; WeaponType: wtNone; Scores: 150),
         (Life: 140; Size: 32; Speed: 0.07; WeaponType: wtEnemyPlasma; BonusChance: 0.7; Scores: 500),
         (Life: 1000; Size: 42; Speed: 0.01; WeaponType: wtEnemyLaserGun2; BonusChance: 0.3; Scores: 400),
         (Life: 50; Size: 32; Speed: 0.5; WeaponType: wtEnemyLaserGun; BonusChance: 0.05; Scores: 200),
         (Life: 10000; Size: 128; Speed: 0.2; WeaponType: wtBossGun; BonusChance: 0; Scores: 2000)
        );

const
  SpawnCount = 31;
  EnemySpawn : array[1..SpawnCount] of record
    Kind    : TEnemyKind;
    Spawn   : TSpawnType;
    Config  : TConfig;
    Position: TVec2f;
    Count   : LongInt;
    Time    : LongInt;
    Duration : LongInt;
  end = (//(Kind: ekTriangle; Spawn: stMidlle; Config: coBlock; Position:(x:0; y:0); Count:4; Time: 50; Duration: 500)

          (Kind: ekTriangle; Spawn: stMidlle; Config: coBlock; Position:(x:0; y:0); Count: 20; Time: 500; Duration: 500),

          (Kind: ekTriangle; Spawn: stRight; Config: coBlock; Position:(x:0; y:0); Count: 20; Time: -1; Duration: 500),
          (Kind: ekStupidShip; Spawn: stLeft; Config: coBlock; Position:(x:0; y:100); Count: 10; Time: 0; Duration: 500),

          (Kind: ekTriangle; Spawn: stRight; Config: coBlock; Position:(x:0; y:0); Count: 10; Time: -1; Duration: 500),
          (Kind: ekFighter; Spawn: stAll; Config: coFlight; Position:(x:0; y:0); Count: 20; Time: 50; Duration: 1000),

          (Kind: ekFighter; Spawn: st2Lines; Config: coFlight; Position:(x:0; y:0); Count: 8; Time: -1; Duration: 1000),
          (Kind: ekTriangle; Spawn: stMidlle; Config: coBlock; Position:(x:0; y:0); Count: 10; Time: 1000; Duration: 1000),
          (Kind: ekTriangle; Spawn: stMidlle; Config: coBlock; Position:(x:0; y:50); Count: 10; Time: 2000; Duration: 1000),
          (Kind: ekCruiser; Spawn: stAll; Config: coNone; Count: 2; Time: 2000; Duration: 0),
          (Kind: ekStupidShip; Spawn: stMidlle; Config: coBlock; Position:(x:0; y:100); Count: 10; Time: 3000; Duration: 1000),

          (Kind: ekCruiser; Spawn: st2Lines; Config: coNone; Position:(x:0; y:0); Count: 2; Time: -1; Duration: 0),
          (Kind: ekTriangle; Spawn: stMidlle; Config: coBlock; Position:(x:0; y:0); Count: 10; Time: 1000; Duration: 1000),
          (Kind: ekTriangle; Spawn: stMidlle; Config: coBlock; Position:(x:0; y:50); Count: 10; Time: 2000; Duration: 1000),
          (Kind: ekFighter; Spawn: st2Lines; Config: coFlight; Position:(x:0; y:0); Count: 8; Time: 1500; Duration: 1000),

          (Kind: ekStupidShip; Spawn: stCenter; Config: coCircle; Position:(x:150; y:280); Count: 20; Time: -1; Duration: 300),
          (Kind: ekTriangle; Spawn: stCenter; Config: coCircle; Position:(x:120; y:280); Count: 11; Time: 300; Duration: 300),
          (Kind: ekTriangle; Spawn: stCenter; Config: coCircle; Position:(x:90; y:280); Count: 8; Time: 600; Duration: 300),
          (Kind: ekTriangle; Spawn: stCenter; Config: coCircle; Position:(x:60; y:280); Count: 4; Time: 900; Duration: 300),
          (Kind: ekCruiser; Spawn: stCenter; Config: coCircle; Position:(x:30; y:280); Count: 2; Time: 1200; Duration: 0),

          (Kind: ekFighter; Spawn: stCenter; Config: coFlight; Position:(x:0; y:0); Count: 20; Time: -1; Duration: 5000),
          (Kind: ekTriangle; Spawn: stMidlle; Config: coBlock; Position:(x:0; y:0); Count: 40; Time: 0; Duration: 10000),
          (Kind: ekCruiser; Spawn: stAll; Config: coNone; Count: 1; Time: 5000; Duration: 00),


          (Kind: ekBoss; Spawn: stMidlle; Config: coNone; Count: 1; Time: -1; Duration: 0),
          (Kind: ekTriangle; Spawn: stMidlle; Config: coBlock; Position:(x:0; y:0); Count: 10; Time: 10000; Duration: 1000),
          (Kind: ekFighter; Spawn: st2Lines; Config: coFlight; Position:(x:0; y:0); Count: 5; Time: 20000; Duration: 1000),
          (Kind: ekStupidShip; Spawn: stMidlle; Config: coLine; Position:(x:0; y:300); Count: 7; Time: 25000; Duration: 1000),
          (Kind: ekCruiser; Spawn: stAll; Config: coNone; Count: 1; Time: 25000; Duration: 0),


          (Kind: ekTriangle; Spawn: stMidlle; Config: coBlock; Position:(x:0; y:0); Count: 10; Time: 50000; Duration: 1000),
          (Kind: ekFighter; Spawn: st2Lines; Config: coFlight; Position:(x:0; y:0); Count: 5; Time: 60000; Duration: 1000),
          (Kind: ekStupidShip; Spawn: stMidlle; Config: coLine; Position:(x:0; y:300); Count: 7; Time: 65000; Duration: 1000),
          (Kind: ekCruiser; Spawn: stAll; Config: coNone; Count: 1; Time: 65000; Duration: 0)

        );

type
  TEnemyEng = class
    constructor Create;
    destructor Destoy; overload;
  private
    FSample : LongInt;
    FEmemyTex : array[TEnemyKind] of ITexture;
    FSpawned: array[1..SpawnCount] of LongInt;
    FLastTime   : LongInt;
    FLastSpawnCP: LongInt;
    FCount      : LongInt;
  public
    Enemies : array[1..150] of record
      Weapon     : TWeapon;
      Kind       : TEnemyKind;
      Dead       : Boolean;
      Dir        : TVec2f;
      CorrectPos : TVec2f;
      Pos        : TVec2f;
      Config     : TConfig;
      Angle      : Single;
      CreateTime : LongInt;
      Health     : Single;
      Fade       : Single;
      Data1      : LongInt;
      Rand     : Single;
    end;
    procedure Restart;
    procedure Coll(idx: Integer);
    procedure Hit(idx: Integer; Weapon: TWeaponType);
    procedure OnUpdate(dt : LongInt);
    procedure OnRender;
  end;


implementation

uses
  UGameScene,
  Main;

constructor TEnemyEng.Create;
begin
  FSample := SQUALL_Sample_LoadFile('Media\explosion.wav', 1, 0);
  ResMan.Load('Media\Enemy1.dds', FEmemyTex[ekTriangle]);
  ResMan.Load('Media\Enemy2.dds', FEmemyTex[ekStupidShip]);
  ResMan.Load('Media\Enemy3.dds', FEmemyTex[ekCube]);
  ResMan.Load('Media\Enemy4.dds', FEmemyTex[ekCruiser]);
  ResMan.Load('Media\Enemy5.dds', FEmemyTex[ekFighter]);
  ResMan.Load('Media\Boss.dds', FEmemyTex[ekBoss]);
  Restart;
end;

destructor TEnemyEng.Destoy;
begin
  SQUALL_Sample_Unload(FSample);
end;

procedure TEnemyEng.Restart;
var
  i : Integer;
begin
  for I := 1 to SpawnCount do
    FSpawned[i] := 0;

  for I := 1 to High(Enemies) do
    Enemies[i].Kind := ekNone;

  FLastTime := 0;
  FLastSpawnCP := 1;
end;

procedure TEnemyEng.Hit(idx: Integer; Weapon: TWeaponType);
begin
  Enemies[idx].Health := Enemies[idx].Health - WeaponParam[Weapon].Power;
  if Enemies[idx].Health <= 0 then
  begin
    TGameScene(Game.GameScene).AddScore(EnemyParam[Enemies[idx].Kind].Scores, Enemies[idx].Pos);
    if Random < EnemyParam[Enemies[idx].Kind].BonusChance then
      TGameScene(Game.GameScene).AddBonus(Enemies[idx].Pos);
    Enemies[idx].Dead := True;
    SQUALL_Channel_SetVolume(SQUALL_Sample_Play(FSample, 0, 0, 1), 100);
  end;
end;

procedure TEnemyEng.Coll(idx: Integer);
begin
  Enemies[idx].Dead := True;
  SQUALL_Channel_SetVolume(SQUALL_Sample_Play(FSample, 0, 0, 1), 100);
end;

procedure TEnemyEng.OnUpdate(dt : LongInt);
var
  i,j : Integer;
  Idx : Integer;
  SpawnNeed : Integer;
  spawned : Integer;
  D : TVec2f;
  Rot : Single;
  Win : Boolean;
  AllSpawned: Boolean;
  tCorrectPos : TVec2f;

  procedure AddEnemy(wPos, wCorrectPos, wDir: TVec2f; wKind: TEnemyKind; wConfig: TConfig);
  var
    j : Integer;
  begin
    for j := 1 to High(Enemies) do
    with Enemies[j] do
      if Kind = ekNone then
      begin
        Weapon.SetWeapon(EnemyParam[wKind].WeaponType);
        Weapon.ShootTime := Helpers.Time - Round(WeaponParam[EnemyParam[wKind].WeaponType].ShootDelay * Random);
        Kind := wKind;
        Pos  := wPos;
        Dir  := wDir;
        CorrectPos := wCorrectPos;
        Config := wConfig;
        Fade := 1;
        Dead := False;
        Angle := 0;
        Rand := Random;
        Health := EnemyParam[wKind].Life;
        CreateTime := Helpers.Time;
        Exit;
      end;
  end;
begin

  for I := FLastSpawnCP to SpawnCount do
  with EnemySpawn[i] do
  begin
    if (Time = -1) and (i > FLastSpawnCP) then
    begin
      if (FCount = 0) then
      begin
        AllSpawned := True;
        for J := 1 to i-1 do
          if EnemySpawn[j].Count <> FSpawned[j] then
          begin
            AllSpawned := False;
            Break;
          end;

        if AllSpawned then
        begin
          FLastTime := Game.GameTime;
          FLastSpawnCP := i;
          if i = 15 then
            TGameScene(Game.GameScene).Anonce('Stage 2');
        end else
          break;

      end else
        break;
    end;


    SpawnNeed := Trunc( Min(( Game.GameTime - FLastTime - Time)/Duration * Count, Count));
    spawned := FSpawned[i];
    for J := 1 to SpawnNeed - FSpawned[i] do
    begin
      Idx := spawned + J-1;
      case Config of
        coBlock:
         tCorrectPos:= Vec2f((Idx mod 10 - 4.5) * 0.1, Idx div 10 + 2) * Vec2f(WorldSize*0.7, EnemyParam[Kind].Size) + Vec2f(WorldSize*0.5, 0) + Position;
        coLine:
          tCorrectPos:= Vec2f((Idx - (Count-1)*0.5)/Count, 0) * Vec2f(WorldSize, 0) + Vec2f(WorldSize*0.5, 0) + Position;
        coCircle:
         tCorrectPos:= Vec2f(0,Position.x).Rotate(Idx/Count * 2 * PI) + Vec2f(WorldSize, Game.ScreenHeight - Position.y) * 0.5;
      end;

      case Spawn of
      stAll:
          AddEnemy(Vec2f(random*WorldSize, -EnemyParam[Kind].Size ), tCorrectPos, Vec2f(0,1), Kind, Config);
      st2Lines:
      begin
          AddEnemy(Vec2f(WorldSize/4, -J*(EnemyParam[Kind].Size +10)), tCorrectPos, Vec2f(0,1), Kind, Config);
          AddEnemy(Vec2f(WorldSize/4*3, -J*(EnemyParam[Kind].Size +10)), tCorrectPos, Vec2f(0,1), Kind, Config);
      end;
      stMidlle:
          AddEnemy(Vec2f(WorldSize/2, -J*(EnemyParam[Kind].Size +10)), tCorrectPos, Vec2f(0,1), Kind, Config);
      stLeft:
        AddEnemy(Vec2f(0, -(EnemyParam[Kind].Size )), tCorrectPos, Vec2f(0,1), Kind, Config);
      stRight:
        AddEnemy(Vec2f(WorldSize, -(EnemyParam[Kind].Size)), tCorrectPos, Vec2f(0,1), Kind, Config);
      stCenter:
        AddEnemy(Vec2f(WorldSize*0.5, (Game.ScreenHeight- Position.y)*0.5), tCorrectPos, Vec2f(0,1), Kind, Config);
      end;
      Inc(FSpawned[i]);
    end;


  end;

  FCount := 0;
  for I := 1 to High(Enemies) do
  with Enemies[i], EnemyParam[Kind] do
    if Kind <> ekNone then
    begin
      Inc(FCount);

      case Kind of
        ekTriangle:
        begin
         Angle := Angle + dt * 0.5;
        end;

        ekFighter:
        begin

        end;
        ekBoss:
        begin
          if (Pos.y > 150) and (Dir.y = 1) then
          begin
            Dir := Vec2f(1, 0);
            Data1 := Helpers.Time;
          end;

          if (Helpers.Time-Data1>3000) and (Dir.y = 0) then
          begin
            Dir := Vec2f(Random(2)*2-1, 0)*(2+Random(3));
            Data1 := Helpers.Time;
          end;


          if Pos.x < 200 then
            Dir := Vec2f(1, 0);

          if Pos.x > WorldSize -200 then
            Dir := Vec2f(-1, 0);
        end;
      end;

      case Config of
        coNone: ;
        coBlock, coCircle, coLine:
        begin
          Dir := CorrectPos - Pos;
        end;

        coFlight:
        begin
          D := (Vec2f(WorldSize, Game.ScreenHeight - 500)*0.5 - Pos);
          Rot:= Dir.Angle(D);
          Dir := Dir.Rotate(Sign(Rot)*Min(abs(Rot),dt/900)).Normal;

          Dir := Dir.Lerp(D.Normal, dt/500*(d.Length/100)).Normal;
          Angle := -Dir.Angle(Vec2f(0,1)) * rad2deg ;
        end;
      end;

      Weapon.Shoot(Pos, (Game.Player.Pos - Pos).Normal, Dir);
      if Dead then
      begin
        Fade := Fade - 0.004* dt;
        if Fade <=0 then
          Kind := ekNone;
      end;

      if (Config = coFlight) or (Dir.Length > Speed * dt * 0.5) then
        Pos := Pos + Dir.Normal * Speed * dt;

      if Pos.y > Game.ScreenHeight + Size then
        Kind := ekNone;

    end;

    if (EnemySpawn[SpawnCount].Count = FSpawned[SpawnCount]) then
    begin
      Win := True;
      for I := 1 to High(Enemies) do
        if Enemies[i].Kind <> ekNone then
          Win := False;
      if Win then
        TGameScene(Game.GameScene).Win;
    end;

end;

procedure TEnemyEng.OnRender;
var
  i : Integer;
begin
  Render2d.BatchBegin;
  for I := 1 to High(Enemies) do
    with Enemies[i], EnemyParam[Kind] do
      if Kind <> ekNone then
        Render2d.DrawSprite(FEmemyTex[Kind].Frame, Pos.x -Size/2 + Game.OffSet.x + Sin(Helpers.time*0.002+rand*PI*2)*6.0, Pos.y-Size/2 + Cos(Helpers.time*0.001+rand*PI*2)*6.0 + Game.OffSet.y, Size, Size, clWhite*Fade, Angle);
  Render2d.BatchEnd;
end;

end.

