unit Units.Player;

interface

uses
  QuadEngine, Vec2f, Units.Custom, IniFiles, Subject, CustomSubject, GlobalTypes;

type
  TSexType = (
    stMale = 0,
    stFemale = 1
  );

  TUnitsPlayer = class(TCustomUnit)
  private
    FName: WideString;
    FPortrait: Integer;
    FMaxStamina: Integer;
    FStamina: Single;
    FInventory: Integer;
    FIsAction: Boolean;
    FParent: TUnitsPlayer;
    FChild: TUnitsPlayer;
    FGunSkill: Integer;
    FEngineerSkill: Integer;

    FHeadSkin: Integer;
    FSubject: array[0..3] of TSubject;
    FGunAction: TSubject;
    FSex: TSexType;
    function GetSubject(Index: Integer): TSubject;
    procedure SetSubject(Index: Integer; Value: TSubject);
  public
    constructor Create(const APosition: TVec2f);
    procedure Process(const Delta: Double); override;
    procedure Draw; override;
    procedure Join(const AUnit: TUnitsPlayer);
    procedure Load(Aini: TIniFile; ASectionName: string);
    procedure SubjectClear;
    procedure NextGun;
    procedure PickUp(ASubject: TCustomSubject);
    function ExistsSubject(ASubjectType: TSubjectType; AIsActive: Boolean = False): Boolean;
    function FindSubject(ASubjectType: TSubjectType; AIsActive: Boolean = False): TSubject;

    property Child: TUnitsPlayer read FChild write FChild;
    property Parent: TUnitsPlayer read FParent write FParent;
    property Name: WideString read FName;
    property MaxStamina: Integer read FMaxStamina;
    property Stamina: Single read FStamina;
    property Portrait: Integer read FPortrait;
    property Inventory: Integer read FInventory;
    property Subjects[Index: Integer]: TSubject read GetSubject write SetSubject;
    property GunAction: TSubject read FGunAction;
    property Sex: TSexType read FSex;
    property GunSkill: Integer read FGunSkill;
    property EngineerSkill: Integer read FEngineerSkill;
  end;

implementation

uses
  Resources, GameMode.Planet, GameOverlay, Math;

constructor TUnitsPlayer.Create(const APosition: TVec2f);
begin
  inherited Create(APosition);
  FParent := nil;
  FSpeed[baStep] := 150;
  FRadius := 11;

  FName := 'Name';
  FMaxHP := 100;
  FPortrait := Random(9);
  FMaxStamina := 100;
  FInventory := 1;
  SubjectClear;
  FHeadSkin := Random(3);
  FGunAction := nil;
  FGunSkill := 50;
  FEngineerSkill := 50;
end;

procedure TUnitsPlayer.NextGun;
var
  i, n: Integer;
begin
  n := -1;
  for i := 0 to FInventory - 1 do
    if Assigned(FSubject[i]) and (FSubject[i] = FGunAction) then
    begin
      n := i;
      Break;
    end;

  for i := n + 1 to FInventory - 1 do
    if Assigned(FSubject[i]) then
      case FSubject[i].SubjectType of
        stGunUMP, stGunBeretta92F:
          begin
            FGunAction := FSubject[i];
            Exit;
          end;
      end;

  for i := 0 to n - 1 do
    if Assigned(FSubject[i]) then
      case FSubject[i].SubjectType of
        stGunUMP, stGunBeretta92F:
          begin
            FGunAction := FSubject[i];
            Exit;
          end;
      end;
  FGunAction := nil;
end;

procedure TUnitsPlayer.PickUp(ASubject: TCustomSubject);
var
  i: Integer;
begin
  if not Assigned(ASubject.Subject) then
    Exit;

  for i := 0 to FInventory - 1 do
    if Assigned(Subjects[i]) and (Subjects[i].SubjectType = ASubject.Subject.SubjectType) then
    begin
      ASubject.Subject := Subjects[i].Add(ASubject.Subject);
      if not Assigned(ASubject.Subject) then
        Exit;
    end;

  for i := 0 to FInventory - 1 do
    if not Assigned(Subjects[i]) then
    begin
      Subjects[i] := ASubject.Subject;
      ASubject.Subject := nil;
      Exit;
    end;

  TGameOverlay.AddDialog(Portrait, 'Нет места!', lEarth);
end;

procedure TUnitsPlayer.SubjectClear;
var
  i: Integer;
begin
  for i := 0 to FInventory - 1 do
    FSubject[i] := nil;
end;

procedure TUnitsPlayer.Process(const Delta: Double);
var
  Distance: Single;
  i: integer;
  Dmg: Integer;
begin
  Dmg := TGlobals.GameModes.Level.LevelMap.GetFieldToPosition(Position).Damage;
  if (Dmg > 0) and not ExistsSubject(stSpaceSuit, True) then
    Damage(Dmg * Delta);

  if Assigned(Parent) then
  begin
    Distance := Position.Distance(Parent.Position);
    if Distance > 32 then
    begin
      if Distance - 32 < Speed * Delta then
        Position := Position - (Position - Parent.Position).Normalize * (Distance - 32)
      else
        Position := Position - (Position - Parent.Position).Normalize * Speed * Delta;
    end;
  end;

  for i := 0 to FInventory - 1 do
    if Assigned(Subjects[i]) then
      Subjects[i].Process(Delta);
end;

procedure TUnitsPlayer.Load(Aini: TIniFile; ASectionName: string);
begin
  FName := Aini.ReadString(ASectionName, 'Name', 'Name');
  FMaxHP := Max(50, Min(250, Aini.ReadInteger(ASectionName, 'MaxHP', 100)));
  FHP := FMaxHP;
  FPortrait := Aini.ReadInteger(ASectionName, 'Portrait', 0);
  FMaxStamina := Max(50, Min(150, Aini.ReadInteger(ASectionName, 'Stamina', 100)));
  FSpeed[baStep] := Max(100, Min(200, Aini.ReadInteger(ASectionName, 'Speed', 150)));
  FInventory := Max(2, Min(4, Aini.ReadInteger(ASectionName, 'Inventory', 2)));
  FSex := TSexType(Max(0, Min(1, Aini.ReadInteger(ASectionName, 'Sex', 0))));
  FGunSkill := Max(0, Min(100, Aini.ReadInteger(ASectionName, 'GunSkill', 50)));
  FEngineerSkill := Max(0, Min(100, Aini.ReadInteger(ASectionName, 'EngineerSkill', 50)));
  SubjectClear;
end;

function TUnitsPlayer.ExistsSubject(ASubjectType: TSubjectType; AIsActive: Boolean = False): Boolean;
var
  i: Integer;
begin
  for i := 0 to FInventory - 1 do
    if Assigned(Subjects[i]) and (Subjects[i].SubjectType = ASubjectType) and (Subjects[i].IsAction or not AIsActive) then
      Exit(True);
  Result := False;
end;

function TUnitsPlayer.FindSubject(ASubjectType: TSubjectType; AIsActive: Boolean = False): TSubject;
var
  i: Integer;
begin
  for i := 0 to FInventory - 1 do
    if Assigned(Subjects[i]) and (Subjects[i].SubjectType = ASubjectType) and (Subjects[i].IsAction or not AIsActive) then
      Exit(Subjects[i]);
  Result := nil;
end;

procedure TUnitsPlayer.Draw;
var
  Frame: Integer;
begin
  Frame := 0;
  //TTextures.QuadGBuffer.HeightMap.GetPixelColor()

  if ExistsSubject(stSpaceSuit, True) then
    Frame := 16;

  TGlobals.QuadRender.SetBlendMode(qbmSrcAlpha);

  if TGlobals.GameModes.Level.ActionPlayer = Self then
    TTextures.Hero.DrawRotFrame(Position, Angle, 1, Frame + 0, $FF008800)
  else
    TTextures.Hero.DrawRotFrame(Position, Angle, 1, Frame + 0);

  if Inventory > 2 then
    TTextures.Hero.DrawRotFrame(Position, Angle, 1, Frame + 1);
  TTextures.Hero.DrawRotFrame(Position, Angle, 1, Frame + 2+FHeadSkin);


                                               {
  TTextures.QuadGBuffer.DrawLight(TVec3f.Create(
    (Position.X - TGlobals.QuadCamera.GetPosition.X) / TGlobals.GWidth,
    (Position.Y - TGlobals.QuadCamera.GetPosition.Y) / TGlobals.GHeight,
    0.04), 512, $FFFFFFFF);
                    }
end;

procedure TUnitsPlayer.Join(const AUnit: TUnitsPlayer);
var
  NewParent: TUnitsPlayer;
begin
  if Assigned(AUnit) then
  begin
    NewParent := Self;
    while Assigned(NewParent) do
    begin
      if NewParent = AUnit then
        Exit;
      NewParent := NewParent.Parent;
    end;
    NewParent := Self;
    while Assigned(NewParent) do
    begin
      if NewParent = AUnit then
        Exit;
      NewParent := NewParent.Child;
    end;

    NewParent := AUnit;
    while Assigned(NewParent.Child) do
      NewParent := NewParent.Child;

    NewParent.Child := Self;
    Parent := NewParent;
  end
  else
  begin
    if Assigned(Child) then
      Child.Parent := Parent;
    if Assigned(Parent) then
      Parent.Child := Child;
    Child := nil;
    Parent := nil;
  end;
end;

function TUnitsPlayer.GetSubject(Index: Integer): TSubject;
begin
  Result := FSubject[Index];
end;

procedure TUnitsPlayer.SetSubject(Index: Integer; Value: TSubject);
begin
  FSubject[Index] := Value;
end;

end.
