unit DiscEnemy;

interface

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

const
  ENEMY_SPEED = 80;
  TURN_SPEED = 6;
  ROTATION_SPEED = 2 * ENEMY_SPEED; 
  SPHERE_RADIUS = 0.44;
  VIEW_DISTANCE = 10;
  RELOAD_TIME  = 0.4;

type
TAction = (
  AAimToHero  = 0,
  AMoveToHero = 1,
  AMoveBySide = 2,
  AFire       = 3);

TDiscEnemy = class(TComponent)
  private
    const
      MAX_ENEMIES = 50;
    class var FEnemies: array [0..MAX_ENEMIES] of TDiscEnemy;
  protected
    FMesh: TMesh;
    FMeshRenderer: TMeshRenderer;
    FUpdateTimer: Single;
    FActive: Boolean;
    FLife: SmallInt;
    FEnemy: TGameObject;
    FSQRRadius: Single;
    FAction: TAction;
    FActionTimer: Single;
    FSideMove: Single;
    FReloadTimer: Single;
    FBullet: Byte;
  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 'DiscEnemy'}
{$REGION 'Disc'}
function CreateDiscMesh(Radius: Single = 0.4; Length: Single = 0.1; Edges: Word = 12): TMesh;
var
  Vertex:   TVertexes;
  Normals:  TNormals;
  Textures: TTextures;
  Faces: TFaces;
  i: Integer;
begin
  SetLength(Vertex, Edges * 2 + 2);
  SetLength(Textures, Edges * 2 + 2);
  SetLength(Normals, Edges * 2 + 2);
  SetLength(Faces, (Edges - 1) * 2 + (Edges - 1) * 2); //2 triangles on each side panel, and 2 circle
  for i := 0 to Edges - 1 do
  begin
    Vertex[i        ] := Vector3(GetVector(i * 360 / (Edges - 1), Radius).X * 0.1,  Length * 0.5, -GetVector(i * 360 / (Edges - 1), Radius * 0.1).Y);
    Vertex[i + Edges] := Vector3(GetVector(i * 360 / (Edges - 1), Radius).X, -Length * 0.5, -GetVector(i * 360 / (Edges - 1), Radius).Y);
    Normals[i        ] := Vector3(GetVector(i * 360 / (Edges - 1), Radius).X,  0, -GetVector(i * 360 / (Edges - 1), Radius).Y);
    Normals[i + Edges] := Vector3(GetVector(i * 360 / (Edges - 1), Radius).X,  0, -GetVector(i * 360 / (Edges - 1), Radius).Y);
    Textures[i        ] := Vector2(1 / (Edges - 1) * i, 0);
    Textures[i + Edges] := Vector2(1 / (Edges - 1) * i, 0.3);
  end;
  Vertex[Edges + Edges + 0] := Vector3(0,  Length * 0.5, 0);
  Vertex[Edges + Edges + 1] := Vector3(0, -Length * 0.5, 0);
  Normals[Edges + Edges + 0] := Vector3(0, 0,  1);
  Normals[Edges + Edges + 1] := Vector3(0, 0, -1);
  Textures[Edges + Edges + 0] := Vector2(0, 0);
  Textures[Edges + Edges + 1] := Vector2(0, 1);

  for i := 0 to Edges - 2 do
  begin
    Faces[i * 2 + 0] := Face( i + 1, i, i + Edges);
    Faces[i * 2 + 1] := Face( i + Edges + 1, i + 1, i + Edges);
    Faces[(i + Edges - 1) * 2 + 0] := Face( i, i + 1, Edges + Edges + 0);
    Faces[(i + Edges - 1) * 2 + 1] := Face( i + Edges, Edges + Edges + 1, i + Edges + 1);
  end;

  Result := TMesh.Create(Faces, Vertex, Textures, Normals);
  SetLength(Vertex, 0);
  SetLength(Textures, 0);
  SetLength(Normals, 0);
  SetLength(Faces, 0);
end;

{$ENDREGION}

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

class procedure TDiscEnemy.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 TDiscEnemy.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].GameObject.LocalRotation := Rotation;
      FEnemies[i].Enable := True;
      FEnemies[i].FMeshRenderer.Enable := True;
      FEnemies[i].FEnemy.LocalPosition := Vector3(0, TPlanetoid.Instance.Radius + SPHERE_RADIUS * 0.8, 0);
      Break;
    end;
end;

procedure TDiscEnemy.OnStart;
begin
  FLife := 1;
  FEnemy := TGameObject.Create(GameObject);
  FEnemy.LocalPosition := Vector3(0, 0, 0);
  FMesh := CreateDiscMesh;//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;
  FAction := AAimToHero;
end;

procedure TDiscEnemy.OnUpdate(const Delta: Double);
var
  i, j: Word;
  Temp: TVector3;
  A: Single;
begin            

  FActionTimer := FActionTimer - Delta;
  if (FActionTimer <= 0) then
  begin
    case FAction of
      AAimToHero: begin
        FActionTimer := 5;
        if ((THero.FInstance.Position - FEnemy.Position).Magnitude > VIEW_DISTANCE) then
        begin
          FAction := AMoveToHero;
        end
        else
        begin
          FAction := AMoveBySide;
          FSideMove := Random(2) * 2 - 1;
          if Random(10) = 0 then
          begin
            FAction := AFire;
            FReloadTimer := RELOAD_TIME;
            FBullet := 3;
          end;
        end;
      end;
      AMoveToHero: begin
        FAction:= AAimToHero;
        FActionTimer := 0.8;
      end;
      AMoveBySide: begin
        FAction:= AAimToHero;
        FActionTimer := 0.8;
      end;
      AFire: begin
        FAction:= AMoveBySide; 
        FActionTimer := 2;
      end;
    end;
  end;

  case FAction of
    AAimToHero: 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;
    AMoveToHero: begin
      GameObject.LocalRotation := Rotate( delta * TURN_SPEED, Vector3(1, 0, 0)) * GameObject.LocalRotation;
    end;
    AMoveBySide: begin
      GameObject.LocalRotation := Rotate( delta * TURN_SPEED * FSideMove, Vector3(0, 0, 1)) * GameObject.LocalRotation;
    end;     
    AFire: begin
      if (FBullet > 0) then
      begin
        FReloadTimer := FReloadTimer - Delta;
        if (FReloadTimer <= 0) then
        begin
          FReloadTimer := RELOAD_TIME;
          Dec(FBullet);   
          TBullet.Fire(Self.GameObject,  Rotate((Random(100) - 50) * 0.01, Vector3(0, -1, 0)), BULLET_SPEED * 0.4);
        end;
      end;

    end;
  end;
  FEnemy.LocalRotation := Rotate( delta * ROTATION_SPEED, Vector3(0, 1,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);       
        Bullets[i].Hide;
      end;
      if (FLife <= 0) then
      begin          
        World.AddPoints(1000);    
        World.Stress(7);
        FEnable := False;
        FMeshRenderer.Enable := False;   
        World.CountEnemies(-1);
        GameObject.LocalRotation := IDENTITY_MATRIX3;
        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 TDiscEnemy.OnDestroy;
begin

end;
{$ENDREGION}

end.
