unit _Units;

interface

uses
  QuadEngine,
  _Resources,
  _Geometry,
  _Forms,
  _Map,
  _Camera,
  _Effects;

const
  Max_Units = 50;

const
  SNone     = 0;// passive objects
  SPlayer   = 1;
  SComputer = 2;

type
TBaseUnit = class;
PBaseUnit = ^TBaseUnit;

TCallbackMethod = procedure (Position: TVector2; Power: Single; Side: Byte) of object;  

TBaseUnit = class     
    FLife: Single;
    FPosition: TVector2;     
    FNeedPosition: TVector2;
    FAngle: Single;     
    FSide: Byte;
    FColor: Cardinal;
    FCanFire: Boolean;
  public
    KillMe: Boolean;
    NeedAngle: Single;
    procedure Draw(); virtual; abstract;
    procedure Act(dt: Single); virtual; abstract; 
end;

TMainUnit = class(TBaseUnit)
    FMaxDistance: Single;
    FBodyAngle: Single;
    FStartAnimation: Single;  
    procedure DrawArrows();
  public
    constructor Create(Position: TVector2; Angle: Single; Side: Byte);
    procedure Draw(); override;
    procedure Act(dt: Single); override;
end;      

TBullet = class(TBaseUnit)
    FVector: TVector2;
    FDistance,
    FCurrentDistance: Single;
    FWind: Single;
    FTimer: Single;
    FMap: TMap;
    FHit: TCallBackMethod;  
    Active: Boolean;
  public
    constructor Create(Position: TVector2; Vector: TVector2; Distance: Single; Angle, Wind: Single; Side: Byte; Map: TMap; HitMethod: TCallBackMethod);
    procedure Draw(); override;
    procedure Act(dt: Single); override;
end;

THardBullet = class(TBullet)
   public
    procedure Draw(); override;  
end;

implementation

function GetColor(Side: Byte): Cardinal;
begin
  Result:= $ffffffff;
  case Side of     
    0:Result:= $ff22ff22;
    1:Result:= $ffff2222;
  end;
end;

{$REGION 'Base Unit'}
constructor TMainUnit.Create(Position: TVector2; Angle: Single; Side: Byte);
begin                    
  FNeedPosition:= Position;
  FPosition:= Position;
  FBodyAngle:= Angle;  
  FAngle:= Angle;
  FSide:=  Side;
  FColor:= GetColor(FSide);
  NeedAngle:= Angle;
  FStartAnimation:= 1;
  FLife:= 100;
  FCanFire:= true;
end;

procedure TMainUnit.DrawArrows(); 
var
  Center: TVector2;
  Pos: TVector2;
  NeedArrow: Boolean;
begin
  NeedArrow:= false;
  Center:= Vector2(Window.Width / 2, Window.Height / 2);
  Pos:= Vector2(Camera.GetX(FPosition.X), Camera.GetY(FPosition.Y)) - Center;  
  if (Pos.Y < -Center.Y) then
    begin
      Pos:= Pos * -(Center.Y / Pos.Y);
      NeedArrow:= True;
    end;
  if (Pos.Y > Center.Y) then
    begin
      Pos:= Pos * (Center.Y / Pos.Y); 
      NeedArrow:= True;
    end;
  if (Pos.X < -Center.X) then
    begin
      Pos:= Pos * -(Center.X / Pos.X); 
      NeedArrow:= True;
    end;
  if (Pos.X > Center.X) then     
    begin
      Pos:= Pos * (Center.X / Pos.X); 
      NeedArrow:= True;
    end;

  if (NeedArrow) then
    begin
      Pos:= (Pos * 0.97) + Center;
      Resources.ArrowTexture.DrawRot(Pos.X, Pos.Y, GetAngle(Pos - Center), 0.25 + 0.25 * Camera.Scale, FColor);
    end;
end;


procedure TMainUnit.Draw();
var
  Center: TVector2;
  Pos: TVector2;
begin
  Resources.UnitTexture.DrawRotFrame(Camera.GetX(FPosition.X), Camera.GetY(FPosition.Y), FBodyAngle, Camera.Scale, 15);
  Pos:= FPosition + GetVector(FBodyAngle +  135      , 40 * (1 - FStartAnimation));
  Resources.UnitTexture.DrawRotFrame(Camera.GetX(Pos.X), Camera.GetY(Pos.Y), FBodyAngle +   0, Camera.Scale, 3);
  Pos:= FPosition + GetVector(FBodyAngle +  135 +  90, 40 * (1 - FStartAnimation));
  Resources.UnitTexture.DrawRotFrame(Camera.GetX(Pos.X), Camera.GetY(Pos.Y), FBodyAngle +  90, Camera.Scale, 3);
  Pos:= FPosition + GetVector(FBodyAngle +  135 + 180, 40 * (1 - FStartAnimation));
  Resources.UnitTexture.DrawRotFrame(Camera.GetX(Pos.X), Camera.GetY(Pos.Y), FBodyAngle + 180, Camera.Scale, 3);
  Pos:= FPosition + GetVector(FBodyAngle +  135 + 270, 40 * (1 - FStartAnimation));
  Resources.UnitTexture.DrawRotFrame(Camera.GetX(Pos.X), Camera.GetY(Pos.Y), FBodyAngle + 270, Camera.Scale, 3);

  Resources.UnitTexture.DrawRotFrame(Camera.GetX(FPosition.X), Camera.GetY(FPosition.Y), FBodyAngle, Camera.Scale, 2);
  
  Pos:= FPosition + GetVector(FAngle, -40 * FStartAnimation);
  Resources.UnitTexture.DrawRotFrame(Camera.GetX(Pos.X), Camera.GetY(Pos.Y), FAngle, Camera.Scale, 1);

  Resources.UnitTexture.DrawRotFrame(Camera.GetX(FPosition.X), Camera.GetY(FPosition.Y), FBodyAngle, Camera.Scale, 0, FColor);

  DrawArrows();
  Pos:= Camera.GetPos(FPosition) + Vector2(-100 * Camera.Scale, -100 * Camera.Scale);
  if (FLife > 0) then
    Quad.Rectangle(Pos.X, Pos.Y, Pos.X + FLife * 2 * Camera.Scale, Pos.Y + 10, FColor);
end;

procedure TMainUnit.Act(dt: Single);
begin
  FAngle:= _Geometry.RotateToAngle(FAngle, NeedAngle, 2);
  if (FStartAnimation <> 0) then
    if FStartAnimation > 0 then
      FStartAnimation:= FStartAnimation - dt
    else                                    
      FStartAnimation:= 0;
  if (FLife < 0) then
    FLife:= FLife - dt * 1000;
  if (FLife < -2500) then
    begin
      Effects.Blow(FPosition, 50, 1.5);
      Effects.Stress(1);
      KillMe:= True;
    end;
  if (FPosition.Distance(FNeedPosition) > 10) then
    FPosition:= FPosition + (FNeedPosition - FPosition) / (FNeedPosition - FPosition).Length * dt * 200;
  if FLife < 10 then
    Effects.AddSmokeEffect(
      FPosition + Vector2((Random(1000) - 500) * 0.01, (Random(1000) - 500) * 0.01),
      Vector2(400, 300) + Vector2((Random(1000) - 500) * 0.1, (Random(1000) - 500) * 0.1), 0.1, Random(200) / 100);
end;
{$ENDREGION}

{$REGION 'Bullet'}
constructor TBullet.Create;
begin
  FWind:= Wind;
  FPosition:= Position;    
  FNeedPosition:= Position;
  FVector:= Vector;
  FMap:= Map;
  FAngle:= Angle;
  FSide:= Side;
  FHit:= HitMethod;
  FCurrentDistance:= 0;
  FDistance:= Distance;
  FTimer:= 1;
  Active:= true;
end;

procedure TBullet.Draw();
begin
  if Active then
    Resources.ArrowTexture.DrawRot(Camera.GetX(FPosition.X), Camera.GetY(FPosition.Y), FAngle, Camera.Scale);
  if (FCurrentDistance > FDistance) and (Active) then
    begin
      FMap.AddCrater(FPosition + Vector2(256, 256), Random(360), 160 + Random(80));
      FHit(FPosition, 60, FSide);
      Effects.Blow(FPosition, 50, 1.5);
      Effects.Stress(1);
      Active:= false;
    end;
end;

procedure TBullet.Act(dt: Single);
begin     
  if FTimer < 0 then
    KillMe:= True;                       
  if FCurrentDistance > FDistance then
    begin
      FTimer:= FTimer - dt;
    end
  else
    begin
      FPosition:= FPosition + FVector * dt ;
      FVector:= FVector +  GetVector(FWind, 4);
      FCurrentDistance:= FCurrentDistance + (FVector * dt).Length;
    end;
end;      

procedure THardBullet.Draw();
begin
  if Active then
    Resources.ArrowTexture.DrawRot(Camera.GetX(FPosition.X), Camera.GetY(FPosition.Y), FAngle, Camera.Scale, $ffff0000);
  if (FCurrentDistance > FDistance) and (Active) then
    begin
      FMap.AddCrater(FPosition + Vector2(256, 256), Random(360), 220 + Random(80));
      FHit(FPosition, 20, FSide);
      Effects.Blow(FPosition, 50, 1.5);
      Effects.Stress(1);
      Active:= false;
    end;
end;
{$ENDREGION}

end.
