unit Bullet;

interface       

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

const
  MAX_BULLETS = 300;

type
TBullet = class(TComponent)
  protected 
    class var FMesh: TMesh;
  protected
    FActive: Boolean;
    FLife: Single;
    FOwner: TGameObject;
    FVelocity: TVector3;
  public
    PreviosPosition: TVector3;
    class procedure Init();
    class procedure Fire(Owner: TGameObject; Position, Velocity: TVector3; Rotation: TMatrix3);
    procedure Hide;
    procedure OnStart(); override;
    procedure OnUpdate(const Delta: Double); override;
    procedure OnDestroy(); override;
    property Active: Boolean read FActive write FActive;
    property Owner: TGameObject read FOwner;
  end;

var
  Bullets: array [0..MAX_BULLETS] of TBullet;

implementation

uses
  BlowEffect;

{$REGION 'Cylinder'}
function CreateCylinderMesh(Radius: Single = 0.2; Length: Single = 1; Edges: Word = 16): 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), 0).X, -GetVector(i * 360 / (Edges - 1), 0).Y,  Length * 0.5);
    Vertex[i + Edges] := Vector3(GetVector(i * 360 / (Edges - 1), Radius).X, -GetVector(i * 360 / (Edges - 1), Radius).Y, -Length * 0.5);
    Normals[i        ] := Vector3(GetVector(i * 360 / (Edges - 1), 0).X, -GetVector(i * 360 / (Edges - 1), 0).Y,  0);
    Normals[i + Edges] := Vector3(GetVector(i * 360 / (Edges - 1), Radius).X, -GetVector(i * 360 / (Edges - 1), Radius).Y,  0);
    Textures[i        ] := Vector2(1 / (Edges - 1) * i, 0);
    Textures[i + Edges] := Vector2(1 / (Edges - 1) * i, 1);
  end;
  Vertex[Edges + Edges + 0] := Vector3(0, 0,  Length * 0.5);
  Vertex[Edges + Edges + 1] := Vector3(0, 0, -Length * 0.5);
  Normals[Edges + Edges + 0] := Vector3(0,  1, 0);
  Normals[Edges + Edges + 1] := Vector3(0, -1, 0);
  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 + Edges, i);
    Faces[i * 2 + 1] := Face( i + Edges + 1, i + Edges, i + 1);
    Faces[(i + Edges - 1) * 2 + 0] := Face( i, Edges + Edges + 0, i + 1);
    Faces[(i + Edges - 1) * 2 + 1] := Face( i + Edges, i + Edges + 1, Edges + Edges + 1);
  end;

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

{$REGION 'BaseEnemy'}
class procedure TBullet.Init;
var
  i: Word;
begin
  for i := 0 to MAX_BULLETS do
    Bullets[i] :=  TBullet.Create(TGameObject.Create(nil));
end;

class procedure TBullet.Fire(Owner: TGameObject; Position, Velocity: TVector3; Rotation: TMatrix3);
var          
  i: Word;
begin
  for i := 0 to MAX_BULLETS do
    if (not Bullets[i].FActive) then
    begin              
      Bullets[i].FOwner := Owner;
      Bullets[i].FActive := True;
      Bullets[i].FVelocity := Velocity;             
      Bullets[i].GameObject.LocalRotation := Rotation;
      Bullets[i].GameObject.LocalPosition := Position + Velocity;
      Bullets[i].FLife := 10;
      break;
    end;
end;

procedure TBullet.Hide;
begin
  FLife := 0.01;
  TBlowEffect.Blow(GameObject.LocalPosition);
  GameObject.LocalPosition := Vector3(0, 0, -100);
  FActive := False;
end;

procedure TBullet.OnStart;
begin
  if FMesh = nil then
    FMesh := CreateCylinderMesh(0.02, 1, 4);
  FActive := false;
  GameObject.LocalPosition := Vector3(0, 0, -100);
  GameObject.AddComponent(TMeshRenderer.CreateMeshRenderer(FMesh, TColorMaterial.Create(Vector3(0.9,0.9,0.9))));
end;

procedure TBullet.OnUpdate(const Delta: Double);
var
  factor: Single;
begin
  if (FActive) then
  begin                                                     
    PreviosPosition := GameObject.LocalPosition;
    GameObject.LocalPosition := GameObject.LocalPosition + FVelocity;
    if (GameObject.LocalPosition.Z < 0) then
    begin
      factor := (- GameObject.LocalPosition.Z) / (PreviosPosition.Z - GameObject.LocalPosition.Z);
      PreviosPosition := GameObject.LocalPosition + (PreviosPosition - GameObject.LocalPosition) * factor;
      TBlowEffect.Blow(PreviosPosition);
      FActive := False;
    end;
    FLife := FLife - Delta;
    if (FLife < 0) then
    begin
      FActive := False;
    end;
  end;
end;

procedure TBullet.OnDestroy;
begin

end;
{$ENDREGION}

end.
