unit CustomObject;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.ValEdit,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls, TypInfo, Vcl.ExtCtrls, System.Generics.Collections, IniFiles;

type
  TObjectType = (
    otNone = 0,
    otStartPoint = 1,
    otButton = 2,
    otSwitch = 3,
    otDoorClosed = 4,
    otDoorOpened = 5,
    otSubject = 6,
    otTimer = 7,
    otNot = 8,
    otConsole = 9,
    otTrap = 10,
    otLight = 11,
    otActionRegion = 12,
    otIPADConsole = 13,
    otMonster = 14,
    otCapacitor = 15,
    otOr = 16,
    otDamageRegion = 17
  );

  TObjectValues = record
    Name: String;
    Value: Variant;
  end;

  TCustomObject = class
  private
    FID: Integer;
    FPosition: TPoint;
    FParentList: TList<TCustomObject>;
    FChildList: TList<TCustomObject>;
    FValues: array[0..5] of TObjectValues;
    FValueCount: Integer;
    FTalks: TStringList;
    class var FNextID: Word;
  public
    class var ObjectList: TList<TCustomObject>;
    class function ObjByID(const AID: Integer): TCustomObject;
    constructor Create(AObjectType: TObjectType; APoint: TPoint; AID: Word = 0);
    destructor Destroy; override;
    procedure Learning(AObj: TCustomObject);
    procedure ParentAdd(AObj: TCustomObject);
    procedure ChildAdd(AObj: TCustomObject);
    procedure Save(Aini: TIniFile); virtual;
    procedure Load(Aini: TIniFile); virtual;
    procedure LoadEditList(AValueListEditor: TValueListEditor); virtual;
    procedure SaveEditList(const KeyName: string; const KeyValue: string); virtual;
    procedure AddValues(AName: String; AValue: Variant);
  published
    function GetValue(Name: String): Variant;
    property ID: Integer read FID write FID;
    property Position: TPoint read FPosition write FPosition;
    property ParentList: TList<TCustomObject> read FParentList;
    property ChildList: TList<TCustomObject> read FChildList;
    property Talks: TStringList read FTalks;
  end;

implementation

function TCustomObject.GetValue(Name: String): Variant;
var
  i: Integer;
begin
  for i := 0 to FValueCount - 1 do
    if AnsiCompareText(FValues[i].Name, Name) = 0 then
      Exit(FValues[i].Value);
  Result := null;
end;

procedure TCustomObject.LoadEditList(AValueListEditor: TValueListEditor);
var
  i: Integer;
begin
  AValueListEditor.InsertRow('Id', IntToStr(FID), true);
  AValueListEditor.ItemProps['Id'].ReadOnly := True;

  for i := 0 to FValueCount - 1 do
    AValueListEditor.InsertRow(FValues[i].Name, VarToStr(FValues[i].Value), true);
end;

procedure TCustomObject.SaveEditList(const KeyName: string; const KeyValue: string);
var
  i: Integer;
begin
  for i := 0 to FValueCount - 1 do
    if FValues[i].Name = KeyName then
    begin
      FValues[i].Value := KeyValue;
      Break;
    end;
end;

procedure TCustomObject.AddValues(AName: String; AValue: Variant);
begin
  FValues[FValueCount].Name := AName;
  FValues[FValueCount].Value := AValue;
  Inc(FValueCount);
end;

constructor TCustomObject.Create(AObjectType: TObjectType; APoint: TPoint; AID: Word = 0);
var
  i: Integer;
begin
  FValueCount := 0;
  case AObjectType of
    otButton: AddValues('Radius', 1);
    otConsole: AddValues('Progress', 1);
    otIPADConsole:
      begin
        AddValues('Progress', 1);
        AddValues('Name', '');
      end;
    otTrap:
      begin
        AddValues('Radius', 12);
        AddValues('Damage', 50);
      end;
    otLight:
      begin
        AddValues('Radius', 1);
        AddValues('Height', 0.015);
        AddValues('Color', 'FFFFFFFF');
      end;
    otTimer: AddValues('Time', 1);
    otSubject:
      begin
        AddValues('Type', 1);
        AddValues('Radius', 24);
        AddValues('Count', 1);
        AddValues('Damage', 0);
      end;
    otActionRegion:
      begin
        AddValues('Left', -1);
        AddValues('Top', -1);
        AddValues('Width', 3);
        AddValues('Height', 3);
      end;
    otDamageRegion:
      begin
        AddValues('Left', -1);
        AddValues('Top', -1);
        AddValues('Width', 3);
        AddValues('Height', 3);
        AddValues('Damage', 10);
      end;
    otStartPoint: AddValues('Angle', 0);
    otMonster:
      begin
        AddValues('Scale', 1);
        AddValues('HP', 100);
        AddValues('Damage', 50);
      end;
  end;

  FTalks := TStringList.Create;

  if AID = 0 then
  begin
    Inc(FNextID);
    FID := FNextID;
  end
  else
  begin
    if AID >= FNextID then
      FNextID := AID;
    FID := AID;
  end;
  FPosition := APoint;
  ObjectList.Add(Self);
  FParentList := TList<TCustomObject>.Create;
  FChildList := TList<TCustomObject>.Create;
end;

destructor TCustomObject.Destroy;
var
  i: Integer;
begin
  for i := 0 to FParentList.Count - 1 do
    FParentList[i].Learning(Self);
  FParentList.Free;

  for i := 0 to FChildList.Count - 1 do
    FChildList[i].Learning(Self);
  FChildList.Free;
  ObjectList.Remove(Self);

  FTalks.Free;
  inherited;
end;

procedure TCustomObject.Save(Aini: TIniFile);
var
  i: Integer;
  Str: String;
begin
  Str := 'Join_' + IntToStr(ID);
  for i := 0 to FParentList.Count - 1 do
    Aini.WriteInteger(Str, 'ID_' + IntToStr(i), FParentList[i].ID);

  Str := 'Obj_' + IntToStr(ID);
  for i := 0 to FValueCount - 1 do
    Aini.WriteString(Str, FValues[i].Name, VarToStr(FValues[i].Value));

  Str := 'Talks_' + IntToStr(ID);
  for i := 0 to Talks.Count - 1 do
    Aini.WriteString(Str, 'ID_' + IntToStr(i), Talks[i]);
end;

procedure TCustomObject.Load(Aini: TIniFile);
var
  Strings: TStringList;
  i: Integer;
  Str: String;
  Obj: TCustomObject;
begin
  Strings := TStringList.Create;
  Str := 'Join_' + IntToStr(ID);
  Aini.ReadSection(Str, Strings);
  for i := 0 to Strings.Count - 1 do
  begin
    Obj := ObjByID(Aini.ReadInteger(Str, Strings[i], 0));
    if Assigned(Obj) then
    begin
      ParentAdd(Obj);
      Obj.ChildAdd(Self);
    end;
  end;
  Strings.Clear;

  Str := 'Obj_' + IntToStr(ID);
  Aini.ReadSection(Str, Strings);
  if Strings.Count > 0 then
  begin
    FValueCount := 0;
    for i := 0 to Strings.Count - 1 do
      AddValues(Strings[i], Aini.ReadString(Str, Strings[i], '0'));
  end;
  Strings.Clear;

  Str := 'Talks_' + IntToStr(ID);
  Aini.ReadSection(Str, Strings);
  for i := 0 to Strings.Count - 1 do
    Talks.Add(Aini.ReadString(Str, Strings[i], ''));

  Strings.Free;
end;

procedure TCustomObject.Learning(AObj: TCustomObject);
begin
  FParentList.Remove(AObj);
  FChildList.Remove(AObj);
end;

procedure TCustomObject.ParentAdd(AObj: TCustomObject);
begin
  if Self <> AObj then
    FParentList.Add(AObj);
end;

procedure TCustomObject.ChildAdd(AObj: TCustomObject);
begin
  if Self <> AObj then
    FChildList.Add(AObj);
end;

class function TCustomObject.ObjByID(const AID: Integer): TCustomObject;
var
  i: Integer;
begin
  if AID = 0 then
    Exit(nil);
  for i := 0 to ObjectList.Count - 1 do
    if ObjectList[i].ID = AID then
      Exit(ObjectList[i]);
  Result := nil;
end;

initialization
  TCustomObject.FNextID := 0;
  TCustomObject.ObjectList := TList<TCustomObject>.Create;

finalization
  if Assigned(TCustomObject.ObjectList) then
    TCustomObject.ObjectList.Free;

end.
