unit UUnit;

{$mode delphi}

interface

uses
  Classes, SysUtils, fgl, UGeometry, DOM, UXMLUtils, UItemList;

type

  { TUnitKind }

  TUnitKind = class(TItem)
    PowerAttack: Integer;
    PowerDefense: Integer;
    Moves: Integer;
    Cost: Integer;
    Power: Integer;
    ViewRange: Integer;
    class function GetFields: TItemFields; override;
    procedure GetValue(Index: Integer; out Value); override;
    procedure SetValue(Index: Integer; var Value); override;
    class function GetClassSysName: string; override;
    class function GetClassName: string; override;
  end;

  { TUnitKinds }

  TUnitKinds = class(TItemList)
    constructor Create(FreeObjects: Boolean = True);
    class function GetItemClass: TItemClass; override;
  end;

  TUnit = class
  private
    FId: Integer;
    FMapCell: TObject; // TCell
    FPlayer: TObject; // TPlayer
    procedure SetId(AValue: Integer);
    procedure SetMapCell(AValue: TObject);
    procedure SetPlayer(AValue: TObject);
  public
    Game: TObject; // TGame
    Kind: TUnitKind;
    KindId: Integer; // Temporary
    Power: Integer;
    Moves: Integer;
    Position: TPoint;
    PlayerId: Integer; // Temporary
    procedure FixRefId;
    function GetPowerAttack: Integer;
    function GetPowerDefense: Integer;
    procedure LoadFromNode(Node: TDOMNode);
    procedure SaveToNode(Node: TDOMNode);
    procedure Assign(Source: TUnit);
    constructor Create;
    destructor Destroy; override;
    property Player: TObject read FPlayer write SetPlayer; // TPlayer
    property MapCell: TObject read FMapCell write SetMapCell; // TMapCell;
    property Id: Integer read FId write SetId;
  end;

  { TUnits }

  TUnits = class(TFPGObjectList<TUnit>)
    NewId: Integer;
    Game: TObject; //TGame;
    procedure FixRefId;
    function FindById(Id: Integer): TUnit;
    function GetNewId: Integer;
    procedure LoadFromNode(Node: TDOMNode);
    procedure SaveToNode(Node: TDOMNode);
    function AddNew(Kind: TUnitKind; Power: Integer): TUnit;
    constructor Create(FreeObjects: Boolean = True);
    procedure Assign(Source: TUnits);
  end;


implementation

uses
  UPlayer, UMap, UGame;

resourcestring
  SStack = 'Stack';
  SCost = 'Cost';
  SMoves = 'Moves';
  SPowerAttack =  'Attack power';
  SPowerDefense = 'Defense power';
  SViewRange = 'View range';
  SUnitKind = 'Unit kind';


{ TUnitKind }

class function TUnitKind.GetFields: TItemFields;
begin
  Result := inherited;
  Result.AddField(2, 'Stack', SStack, dtInteger);
  Result.AddField(3, 'Cost', SCost, dtInteger);
  Result.AddField(4, 'Moves', SMoves, dtInteger);
  Result.AddField(5, 'PowerAttack', SPowerAttack, dtInteger);
  Result.AddField(6, 'PowerDefense', SPowerDefense, dtInteger);
  Result.AddField(7, 'ViewRange', SViewRange, dtInteger);
end;

procedure TUnitKind.GetValue(Index: Integer; out Value);
begin
  if Index = 1 then string(Value) := Name
  else if Index = 2 then Integer(Value) := Power
  else if Index = 3 then Integer(Value) := Cost
  else if Index = 4 then Integer(Value) := Moves
  else if Index = 5 then Integer(Value) := PowerAttack
  else if Index = 6 then Integer(Value) := PowerDefense
  else if Index = 7 then Integer(Value) := ViewRange
  else inherited;
end;

procedure TUnitKind.SetValue(Index: Integer; var Value);
begin
  if Index = 1 then Name := string(Value)
  else if Index = 2 then Power := Integer(Value)
  else if Index = 3 then Cost := Integer(Value)
  else if Index = 4 then Moves := Integer(Value)
  else if Index = 5 then PowerAttack := Integer(Value)
  else if Index = 6 then PowerDefense := Integer(Value)
  else if Index = 7 then ViewRange := Integer(Value)
  else inherited;
end;

class function TUnitKind.GetClassSysName: string;
begin
  Result := 'UnitKind';
end;

class function TUnitKind.GetClassName: string;
begin
  Result := SUnitKind;
end;

{ TUnits }

procedure TUnits.FixRefId;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    Items[I].FixRefId;
end;

function TUnits.FindById(Id: Integer): TUnit;
var
  I: Integer;
begin
  I := 0;
  while (I < Count) and (Items[I].Id <> Id) do Inc(I);
  if I < Count then Result := Items[I]
    else Result := nil;
end;

function TUnits.GetNewId: Integer;
begin
  Result := NewId;
  Inc(NewId);
end;

procedure TUnits.LoadFromNode(Node: TDOMNode);
var
  Node2: TDOMNode;
  NewItem: TUnit;
begin
  Count := 0;
  Node2 := Node.FirstChild;
  while Assigned(Node2) and (Node2.NodeName = 'Unit') do begin
    NewItem := TUnit.Create;
    NewItem.Game := Game;
    NewItem.LoadFromNode(Node2);
    Add(NewItem);
    Node2 := Node2.NextSibling;
  end;
end;

procedure TUnits.SaveToNode(Node: TDOMNode);
var
  I: Integer;
  NewNode2: TDOMNode;
begin
  for I := 0 to Count - 1 do
  with Items[I] do begin
    NewNode2 := Node.OwnerDocument.CreateElement('Unit');
    Node.AppendChild(NewNode2);
    SaveToNode(NewNode2);
  end;
end;

function TUnits.AddNew(Kind: TUnitKind; Power: Integer): TUnit;
begin
  Result := TUnit.Create;
  Result.Id := GetNewId;
  Result.Kind := Kind;
  Result.Power := Power;
  Result.Game := Game;
  Add(Result);
end;

constructor TUnits.Create(FreeObjects: Boolean);
begin
  inherited;
  NewId := 1;
end;

procedure TUnits.Assign(Source: TUnits);
var
  I: Integer;
begin
  while Count > Source.Count do
    Delete(Count - 1);
  while Count < Source.Count do
    Add(TUnit.Create);
  for I := 0 to Count - 1 do begin
    Items[I].Assign(Source[I]);
  end;
  NewId := Source.NewId;
end;

{ TUnit }

procedure TUnit.SetId(AValue: Integer);
begin
  if FId = AValue then Exit;
  FId := AValue;
end;

procedure TUnit.SetMapCell(AValue: TObject);
var
  OldValue: TCell;
begin
  if FMapCell = AValue then Exit;
  OldValue := TCell(FMapCell);
  FMapCell := nil;
  if Assigned(OldValue) then TCell(OldValue).OneUnit := nil;
  FMapCell := AValue;
  if Assigned(FMapCell) then TCell(FMapCell).OneUnit := Self;
end;

procedure TUnit.SetPlayer(AValue: TObject);
begin
  if FPlayer = AValue then Exit;
  if Assigned(FPlayer) then TPlayer(FPlayer).Units.Remove(Self);
  FPlayer := AValue;
  if Assigned(FPlayer) then TPlayer(FPlayer).Units.Add(Self);
end;

procedure TUnit.FixRefId;
begin
  Player := TGame(Game).Players.FindById(PlayerId);
  Kind := TUnitKind(TGame(Game).GameSystem.UnitKinds.FindById(KindId));
end;

function TUnit.GetPowerAttack: Integer;
begin
  Result := Power * Kind.PowerAttack;
end;

function TUnit.GetPowerDefense: Integer;
begin
  Result := Power * Kind.PowerDefense;
end;

procedure TUnit.LoadFromNode(Node: TDOMNode);
begin
  Id := ReadInteger(Node, 'Id', 0);
  Power := ReadInteger(Node, 'Power', 0);
  Moves := ReadInteger(Node, 'Moves', 0);
  KindId := ReadInteger(Node, 'Kind', 0);
  PlayerId := ReadInteger(Node, 'Player', 0);
end;

procedure TUnit.SaveToNode(Node: TDOMNode);
begin
  WriteInteger(Node, 'Id', Id);
  WriteInteger(Node, 'Power', Power);
  WriteInteger(Node, 'Moves', Moves);
  if Assigned(Kind) then WriteInteger(Node, 'Kind', Kind.Id)
    else WriteInteger(Node, 'Kind', 0);
  if Assigned(Player) then WriteInteger(Node, 'Player', TPlayer(Player).Id)
    else WriteInteger(Node, 'Player', 0);
end;

procedure TUnit.Assign(Source: TUnit);
begin
  Kind := Source.Kind;
  Moves := Source.Moves;
  Power := Source.Power;
  Id := Source.Id;
end;

constructor TUnit.Create;
begin
  Player := nil;
end;

destructor TUnit.Destroy;
begin
  inherited Destroy;
end;

{ TUnitKinds }

constructor TUnitKinds.Create(FreeObjects: Boolean);
begin
  inherited;
end;

class function TUnitKinds.GetItemClass: TItemClass;
begin
  Result := TUnitKind;
end;

end.

