unit Hero;

interface

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

const
  INVISIBLE_TIME = 6 * PI;
  HERO_HEIGHT = 2.1;     
  VINT_HEIGHT = 0.0;
  ROTATION_FACTOR = 1.2;
  HERO_SPEED = 6;

type
THeroState = (
  HSFly,
  HSWakeUp,   
  HSFlyOut,
  HSLanding,
  HSDeath
  );

THero = class(TComponent)
  private            
    FHero: TGameObject;
    FMesh: TMesh;
    FMoveVelocity: TVector3; 
    FVint: TGameObject;
    FVintAngle,
    FVintAngleSpeed: Single;
    FAngle,
    FNeedAngle: Single;
    FAim: TVector3;
    FReloadTimer: Single;   
    FRocketReloadTimer: Single;
    FLife: Integer;
    FLifeLabel: TTextMesh;  
    FState: THeroState;
    procedure Fire();    
    procedure RocketFire();
  public
    procedure Aim(AimPosition: TVector3);
    procedure OnStart; override;
    procedure OnUpdate(const Delta: Double); override;
    procedure OnDestroy; override;
  public
    class var FInstance: THero;
  end;

implementation

uses
  Bullet,      
  Rocket,
  WorldScene;

{$REGION 'Hero'}
procedure THero.OnStart;
var
  Material : TMaterial;
begin
  FInstance := Self;
  FMesh := Resources.LoadMMSMesh('data/models/helicopter.mms', 0.14);
  FHero := TGameObject.Create(GameObject, Vector3(0, 0, 0.1));
  Material := TTexturedMaterial.Create(LoadPNGTexture('data/textures/heli.png'));
  FHero.AddComponent(TMeshRenderer.CreateMeshRenderer(FMesh, Material));
  FVint := TGameObject.Create(FHero, Vector3(0, 0, VINT_HEIGHT));

  FMesh := Resources.LoadMMSMesh('data/models/vint.mms', 0.14);
  FVint.AddComponent(TMeshRenderer.CreateMeshRenderer(FMesh, Material));
  
  GameObject.LocalPosition := VECTOR3_NULL;
  FLife := 200;
  
  FLifeLabel := TTextMesh.Create(TGameObject.Create(nil, Vector3(0, 20, 0)));
  FLifeLabel.GameObject.Layer := 15;
  FLifeLabel.Align := TTextAlign(0);

  FLifeLabel.Text := IntToStr(FLife);
  FState := HSWakeUp;
  FVintAngleSpeed := 0;
  FAngle := -45;
//  GameObject.LocalRotation := Rotate(45, Vector3(1, 0, 0)) * GameObject.LocalRotation;
end;

procedure THero.OnUpdate(const Delta: Double);
var
  i, j: Word;
  Speed: Single;
  Angles: TVector3;
const
  DELTA_SPEED = 0.05;
begin                    
  if (FState = HSWakeUp) then
  begin
    FVintAngleSpeed := FVintAngleSpeed + 3;   
    FVintAngle := GetRound(FVintAngle + FVintAngleSpeed * Delta);
    FVint.LocalRotation := Rotate(FVintAngle, Vector3(0,0,1));
    if (FVintAngleSpeed > 501) then
    begin
      FVintAngleSpeed := 501;
      FState := HSFlyOut;
    end;
  end;               
  if (FState = HSFlyOut) then
  begin
    FVintAngle := GetRound(FVintAngle + FVintAngleSpeed * Delta);
    FVint.LocalRotation := Rotate(FVintAngle, Vector3(0,0,1));
    FHero.LocalPosition := FHero.LocalPosition + Vector3(0,0,0.26*Delta);
    FAngle := _Geometry.RotateToAngle(FAngle, FNeedAngle, 1);
    Angles := Rotate(FAngle + 45, Vector3(0, 0, 1)) * FMoveVelocity;
    FHero.LocalRotation := Rotate(Angles.Y * 100, Vector3(1, 0, 0))*  Rotate(Angles.X * 100, Vector3(0, -1, 0)) * Rotate(FAngle + 45, Vector3(0, 0, 1));
    if (FHero.LocalPosition.Z > HERO_HEIGHT) then
    begin
      FHero.LocalPosition := Vector3(0,0,HERO_HEIGHT);  
      FState := HSFly;
    end;
  end;         
  if (FState = HSDeath) then
  begin
    FVintAngle := GetRound(FVintAngle + FVintAngleSpeed * Delta);
    FVint.LocalRotation := Rotate(FVintAngle, Vector3(0,0,1));
    FAngle := GetRound(FAngle + 10 * Delta);
    Angles := Rotate(FAngle + 45, Vector3(0, 0, 0.1)) * FMoveVelocity;
    FHero.LocalRotation := Rotate(Angles.Y * 100, Vector3(1, 0, 0))*  Rotate(Angles.X * 100, Vector3(0, -1, 0)) * Rotate(FAngle + 45, Vector3(0, 0, 1));
    FHero.LocalPosition := FHero.LocalPosition + Vector3(0,0,-1*Delta);
//    FHero.LocalPosition := Rotate(Angles.Y * 100, Vector3(1, 0, 0))*  Rotate(Angles.X * 100, Vector3(0, -1, 0)) * Rotate(FAngle + 45, Vector3(0, 0, 1));
    if (FHero.LocalPosition.Z < 0) then
    begin
      FHero.LocalPosition := Vector3(GameObject.LocalPosition.X, GameObject.LocalPosition.Y, 0);
      FLife := FLife - 1;     
      if (FLife < 0) and (FLife > -3) then
        TBlowEffect.Blow(Vector3(FHero.LocalPosition.X, FHero.LocalPosition.Y,0), 2);
      if (FLife < -50) then
      begin
        FState := HSWakeUp;
        GameObject.LocalPosition := Vector3(10 * 4, 10 * 4, 0);
        FHero.LocalPosition := Vector3(0, 0, 0.1);
        FHero.LocalRotation := IDENTITY_MATRIX3;
        FLife := 200;       
        FLifeLabel.Text := IntToStr(FLife);
      end;
    end;
  end;
  if (FState = HSFly) then
  begin
    if (Control.Key[KEY_A]) or (Control.Key[KEY_LEFT]) then
      FMoveVelocity := FMoveVelocity + Rotate(45, Vector3(0, 0, 1)).Transpose * Vector3(-DELTA_SPEED * delta, 0, 0);
    if (Control.Key[KEY_D]) or (Control.Key[KEY_RIGHT]) then
      FMoveVelocity := FMoveVelocity + Rotate(45, Vector3(0, 0, 1)).Transpose * Vector3( DELTA_SPEED * delta, 0, 0);
    if (Control.Key[KEY_W]) or (Control.Key[KEY_UP]) then
      FMoveVelocity := FMoveVelocity + Rotate(45, Vector3(0, 0, 1)).Transpose * Vector3(0, DELTA_SPEED * delta, 0);
    if (Control.Key[KEY_S]) or (Control.Key[KEY_DOWN]) then
      FMoveVelocity := FMoveVelocity + Rotate(45, Vector3(0, 0, 1)).Transpose * Vector3(0,-DELTA_SPEED * delta, 0);

    Speed := FMoveVelocity.Magnitude;
    if (Speed > 1) then
       FMoveVelocity * 1 / Speed;
    FMoveVelocity := FMoveVelocity * 0.97;
    GameObject.LocalPosition := GameObject.LocalPosition + FMoveVelocity;

    FAngle := _Geometry.RotateToAngle(FAngle, FNeedAngle, 3);
    Angles := Rotate(FAngle + 45, Vector3(0, 0, 1)) * FMoveVelocity;
    FHero.LocalRotation := Rotate(Angles.Y * 100, Vector3(1, 0, 0))*  Rotate(Angles.X * 100, Vector3(0, -1, 0)) * Rotate(FAngle + 45, Vector3(0, 0, 1));

    FVintAngle := GetRound(FVintAngle + FVintAngleSpeed * Delta);
    FVint.LocalRotation := Rotate(FVintAngle, Vector3(0,0,1));
    // Fire reload
    if (FReloadTimer > 0) then
      FReloadTimer := FReloadTimer - Delta;
    if (FRocketReloadTimer > 0) then
      FRocketReloadTimer := FRocketReloadTimer - Delta;        
    // Fire control
    if (Control.Mouse.LeftButton) and (FReloadTimer <= 0) then
      Fire();
    if (Control.Mouse.RigthButton) and (FRocketReloadTimer <= 0) then
      RocketFire;
  end; 
  // bullet hit  
  for i := 0 to MAX_BULLETS do
  if (Bullets[i].Active) and (Bullets[i].Owner <> THero.FInstance.GameObject) then
  begin
    if ((Bullets[i].GameObject.Position - FHero.Position).SQRMagnitude < 0.8*0.8) then
    begin
      Bullets[i].Hide;
      FLife := FLife - 1;
      FLifeLabel.Text := IntToStr(FLife);
    end;
  end;   
  // rocket hit
  for i := 0 to MAX_ROCKETS do
  if (Rockets[i].Active) and (Rockets[i].Owner <> THero.FInstance.GameObject) then
  begin
    if ((Rockets[i].GameObject.Position - FHero.Position).SQRMagnitude < 0.8*0.8) then
    begin
      Rockets[i].Hide;
      FLife := FLife - 6;
      FLifeLabel.Text := IntToStr(FLife);
    end;
  end;
  if (FLife < 0) then
  begin
    FState := HSDeath;
  end;
end;

procedure THero.Aim(AimPosition: TVector3);
begin
  FNeedAngle := _Geometry.GetRound(180 -45 -_Geometry.GetAngle(AimPosition - GameObject.LocalPosition));
  FAim := AimPosition;
end;

procedure THero.Fire();
const
  MACHINEGUN_RELOAD_SPEED = 0.4;
var
  BulletVector: TVector3;
  HorAngle: Single;
begin
  BulletVector := FAim - FHero.Position;
  HorAngle := TVector2(BulletVector).Length;
  HorAngle := GetAngle(Vector2(BulletVector.Z, HorAngle));
  BulletVector := BulletVector / BulletVector.Magnitude * 1.0;
  TBullet.Fire(GameObject, Fhero.Position, BulletVector, Rotate(-90-HorAngle, Vector3(0,1,0)) * Rotate(-FNeedAngle + 45, Vector3(0,0,-1)));
  FReloadTimer := MACHINEGUN_RELOAD_SPEED;
end;

procedure THero.RocketFire();
const
  ROCKET_RELOAD_SPEED = 4.2;
var
  BulletVector: TVector3;
  HorAngle: Single;
begin
  BulletVector := FAim - FHero.Position;
  HorAngle := TVector2(BulletVector).Length;
  HorAngle := GetAngle(Vector2(BulletVector.Z, HorAngle));
  BulletVector := BulletVector / BulletVector.Magnitude * 0.03;
  TRocket.Fire(GameObject, Fhero.Position, BulletVector, Rotate(-90-HorAngle, Vector3(0,1,0)) * Rotate(-FNeedAngle + 45, Vector3(0,0,-1)));
  FRocketReloadTimer := ROCKET_RELOAD_SPEED;
end;

procedure THero.OnDestroy;
begin

end;
{$ENDREGION}

end.
