unit BaseEnemy;

interface

uses
  _Geometry,   
  Frost.Forms, 
  Frost.Renderers,
  Frost.Engine,
  Frost.GL,
  Frost.Utils,
  Planetoid,
  OpenGL;

const
  ENEMY_SPEED = 50;
  TURN_SPEED = 5;
  MANEUVER_TIME = 1.6;
  ROTATION_SPEED = 2 * ENEMY_SPEED; 
  SPHERE_RADIUS = 0.14;

type
TManeuver = (
  MNone  = 0,
  MLeft  = 1,
  MRight = 2);

TBaseEnemy = class(TComponent)
  private
    const
      MAX_ENEMIES = 600;
    class var FEnemies: array [0..MAX_ENEMIES] of TBaseEnemy;
  protected
    FMesh: TMesh;
    FMeshRenderer: TMeshRenderer;
    FActive: Boolean;
    FLife: SmallInt; 
    //FMaxLife: SmallInt;
    //FColor: TVector3;
    FManeuverTime: Single;
    FManeuver: TManeuver;
    FEnemy: TGameObject;
    FSQRRadius: Single;  
  public
    class procedure Init();  
    class procedure Stop();
    class procedure Awake(Life: Word; Color: TVector3; Rotation: TMatrix3);
    procedure OnStart(); override;
    procedure OnUpdate(const Delta: Double); override;
    procedure OnDestroy(); override;
  end;

implementation

uses
  Hero,
  Bullet,
  Frost.Particles,
  WorldScene;

{$REGION 'BaseEnemy'}
class procedure TBaseEnemy.Init();
var
  i: Word;
begin
  for i := 0 to MAX_ENEMIES - 1 do
    FEnemies[i] := TBaseEnemy.Create(
      TGameObject.Create(
        nil,
        VECTOR3_NULL,
        Rotate(Random(360), Vector3(1, 0, 0)) * Rotate(Random(360), Vector3(0, 1, 0))));
end;

procedure SetColor(var Mesh: TMesh; Color: TVector3; DetailsNum: Byte = 20);
var              
  i, j, z: Integer;
  F, H: Integer;
begin
  F := DetailsNum;
  H := (F + 1) * (F + 1);
  for z := 0 to 5 do
    for i := 0 to F do
      for j := 0 to F do
      begin
        Mesh.Colors[i + j * (F + 1) + z * (H)] := Vector4(Color.R, Color.G, Color.B, 1);
      end;
end;

class procedure TBaseEnemy.Stop();
var
  i, j: Integer;
begin
  for i := 0 to MAX_ENEMIES - 1 do
    if (FEnemies[i].Enable) then
    begin
      FEnemies[i].FLife := -1;
      FEnemies[i].FEnable := False;
      FEnemies[i].FMeshRenderer.Enable := False;
        for j := 0 to 20 - 1 do
          TParticleRendererLayer.Emit(
            FEnemies[i].FEnemy.Position,
            Vector3(Random(10) - 5, Random(10) - 5, Random(10) - 5) * 0.02,
            4, 0.3, 0.1);
    end;
end;

class procedure TBaseEnemy.Awake(Life: Word; Color: TVector3; Rotation: TMatrix3);
var
  i: Integer;
begin
  for i := 0 to MAX_ENEMIES - 1 do
    if (not FEnemies[i].Enable) then
    begin
      FEnemies[i].FLife := Life;
      //FEnemies[i].FMaxLife := Life;
      FEnemies[i].GameObject.LocalRotation := Rotation;
      FEnemies[i].Enable := True;
      //FEnemies[i].FColor := Color;
      SetColor(FEnemies[i].FMesh, Color, 4);
      FEnemies[i].FMeshRenderer.Enable := True;
      FEnemies[i].FEnemy.LocalPosition := Vector3(0, TPlanetoid.Instance.Radius + SPHERE_RADIUS * 0.8, 0);
      Break;
    end;
end;

procedure TBaseEnemy.OnStart;
begin
  FLife := 1;
  FEnemy := TGameObject.Create(GameObject);
  FEnemy.LocalPosition := Vector3(0, 0, 0);
  FMesh := TPlanetoid.CreatePlanetoidMesh(0.14, 4);
  FMeshRenderer := TMeshRenderer.CreateMeshRenderer(FMesh, TDummyMaterial.Create);
  FEnemy.AddComponent(FMeshRenderer);
  FSQRRadius := SPHERE_RADIUS * SPHERE_RADIUS * 1.5 * 1.5;
  Enable := False;
  FMeshRenderer.Enable := False;
end;

procedure TBaseEnemy.OnUpdate(const Delta: Double);
var
  i, j: Word;
  Temp: TVector3;
  A: Single;
begin
  FManeuverTime := FManeuverTime - Delta;
  if (FManeuverTime < 0) then
  begin
    FManeuverTime := MANEUVER_TIME;
    FManeuver := TManeuver(Random(2) * Random(2) * Random(2) * (Random(2) + 1));
  end;
  case FManeuver of
    MNone: begin      
      Temp := GameObject.Rotation * (THero.FInstance.GameObject.Rotation.Transpose * VECTOR3_UP);
      A := GetAngle(Vector2(Temp.X, Temp.Z));
      if (A > 3) AND (A < 357) and (Random(10) > 3) then
      begin
        if (A > 180) then
          GameObject.LocalRotation := Rotate(-delta * ENEMY_SPEED, Vector3(0, 1, 0)) * GameObject.LocalRotation
        else
          GameObject.LocalRotation := Rotate( delta * ENEMY_SPEED, Vector3(0, 1, 0)) * GameObject.LocalRotation;
      end;
    end;
    MLeft: begin
       GameObject.LocalRotation := Rotate( delta * ENEMY_SPEED * 0.6, Vector3(0, 1, 0)) * GameObject.LocalRotation;
    end;
    MRight: begin
       GameObject.LocalRotation := Rotate(-delta * ENEMY_SPEED * 0.6, Vector3(0, 1, 0)) * GameObject.LocalRotation;
    end;
  end;

  GameObject.LocalRotation := Rotate( delta * TURN_SPEED, Vector3(1, 0, 0)) * GameObject.LocalRotation;
  FEnemy.LocalRotation := Rotate( delta * ROTATION_SPEED, Vector3(1, 0, 0)) * FEnemy.LocalRotation;
  //Bullet collision
  for i := 0 to MAX_BULLETS do
    if (Bullets[i].Active) and (Bullets[i].Owner = THero.FInstance.GameObject) then
    begin
      if ((Bullets[i].Position - FEnemy.Position).SQRMagnitude < FSQRRadius) then
      begin    
        Dec(FLife);
        //SetColor(FEnemies[i].FMesh, FColor * (1 - FMaxLife / FLife), 4);
        Bullets[i].Hide;   
        for j := 0 to 3 do
          TParticleRendererLayer.Emit(
            FEnemy.Position,
            Vector3(Random(10) - 5, Random(10) - 5, Random(10) - 5) * 0.02,
            10, 0.3, 0.1);
      end;
      if (FLife = 10) then
      begin   
        TBaseEnemy.Awake(2, Vector3(1,0.2,0.2), Rotate(120, Vector3(0,1,0)) * GameObject.LocalRotation);
        TBaseEnemy.Awake(2, Vector3(1,0.2,0.2), Rotate(240, Vector3(0,1,0)) * GameObject.LocalRotation);
        World.CountEnemies(2);
        FLife := 2;
      end;
      if (FLife <= 0) then
      begin               
        World.AddPoints(100);
        FEnable := False;              
        GameObject.LocalRotation := IDENTITY_MATRIX3;
        FMeshRenderer.Enable := False;   
        World.CountEnemies(-1);
        for j := 0 to 20 - 1 do
          TParticleRendererLayer.Emit(
            FEnemy.Position,
            Vector3(Random(10) - 5, Random(10) - 5, Random(10) - 5) * 0.02,
            10, 0.3, 0.1);   
        FEnemy.LocalPosition := Vector3(0, 0, 0);
        Break;
     //    World.Stress();
      end;
    end;  
  //Hero
  if (FLife > 0) then  
  if ((THero.FInstance.Position - FEnemy.Position).SQRMagnitude < SPHERE_RADIUS * SPHERE_RADIUS * 3 * 3) then
  begin
    FEnable := False;
    FMeshRenderer.Enable := False;
    World.CountEnemies(-1);
    THero.FInstance.KillLevel;
    for j := 0 to 20 - 1 do
      TParticleRendererLayer.Emit(
            FEnemy.Position,
            Vector3(Random(10) - 5, Random(10) - 5, Random(10) - 5) * 0.02,
            10, 0.3, 0.1);
  end;
end;

procedure TBaseEnemy.OnDestroy;
begin

end;
{$ENDREGION}

end.
