//
// classes.script
//

// init global classes and variables
type TColor = class
   r, g, b, a : Float = 1;
end;

type TVec3f = class
   x, y, z : Float = 0;
end;

type TPos3f = class
   x, y, z : Float = 0;
end;

type TPos2f = class
   x, y : Float = 0;
end;

type TRect = class
   minx, miny, maxx, maxy : Float = 0;
   procedure ToString(var s : string);
   begin
      s:='{'+FloatToStr(minx)+' , '+FloatToStr(miny)+' , '+FloatToStr(maxx)+' , '+FloatToStr(maxy)+'}';
   end;
end;

type TBox = class
   minx, miny, minz, maxx, maxy, maxz : Float = 0;
end;

// TProgress
type TProgress = class
   lastprogresstime : Float;
   progresstick : Integer;
   soundlastprogresstime : Float;
   soundprogresstick : Integer;
   soundcounterlastprogresstime : Float;
   soundcounterprogresstick : Integer;
   lastprogresshistorytime : Float;
   lastmiscplsecmaxtime : Float;
   lastpoolplsecmaxtime : Float;
   lastsearchenemycountmidtime : Float;
   lastsearchenemycountsumtime : Float;
   lastsearchenemycountertime : Float;
   lastprogressstatisticstime : Float;
   lastprogresstopzonestime : Float;
   lastupdateenemyticktime : Float;
   lastupdateenemytickteam : Integer;
   lastsoundprogressfreqtime : array [0..5] of Float;
   lastsoundprogresscounterfreqtime : array [0..5] of Float;
   lastupdateenemyinfotime : Float;
end;

// lan types
type TMapSettingsAdditional = class
   activeoption : Integer;
   startingunits : Integer;
   balloon : Integer;
   cannons : Integer;
   peacetime : Integer;
   century18 : Integer;
   capture : Integer;
   marketdip : Integer;
   teams : Integer;
   autosave : Integer;
   limit : Integer;
   gamespeed : Integer;
   adviserassistant : Integer;
end;

type TMapSettingsGen = class
   randkey0 : Integer;
   randkey1 : Integer;
   mapsize : Integer;
   terraintype : Integer;
   relieftype : Integer;
   resourcestart : Integer;
   resourcemines : Integer;
   season : Integer;
end;

type TMapSettings = class
   gen : TMapSettingsGen;
   additional : TMapSettingsAdditional;
end;

type TMapPlayerInfo = class
   sic, si1, si2, si3 : Integer;
   snc, sn1, sn2, sn3 : String;
end;

type TMapPlayer = class
   id : Integer;
   cid : Integer;
   csid : String;
   name : String;
   team : Integer;
   color : Integer;
   lanid : Integer;
   startx : Float;
   starty : Float;
   aidifficulty : Integer;
   bexists : Boolean;
   bai : Boolean;
   bhuman : Boolean;
   bclosed : Boolean;
   bready : Boolean;
   bloaded : Boolean;
   bleave : Boolean;
end;

type TMap = class
   name : String;
   gamestage : Integer;
   lastenvuid : Integer;
   dlcs : Integer;
   brating : Boolean;
   bbattle : Boolean;
   battlestage : Integer;
   battleind : Integer;
   battlemap : String;
   settings : TMapSettings;
   players : array [0..gc_MaxPlayerCount-1] of TMapPlayer;
   playersinfo : array [0..gc_MaxPlayerCount-1] of TMapPlayerInfo;
end;

// TInt32
//
type TInt32 = class
private
   data: array[0..3] of byte;
public
   function GetInt32: Integer; begin var p: Pointer = data; Result:=Integer(p); end;
   function GetByte0: byte; begin Result:=data[0]; end;
   function GetByte1: byte; begin Result:=data[1]; end;
   function GetByte2: byte; begin Result:=data[2]; end;
   function GetByte3: byte; begin Result:=data[3]; end;
   procedure SetInt32(const val: Integer); begin var p: Pointer = data; Integer(p):=val; end;
   procedure SetByte0(const val: byte); begin data[0]:=val; end;
   procedure SetByte1(const val: byte); begin data[1]:=val; end;
   procedure SetByte2(const val: byte); begin data[2]:=val; end;
   procedure SetByte3(const val: byte); begin data[3]:=val; end;
end;

// TIntegerList
//
// Integer class_ for lists, introduces common behaviours.
type TIntegerList = class
private
{ Private Declarations }
   const cDefaultListGrowthDelta = 16;
   
   fCount, fCapacity, fGrowthDelta: Integer;
   fBufferItem: Pointer;
   fPackMode: Boolean; // If True, Clear/Clean deallocate memory, Default is True
   fDebug: Boolean; // Logging constructor and destructor
   // Default fResetsMemory is True
   fExternalMemory, fResetsMemory: Boolean;
protected
{ Protected Declarations }
   // The base list pointer (untyped)
   fBaseList: Pointer;
   // Must be defined by all subclasses in their constructor(s)
   fItemSize: Integer;
   
   function BufferItem: Pointer;
   begin
      if (fBufferItem=nil) and (fItemSize>0) then
      GetMem(fBufferItem, fItemSize);
      result:=fBufferItem;
   end;
public
{ Public Declarations }
   function GetBaseList: Pointer; begin result:=fBaseList; end;
   procedure SetDebug(const val: Boolean); begin fDebug:=val; end;
   function GetDebug: Boolean; begin result:=fDebug; end;
   // List growth granularity.
   procedure SetGrowthDelta(const val: Integer); begin fGrowthDelta:=val; end;
   function GetGrowthDelta: Integer; begin result:=fGrowthDelta; end;
   // If true (default value) adjusting count will reset added values.
   // Switching this option to true will turn off this memory reset,
   // which can improve performance is that having empty values isn't
   // required.
   procedure SetResetsMemory(const val: Boolean); begin fResetsMemory:=val; end;
   function GetResetsMemory: Boolean; begin result:=fResetsMemory; end;
   procedure SetPackMode(const val: Boolean); begin fPackMode:=val; end;
   function GetPackMode: Boolean; begin result:=fPackMode; end;
   // Only function where list may be alloc'ed & freed.
   // Resizes the array pointed by FBaseList, adjust the subclass's
   // typed pointer accordingly if any.
   procedure SetCapacity(const newcapacity: Integer);
   begin
      if newcapacity<>fCapacity then begin
         if fExternalMemory then begin
            fExternalMemory:=false;
            fBaseList:=nil;
         end;
         ReallocMem(fBaseList, newcapacity*fItemSize);
         if fBaseList=nil then
         fCapacity:=0
         else fCapacity:=newcapacity;
      end;
   end;
   // Current list capacity.
   function GetCapacity: Integer; begin result:=fCapacity; end;
   // Nb of items in the list.
   // When assigning a Count, added items are reset to zero.
   procedure SetCount(const val: Integer);
   begin
      if val>=0 then begin
         if val>fCapacity then
         SetCapacity(val);
         if (val>fCount) and (fResetsMemory) then begin
            var p: Pointer=fBaseList+fItemSize*fCount;
            if p<>nil then
            FillMem(p, (val-fCount)*fItemSize, 0);
         end;
         fCount:=val;
      end;
   end;
   function GetCount: Integer; begin result:=fCount; end;
   procedure SetItemSize(const val: Integer);
   begin
      if fItemSize<>val then begin
         SetCount(0);
         SetCapacity(0);
         fItemSize:=val;
         if fBufferItem<>nil then begin
            FreeMem(fBufferItem);
            fBufferItem:=nil;
         end;
      end;
   end;
   function GetItemSize: Integer; begin result:=fItemSize; end;
   
   // Empties the list without altering capacity.
   procedure Flush;
   begin
      SetCount(0);
   end;
   
   // Empties the list and release.
   procedure Clear;
   begin
      SetCount(0);
      if fPackMode then
      SetCapacity(0);
   end;
   
   procedure Create(itemsize: Integer);
   begin
      if fItemSize<>itemsize then begin
         fPackMode:=false;
         fResetsMemory:=true;
         fGrowthDelta:=cDefaultListGrowthDelta;
         SetItemSize(itemsize);
         if fDebug then
         Log('procedure TIntegerList.Create('+IntToStr(fItemSize)+'); BaseList='+IntToStr(PointerToAddr(fBaseList))+';');
      end;
   end;
   
   constructor TIntegerList;
   begin
      if fDebug then
      Log('constructor TIntegerList;');
      Create(SizeOf(Integer));
   end;
   
   procedure Assign(var src: TIntegerList);
   begin
      SetCapacity(src.GetCount);
      fGrowthDelta:=src.GetGrowthDelta;
      fCount:=fCapacity;
   end;
   
   procedure AddNulls(const nbvals: Integer);
   begin
      if nbvals+fCount>fCapacity then
      SetCapacity(nbvals+fCount);
      var p: Pointer=fBaseList+fCount*fItemSize;
      FillMem(p, nbvals*fItemSize, 0);
      fCount:=fCount+nbvals;
   end;
   
   procedure InsertNulls(const index, nbvals: Integer);
   begin
      var nc: Integer;
      if (index>=0) and (index<fCount) and (nbvals>0) then begin
         var p: Pointer;
         var nc: Integer=fCount+nbvals;
         if nc>fCapacity then
         SetCapacity(nc);
         if index<fCount then begin
            p:=fBaseList+(index+nbvals)*fItemSize;
            MoveMem(fBaseList+index*fItemSize, p, (fCount-index)*fItemSize);
         end;
         p:=fBaseList+index*fItemSize;
         FillMem(p, nbvals*fItemSize, 0);
         fCount:=nc;
      end;
   end;
   
   procedure AdjustCapacityToAtLeast(const size: Integer);
   begin
      if fCapacity<size then
      SetCapacity(size);
   end;
   
   function DataSize: Integer;
   begin
      result:=fItemSize*fCount;
   end;
   
   // Tell the list to use the specified range instead of its own.
   // rangeCapacity should be expressed in bytes.
   // The allocated memory is NOT managed by the list, current content
   // if copied to the location, if the capacity is later changed, regular
   // memory will be allocated, and the specified range no longer used.
   procedure UseMemory(var rangestart: Pointer; const rangecapacity: Integer);
   begin
      var rangecap: Integer=rangecapacity div fItemSize;
      if rangecap<fCount then exit;
      // transfer data
      MoveMem(fBaseList, rangestart, fCount*fItemSize);
      if not fExternalMemory then begin
         FreeMem(fBaseList);
         fExternalMemory:=true;
      end;
      fBaseList:=rangestart;
      fCapacity:=rangecap;
      SetCapacity(fCapacity); // notify subclasses
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<fCount) then begin
         Dec(fCount, 1);
         if index<fCount then begin
            var p: Pointer=fBaseList+index*fItemSize;
            MoveMem(fBaseList+(index+1)*fItemSize, p, (fCount-index)*fItemSize);
         end;
      end;
   end;
   
   procedure DeleteItems(const index, nbvals: Integer);
   begin
      if (index>=0) and (index<fCount) and (nbvals>0) then begin
         if index+nbvals<fCount then begin
            var p: Pointer=fBaseList+index*fItemSize;
            MoveMem(fBaseList+(index+nbvals)*fItemSize, p, (fCount-index-nbvals)*fItemSize);
         end;
         Dec(fCount, nbvals);
      end;
   end;
   
   procedure Exchange(const index1, index2: Integer);
   begin
      var buf : Integer;
      var p : Pointer;
      if (index1<>index2) and (index1>=0) and (index1<fCount) and (index2>=0) and (index2<fCount) then begin
         if fItemSize=SizeOf(Integer) then begin
            p:=fBaseList;
            buf:=Integer(p+index1*fItemSize);
            Integer(p+index1*fItemSize):=Integer(p+index2*fItemSize);
            Integer(p+index2*fItemSize):=buf;
            end else begin
            BufferItem;
            MoveMem(fBaseList+index1*fItemSize, fBufferItem, fItemSize);
            var p: Pointer;
            p:=fBaseList+index1*fItemSize;
            MoveMem(fBaseList+index2*fItemSize, p, fItemSize);
            p:=fBaseList+index2*fItemSize;
            MoveMem(fBufferItem, p, fItemSize);
         end;
      end;
   end;
   
   procedure Move(const curindex, newindex: Integer);
   begin
      if (curindex<>newindex) and (newindex>=0) and (newindex<fCount) and (curindex>=0) and (curindex<fCount) then begin
         BufferItem;
         if fItemSize=SizeOf(Integer) then
         Integer(fBufferItem):=Integer(fBaseList+curindex*fItemSize)
         else MoveMem(fBaseList+curindex*fItemSize, fBufferItem, fItemSize);
         if curindex<newindex then begin
            // curindex+1 necessarily exists since curindex<newindex and newindex<fCount
            var p: Pointer=fBaseList+curindex*fItemSize;
            MoveMem(fBaseList+(curindex+1)*fItemSize, p,(newindex-curindex-1)*fItemSize);
            end else begin
            // newindex+1 necessarily exists since newindex<curindex and curindex<fCount
            var p: Pointer=fBaseList+(newindex+1)*fItemSize;
            MoveMem(fBaseList+newindex*fItemSize, p, (curindex-newindex-1)*fItemSize);
         end;
         if fItemSize=SizeOf(Integer) then
         Integer(fBaseList+newindex*fItemSize):=Integer(fBufferItem)
         else begin
            var p: Pointer=fBaseList+newindex*fItemSize;
            MoveMem(fBufferItem, p, fItemSize);
         end;
      end;
   end;
   
   procedure Reverse;
   begin
      var s, e : Integer;
      s:=0; e:=fCount-1;
      while s<e do begin
         Exchange(s, e);
         Inc(s, 1); Dec(e, 1);
      end;
   end;
   
   procedure Destroy;
   begin
      SetItemSize(0);
      if fDebug then
      Log('procedure TIntegerList.Destroy; fItemSize='+IntToStr(fItemSize)+'; fCount='+IntToStr(fCount)+'; fCapacity='+IntToStr(fCapacity)+'; fBaseList='+IntToStr(PointerToAddr(fBaseList))+';');
   end;
   
   destructor TIntegerList;
   begin
      if fDebug then
      Log('destructor TIntegerList;');
      Destroy;
   end;
   
   function Set(const src: Integer; index: Integer): Boolean;
   begin
      if (index>=0) and (index<fCount) and (fItemSize=SizeOf(src)) then begin
         Integer(fBaseList+index*fItemSize):=src;
         result:=true;
      end else result:=false;
   end;
   
   function Get(index: Integer): Integer;
   begin
      if (index>=0) and (index<fCount) and (fItemSize=SizeOf(result)) then
      result:=Integer(fBaseList+index*fItemSize)
      else result:=0;
   end;
   
   function AddNull: Integer;
   begin
      if fItemSize>0 then begin
         result:=fCount;
         if result=fCapacity then
         SetCapacity(fCapacity+fGrowthDelta);
         //fBaseList[result]:=item;
         inc(fCount, 1);
      end else result:=-1;
   end;
   
   function Add(const src: Integer): Integer;
   begin
      if fItemSize=SizeOf(src) then begin
         result:=AddNull;
         if result<>-1 then
         Integer(fBaseList+result*fItemSize):=src;
      end else result:=-1;
   end;
   
   function IndexOf(const src: Integer): Integer;
   begin
      result:=-1;
      var i: Integer;
      for i:=0 to GetCount-1 do begin
         if Get(i)=src then begin
            result:=i;
            break;
         end;
      end;
   end;
   
   procedure Remove(const src: Integer);
   begin
      Delete(IndexOf(src));
   end;
   
   procedure := (var source : TIntegerList);
   begin
      self.Clear;
      var i : Integer;
      for i:=0 to source.GetCount-1 do
      self.Add(source.Get(i));
   end;
end;

// TPtrList
//
// Base class_ for lists, introduces common behaviours.
type TPtrList = class
private
{ Private Declarations }
   const cDefaultListGrowthDelta = 16;
   
   fCount, fCapacity, fGrowthDelta: Integer;
   fBufferItem: Pointer;
   fPackMode: Boolean; // If True, Clear/Clean deallocate memory, Default is True
   fDebug: Boolean; // Logging constructor and destructor
   // Default fResetsMemory is True
   fExternalMemory, fResetsMemory: Boolean;
protected
{ Protected Declarations }
   // The base list pointer (untyped)
   fBaseList: Pointer;
   // Must be defined by all subclasses in their constructor(s)
   fItemSize: Integer;
   
   function BufferItem: Pointer;
   begin
      if (fBufferItem=nil) and (fItemSize>0) then
      GetMem(fBufferItem, fItemSize);
      result:=fBufferItem;
   end;
public
{ Public Declarations }
   function GetBaseList: Pointer; begin result:=fBaseList; end;
   procedure SetDebug(const val: Boolean); begin fDebug:=val; end;
   function GetDebug: Boolean; begin result:=fDebug; end;
   // List growth granularity.
   procedure SetGrowthDelta(const val: Integer); begin fGrowthDelta:=val; end;
   function GetGrowthDelta: Integer; begin result:=fGrowthDelta; end;
   // If true (default value) adjusting count will reset added values.
   // Switching this option to true will turn off this memory reset,
   // which can improve performance is that having empty values isn't
   // required.
   procedure SetResetsMemory(const val: Boolean); begin fResetsMemory:=val; end;
   function GetResetsMemory: Boolean; begin result:=fResetsMemory; end;
   procedure SetPackMode(const val: Boolean); begin fPackMode:=val; end;
   function GetPackMode: Boolean; begin result:=fPackMode; end;
   // Only function where list may be alloc'ed & freed.
   // Resizes the array pointed by FBaseList, adjust the subclass's
   // typed pointer accordingly if any.
   procedure SetCapacity(const newcapacity: Integer);
   begin
      if newcapacity<>fCapacity then begin
         if fExternalMemory then begin
            fExternalMemory:=false;
            fBaseList:=nil;
         end;
         ReallocMem(fBaseList, newcapacity*fItemSize);
         if fBaseList=nil then
         fCapacity:=0
         else fCapacity:=newcapacity;
      end;
   end;
   // Current list capacity.
   function GetCapacity: Integer; begin result:=fCapacity; end;
   // Nb of items in the list.
   // When assigning a Count, added items are reset to zero.
   procedure SetCount(const val: Integer);
   begin
      if val>=0 then begin
         if val>fCapacity then
         SetCapacity(val);
         if (val>fCount) and (fResetsMemory) then begin
            var p: Pointer=fBaseList+fItemSize*fCount;
            if p<>nil then
            FillMem(p, (val-fCount)*fItemSize, 0);
         end;
         fCount:=val;
      end;
   end;
   function GetCount: Integer; begin result:=fCount; end;
   procedure SetItemSize(const val: Integer);
   begin
      SetCount(0);
      SetCapacity(0);
      fItemSize:=val;
      if fBufferItem<>nil then begin
         FreeMem(fBufferItem);
         fBufferItem:=nil;
      end;
   end;
   function GetItemSize: Integer; begin result:=fItemSize; end;
   
   // Empties the list without altering capacity.
   procedure Flush;
   begin
      SetCount(0);
   end;
   
   // Empties the list and release.
   procedure Clear;
   begin
      SetCount(0);
      if fPackMode then
      SetCapacity(0);
   end;
   
   procedure Create;
   begin
      if fItemSize<>SizeOf(Pointer) then begin
         fPackMode:=false;
         fResetsMemory:=true;
         fGrowthDelta:=cDefaultListGrowthDelta;
         SetItemSize(SizeOf(Pointer)); //SetItemSize(itemsize);
         if fDebug then
         Log('procedure TPtrList.Create('+IntToStr(fItemSize)+'); fBaseList='+IntToStr(PointerToAddr(fBaseList))+';');
      end;
   end;
   
   constructor TPtrList;
   begin
      if fDebug then
      Log('constructor TPtrList;');
      Create; //Create(SizeOf(Pointer));
   end;
   
   procedure Assign(var src: TPtrList);
   begin
      SetCapacity(src.GetCount);
      fGrowthDelta:=src.GetGrowthDelta;
      fCount:=fCapacity;
   end;
   
   procedure AddNulls(const nbvals: Integer);
   begin
      if nbvals+fCount>fCapacity then
      SetCapacity(nbvals+fCount);
      var p: Pointer=fBaseList+fCount*fItemSize;
      FillMem(p, nbvals*fItemSize, 0);
      fCount:=fCount+nbvals;
   end;
   
   procedure InsertNulls(const index, nbvals: Integer);
   begin
      var nc: Integer;
      if (index>=0) and (index<fCount) and (nbvals>0) then begin
         var p: Pointer;
         var nc: Integer=fCount+nbvals;
         if nc>fCapacity then
         SetCapacity(nc);
         if index<fCount then begin
            p:=fBaseList+(index+nbvals)*fItemSize;
            MoveMem(fBaseList+index*fItemSize, p, (fCount-index)*fItemSize);
         end;
         p:=fBaseList+index*fItemSize;
         FillMem(p, nbvals*fItemSize, 0);
         fCount:=nc;
      end;
   end;
   
   procedure AdjustCapacityToAtLeast(const size: Integer);
   begin
      if fCapacity<size then
      SetCapacity(size);
   end;
   
   function DataSize: Integer;
   begin
      result:=fItemSize*fCount;
   end;
   
   // Tell the list to use the specified range instead of its own.
   // rangeCapacity should be expressed in bytes.
   // The allocated memory is NOT managed by the list, current content
   // if copied to the location, if the capacity is later changed, regular
   // memory will be allocated, and the specified range no longer used.
   procedure UseMemory(var rangestart: Pointer; const rangecapacity: Integer);
   begin
      var rangecap: Integer=rangecapacity div fItemSize;
      if rangecap<fCount then exit;
      // transfer data
      MoveMem(fBaseList, rangestart, fCount*fItemSize);
      if not fExternalMemory then begin
         FreeMem(fBaseList);
         fExternalMemory:=true;
      end;
      fBaseList:=rangestart;
      fCapacity:=rangecap;
      SetCapacity(fCapacity); // notify subclasses
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<fCount) then begin
         Dec(fCount, 1);
         if index<fCount then begin
            var p: Pointer=fBaseList+index*fItemSize;
            MoveMem(fBaseList+(index+1)*fItemSize, p, (fCount-index)*fItemSize);
         end;
      end;
   end;
   
   procedure Remove(const ptr : Pointer);
   begin
      if ptr <> nil then
      begin
         var i : Integer;
         for i := 0 to fCount-1 do
         if AddrToPointer(Integer(fBaseList + i*fItemSize)) = ptr then
         begin
            Delete(i);
            exit;
         end;
      end;
   end;
   
   procedure DeleteItems(const index, nbvals: Integer);
   begin
      if (index>=0) and (index<fCount) and (nbvals>0) then begin
         if index+nbvals<fCount then begin
            var p: Pointer=fBaseList+index*fItemSize;
            MoveMem(fBaseList+(index+nbvals)*fItemSize, p, (fCount-index-nbvals)*fItemSize);
         end;
         Dec(fCount, nbvals);
      end;
   end;
   
   procedure Exchange(const index1, index2: Integer);
   begin
      var buf : Integer;
      var p : Pointer;
      if (index1<>index2) and (index1>=0) and (index1<fCount) and (index2>=0) and (index2<fCount) then begin
         if fItemSize=SizeOf(Pointer) then begin
            p:=fBaseList;
            buf:=Integer(p+index1*fItemSize);
            Integer(p+index1*fItemSize):=Integer(p+index2*fItemSize);
            Integer(p+index2*fItemSize):=buf;
            end else begin
            BufferItem;
            MoveMem(fBaseList+index1*fItemSize, fBufferItem, fItemSize);
            var p: Pointer;
            p:=fBaseList+index1*fItemSize;
            MoveMem(fBaseList+index2*fItemSize, p, fItemSize);
            p:=fBaseList+index2*fItemSize;
            MoveMem(fBufferItem, p, fItemSize);
         end;
      end;
   end;
   
   procedure Move(const curindex, newindex: Integer);
   begin
      if (curindex<>newindex) and (newindex>=0) and (newindex<fCount) and (curindex>=0) and (curindex<fCount) then begin
         BufferItem;
         if fItemSize=SizeOf(Pointer) then
         Integer(fBufferItem):=Integer(fBaseList+curindex*fItemSize)
         else MoveMem(fBaseList+curindex*fItemSize, fBufferItem, fItemSize);
         if curindex<newindex then begin
            // curindex+1 necessarily exists since curindex<newindex and newindex<fCount
            var p: Pointer=fBaseList+curindex*fItemSize;
            MoveMem(fBaseList+(curindex+1)*fItemSize, p,(newindex-curindex-1)*fItemSize);
            end else begin
            // newindex+1 necessarily exists since newindex<curindex and curindex<fCount
            var p: Pointer=fBaseList+(newindex+1)*fItemSize;
            MoveMem(fBaseList+newindex*fItemSize, p, (curindex-newindex-1)*fItemSize);
         end;
         if fItemSize=SizeOf(Pointer) then
         Integer(fBaseList+newindex*fItemSize):=Integer(fBufferItem)
         else begin
            var p: Pointer=fBaseList+newindex*fItemSize;
            MoveMem(fBufferItem, p, fItemSize);
         end;
      end;
   end;
   
   procedure Reverse;
   begin
      var s, e : Integer;
      s:=0; e:=fCount-1;
      while s<e do begin
         Exchange(s, e);
         Inc(s, 1); Dec(e, 1);
      end;
   end;
   
   procedure Destroy;
   begin
      SetItemSize(0);
      if fDebug then
      Log('procedure TPtrList.Destroy; fItemSize='+IntToStr(fItemSize)+'; fCount='+IntToStr(fCount)+'; fCapacity='+IntToStr(fCapacity)+'; fBaseList='+IntToStr(PointerToAddr(fBaseList))+';');
   end;
   
   destructor TPtrList;
   begin
      if fDebug then
      Log('destructor TPtrList;');
      Destroy;
   end;
   
   function SetPointer(const src: Pointer; index: Integer): Boolean;
   begin
      if (index>=0) and (index<fCount) and (fItemSize=SizeOf(src)) then begin
         Integer(fBaseList+index*fItemSize):=PointerToAddr(src);
         result:=true;
      end else result:=false;
   end;
   
   function GetPointer(index: Integer): Pointer;
   begin
      if (index>=0) and (index<fCount) and (fItemSize=SizeOf(result)) then
      result:=AddrToPointer(Integer(fBaseList+index*fItemSize))
      else begin
         ErrorLog('TPtrList.GetPointer : index = '+IntToStr(index)+' fcount='+IntToStr(fCount)+' fitemsime'+IntToStr(fitemsize)+' result=nil');
         result:=nil;
      end;
   end;
   
   function AddNull: Integer;
   begin
      if fItemSize>0 then begin
         result:=fCount;
         if result=fCapacity then
         SetCapacity(fCapacity+fGrowthDelta);
         //fBaseList[result]:=item;
         inc(fCount, 1);
      end else result:=-1;
   end;
   
   function AddPointer(const src: Pointer): Integer;
   begin
      if fItemSize=SizeOf(src) then begin
         result:=AddNull;
         if result<>-1 then
         Integer(fBaseList+result*fItemSize):=PointerToAddr(src);
      end else result:=-1;
   end;
   
   procedure := (var source : TPtrList);
   begin
      self.Clear;
      var i : Integer;
      for i:=0 to source.GetCount-1 do
      self.AddPointer(source.GetPointer(i));
   end;
end;

// TSquad
//
type TSquad = class
private
{ Private Declarations }
   fList: TIntegerList;
public
{ Public Declarations }
   fFormation: Integer;
   fBaseCount: Integer;
   fStandGround: Boolean;
   fSearchVictim: Boolean;
   fSelected: Boolean;
   fIndex: Integer;
   fOfficerID: Integer;
   fPlIndex: Integer;
   fGroupName: String;
   fHoldMode: Boolean;
   fAddDamage: Integer;
   fAddShield: Integer;
   fAddDamageHold: Integer;
   fAddShieldHold: Integer;
   fDirX: Float;
   fDirZ: Float;
   fWidth: Integer;
   fHeight: Integer;
   fMoveCount : Integer;
   fAttackCount : Integer;
   fDelayCount : Integer;
   fUID : Integer;
   fTag : Float;
   fAgressive : Boolean;
   fAttackMode : Boolean;
   fHoldModeProgress : Float;
   fNeedRebuild : Boolean;
   fCurX : Float;
   fCurZ : Float;
   
   // ai data
   fArmy : Integer;
   fType : Integer;
   fWarType : Integer;
   
   arGrid : array [0..gc_formation_maskmaxheight] of array [0..gc_formation_maskmaxwidth] of Integer;
   
   procedure CreateBase;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create(SizeOf(Integer));
      
      fIndex:=-1;
      fPlIndex:=-1;
      fGroupName:='';
      fArmy:=-1;
      fType:=gc_ai_armytype_none;
      fNeedRebuild := true;
   end;
   
   procedure Create(formind, basecount, officerid: Integer; standground, searchvictim: Boolean);
   begin
      CreateBase();
      
      fFormation:=formind;
      fBaseCount:=basecount;
      fStandGround:=standground;
      fSearchVictim:=searchvictim;
      fOfficerID:=officerid;
      if formind >= 0 then
      begin
         fWidth := gFormation[formind].width;
         fHeight := gFormation[formind].height;
      end;
   end;
   
   constructor TSquad;
   begin
      CreateBase;
   end;
   
   procedure Destroy;
   begin
      var i : Integer;
      for i := self.fList.GetCount-1 downto 0 do
      begin
         var goHnd : Integer = self.fList.Get(i);
         var pObj : Pointer = StateMachineGetArgDataByInd(GetGameObjectStateMachineHandle(goHnd), gc_argunit_obj);
         if (pObj<>nil) then
         TObj(pObj).squad := -1
         else
         ErrorLog('TSquad pobj=nil');
      end;
      
      self.fList.Destroy;
   end;
   
   destructor TSquad;
   begin
      Destroy;
   end;
   
   procedure SetCount(const val: Integer);
   begin
      self.fList.SetCount(val);
   end;
   
   function GetCount: Integer;
   begin
      result:=self.fList.GetCount;
   end;
   
   procedure Clear;
   begin
      self.fList.Clear;
   end;
   
   function Set(const val: Integer; index: Integer): Boolean;
   begin
      self.fList.Set(val, index);
   end;
   
   function Get(index: Integer): Integer;
   begin
      result:=self.fList.Get(index);
   end;
   
   procedure Assign(var src: TSquad);
   begin
      var i, j : Integer;
      
      fFormation:=src.fFormation;
      self.fList.SetCount(src.GetCount);
      for i := 0 to self.GetCount-1 do
      self.fList.Set(src.Get(i), i);
      
      fBaseCount := src.fBaseCount;
      fStandGround := src.fStandGround;
      fSearchVictim := src.fSearchVictim;
      fOfficerID := src.fOfficerID;
      fHoldMode := src.fHoldMode;
      fAddDamage := src.fAddDamage;
      fAddShield := src.fAddShield;
      fAddDamageHold := src.fAddDamageHold;
      fAddShieldHold := src.fAddShieldHold;
      fDirX := src.fDirX;
      fDirZ := src.fDirZ;
      fWidth := src.fWidth;
      fHeight := src.fHeight;
      fMoveCount := src.fMoveCount;
      //fTag := src.fTag;
      fAgressive := src.fAgressive;
      fAttackMode := src.fAttackMode;
      fHoldModeProgress := src.fHoldModeProgress;
      
      // ai data
      //fArmy := src.fArmy;
      //fType := src.fType;
      //fWarType := src.fWarType;
      
      for i := 0 to gc_formation_maskmaxheight-1 do
      for j := 0 to gc_formation_maskmaxwidth-1 do
      arGrid[i, j] := src.arGrid[i, j];
   end;
   
   function Add(const val: Integer): Integer; begin result:=self.fList.Add(val); end;
   function IndexOf(const val: Integer): Integer; begin result:=self.fList.IndexOf(val); end;
   
   procedure Delete(const index: Integer);
   begin
      var goHnd : Integer = self.fList.Get(index);
      self.fList.Delete(index);
      
      var pObj : Pointer = StateMachineGetArgDataByInd(GetGameObjectStateMachineHandle(goHnd), gc_argunit_obj);
      if (pObj<>nil) then
      TObj(pObj).squad := -1
      else
      ErrorLog('TSquad pobj=nil');
   end;
   
   procedure Remove(const val: Integer);
   begin
      self.fList.Remove(val);
      
      var i, j : Integer;
      
      for i := 0 to fHeight-1 do
      for j := 0 to fWidth-1 do
      if self.arGrid[i, j] = val then
      self.arGrid[i, j] := 0;
      
      var pObj : Pointer = StateMachineGetArgDataByInd(GetGameObjectStateMachineHandle(val), gc_argunit_obj);
      if (pObj<>nil) then
      TObj(pObj).squad := -1
      else
      ErrorLog('TSquad pobj=nil');
   end;
   
   procedure SetIndex(const ind: Integer);
   begin
      fIndex := ind;
      var i : Integer;
      for i := self.fList.GetCount-1 downto 0 do
      begin
         var goHnd : Integer = self.fList.Get(i);
         var pObj : Pointer = StateMachineGetArgDataByInd(GetGameObjectStateMachineHandle(goHnd), gc_argunit_obj);
         if (pObj<>nil) then
         TObj(pObj).squad := ind
         else
         ErrorLog('TSquad pobj=nil');
      end;
   end;
end;

// TSquadList
//
type TSquadList = class
private
{ Private Declarations }
   fList: TPtrList;
protected
{ Protected Declarations }
public
{ Public Declarations }
   fNextUID: Integer;
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   
   constructor TSquadList;
   begin
      Create;
   end;
   
   procedure Clear;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         TSquad(p).Destroy;
         FreeMem(p);
      end;
      self.fList.Clear;
      fNextUID := 1;
   end;
   
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   
   destructor TSquadList;
   begin
      Destroy;
   end;
   
   function GetCount: Integer;
   begin
      result:=self.fList.GetCount;
   end;
   
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<self.fList.GetCount) then
      result:=self.fList.GetPointer(index)
      else begin
         result:=nil;
         ErrorLog('TSquadList.Get : index = '+IntToStr(index)+' pointer=nil');
      end;
   end;
   
   function GetByUID(uid: Integer): Pointer;
   begin
      result:=nil;
      var i : Integer;
      for i:=0 to self.fList.GetCount-1 do
      begin
         var psquad : Pointer = self.fList.GetPointer(i);
         if (psquad<>nil) and (TSquad(psquad).fUID=uid) then
         begin
            result:=psquad;
            break;
         end;
      end;
   end;
   
   function Add: Pointer;
   begin
      GetMem(result, SizeOf(TSquad));
      FillMem(result, SizeOf(TSquad), 0);
      TSquad(result).CreateBase;
      self.fList.AddPointer(result);
      TSquad(result).fIndex:=GetCount-1;
      TSquad(result).fUID := fNextUID;
      fNextUID := fNextUID+1;
   end;
   
   function AddSetup(const basecount, officerid: Integer; standground, searchvictim: Boolean): Pointer;
   begin
      result:=Add;
      TSquad(result).fBaseCount:=basecount;
      TSquad(result).fStandGround:=standground;
      TSquad(result).fSearchVictim:=searchvictim;
      TSquad(result).fOfficerID:=officerid;
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then
      begin
         var i : Integer;
         for i := GetCount-1 downto index+1 do
         TSquad(self.fList.GetPointer(i)).SetIndex(i-1);
         
         var p: Pointer=self.fList.GetPointer(index);
         if p<>nil then begin
            TSquad(p).Destroy;
            FreeMem(p);
         end;
         self.fList.Delete(index);
      end
      else
      ErrorLog('TSquadList.Delete : index = '+IntToStr(index)+' out of bounds');
   end;
end;

// TArmyOrder
//
type TArmyOrder = class
   iType : Integer;
   targetUID : Integer;
   tag : Integer;
   tag2 : Integer;
   tag3 : Integer;
   tagFloat : Float;
end;

// TArmy
//
type TArmy = class
private
{ Private Declarations }
public
{ Public Declarations }
   fSquadList : TPtrList;
   fIndex : Integer;
   fPlIndex : Integer;
   fOffsetCol : Float;
   fOffsetRow : Float;
   fSpec : Integer;
   fTopZone : Integer;
   fCurTopZone : Integer;
   fSpecialOrder : Boolean;
   fOrder : TArmyOrder;
   fX : Float;
   fZ : Float;
   fCurX : Float;
   fCurZ : Float;
   fSquadsCurX : Float;
   fSquadsCurZ : Float;
   fTag : Integer;
   fForce : Integer;
   fLastBattleTime : Float;
   fLastArtTime : Float;
   fActive : Boolean;
   fRegion : Integer;
   fOnEnemyLand : Boolean;
   
   procedure Create();
   begin
      if self.fSquadList.GetItemSize=0 then
      self.fSquadList.Create;
      
      fIndex := -1;
      fPlIndex := -1;
      fOffsetCol := gc_ai_army_offsetcol;
      fOffsetRow := gc_ai_army_offsetrow;
      fSpec := gc_ai_armyspec_default;
      fTopZone := -1;
      fCurTopZone := -1;
      fOrder.iType := gc_ai_armyorder_none;
      fActive := true;
      fRegion := -1;
   end;
   
   constructor TArmy;
   begin
      Create();
   end;
   
   procedure Destroy();
   begin
      var i : Integer;
      for i := self.fSquadList.GetCount-1 downto 0 do
      begin
         var pSquad : Pointer = self.fSquadList.GetPointer(i);
         if (pSquad<>nil) then
         TSquad(pSquad).fArmy := -1
         else
         ErrorLog('Faust, TArmy.Destroy pointer=nil');
      end;
      
      self.fSquadList.Destroy;
   end;
   
   destructor TArmy;
   begin
      Destroy();
   end;
   
   procedure Add(pSquad : Pointer);
   begin
      if pSquad<>nil then
      begin
         TSquad(pSquad).fArmy := fIndex;
         self.fSquadList.AddPointer(pSquad);
      end
      else
      ErrorLog('Faust, TArmy.Add, pointer=nil');
   end;
   
   procedure Delete(ind : Integer);
   begin
      if (ind>=0) and (ind<self.fSquadList.GetCount) then
      begin
         var pSquad : Pointer = self.fSquadList.GetPointer(ind);
         if (pSquad<>nil) then
         begin
            TSquad(pSquad).fArmy := -1;
            self.fSquadList.Delete(ind);
         end
         else
         ErrorLog('Faust, TArmy.Delete pSquad=nil');
      end
      else
      ErrorLog('Faust, TArmy.Delete, index out of range');
   end;
   
   function IndexOf(pSquad : Pointer) : Integer;
   begin
      Result := -1;
      var i : Integer;
      var count : Integer = self.fSquadList.GetCount;
      for i := 0 to count-1 do
      if self.fSquadList.GetPointer(i) = pSquad then
      begin
         Result := i;
         break;
      end;
   end;
   
   procedure Remove(pSquad : Pointer);
   begin
      Delete(IndexOf(pSquad));
   end;
   
   procedure Clear;
   begin
      var i : Integer;
      for i := self.fSquadList.GetCount-1 downto 0 do
      begin
         var pSquad : Pointer = self.fSquadList.GetPointer(i);
         if (pSquad<>nil) then
         TSquad(pSquad).fArmy := -1
         else
         ErrorLog('Faust, TArmy.Clear pointer=nil');
      end;
      
      self.fSquadList.Clear;
   end;
   
   function GetSquad(ind : Integer) : Pointer;
   begin
      if (ind >= 0) and (ind < fSquadList.GetCount) then
      Result := fSquadList.GetPointer(ind)
      else
      Result := nil;
   end;
end;

// TArmyList
//
type TArmyList = class
private
{ Private Declarations }
   fList: TPtrList;
public
{ Public Declarations }
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   
   constructor TArmyList;
   begin
      Create;
   end;
   
   procedure Clear;
   begin
      var i : Integer;
      var p : Pointer;
      for i := 0 to self.fList.GetCount-1 do
      begin
         p := self.fList.GetPointer(i);
         if (p<>nil) then
         begin
            TArmy(p).Destroy;
            FreeMem(p);
         end
         else
         ErrorLog('Faust, TArmyList.Clear pointer=nil');
      end;
      self.fList.Clear;
   end;
   
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   
   destructor TArmyList;
   begin
      Destroy;
   end;
   
   function GetCount: Integer;
   begin
      result := self.fList.GetCount;
   end;
   
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<GetCount) then
      result := self.fList.GetPointer(index)
      else
      begin
         result := nil;
         ErrorLog('Faust, TArmyList.Get, index out of range. index = '+IntToStr(index));
      end;
   end;
   
   function Add: Pointer;
   begin
      GetMem(result, SizeOf(TArmy));
      FillMem(result, SizeOf(TArmy), 0);
      TArmy(result).Create();
      self.fList.AddPointer(result);
      TArmy(result).fIndex := fList.GetCount-1;
   end;
   
   function AddSetup(plInd : Integer): Pointer;
   begin
      result:=Add;
      TArmy(result).fPlIndex := plInd;
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then
      begin
         var i, j : Integer;
         for i := self.fList.GetCount-1 downto 0 do
         begin
            var pArmy : Pointer = self.fList.GetPointer(i);
            if (pArmy<>nil) then
            begin
               if i > index then
               TArmy(pArmy).fIndex := i-1;
               
               for j := TArmy(pArmy).fSquadList.GetCount-1 downto 0 do
               begin
                  var pSquad : Pointer = TArmy(pArmy).fSquadList.GetPointer(j);
                  if pSquad <> nil then
                  begin
                     var aInd : Integer = TSquad(pSquad).fArmy;
                     if aInd > index then
                     TSquad(pSquad).fArmy := aInd-1;
                  end;
               end;
            end
            else
            ErrorLog('Faust, TArmyList.Delete pointer=nil');
         end;
         
         var p : Pointer = self.fList.GetPointer(index);
         if p <> nil then
         begin
            TArmy(p).Destroy;
            FreeMem(p);
         end;
         self.fList.Delete(index);
      end
      else
      ErrorLog('Faust, TArmyList.Delete index = '+IntToStr(index)+' out of bounds');
   end;
   
   function IndexOf(pArmy : Pointer) : Integer;
   begin
      Result := -1;
      var i : Integer;
      var count : Integer = GetCount;
      for i := 0 to count-1 do
      if fList.GetPointer(i) = pArmy then
      begin
         Result := i;
         break;
      end;
   end;
   
   procedure Remove(pArmy : Pointer);
   begin
      Delete(IndexOf(pArmy));
   end;
end;

// TGridList
//
// Integer class_ for lists, introduces common behaviours.
type TGridList = class
private
{ Private Declarations }
   const cDefaultListGrowthDelta = 16;
   
   fCount, fCapacity, fGrowthDelta: Integer;
   fBufferItem: Pointer;
   fPackMode: Boolean; // If True, Clear/Clean deallocate memory, Default is True
   // Default fResetsMemory is True
   fExternalMemory, fResetsMemory: Boolean;
protected
{ Protected Declarations }
   // The base list pointer (untyped)
   fBaseList: Pointer;
   // Must be defined by all subclasses in their constructor(s)
   fItemSize: Integer;
   
   function BufferItem: Pointer;
   begin
      if (fBufferItem=nil) and (fItemSize>0) then
      GetMem(fBufferItem, fItemSize);
      result:=fBufferItem;
   end;
public
{ Public Declarations }
   fMatCount: array [0..gc_obj_material_count-1] of Integer;
   fPlCount: array [0..gc_MaxPlayerCount-1] of Integer;
   fMask: Integer = 0;
   fPlMask: Integer = 0;
   fChecked: Boolean = false;
   
   owner : Integer;
   dist : Integer;
   
   fSELastCheckMask : Integer;
   fSELastCheckPlMask : Integer;
   fSELastCheckTime : Float;
   fSELastCheckResult : Boolean;
   
   // List growth granularity.
   procedure SetGrowthDelta(const val: Integer); begin fGrowthDelta:=val; end;
   function GetGrowthDelta: Integer; begin result:=fGrowthDelta; end;
   // If true (default value) adjusting count will reset added values.
   // Switching this option to true will turn off this memory reset,
   // which can improve performance is that having empty values isn't
   // required.
   procedure SetResetsMemory(const val: Boolean); begin fResetsMemory:=val; end;
   function GetResetsMemory: Boolean; begin result:=fResetsMemory; end;
   procedure SetPackMode(const val: Boolean); begin fPackMode:=val; end;
   function GetPackMode: Boolean; begin result:=fPackMode; end;
   // Only function where list may be alloc'ed & freed.
   // Resizes the array pointed by FBaseList, adjust the subclass's
   // typed pointer accordingly if any.
   procedure SetCapacity(const newcapacity: Integer);
   begin
      if newcapacity<>fCapacity then begin
         if fExternalMemory then begin
            fExternalMemory:=false;
            fBaseList:=nil;
         end;
         ReallocMem(fBaseList, newcapacity*fItemSize);
         if fBaseList=nil then
         fCapacity:=0
         else fCapacity:=newcapacity;
      end;
   end;
   // Current list capacity.
   function GetCapacity: Integer; begin result:=fCapacity; end;
   // Nb of items in the list.
   // When assigning a Count, added items are reset to zero.
   procedure SetCount(const val: Integer);
   begin
      if val>=0 then begin
         if val>fCapacity then
         SetCapacity(val);
         if (val>fCount) and (fResetsMemory) then begin
            var p: Pointer=fBaseList+fItemSize*fCount;
            if p<>nil then
            FillMem(p, (val-fCount)*fItemSize, 0);
         end;
         fCount:=val;
      end;
   end;
   function GetCount: Integer; begin result:=fCount; end;
   procedure SetItemSize(const val: Integer);
   begin
      SetCount(0);
      SetCapacity(0);
      fItemSize:=val;
      if fBufferItem<>nil then begin
         FreeMem(fBufferItem);
         fBufferItem:=nil;
      end;
   end;
   function GetItemSize: Integer; begin result:=fItemSize; end;
   
   // Empties the list without altering capacity.
   procedure Flush;
   begin
      SetCount(0);
   end;
   
   // Empties the list and release.
   procedure Clear;
   begin
      SetCount(0);
      if fPackMode then
      SetCapacity(0);
   end;
   
   procedure Create(itemsize: Integer);
   begin
      if fItemSize<>itemsize then begin
         fPackMode:=false;
         fResetsMemory:=true;
         fGrowthDelta:=cDefaultListGrowthDelta;
         SetItemSize(itemsize);
      end;
   end;
   
   constructor TGridList;
   begin
      Create(SizeOf(Integer));
   end;
   
   procedure Assign(var src: TGridList);
   begin
      SetCapacity(src.GetCount);
      fGrowthDelta:=src.GetGrowthDelta;
      fCount:=fCapacity;
   end;
   
   procedure AddNulls(const nbvals: Integer);
   begin
      if nbvals+fCount>fCapacity then
      SetCapacity(nbvals+fCount);
      var p: Pointer=fBaseList+fCount*fItemSize;
      FillMem(p, nbvals*fItemSize, 0);
      fCount:=fCount+nbvals;
   end;
   
   procedure InsertNulls(const index, nbvals: Integer);
   begin
      var nc: Integer;
      if (index>=0) and (index<fCount) and (nbvals>0) then begin
         var p: Pointer;
         var nc: Integer=fCount+nbvals;
         if nc>fCapacity then
         SetCapacity(nc);
         if index<fCount then begin
            p:=fBaseList+(index+nbvals)*fItemSize;
            MoveMem(fBaseList+index*fItemSize, p, (fCount-index)*fItemSize);
         end;
         p:=fBaseList+index*fItemSize;
         FillMem(p, nbvals*fItemSize, 0);
         fCount:=nc;
      end;
   end;
   
   procedure AdjustCapacityToAtLeast(const size: Integer);
   begin
      if fCapacity<size then
      SetCapacity(size);
   end;
   
   function DataSize: Integer;
   begin
      result:=fItemSize*fCount;
   end;
   
   // Tell the list to use the specified range instead of its own.
   // rangeCapacity should be expressed in bytes.
   // The allocated memory is NOT managed by the list, current content
   // if copied to the location, if the capacity is later changed, regular
   // memory will be allocated, and the specified range no longer used.
   procedure UseMemory(var rangestart: Pointer; const rangecapacity: Integer);
   begin
      var rangecap: Integer=rangecapacity div fItemSize;
      if rangecap<fCount then exit;
      // transfer data
      MoveMem(fBaseList, rangestart, fCount*fItemSize);
      if not fExternalMemory then begin
         FreeMem(fBaseList);
         fExternalMemory:=true;
      end;
      fBaseList:=rangestart;
      fCapacity:=rangecap;
      SetCapacity(fCapacity); // notify subclasses
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<fCount) then begin
         Dec(fCount, 1);
         if index<fCount then begin
            var p: Pointer=fBaseList+index*fItemSize;
            MoveMem(fBaseList+(index+1)*fItemSize, p, (fCount-index)*fItemSize);
         end;
      end;
   end;
   
   procedure DeleteItems(const index, nbvals: Integer);
   begin
      if (index>=0) and (index<fCount) and (nbvals>0) then begin
         if index+nbvals<fCount then begin
            var p: Pointer=fBaseList+index*fItemSize;
            MoveMem(fBaseList+(index+nbvals)*fItemSize, p, (fCount-index-nbvals)*fItemSize);
         end;
         Dec(fCount, nbvals);
      end;
   end;
   
   procedure Exchange(const index1, index2: Integer);
   begin
      var buf : Integer;
      var p : Pointer;
      if (index1<>index2) and (index1>=0) and (index1<fCount) and (index2>=0) and (index2<fCount) then begin
         if fItemSize=SizeOf(Integer) then begin
            p:=fBaseList;
            buf:=Integer(p+index1*fItemSize);
            Integer(p+index1*fItemSize):=Integer(p+index2*fItemSize);
            Integer(p+index2*fItemSize):=buf;
            end else begin
            BufferItem;
            MoveMem(fBaseList+index1*fItemSize, fBufferItem, fItemSize);
            var p: Pointer;
            p:=fBaseList+index1*fItemSize;
            MoveMem(fBaseList+index2*fItemSize, p, fItemSize);
            p:=fBaseList+index2*fItemSize;
            MoveMem(fBufferItem, p, fItemSize);
         end;
      end;
   end;
   
   procedure Move(const curindex, newindex: Integer);
   begin
      if (curindex<>newindex) and (newindex>=0) and (newindex<fCount) and (curindex>=0) and (curindex<fCount) then begin
         BufferItem;
         if fItemSize=SizeOf(Integer) then
         Integer(fBufferItem):=Integer(fBaseList+curindex*fItemSize)
         else MoveMem(fBaseList+curindex*fItemSize, fBufferItem, fItemSize);
         if curindex<newindex then begin
            // curindex+1 necessarily exists since curindex<newindex and newindex<fCount
            var p: Pointer=fBaseList+curindex*fItemSize;
            MoveMem(fBaseList+(curindex+1)*fItemSize, p,(newindex-curindex-1)*fItemSize);
            end else begin
            // newindex+1 necessarily exists since newindex<curindex and curindex<fCount
            var p: Pointer=fBaseList+(newindex+1)*fItemSize;
            MoveMem(fBaseList+newindex*fItemSize, p, (curindex-newindex-1)*fItemSize);
         end;
         if fItemSize=SizeOf(Integer) then
         Integer(fBaseList+newindex*fItemSize):=Integer(fBufferItem)
         else begin
            var p: Pointer=fBaseList+newindex*fItemSize;
            MoveMem(fBufferItem, p, fItemSize);
         end;
      end;
   end;
   
   procedure Reverse;
   begin
      var s, e : Integer;
      s:=0; e:=fCount-1;
      while s<e do begin
         Exchange(s, e);
         Inc(s, 1); Dec(e, 1);
      end;
   end;
   
   destructor TGridList;
   begin
      //Clear;
      SetCount(0);
      SetCapacity(0);
      if Assigned(fBufferItem) then
      FreeMem(fBufferItem);
   end;
   
   function Set(const src: Integer; index: Integer): Boolean;
   begin
      if (index>=0) and (index<fCount) and (fItemSize=SizeOf(src)) then begin
         Integer(fBaseList+index*fItemSize):=src;
         result:=true;
      end else result:=false;
   end;
   
   function Get(index: Integer): Integer;
   begin
      if (index>=0) and (index<fCount) and (fItemSize=SizeOf(result)) then
      result:=Integer(fBaseList+index*fItemSize)
      else result:=0;
   end;
   
   function AddNull: Integer;
   begin
      if fItemSize>0 then begin
         result:=fCount;
         if result=fCapacity then
         SetCapacity(fCapacity+fGrowthDelta);
         //fBaseList[result]:=item;
         inc(fCount, 1);
      end else result:=-1;
   end;
   
   function Add(const gohnd, mat, pl: Integer): Integer;
   begin
      if fItemSize=SizeOf(gohnd) then begin
         result:=AddNull;
         if result<>-1 then
         begin
            Integer(fBaseList+result*fItemSize):=gohnd;
            if (fMatCount[mat]=0) then
            fMask := fMask or (1 shl mat);
            if (fPlCount[pl]=0) then
            fPlMask := fPlMask or (1 shl pl);
            fMatCount[mat]:=fMatCount[mat]+1;
            fPlCount[pl]:=fPlCount[pl]+1;
         end
      end else result:=-1;
   end;
   
   function IndexOf(const gohnd: Integer): Integer;
   begin
      result:=-1;
      var i: Integer;
      for i:=0 to GetCount-1 do begin
         if Get(i)=gohnd then begin
            result:=i;
            break;
         end;
      end;
   end;
   
   procedure Remove(const gohnd, mat, pl: Integer);
   begin
      var ind : Integer = IndexOf(gohnd);
      if (ind>-1) then
      begin
         Delete(ind);
         fMatCount[mat]:=fMatCount[mat]-1;
         fPlCount[pl]:=fPlCount[pl]-1;
         if (fMatCount[mat]<=0) then
         fMask := fMask xor (1 shl mat);
         if (fPlCount[pl]<=0) then
         fPlMask := fPlMask xor (1 shl pl);
      end;
   end;
end;

// TResGridList
//
type TResGridList = class
private
{ Private Declarations }
   const cDefaultListGrowthDelta = 16;
   
   fCount, fCapacity, fGrowthDelta: Integer;
   fBufferItem: Pointer;
   fPackMode: Boolean; // If True, Clear/Clean deallocate memory, Default is True
   // Default fResetsMemory is True
   fExternalMemory, fResetsMemory: Boolean;
protected
{ Protected Declarations }
   // The base list pointer (untyped)
   fBaseList: Pointer;
   // Must be defined by all subclasses in their constructor(s)
   fItemSize: Integer;
   
   function BufferItem: Pointer;
   begin
      if (fBufferItem=nil) and (fItemSize>0) then
      GetMem(fBufferItem, fItemSize);
      result:=fBufferItem;
   end;
public
{ Public Declarations }
   fResCount: array [0..gc_ResCount-1] of Integer;
   fWorkerCount: array [0..gc_MaxPlayerCount-1] of array [0..gc_ResCount-1] of Integer;
   
   function GetBaseList: Pointer; begin result:=fBaseList; end;
   
   // List growth granularity.
   procedure SetGrowthDelta(const val: Integer); begin fGrowthDelta:=val; end;
   function GetGrowthDelta: Integer; begin result:=fGrowthDelta; end;
   // If true (default value) adjusting count will reset added values.
   // Switching this option to true will turn off this memory reset,
   // which can improve performance is that having empty values isn't
   // required.
   procedure SetResetsMemory(const val: Boolean); begin fResetsMemory:=val; end;
   function GetResetsMemory: Boolean; begin result:=fResetsMemory; end;
   procedure SetPackMode(const val: Boolean); begin fPackMode:=val; end;
   function GetPackMode: Boolean; begin result:=fPackMode; end;
   // Only function where list may be alloc'ed & freed.
   // Resizes the array pointed by FBaseList, adjust the subclass's
   // typed pointer accordingly if any.
   procedure SetCapacity(const newcapacity: Integer);
   begin
      if newcapacity<>fCapacity then begin
         if fExternalMemory then begin
            fExternalMemory:=false;
            fBaseList:=nil;
         end;
         ReallocMem(fBaseList, newcapacity*fItemSize);
         if fBaseList=nil then
         fCapacity:=0
         else fCapacity:=newcapacity;
      end;
   end;
   // Current list capacity.
   function GetCapacity: Integer; begin result:=fCapacity; end;
   // Nb of items in the list.
   // When assigning a Count, added items are reset to zero.
   procedure SetCount(const val: Integer);
   begin
      if val>=0 then begin
         if val>fCapacity then
         SetCapacity(val);
         if (val>fCount) and (fResetsMemory) then begin
            var p: Pointer=fBaseList+fItemSize*fCount;
            if p<>nil then
            FillMem(p, (val-fCount)*fItemSize, 0);
         end;
         fCount:=val;
      end;
   end;
   function GetCount: Integer; begin result:=fCount; end;
   procedure SetItemSize(const val: Integer);
   begin
      SetCount(0);
      SetCapacity(0);
      fItemSize:=val;
      if fBufferItem<>nil then begin
         FreeMem(fBufferItem);
         fBufferItem:=nil;
      end;
   end;
   function GetItemSize: Integer; begin result:=fItemSize; end;
   
   // Empties the list without altering capacity.
   procedure Flush;
   begin
      SetCount(0);
   end;
   
   // Empties the list and release.
   procedure Clear;
   begin
      SetCount(0);
      if fPackMode then
      SetCapacity(0);
   end;
   
   procedure Create(itemsize: Integer);
   begin
      if fItemSize<>itemsize then begin
         fPackMode:=false;
         fResetsMemory:=true;
         fGrowthDelta:=cDefaultListGrowthDelta;
         SetItemSize(itemsize);
      end;
   end;
   
   constructor TResGridList;
   begin
      Create(SizeOf(Integer));
   end;
   
   procedure Assign(var src: TResGridList);
   begin
      SetCapacity(src.GetCount);
      fGrowthDelta:=src.GetGrowthDelta;
      fCount:=fCapacity;
   end;
   
   procedure AddNulls(const nbvals: Integer);
   begin
      if nbvals+fCount>fCapacity then
      SetCapacity(nbvals+fCount);
      var p: Pointer=fBaseList+fCount*fItemSize;
      FillMem(p, nbvals*fItemSize, 0);
      fCount:=fCount+nbvals;
   end;
   
   procedure InsertNulls(const index, nbvals: Integer);
   begin
      var nc: Integer;
      if (index>=0) and (index<fCount) and (nbvals>0) then begin
         var p: Pointer;
         var nc: Integer=fCount+nbvals;
         if nc>fCapacity then
         SetCapacity(nc);
         if index<fCount then begin
            p:=fBaseList+(index+nbvals)*fItemSize;
            MoveMem(fBaseList+index*fItemSize, p, (fCount-index)*fItemSize);
         end;
         p:=fBaseList+index*fItemSize;
         FillMem(p, nbvals*fItemSize, 0);
         fCount:=nc;
      end;
   end;
   
   procedure AdjustCapacityToAtLeast(const size: Integer);
   begin
      if fCapacity<size then
      SetCapacity(size);
   end;
   
   function DataSize: Integer;
   begin
      result:=fItemSize*fCount;
   end;
   
   // Tell the list to use the specified range instead of its own.
   // rangeCapacity should be expressed in bytes.
   // The allocated memory is NOT managed by the list, current content
   // if copied to the location, if the capacity is later changed, regular
   // memory will be allocated, and the specified range no longer used.
   procedure UseMemory(var rangestart: Pointer; const rangecapacity: Integer);
   begin
      var rangecap: Integer=rangecapacity div fItemSize;
      if rangecap<fCount then exit;
      // transfer data
      MoveMem(fBaseList, rangestart, fCount*fItemSize);
      if not fExternalMemory then begin
         FreeMem(fBaseList);
         fExternalMemory:=true;
      end;
      fBaseList:=rangestart;
      fCapacity:=rangecap;
      SetCapacity(fCapacity); // notify subclasses
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<fCount) then begin
         Dec(fCount, 1);
         if index<fCount then begin
            var p: Pointer=fBaseList+index*fItemSize;
            MoveMem(fBaseList+(index+1)*fItemSize, p, (fCount-index)*fItemSize);
         end;
      end;
   end;
   
   procedure DeleteItems(const index, nbvals: Integer);
   begin
      if (index>=0) and (index<fCount) and (nbvals>0) then begin
         if index+nbvals<fCount then begin
            var p: Pointer=fBaseList+index*fItemSize;
            MoveMem(fBaseList+(index+nbvals)*fItemSize, p, (fCount-index-nbvals)*fItemSize);
         end;
         Dec(fCount, nbvals);
      end;
   end;
   
   procedure Exchange(const index1, index2: Integer);
   begin
      var buf : Integer;
      var p : Pointer;
      if (index1<>index2) and (index1>=0) and (index1<fCount) and (index2>=0) and (index2<fCount) then begin
         if fItemSize=SizeOf(Integer) then begin
            p:=fBaseList;
            buf:=Integer(p+index1*fItemSize);
            Integer(p+index1*fItemSize):=Integer(p+index2*fItemSize);
            Integer(p+index2*fItemSize):=buf;
            end else begin
            BufferItem;
            MoveMem(fBaseList+index1*fItemSize, fBufferItem, fItemSize);
            var p: Pointer;
            p:=fBaseList+index1*fItemSize;
            MoveMem(fBaseList+index2*fItemSize, p, fItemSize);
            p:=fBaseList+index2*fItemSize;
            MoveMem(fBufferItem, p, fItemSize);
         end;
      end;
   end;
   
   procedure Move(const curindex, newindex: Integer);
   begin
      if (curindex<>newindex) and (newindex>=0) and (newindex<fCount) and (curindex>=0) and (curindex<fCount) then begin
         BufferItem;
         if fItemSize=SizeOf(Integer) then
         Integer(fBufferItem):=Integer(fBaseList+curindex*fItemSize)
         else MoveMem(fBaseList+curindex*fItemSize, fBufferItem, fItemSize);
         if curindex<newindex then begin
            // curindex+1 necessarily exists since curindex<newindex and newindex<fCount
            var p: Pointer=fBaseList+curindex*fItemSize;
            MoveMem(fBaseList+(curindex+1)*fItemSize, p,(newindex-curindex-1)*fItemSize);
            end else begin
            // newindex+1 necessarily exists since newindex<curindex and curindex<fCount
            var p: Pointer=fBaseList+(newindex+1)*fItemSize;
            MoveMem(fBaseList+newindex*fItemSize, p, (curindex-newindex-1)*fItemSize);
         end;
         if fItemSize=SizeOf(Integer) then
         Integer(fBaseList+newindex*fItemSize):=Integer(fBufferItem)
         else begin
            var p: Pointer=fBaseList+newindex*fItemSize;
            MoveMem(fBufferItem, p, fItemSize);
         end;
      end;
   end;
   
   procedure Reverse;
   begin
      var s, e : Integer;
      s:=0; e:=fCount-1;
      while s<e do begin
         Exchange(s, e);
         Inc(s, 1); Dec(e, 1);
      end;
   end;
   
   destructor TResGridList;
   begin
      //Clear;
      SetCount(0);
      SetCapacity(0);
      if Assigned(fBufferItem) then
      FreeMem(fBufferItem);
   end;
   
   function Set(const src: Integer; index: Integer): Boolean;
   begin
      if (index>=0) and (index<fCount) and (fItemSize=SizeOf(src)) then begin
         Integer(fBaseList+index*fItemSize):=src;
         result:=true;
      end else result:=false;
   end;
   
   function Get(index: Integer): Integer;
   begin
      if (index>=0) and (index<fCount) and (fItemSize=SizeOf(result)) then
      result:=Integer(fBaseList+index*fItemSize)
      else result:=0;
   end;
   
   function AddNull: Integer;
   begin
      if fItemSize>0 then begin
         result:=fCount;
         if result=fCapacity then
         SetCapacity(fCapacity+fGrowthDelta);
         //fBaseList[result]:=item;
         inc(fCount, 1);
      end else result:=-1;
   end;
   
   function Add(const gohnd, restype: Integer): Integer;
   begin
      if fItemSize=SizeOf(gohnd) then begin
         result:=AddNull;
         if result<>-1 then
         begin
            Integer(fBaseList+result*fItemSize):=gohnd;
            fResCount[restype]:=fResCount[restype]+1;
         end
      end else result:=-1;
   end;
   
   function IndexOf(const gohnd: Integer): Integer;
   begin
      result:=-1;
      var i: Integer;
      for i:=0 to GetCount-1 do begin
         if Get(i)=gohnd then begin
            result:=i;
            break;
         end;
      end;
   end;
   
   procedure Remove(const gohnd, restype: Integer);
   begin
      var ind : Integer = IndexOf(gohnd);
      if (ind>-1) then
      begin
         Delete(ind);
         fResCount[restype]:=fResCount[restype]-1;
      end;
   end;
end;

type TAiIdea = class
   cid : Integer;
   id : Integer;
   //sid : String;
   ideatype : Integer; //obj or upgrade
   count : Integer;
   airole : Integer;
end;

type TIdeasList = class
private
{ Private Declarations }
   fList: TPtrList;
public
{ Public Declarations }
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   
   constructor TIdeasList;
   begin
      Create;
   end;
   
   procedure Clear;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         FreeMem(p);
      end;
      self.fList.Clear;
   end;
   
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   
   destructor TIdeasList;
   begin
      Destroy;
   end;
   
   function GetCount: Integer; begin result:=self.fList.GetCount; end;
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<GetCount) then
      result:=self.fList.GetPointer(index)
      else begin
         result := nil;
         ErrorLog('TIdeasList.Get : index = '+IntToStr(index)+' pointer=nil');
      end;
   end;
   
   function Add: Pointer;
   begin
      GetMem(result, SizeOf(TAiIdea));
      FillMem(result, SizeOf(TAiIdea), 0);
      self.fList.AddPointer(result);
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then begin
         var p: Pointer=self.fList.GetPointer(index);
         if p<>nil then
         begin
            FreeMem(p);
            self.fList.Delete(index);
         end;
      end
      else
      ErrorLog('TIdeasList.Delete(const : index = '+IntToStr(index)+' index out of bounds');
   end;
end;

type TAIBuildingProject = class
   cid : Integer;
   sid : String;
   id : Integer;
   used : Boolean;
   placefound : Boolean;
   founded : Boolean;
   peasantscalled : Boolean;
   usage : Integer;
   nearx : Float;
   nearz : Float;
   x : Float;
   y : Float;
   options : Integer;
   attemptstostand : Integer;
   attemptstofindapprplace : Integer;
   maxpeasants : Integer;
   minpeasants : Integer;
   npeasantscalled : Integer;
   gohnd : Integer;
   curRad : Integer;
   curInd : Integer;
   maxTryDist : Integer;
   
   procedure SetupBuildingProject(cid, id : Integer; var sid : String; nearx, nearz: Float; options : Integer);
   begin
      self.used := true;
      self.sid := sid;
      self.cid := cid;
      self.id := id;
      self.placefound  :=  false;
      self.peasantscalled  :=  false;
      self.attemptstostand := 0;
      self.attemptstofindapprplace := 0;
      self.nearx := nearx;
      self.nearz := nearz;
      self.options := options;
      self.npeasantscalled := 0;
      self.curRad := 0;
      self.curInd := 0;
      self.maxTryDist := -1;
   end;
   
   procedure ResetBuildingProject();
   begin
      self.used := false;
      self.npeasantscalled := 0;
      self.gohnd := 0;
      self.attemptstostand := 0;
      self.curRad := 0;
      self.curInd := 0;
      self.maxTryDist := -1;
   end;
   
   procedure Create();
   begin
      ResetBuildingProject();
   end;
   
   constructor TAIBuildingProject;
   begin
      Create();
   end;
   
   destructor TAIBuildingProject;
   begin
   end;
end;

type TUnitUpgrade = class
   unitid : Integer;
   defenceupgrade : Integer;
   attackupgrade : Integer;
   defenceupgradeid : array[0..gc_ai_maxupgradelevel-1] of Integer;
   attackupgradeid : array[0..gc_ai_maxupgradelevel-1] of Integer;
end;

type TUniqUpgrade = class
   cid : Integer;
   unitid : Integer;
   airole : Integer;
   levelid : array[0..gc_ai_maxupgradelevel-1] of Integer;
   levelsid : array[0..gc_ai_maxupgradelevel-1] of String;
   
   procedure Create();
   begin
      airole := gc_ai_unit_none;
   end;
   
   constructor TUniqUpgrade;
   begin
      Create();
   end;
   
   procedure Destroy();
   begin
   end;
   
   destructor TUniqUpgrade;
   begin
      Destroy;
   end;
end;

type TUniqUpgList = class
private
{ Private Declarations }
   fList: TPtrList;
protected
{ Protected Declarations }
public
{ Public Declarations }
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   
   constructor TUniqUpgList;
   begin
      Create;
   end;
   
   procedure Clear;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         TUniqUpgrade(p).Destroy;
         FreeMem(p);
      end;
      self.fList.Clear;
   end;
   
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   
   destructor TUniqUpgList;
   begin
      Destroy;
   end;
   
   function GetCount: Integer; begin result:=self.fList.GetCount; end;
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<GetCount) then
      result:=self.fList.GetPointer(index)
      else begin
         result := nil;
         ErrorLog('TUniqUpgList.Get : index = '+IntToStr(index)+' pointer=nil');
      end;
   end;
   
   function GetById(id : Integer) : Pointer;
   begin
      result:=nil;
      var i : Integer;
      for i:=0 to self.fList.GetCount-1 do begin
         var p : Pointer = self.fList.GetPointer(i);
         if (p<>nil) and (TUniqUpgrade(p).unitid = id) then
         begin
            result:=p;
            break;
         end;
      end;
   end;
   
   function Add: Pointer;
   begin
      GetMem(result, SizeOf(TUniqUpgrade));
      FillMem(result, SizeOf(TUniqUpgrade), 0);
      TUniqUpgrade(result).Create();
      self.fList.AddPointer(result);
      //TUniqUpgrade(result).fIndex:=GetCount-1;
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then
      begin
         //var i : Integer;
         //for i := GetCount-1 downto index+1 do
         //TUniqUpgrade(self.fList.GetPointer(i)).SetIndex(i-1);
         
         var p: Pointer=self.fList.GetPointer(index);
         if p<>nil then begin
            TUniqUpgrade(p).Destroy;
            FreeMem(p);
         end;
         self.fList.Delete(index);
      end
      else
      ErrorLog('TUniqUpgList.Delete : index = '+IntToStr(index)+' out of bounds');
   end;
end;

type TAiProtectedBuilding = class
   targetHnd : Integer;
   aipriority : Integer;
   defenders : TIntegerList;
   
   procedure Create();
   begin
      self.defenders.Create(SizeOf(Integer));
   end;
   
   constructor TAiProtectedBuilding;
   begin
      Create();
   end;
   
   procedure Destroy();
   begin
      self.defenders.Destroy;
   end;
   
   destructor TAiProtectedBuilding;
   begin
      Destroy();
   end;
   
end;

type TAiProtectedBuildingList = class
private
   fList : TPtrList;
public
protected
{ Protected Declarations }
public
{ Public Declarations }
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   
   constructor TAiProtectedBuildingList;
   begin
      Create;
   end;
   
   procedure Clear;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         TAiProtectedBuilding(p).Destroy;
         FreeMem(p);
      end;
      self.fList.Clear;
   end;
   
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   
   destructor TAiProtectedBuildingList;
   begin
      Destroy;
   end;
   
   function GetCount: Integer; begin result:=self.fList.GetCount; end;
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<GetCount) then
      result:=self.fList.GetPointer(index)
      else begin
         result := nil;
         ErrorLog('TAiProtectedBuildingList.Get : index = '+IntToStr(index)+' pointer=nil');
      end;
   end;
   
   function Add: Pointer;
   begin
      GetMem(result, SizeOf(TAiProtectedBuilding));
      FillMem(result, SizeOf(TAiProtectedBuilding), 0);
      TAiProtectedBuilding(result).Create();
      self.fList.AddPointer(result);
      //TAiProtectedBuilding(result).fIndex:=GetCount-1;
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then
      begin
         //var i : Integer;
         //for i := GetCount-1 downto index+1 do
         //TUniqUpgrade(self.fList.GetPointer(i)).SetIndex(i-1);
         
         var p: Pointer=self.fList.GetPointer(index);
         if p<>nil then begin
            TAiProtectedBuilding(p).Destroy;
            FreeMem(p);
         end;
         self.fList.Delete(index);
      end
      else
      ErrorLog('TAiProtectedBuildingList.Delete : index = '+IntToStr(index)+' out of bounds');
   end;
   
   function IndexByHandle(const goHnd: Integer) : Integer;
   begin
      Result:=-1;
      var i : Integer;
      for i:=0 to self.flist.GetCount-1 do
      begin
         var p : Pointer=self.fList.GetPointer(i);
         if TAiProtectedBuilding(p).targetHnd=goHnd then
         begin
            Result:=i;
            break;
         end;
      end;
   end;
   
   function GetByHandle(const goHnd: Integer) : Pointer;
   begin
      result:=nil;
      var i : Integer;
      for i:=0 to self.flist.GetCount-1 do
      begin
         var p : Pointer=self.fList.GetPointer(i);
         if TAiProtectedBuilding(p).targetHnd=goHnd then
         begin
            Result:=p;
            break;
         end;
      end;
   end;
end;

type TAiData = class
   basenation : Integer; // base country id;
   bestprojects : TIdeasList;
   centerfound : Boolean;
   centerx : Float;
   centerz : Float;
   agressorssent : Integer;
   
   defencestage : Boolean;
   makediversion : Boolean;
   landbattle : Boolean;
   waterbattle : Boolean;
   
   controllednations : array[0..gc_MaxCountryCount-1] of Boolean;
   upgradeid : array[0..gc_MaxCountryCount-1] of array[0..gc_ai_upgrade_max_types-1] of Integer;
   
   unitupg : array[0..gc_MaxCountryCount-1] of array[0..gc_country_maxupgradecount-1] of TUnitUpgrade;
   uniqupg : array[0..gc_MaxCountryCount-1] of TUniqUpgList;
   
   developmentera : array[0..gc_MaxCountryCount-1] of Integer;
   mines : TIntegerList;
   orelist : TIntegerList;
   
   peasantlist   : TIntegerList;
   freepeasant   : TIntegerList;
   ailist        : TIntegerList;
   buildingslist : TIntegerList;
   storelist     : TIntegerList;
   milllist      : TIntegerList;
   unbuildhouses : TIntegerList;
   defenders     : TIntegerList;
   guards        : TIntegerList;
   agressors     : TIntegerList;
   freewarriors  : TIntegerList;
   
   towers        : TIntegerList;
   armylist      : TArmyList;
   
   unitsamount : array [0..gc_MaxCountryCount-1] of array [0..gc_country_maxmembers-1] of Integer;
   avUnits : array [0..gc_MaxCountryCount-1] of array [0..gc_country_maxmembers-1] of Boolean;
   officer17 : array [0..gc_MaxCountryCount-1] of Integer;
   drummer17 : array [0..gc_MaxCountryCount-1] of Integer;
   officer18 : array [0..gc_MaxCountryCount-1] of Integer;
   drummer18 : array [0..gc_MaxCountryCount-1] of Integer;
   
   aiunit : array [0..gc_MaxCountryCount-1] of array [0..gc_country_maxmembers-1] of Integer; //index in gPlayer.objbase
   buildlink : array[0..gc_MaxCountryCount-1] of array [0..gc_country_maxmembers-1] of Integer; //[country][airole] = building id
   buildprojects : array[0..gc_ai_max_construction_sites-1] of TAIBuildingProject;
   resbalance : array[0..gc_ResCount-1] of array[0..2] of Integer; //{wood..food..stone} => %of remaingn, food = const {ores} => max level of mines development
   
   onFood, onStone, onWood : Integer;
   defbuildings : TAiProtectedBuildingList;
   bFlags : array[0..gc_ai_max_flags] of Boolean;
   
   inittime : Float;
   bhumanai : Boolean;
   bprogressEconomy : Boolean;
   bprogressConstruction : Boolean;
   bprogressProduce : Boolean;
   bprogressUpgrades : Boolean;
   bprogressWar : Boolean;
   
   damagedBuildings : TIntegerList;
   
   guardsCount : Integer;
   guardsTotal : Integer;
   
   procedure Reset;
   begin
      self.bestProjects.Clear;
      self.centerFound := false;
      self.centerX := 0;
      self.centerZ := 0;
      self.agressorsSent := 0;
      
      self.defenceStage := false;
      self.makeDiversion := true;
      self.landBattle := true;
      self.waterBattle := true;
      
      var i, j : Integer;
      for i := 0 to gc_MaxCountryCount-1 do
      begin
         self.controlledNations[i] := false;
         for j := 0 to gc_country_maxmembers-1 do
         begin
            self.unitsAmount[i][j] := 0;
            self.aiunit[i][j] := 0;
            self.buildLink[i][j] := 0;
         end;
      end;
      
      for i := 0 to gc_MaxCountryCount-1 do
      self.developmentera[i] := gc_ai_era_food;
      
      self.mines.Clear;
      self.oreList.Clear;
      self.defenders.Clear;
      self.guards.Clear;
      self.agressors.Clear;
      self.freewarriors.Clear;
      self.damagedBuildings.Clear;
      self.towers.Clear;
      self.armyList.Clear;
      
      for i := 0 to gc_ai_max_construction_sites-1 do
      self.buildprojects[i].ResetBuildingProject;
      
      for i := 0 to gc_ResCount-1 do
      for j := 0 to 2 do
      self.resbalance[i][j] := 0;
      
      self.onFood := 0;
      self.onStone := 0;
      self.onWood := 0;
      
      self.defBuildings.Clear;
      for i := 0 to gc_ai_max_flags-1 do
      self.bFlags[i] := false;
      
      for i := 0 to gc_MaxCountryCount-1 do
      for j := 0 to gc_country_maxmembers-1 do
      avUnits[i][j] := true;
      
      for i := 0 to gc_MaxCountryCount-1 do
      begin
         officer17[i] := 0;
         drummer17[i] := 0;
         officer18[i] := 0;
         drummer18[i] := 0;
      end;
      
      self.bhumanai := False;
      self.bprogressEconomy := True;
      self.bprogressConstruction := True;
      self.bprogressProduce := True;
      self.bprogressUpgrades := True;
      self.bprogressWar := True;
   end;
   
   procedure Create;
   begin
      centerfound := false;
      
      self.peasantlist.Create(SizeOf(Integer));
      self.freepeasant.Create(SizeOf(Integer));
      self.ailist.Create(SizeOf(Integer));
      self.bestprojects.Create();
      self.buildingslist.Create(SizeOf(Integer));
      self.mines.Create(SizeOf(Integer));
      self.orelist.Create(SizeOf(Integer));
      self.storelist.Create(SizeOf(Integer));
      self.milllist.Create(SizeOf(Integer));
      self.unbuildhouses.Create(SizeOf(Integer));
      self.defenders.Create(SizeOf(Integer));
      self.guards.Create(SizeOf(Integer));
      self.agressors.Create(SizeOf(Integer));
      self.freewarriors.Create(SizeOf(Integer));
      self.damagedBuildings.Create(SizeOf(Integer));
      self.armylist.Create;
      
      self.towers.Create(SizeOf(Integer));
      self.defbuildings.Create;
      
      var i : Integer;
      for i :=0 to gc_ai_max_construction_sites-1 do
      self.buildprojects[i].Create();
      for i :=0 to gc_MaxCountryCount-1 do begin
         self.uniqupg[i].Create();
      end;
      
      Reset;
   end;
   
   constructor TAiData;
   begin
      Create();
   end;
   
   procedure Destroy;
   begin
      self.peasantlist.Destroy;
      self.freepeasant.Destroy;
      self.ailist.Destroy;
      self.bestprojects.Destroy;
      self.buildingslist.Destroy;
      self.mines.Destroy;
      self.orelist.Destroy;
      self.storelist.Destroy;
      self.milllist.Destroy;
      self.unbuildhouses.Destroy;
      self.defenders.Destroy;
      self.guards.Destroy;
      self.agressors.Destroy;
      self.freewarriors.Destroy;
      
      self.towers.Destroy;
      self.defbuildings.Destroy;
      self.damagedBuildings.Destroy;
      
      var i : Integer;
      //for i :=0 to gc_ai_max_construction_sites-1 do
      //   self.buildprojects[i].Destroy;
      for i :=0 to gc_MaxCountryCount-1 do begin
         self.uniqupg[i].Destroy;
      end;
   end;
   
   destructor TAiData;
   begin
      Destroy;
   end;
end;

global WallGd8 : array[0..9] of Integer; // = (7,6,5,0,0,4,1,2,3); //static byte GD8[9]={7,6,5,0,0,4,1,2,3};
WallGd8[0] := 7;
WallGd8[1] := 6;
WallGd8[2] := 5;
WallGd8[3] := 0;
WallGd8[4] := 0;
WallGd8[5] := 4;
WallGd8[6] := 1;
WallGd8[7] := 2;
WallGd8[8] := 3;

global WallDx8 : array[0..8] of Integer; // = (0),(1),(1),(1),(0),(-1),(-1),(-1); //static char DX8[8]={0,1,1,1,0,-1,-1,-1};
WallDx8[0] := 0;
WallDx8[1] := 1;
WallDx8[2] := 1;
WallDx8[3] := 1;
WallDx8[4] := 0;
WallDx8[5] := -1;
WallDx8[6] := -1;
WallDx8[7] := -1;

global WallDy8 : array[0..8] of Integer; //= (-1,-1,0,1,1,1,0,-1); //static char DY8[8]={-1,-1,0,1,1,1,0,-1};
WallDy8[0] := -1;
WallDy8[1] := -1;
WallDy8[2] := 0;
WallDy8[3] := 1;
WallDy8[4] := 1;
WallDy8[5] := 1;
WallDy8[6] := 0;
WallDy8[7] := -1;

//Walls system:
type TWallCell = class
public
   fSprite : Integer;
   x : Integer;
   y : Integer;
   wallType : Integer;
   cid : Integer;
   visible : Boolean;
   goHnd : Integer; //unit handle
   plInd : Integer;
   
   function GetSprite : Integer;
   begin
      Result := self.fSprite;
   end;
   
   procedure SetSprite(value : Integer; binstant : Boolean);
   begin
      if (value<>fSprite) and (goHnd<>0) then
      begin
         if (binstant) then
         GameObjectDestroyByHandle(goHnd)
         else
         begin
            gint_unit_tagstate := gc_statetag_essential_death;
            GameObjectExecuteStateByHandle(goHnd, 'SetTagStates');
            //GameObjectRequestToDestroyByHandle(goHnd);
         end;
         self.goHnd := 0;
      end;
      self.fSprite := value;
   end;
   
   procedure Create;
   begin
      fSprite := -1;
   end;
   
   constructor TWallCell;
   begin
      Create;
   end;
   
   procedure Destroy;
   begin
      if (goHnd<>0) then
      GameObjectDestroyByHandle(goHnd);
   end;
   
   destructor TWallCell;
   begin
      Destroy;
   end;
end;

type TWallCluster = class
public
   wallType : Integer;
   cornPt : TIntegerList; //simple Integer values
   Cells : TPtrList; //TWallCell
   lastX : Integer;
   lastY : Integer;
   finalX : Integer;
   finalY : Integer;
   
   plIndex : Integer;
   cid : Integer;
   firstWall : Boolean;
   buildWall : Boolean;
   
   function AddCell: Pointer;
   begin
      GetMem(result, SizeOf(TWallCell));
      FillMem(result, SizeOf(TWallCell), 0);
      self.Cells.AddPointer(result);
      TWallCell(result).Create;
   end;
   
   function GetCell(const index: Integer): Pointer;
   begin
      var p: Pointer=self.Cells.GetPointer(index);
      Result:=p;
   end;
   
   procedure DeleteCell(const index: Integer);
   begin
      var p: Pointer=self.Cells.GetPointer(index);
      if p<>nil then
      begin
         TWallCell(p).Destroy;
         FreeMem(p);
      end;
      self.Cells.Delete(index);
   end;
   
   procedure Create;
   begin
      if self.Cells.GetItemSize=0 then
      self.Cells.Create;
      if  self.cornPt.GetItemSize=0 then
      self.cornPt.Create(SizeOf(Integer));
   end;
   
   constructor TWallCluster;
   begin
      Create;
   end;
   
   procedure Clear(const bDestroyObj : Boolean);
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.Cells.GetCount-1 do begin
         p:=self.Cells.GetPointer(i);
         if (bDestroyObj) then
         TWallCell(p).Destroy;
         FreeMem(p);
      end;
      self.cornPt.Clear;
      self.Cells.Clear;
   end;
   
   procedure Destroy;
   begin
      self.Clear(True);
      self.Cells.Destroy;
      self.cornPt.Destroy;
   end;
   
   destructor TWallCluster;
   begin
      Destroy;
   end;
   
   procedure SetSize(n : Integer);
   begin
      var i : Integer;
      if (n<0) then
      ErrorLog('n<0 at SetSize at TWallCluster');
      if n>self.cells.GetCount then
      begin
         for i:=0 to n-self.cells.GetCount do
         self.AddCell;
      end
      else
      if (n<self.cells.GetCount) then
      for i:=self.cells.GetCount-1 downto n do
      self.DeleteCell(i);
   end;
   
   procedure AddPoint(const x, y : Integer);
   begin
      var p : Pointer = self.AddCell;
      TWallCell(p).x := x;
      TWallCell(p).y := y;
      TWallCell(p).wallType := self.wallType;
   end;
   
   procedure UndoSegment();
   begin
      if (self.cornPt.GetCount=0) then exit;
      self.SetSize(self.cornPt.get(self.cornPt.GetCount-1));
      if (self.cornPt.GetCount>0) and (self.cells.GetCount>0) then
      begin
         var p : Pointer = self.cells.GetPointer(self.cells.GetCount-1);
         self.lastX := TWallCell(p).x;
         self.lastY := TWallCell(p).y;
      end;
   end;
   
   procedure SetPreviousSegment();
   begin
      if (self.cornPt.GetCount=0) then exit;
      self.UndoSegment();
      if (self.cornPt.GetCount>1) then
      begin
         self.cornPt.Delete(self.cornPt.GetCount-1);
         self.UndoSegment();
      end;
   end;
   
   procedure KeepSegment();
   begin
      self.cornPt.Add(self.cells.GetCount);
      var p : Pointer = self.cells.GetPointer(self.cells.GetCount-1);
      self.lastX := TWallCell(p).x;
      self.lastY := TWallCell(p).y;
   end;
   
   function GetDir8(const dx, dy : Integer) : Integer;
   begin
      Result := WallGd8[dx+dx+dx+dy+4];
   end;
   
   function GetDx8(index : Integer) : Integer;
   begin
      Result := WallDx8[index];
   end;
   
   function GetDy8(index : Integer) : Integer;
   begin
      Result := WallDy8[index];
   end;
   
   procedure CreateSprites();
   begin
      if (self.cells.GetCount=0) then
      exit;
      if (self.cells.GetCount=1) then
      begin
         var p : Pointer = self.cells.GetPointer(0);
         TWallCell(p).SetSprite(2, True);
      end
      else
      begin
         var p0 : Pointer = self.cells.GetPointer(0);
         var p1 : Pointer = self.cells.GetPointer(1);
         var p2 : Pointer = self.cells.GetPointer(self.cells.GetCount-2);
         var dir0 : Integer = self.GetDir8(TWallCell(p1).x-TWallCell(p0).x, TWallCell(p1).y-TWallCell(p0).y);
         TWallCell(p0).SetSprite(dir0 and 3, True);
         p1 := self.cells.GetPointer(self.cells.GetCount-1);
         dir0 := self.GetDir8(TWallCell(p1).x-TWallCell(p2).x, TWallCell(p1).y-TWallCell(p2).y);
         TWallCell(p1).SetSprite(dir0 and 3, True);
         var i : Integer;
         for i:=1 to self.cells.GetCount-2 do
         begin
            p0 := self.cells.GetPointer(i);
            p1 := self.cells.GetPointer(i+1);
            p2 := self.cells.GetPointer(i-1);
            dir0 := self.GetDir8(TWallCell(p0).x-TWallCell(p2).x, TWallCell(p0).y-TWallCell(p2).y);
            var dir1 : Integer = self.GetDir8(TWallCell(p1).x-TWallCell(p0).x, TWallCell(p1).y-TWallCell(p0).y);
            var ddir : Integer = (dir1-dir0) and 7;
            if ddir<0 then ErrorLog('ddir<0 at CreateSprites;');
            if (ddir>3) then ddir := ddir-8;
            var addir : Integer = floor(abs(ddir));
            if (addir>1) then
            TWallCell(p0).SetSprite(12, True)
            else
            begin
               if (addir<>0) then
               begin
                  if (ddir<0) then
                  TWallCell(p0).SetSprite(4+((dir0+4) and 7), True)
                  else
                  TWallCell(p0).SetSprite(4+((dir0+9) and 7), True);
               end
               else
               TWallCell(p0).SetSprite(dir0 and 3, True);
            end;
         end;
      end;
   end;
   
   procedure NormalizePosToMapInt(var x, y : Integer);
   begin
      var hw : Integer = GetMapWidth div 2;
      var hh : Integer = GetMapHeight div 2;
      if x<-hw then x:=-hw
      else
      if x>=hw then x:=hw-1;
      if y<-hh then y:=-hh
      else
      if y>=hh then y:=hh-1;
   end;
   
   procedure ConnectToPoint(const ix, iy : Integer);
   begin
      var x : Integer = ix;
      var y : Integer = iy;
      NormalizePosToMapInt(x, y);
      if (self.cells.GetCount=0) then
      begin //first point
         self.lastX := x;
         self.lastY := y;
         self.finalX := x;
         self.finalY := y;
         self.AddPoint(x, y);
         self.cornPt.Add(1);
      end
      else
      begin
         if ((x=self.lastX) and (y=self.lastY)) then
         begin
            self.finalX := x;
            self.finalY := y;
            exit;
         end;
         var np : Integer;
         var xp : Integer = x;
         var yp : Integer = y;
         var dx, dy, lsx, lsy : Integer;
         lsx := self.lastX;
         lsy := self.lastY;
         self.finalX := x;
         self.finalY := y;
         while (true) do
         begin
            if (self.cells.GetCount<2) then
            begin
               if (self.cells.GetCount=0) then self.AddPoint(self.lastX, self.lastY);
               dx := x-self.lastX;
               dy := y-self.lastY;
               if (dx>1) then dx := 1;
               if (dy>1) then dy := 1;
               if (dx<-1) then dx := -1;
               if (dy<-1) then dy := -1;
               lsx := self.lastX+dx;
               lsy := self.lastY+dy;
               self.AddPoint(lsx, lsy);
               np := np+1;
            end
            else
            begin
               var p1 : Pointer = self.cells.GetPointer(self.cells.GetCount-1);
               var p2 : Pointer = self.cells.GetPointer(self.cells.GetCount-2);
               var dx0 : Integer = TWallCell(p1).x-TWallCell(p2).x;
               var dy0 : Integer = TWallCell(p1).y-TWallCell(p2).y;
               var dir1 : Integer = self.GetDir8(dx0, dy0);
               dx := x-lsx;
               dy := y-lsY;
               if (dx>1) then dx := 1;
               if (dy>1) then dy := 1;
               if (dx<-1) then dx := -1;
               if (dy<-1) then dy := -1;
               var dir2 : Integer = self.GetDir8(dx, dy);
               var ddir : Integer = (dir2-dir1) and 7;
               if (ddir>3) then ddir := ddir-8;
               var addir : Integer = floor(abs(ddir));
               var x0 : Integer = TWallCell(p1).x;
               var y0 : Integer = TWallCell(p1).y;
               if (addir<=1) then
               begin
                  lsx := x0+dx;
                  lsy := y0+dy;
                  self.AddPoint(lsx, lsy);
               end
               else
               begin
                  if ((addir=2) and ((dir1=0) or (dir1=2) or (dir1=4) or (dir1=6))) then
                  begin
                     lsx := x0+dx;
                     lsy := y0+dy;
                     self.AddPoint(lsx, lsy);
                  end
                  else
                  begin
                     if ((dir1=0) or (dir1=2) or (dir1=4) or (dir1=6)) then
                     begin
                        if (ddir<0) then
                        begin
                           dx := self.Getdx8((dir1-2) and 7);
                           dy := self.Getdy8((dir1-2) and 7);
                        end
                        else
                        begin
                           dx := self.Getdx8((dir1+2) and 7);
                           dy := self.Getdy8((dir1+2) and 7);
                        end;
                     end
                     else
                     begin
                        if (ddir>0) then
                        begin
                           dx := self.Getdx8((dir1+1) and 7);
                           dy := self.Getdy8((dir1+1) and 7);
                        end
                        else
                        begin
                           dx := self.Getdx8((dir1-1) and 7);
                           dy := self.Getdy8((dir1-1) and 7);
                        end;
                     end;
                     lsx := x0+dx;
                     lsy := y0+dy;
                     self.AddPoint(lsx, lsy);
                  end;
               end;
            end;
            if ((lsx=x) and (lsy=y)) then break;
         end;
      end;
   end;
   
   procedure SetWallBuildMode(plIndex : Integer; cid, usage : Integer);
   begin
      self.Clear(True);
      if cid<>-1 then
      begin
         self.plIndex := plIndex;
         self.cid := cid;
         self.wallType := usage;
         self.firstWall := true;
         self.buildWall := true;
      end
      else
      begin
         self.buildWall := false;
      end;
   end;
   
   procedure := (var source : TWallCluster);
   begin
      self.wallType := source.wallType;
      self.cornPt := source.cornPt;
      self.cells.Clear;
      var i : Integer;
      for i:=0 to source.cells.GetCount-1 do
      begin
         var pCell : Pointer = self.AddCell;
         var pSrcCell : Pointer = source.GetCell(i);
         TWallCell(pCell).fSprite := TWallCell(pSrcCell).fSprite;
         TWallCell(pCell).x := TWallCell(pSrcCell).x;
         TWallCell(pCell).y := TWallCell(pSrcCell).y;
         TWallCell(pCell).wallType := TWallCell(pSrcCell).wallType;
         TWallCell(pCell).cid := TWallCell(pSrcCell).cid;
         TWallCell(pCell).visible := TWallCell(pSrcCell).visible;
         TWallCell(pCell).goHnd := TWallCell(pSrcCell).goHnd;
         TWallCell(pCell).plInd := TWallCell(pSrcCell).plInd;
      end;
      self.lastX := source.lastX;
      self.lastY := source.lastY;
      self.finalX := source.finalX;
      self.finalY := source.finalY;
      self.plIndex := source.plIndex;
      self.cid := source.cid;
      self.firstWall := source.firstWall;
      self.buildWall := source.buildWall;
   end;
end;

type TWallSystem = class
private
{ Private Declarations }
protected
{ Protected Declarations }
public
{ Public Declarations }
   fList: TPtrList; //TWallCluster
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   
   constructor TWallSystem;
   begin
      Create;
   end;
   
   procedure Clear;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         TWallCluster(p).Destroy;
         FreeMem(p);
      end;
      self.fList.Clear;
   end;
   
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   
   destructor TWallSystem;
   begin
      Destroy;
   end;
   
   function GetCount: Integer;
   begin
      result:=self.fList.GetCount;
   end;
   
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<GetCount) then
      result:=self.fList.GetPointer(index)
      else begin
         result := nil;
         ErrorLog('TWallSystem.Get : index = '+IntToStr(index)+' pointer=nil');
      end;
   end;
   
   function Add: Pointer;
   begin
      GetMem(result, SizeOf(TWallCluster));
      FillMem(result, SizeOf(TWallCluster), 0);
      self.fList.AddPointer(result);
      TWallCluster(result).Create;
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then
      begin
         var p: Pointer=self.fList.GetPointer(index);
         if p<>nil then begin
            TWallCluster(p).Destroy;
            FreeMem(p);
         end;
         self.fList.Delete(index);
      end
      else
      ErrorLog('TWallSystem.Delete : index = '+IntToStr(index)+' out of bounds');
   end;
   
   procedure RemoveHandle(const plIndex, goHnd : Integer);
   begin
      var i, j : Integer;
      for[MAIN] i := 0 to self.GetCount-1 do
      begin
         var p : Pointer = self.get(i);
         if TWallCluster(p).plIndex = plIndex then
         begin
            for j := 0 to TWallCluster(p).Cells.GetCount-1 do
            begin
               var pCell : Pointer = TWallCluster(p).Cells.GetPointer(j);
               if (TWallCell(pCell).goHnd = goHnd) then
               begin
                  TWallCell(pCell).goHnd := 0;
                  break(MAIN);
               end;
            end;
         end;
      end;
   end;
   
   function GetWallClusterByHandle(const plIndex, goHnd : Integer; var wallCluster : Pointer) : Integer;
   begin
      Result := -1;
      var i, j : Integer;
      for[MAIN] i := 0 to self.GetCount-1 do
      begin
         var p : Pointer = self.get(i);
         if TWallCluster(p).plIndex = plIndex then
         begin
            for j := 0 to TWallCluster(p).Cells.GetCount-1 do
            begin
               var pCell : Pointer = TWallCluster(p).Cells.GetPointer(j);
               if (TWallCell(pCell).goHnd = goHnd) then
               begin
                  Result := j;
                  wallCluster := p;
                  break(MAIN);
               end;
            end;
         end;
      end;
   end;
   
   procedure Reset;
   begin
      var i, j : Integer;
      for i:=self.GetCount-1 downto 0 do
      begin
         var pCluster : Pointer = self.Get(i);
         if pCluster<>nil then
         begin
            for j := 0 to TWallCluster(pCluster).Cells.GetCount-1 do
            begin
               var pCell : Pointer = TWallCluster(pCluster).Cells.GetPointer(j);
               if pCell<>nil then
               TWallCell(pCell).goHnd := 0;
            end;
         end;
         Delete(i);
      end;
   end;
   
   procedure := (var source : TWallSystem);
   begin
      self.Clear;
      var i, j : Integer;
      for i:=0 to source.GetCount-1 do
      begin
         var pWallCluster : Pointer = self.Add;
         var pSrcWallCluster : Pointer = source.Get(i);
         
         TWallCluster(pWallCluster).wallType := TWallCluster(pSrcWallCluster).wallType;
         TWallCluster(pWallCluster).cornPt := TWallCluster(pSrcWallCluster).cornPt;
         TWallCluster(pWallCluster).cells.Clear;
         for j:=0 to TWallCluster(pSrcWallCluster).cells.GetCount-1 do
         begin
            var pCell : Pointer = TWallCluster(pWallCluster).AddCell;
            var pSrcCell : Pointer = TWallCluster(pSrcWallCluster).GetCell(j);
            TWallCell(pCell).fSprite := TWallCell(pSrcCell).fSprite;
            TWallCell(pCell).x := TWallCell(pSrcCell).x;
            TWallCell(pCell).y := TWallCell(pSrcCell).y;
            TWallCell(pCell).wallType := TWallCell(pSrcCell).wallType;
            TWallCell(pCell).cid := TWallCell(pSrcCell).cid;
            TWallCell(pCell).visible := TWallCell(pSrcCell).visible;
            TWallCell(pCell).goHnd := TWallCell(pSrcCell).goHnd;
            TWallCell(pCell).plInd := TWallCell(pSrcCell).plInd;
         end;
         TWallCluster(pWallCluster).lastX := TWallCluster(pSrcWallCluster).lastX;
         TWallCluster(pWallCluster).lastY := TWallCluster(pSrcWallCluster).lastY;
         TWallCluster(pWallCluster).finalX := TWallCluster(pSrcWallCluster).finalX;
         TWallCluster(pWallCluster).finalY := TWallCluster(pSrcWallCluster).finalY;
         TWallCluster(pWallCluster).plIndex := TWallCluster(pSrcWallCluster).plIndex;
         TWallCluster(pWallCluster).cid := TWallCluster(pSrcWallCluster).cid;
         TWallCluster(pWallCluster).firstWall := TWallCluster(pSrcWallCluster).firstWall;
         TWallCluster(pWallCluster).buildWall := TWallCluster(pSrcWallCluster).buildWall;
      end;
   end;
end;

type TPlayerLists = class
   buildings : TIntegerList;
   storehouses : TIntegerList;
   ports : TIntegerList;
   deluids : TIntegerList; // contain buildings uids that was deleted by pressing del hotkey, to prevent peasants adviser to repair them
   
   procedure Create();
   begin
      self.buildings.Create(SizeOf(Integer));
      self.storehouses.Create(SizeOf(Integer));
      self.ports.Create(SizeOf(Integer));
      self.deluids.Create(SizeOf(Integer));
   end;
   
   constructor TPlayerLists;
   begin
      Create();
   end;
   
   procedure Destroy();
   begin
      self.buildings.Destroy();
      self.storehouses.Destroy();
      self.ports.Destroy();
      self.deluids.Destroy();
   end;
   
   destructor TPlayerLists;
   begin
      Destroy();
   end;
end;

type TPlayerStatistics = class
   restotal : array [0..gc_ResCount-1] of Integer;
   resonupgrade : array [0..gc_ResCount-1] of Integer;
   resonmines : array [0..gc_ResCount-1] of Integer;
   resonunits : array [0..gc_ResCount-1] of Integer;
   resonbuildings : array [0..gc_ResCount-1] of Integer;
   resonlife : array [0..gc_ResCount-1] of Integer;
   resbuy : array [0..gc_ResCount-1] of Integer;
   ressell : array [0..gc_ResCount-1] of Integer;
   killed : array [0..gc_MaxCountryCount-1] of array [0..gc_country_maxmembers-1] of Integer;
   produced : array [0..gc_MaxCountryCount-1] of array [0..gc_country_maxmembers-1] of Integer;
   population : TIntegerList;
   scores : TIntegerList;
   
   procedure Create(); begin
      self.population.Create(SizeOf(Integer));
      self.population.SetGrowthDelta(1024);
      self.scores.Create(SizeOf(Integer));
      self.scores.SetGrowthDelta(1024);
   end;
   constructor TPlayerStatistics; begin Create(); end;
   procedure Destroy(); begin self.population.Destroy(); self.scores.Destroy(); end;
   destructor TPlayerStatistics; begin Destroy(); end;
end;

type TAdviser = class
   bvisible : Boolean;
   benable : Boolean;
   bavailable : Boolean;
   bconstruct : Boolean;
   beconomy : Boolean;
   beconomyadvanced : Boolean;
   bcontrolpeasants : Boolean;
   bproducepeasants : Boolean;
   brepairbuildings : Boolean;
   bfillmines : Boolean;
   bmarketadvises : Boolean;
   bmanualresource : Boolean;
   resonfood : Float;
   resonwood : Float;
   resonstone : Float;
   bupgrades : Boolean;
   bupgradesunavailable : Boolean;
   bquartermaster : Boolean;
   bsetupguards : Boolean;
   bproduceofficers : Boolean;
   bshowsquads : Boolean;
   settings : Integer;
   version : Integer;
end;

type TPlayerAdviserSquad = class
   cid : Integer;
   id : Integer;
   count : Integer;
   bofficers : Boolean;
end;

type TPlayerAdviser = class
   adviser : TAdviser;
   buildings : TIntegerList;
   upgradeseconomy : TIntegerList;
   upgradesmilitary : TIntegerList;
   produce : TIntegerList;
   squads : TPtrList;
   officers : TIntegerList;
   drummers : TIntegerList;
   peasants : TIntegerList;

   procedure Create;
   begin
      self.buildings.Create(SizeOf(Integer));
      self.upgradeseconomy.Create(SizeOf(Integer));
      self.upgradesmilitary.Create(SizeOf(Integer));
      self.produce.Create(SizeOf(Integer));
      self.squads.Create;
      self.officers.Create(SizeOf(Integer));
      self.drummers.Create(SizeOf(Integer));
      self.peasants.Create(SizeOf(Integer));
   end;

   procedure Destroy;
   begin
      self.buildings.Destroy;
      self.upgradeseconomy.Destroy;
      self.upgradesmilitary.Destroy;
      self.produce.Destroy;
      self.squads.Destroy;
      self.officers.Destroy;
      self.drummers.Destroy;
      self.peasants.Destroy;
   end;

   constructor TPlayerAdviser;
   begin
      Create();
   end;

   destructor TPlayerAdviser;
   begin
      Create();
   end;
end;

type TObjWeapon = class
   damage : Integer;
   damageinit : Integer;
   damagestatic : Integer;
   damagepercent : Integer;
   radiusmax : Float;
   pause : Float;
   dispertion : Float;
end;

type TObjWeaponStatic = class
   enabled : Boolean;
   radiusmin : Float;
   addradius : Float;
   detectradiusmin : Float;
   detectradiusmax : Float;
   cost : array [0..gc_ResCount-1] of Integer;
   weaponsid : String;
   weaponid : Integer;
   kind : Integer;
   attmask : Integer;
   fxshot : String;
end;

type TObjBase = class
   sid : String; // to fix when indexes corrupted
   maxhp : Integer;
   shield : Integer;
   price : array [0..gc_ResCount-1] of Integer;
   buildtime : Float;
   fishingspeed : Integer;
   fishingmax : Integer;
   speed : Float;
   protection : array [0..gc_obj_weapon_kind_count-1] of Integer;
   weapon : array [0..gc_obj_MaxWeapon-1] of TObjWeapon;
   bproduceenabled : Boolean;
   
   procedure Create();
   begin
      buildtime := 64*gc_frames_to_time;
      speed := gc_obj_speed_default;
      bproduceenabled := True;
   end;
   
   constructor TObjBase; begin Create; end;
   destructor TObjBase; begin end;
end;

type TObjProp = class
   id : Integer;
   sid : String;
   peasantabsorber : Integer;
   transport : Integer;
   material : Integer;
   radius : Float;
   rotatespeed : Float;
   costpercent : Float;
   weapon : array [0..gc_obj_MaxWeapon-1] of TObjWeaponStatic;
   searchradius : Float;
   minattackradius : Float;
   cankill : array [0..gc_obj_material_count-1] of Boolean;
   mmask : Integer;
   kmask : Integer;
   vision : Integer;
   consume : array [0..gc_ResCount-1] of Integer;
   explmedia : Integer;
   explradius : Float;
   media : Integer;
   score : Integer;
   motionstyle : Integer;
   usage : Integer;
   artdepo : array [0..gc_MaxArtilleryType-1] of Integer;
   artind : Integer;
   farm : Integer;
   resourcebase : array [0..gc_ResCount-1] of Boolean;
   produce : array [0..gc_ResCount-1] of Integer;
   bbuilding : Boolean;
   bwall : Boolean;
   bgate : Boolean;
   bslowdeath : Boolean;
   bcapture : Boolean;
   bcancapture : Boolean;
   bprotector : Boolean;
   bshotdirection : Boolean;
   bshotforward : Boolean;
   bshotleftflank : Boolean;
   bshotrightflank : Boolean;
   bshotback : Boolean;
   bshowdelay : Boolean;
   bturnoff : Boolean;
   bstandground : Boolean;
   bnohungry : Boolean;
   bdrummer : Boolean;
   bofficer : Boolean;
   bmercenary : Boolean;
   bfastunit : Boolean;
   bpriest : Boolean;
   bartillery : Boolean;
   bartdepo : Boolean;
   bartprepare : Boolean;
   bmarket : Boolean;
   bcansetrally : Boolean;
   airole : Integer;
   aiforce : Integer;
   walkintervalfactor : Float;
   exitmaxdelay : Float;
   
   procedure Create();
   begin
      material := gc_obj_material_none;
      radius := (gc_obj_radius_default/gc_pixels_to_tile); // _misc_PixelsToTiles
      rotatespeed := gc_obj_rotatespeed_default;
      costpercent := 100;
      explmedia := gc_obj_explmedia_default;
      media := gc_obj_media_land;
      motionstyle := gc_obj_motionstyle_default;
      usage := gc_obj_usage_none;
      airole := gc_ai_unit_none;
      walkintervalfactor := 1;
   end;
   
   constructor TObjProp; begin Create; end;
   destructor TObjProp; begin end;
end;

type TPlayer = class
   id : Integer;
   objbase : array [0..gc_MaxCountryCount-1] of array [0..gc_country_maxmembers-1] of TObjBase;
   cid : Integer;
   csid : String;
   res : array [0..gc_ResCount-1] of Integer;
   setres : array [0..gc_ResCount-1] of Integer;
   lanres : array [0..gc_ResCount-1] of Integer;
   resefficiency : array [0..gc_MaxCountryCount-1] of array [0..gc_ResCount-1] of Integer;
   fieldlife : Integer;
   victorystate : Integer;
   victorystategametime : Float;
   unitcount : Integer;
   farm : Integer;
   myplmask : Integer;
   enemyplmask : Integer;
   team : Integer;
   difficulty : Integer;
   bexists : Boolean;
   bgeology : Boolean;
   bballoon : Boolean;
   bfamine : Boolean;
   brebellion : Boolean;
   bai : Boolean;
   bneutral : Boolean;
   //class_ City{
   aidata : TAiData;
   squads : TSquadList;
   //for alarm
   lastattacktime : Float;
   lastprogresstime : Float;
   lastcheckexists : Float;
   lastcalcsquadsmovecounttime : Float;
   lastprogressaitime : Float;
   lastseedwheattime : Float;
   progresstick : Integer;
   searchenemyprev : Integer;
   searchenemylast : Integer;
   searchenemycur : Integer;
   searchenemylasttime : Float;
   artlimit : array [0..gc_MaxArtilleryType-1] of Integer;
   artcount : array [0..gc_MaxArtilleryType-1] of Integer;
   counter : TPlayerCounters;
   stat : TPlayerStatistics;
   upgstate : array [0..gc_MaxCountryCount-1] of array [0..gc_country_maxupgradecount-1] of TPlayerUpgradeState;
   lists : TPlayerLists;
   playeradviser : TPlayerAdviser;
   
   procedure Create;
   begin
      var i, j : Integer;
      for i:=0 to gc_MaxCountryCount-1 do
      for j:=0 to gc_country_maxmembers-1 do
      self.objbase[i][j].Create;
      self.squads.Create;
      self.lists.Create;
      self.aidata.Create;
      self.stat.Create;
      self.playeradviser.Create;
   end;
   
   constructor TPlayer; begin Create; end;
   
   procedure Destroy;
   begin
      self.squads.Destroy;
      self.lists.Destroy;
      self.aidata.Destroy;
      self.playeradviser.Destroy;
   end;
   
   destructor TPlayer; begin Destroy; end;
   
   procedure DisbandSquad(sqInd : Integer);
   begin
      if (sqInd >= 0) and (sqInd < squads.GetCount) then
      begin
         var pSquad : Pointer = squads.Get(sqInd);
         if (pSquad <> nil) and (TSquad(pSquad).fArmy >= 0) and (TSquad(pSquad).fArmy < aiData.armyList.GetCount) then
         begin
            var aInd : Integer = TSquad(pSquad).fArmy;
            if (aInd>=0) then
            begin
               var pArmy : Pointer = aiData.armyList.Get(aInd);
               if pArmy <> nil then
               begin
                  TArmy(pArmy).Remove(pSquad);
                  if TArmy(pArmy).fSquadList.GetCount = 0 then
                  self.aiData.armyList.Delete(aInd);
               end;
            end;
         end;
         
         self.squads.Delete(sqInd);
      end;
   end;
end;

type TArmyInfo = class
   infantry : Integer;
   shooters : Integer;
   cavalry  : Integer;
   cannons  : Integer;
   mortars  : Integer;
   mcannons : Integer;
   towers   : Integer;
   ships    : Integer;
   force    : Integer;
   landCount : Integer;
   waterCount : Integer;
   region : Integer;
   
   regions : TIntegerList;
   
   minx : Integer;
   maxx : Integer;
   miny : Integer;
   maxy : Integer;
   
   procedure Create;
   begin
      minx := -1;
      maxx := -1;
      miny := -1;
      maxy := -1;
      region := -1;
      
      self.regions.Create(SizeOf(Integer));
   end;
   
   constructor TArmyInfo;
   begin
      Create;
   end;
   
   procedure Destroy;
   begin
      self.regions.Destroy;
   end;
   
   destructor TArmyInfo;
   begin
      Destroy;
   end;
   
   procedure IncludePos(x, y : Integer);
   begin
      if (minX < 0) or (x < minX) then
      minX := x;
      if (maxX < 0) or (x > maxX) then
      maxX := x;
      if (minY < 0) or (y < minY) then
      minY := y;
      if (maxY < 0) or (y > maxY) then
      maxY := y;
   end;
   
   procedure IncludeRegions(cRegion : Integer; var list : TIntegerList);
   begin
      if cRegion >= 0 then
      begin
         if region <> cRegion then
         begin
            case region of
               -1 : region := cRegion;
               -2 : begin
                  if regions.IndexOf(cRegion) < 0 then
                  regions.Add(cRegion);
               end
               else
               begin
                  if cRegion <> region then
                  begin
                     regions.Add(region);
                     regions.Add(cRegion);
                     region := -2;
                  end;
               end;
            end;
         end;
      end
      else
      begin
         var i : Integer;
         for i := list.GetCount-1 downto 0 do
         begin
            var region : Integer = list.Get(i);
            if regions.IndexOf(region) < 0 then
            regions.Add(region);
         end;
      end;
   end;
end;

type TArmyInfoList = class
private
{ Private Declarations }
   fList: TPtrList;
public
{ Public Declarations }
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   
   constructor TArmyInfoList;
   begin
      Create;
   end;
   
   procedure Clear;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         TArmyInfo(p).Destroy;
         FreeMem(p);
      end;
      self.fList.Clear;
   end;
   
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   
   destructor TArmyInfoList;
   begin
      Destroy;
   end;
   
   function GetCount: Integer;
   begin
      result := self.fList.GetCount;
   end;
   
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<GetCount) then
      result:=self.fList.GetPointer(index)
      else begin
         result := nil;
         ErrorLog('TArmyInfoList.Get : index = '+IntToStr(index)+' pointer=nil');
      end;
   end;
   
   function Add: Pointer;
   begin
      GetMem(result, SizeOf(TArmyInfo));
      FillMem(result, SizeOf(TArmyInfo), 0);
      TArmyInfo(result).Create;
      self.fList.AddPointer(result);
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then
      begin
         var p: Pointer=self.fList.GetPointer(index);
         if p<>nil then
         begin
            TArmyInfo(p).Destroy;
            FreeMem(p);
         end;
         self.fList.Delete(index);
      end
      else
      ErrorLog('TArmyInfoList.Delete : index = '+IntToStr(index)+' out of bounds');
   end;
end;

type TTower = class
   hnd : Integer;
   pause : Float;
   radius : Float;
   update : Boolean;
   remove : Boolean;
end;

type TTowerList = class
private
{ Private Declarations }
   fList: TPtrList;
public
{ Public Declarations }
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   
   constructor TTowerList;
   begin
      Create;
   end;
   
   procedure Clear;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         if p<>nil then
         FreeMem(p);
      end;
      self.fList.Clear;
   end;
   
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   
   destructor TTowerList;
   begin
      Destroy;
   end;
   
   function GetCount: Integer;
   begin
      result := self.fList.GetCount;
   end;
   
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<GetCount) then
      result:=self.fList.GetPointer(index)
      else begin
         result := nil;
         ErrorLog('TTowerList.Get : index = '+IntToStr(index)+' pointer=nil');
      end;
   end;
   
   function GetByHandle(goHnd: Integer): Pointer;
   begin
      var i : Integer;
      Result := nil;
      for i := self.fList.GetCount-1 downto 0 do
      begin
         var pTower : Pointer = self.fList.GetPointer(i);
         if (pTower<>nil) and (TTower(pTower).hnd = goHnd) then
         begin
            Result := pTower;
            break;
         end;
      end;
   end;
   
   function Add(goHnd : Integer): Pointer;
   begin
      GetMem(result, SizeOf(TTower));
      FillMem(result, SizeOf(TTower), 0);
      self.fList.AddPointer(result);
      TTower(result).hnd := goHnd;
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then
      begin
         var p: Pointer=self.fList.GetPointer(index);
         if p<>nil then
         FreeMem(p);
         self.fList.Delete(index);
      end
      else
      ErrorLog('TTowerList.Delete : index = '+IntToStr(index)+' out of bounds');
   end;
end;

type TEnemyInfo = class
public
{ Public Declarations }
   armyInfos : TArmyInfoList;
   towers : TTowerList;
   towerMap : array [0..gc_scangrid_countx-1] of array [0..gc_scangrid_county-1] of Float;
   
   team : Integer;
   enemyMask : Integer;
   changed : Boolean;
   enabled : Boolean;
   maxArmyForce : Integer;
   
   procedure Create;
   begin
      self.armyInfos.Create;
      self.towers.Create;
   end;
   
   constructor TEnemyInfo;
   begin
      Create;
   end;
   
   procedure Destroy;
   begin
      self.armyInfos.Destroy;
      self.towers.Destroy;
   end;
   
   destructor TEnemyInfo;
   begin
      Destroy;
   end;
   
   procedure Reset;
   begin
      self.armyInfos.Clear;
      self.towers.Clear;
      self.enemyMask := 0;
      
      var p : Pointer = self.towerMap;
      FillMem(p, SizeOf(self.towerMap), 0);
   end;
end;

type TEnemyInfoList = class
private
{ Private Declarations }
   fList: TPtrList;
public
{ Public Declarations }
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   
   constructor TEnemyInfoList;
   begin
      Create;
   end;
   
   procedure Clear;
   begin
      var i: Integer;
      var p: Pointer;
      for i := 0 to self.fList.GetCount-1 do begin
         p := self.fList.GetPointer(i);
         if (p<>nil) then
         begin
            TEnemyInfo(p).Destroy;
            FreeMem(p);
         end;
      end;
      self.fList.Clear;
   end;
   
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   
   destructor TEnemyInfoList;
   begin
      Destroy;
   end;
   
   function GetCount: Integer;
   begin
      result := self.fList.GetCount;
   end;
   
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<GetCount) then
      result:=self.fList.GetPointer(index)
      else begin
         result := nil;
         ErrorLog('TEnemyInfoList.Get : index = '+IntToStr(index)+' pointer=nil');
      end;
   end;
   
   function Add: Pointer;
   begin
      GetMem(result, SizeOf(TEnemyInfo));
      FillMem(result, SizeOf(TEnemyInfo), 0);
      TEnemyInfo(result).Create();
      self.fList.AddPointer(result);
   end;
   
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then
      begin
         var p: Pointer = self.fList.GetPointer(index);
         if p <> nil then
         begin
            TEnemyInfo(p).Destroy;
            FreeMem(p);
         end;
         self.fList.Delete(index);
      end
      else
      ErrorLog('TEnemyInfoList.Delete : index = '+IntToStr(index)+' out of bounds');
   end;
end;

type TPlayerArgs = class
   fid: Integer;
   fposx: Float;
   fposy: Float;
   fposz: Float;
   fvisposx: Float;
   fvisposz: Float;
   fdirx: Float;
   fdirz: Float;
   ftrgx: Float;
   ftrgy: Float;
   ftrgz: Float;
   faddord: Boolean;
   fdofirst: Boolean;
   frebuild: Boolean;
   fcenter: Boolean;
   fmode: Integer;
   fgroup: Integer;
   fplayer: Integer;
   fcid: Integer;
   fracename: String;
   fbasename: String;
   fhandle: Integer;
   fcapture: Boolean;
   fordtyp: Integer;
   ftarget: Integer;
   fclrord: Boolean;
   fintlst: Pointer;
   famount: Integer;
   fbstate: Boolean;
   fform: Integer;
   fsquad: Integer;
   fofficer: Integer;
   fdrummer: Integer;
   fposition: Boolean;
   flocktrg: Boolean;
   fsid: String;
   find: Integer;
   fresult: Integer;
   fdead: Boolean;
   fweaponid : Integer;
   frnd: Float;
   fbyte0 : Byte;
   fbyte1 : Byte;
   fpointer0 : Pointer;
   fpointer1 : Pointer;
end;

type TCustomObjShotPoint = class
   x, y, z : Float;
   bcustomdir : Boolean;
   bcustomweaponind : Boolean;
   minangle : Float;
   maxangle : Float;
   weaponind : Integer;
end;

type TCustomObjSmokePoint = class
   x, y, z : Float;
   nx, ny, nz : Float;
end;

type TCustomObjDecal = class
   bexists : Boolean;
   scale : Float;
   offx : Float;
   offz : Float;
   angle : Float;
end;

type TCustomBuildPointsWall = class
   builderCount : Integer;
   builderPoints : array [0..gc_MaxWallBuilderPointsCount-1] of TPos2f;
end;

type TCustomObjAABB = class
   buse : Boolean;
   minx : Float;
   maxx : Float;
   miny : Float;
   maxy : Float;
   minz : Float;
   maxz : Float;
end;

type TCustomObjPoints = class
   builderCount : Integer;
   builderPoints : array [0..gc_MaxBuilderCount-1] of TPos2f;
   exitCount : Integer;
   exitPoints : array [0..gc_MaxExitPointsCount-1] of TPos3f;
   shotCount : Integer;
   shotPoints : array [0..gc_MaxShotPointsCount-1] of TCustomObjShotPoint;
   smokeCount : Integer;
   smokePoints : array [0..gc_MaxSmokePointsCount-1] of TCustomObjSmokePoint;
   resourcePoint : TPos3f;
   aabb : TCustomObjAABB;
   decal : TCustomObjDecal;
end;

type TWeaponChild = class
   mincount : Integer;
   maxcount : Integer;
   buseparentdestpos : Boolean;
   list : TIntegerList;
   
   procedure Create; begin self.list.Create(SizeOf(Integer)); end;
   constructor TWeaponChild; begin Create; end;
   procedure Destroy; begin self.list.Destroy; end;
   destructor TWeaponChild; begin Destroy; end;
end;

type TWeaponCustomExplosion = class
   mincount : Integer;
   maxcount : Integer;
   list : TIntegerList;
   
   procedure Create; begin self.list.Create(SizeOf(Integer)); end;
   constructor TWeaponCustomExplosion; begin Create; end;
   procedure Destroy; begin self.list.Destroy; end;
   destructor TWeaponCustomExplosion; begin Destroy; end;
end;

type TWeapon = class
   sid : String;
   id : Integer;
   fx : String;
   projbasename : String;
   damage : Integer;
   radius : Float;
   gravity : Float;
   angle : Float;
   speed : Float;
   propagation : Integer;
   time : Float;
   trailfx : String;
   sound : String;
   sndind : Integer;
   buseownerdamage : Boolean;
   buseownerfxshot : Boolean;
   buseownercustomobjpoints : Boolean;
   brotate : Boolean;
   bcollisiondetection : Boolean;
   bcheckfriendonline : Boolean;
   volumeclippedfreq : Float;
   expdecname : String;
   
   childs : TWeaponChild;
   customexplosions : array [0..gc_obj_explmedia_count-1] of TWeaponCustomExplosion;
   syncweaponslist : TIntegerList;
   
   procedure Create;
   begin
      self.childs.Create;
      var i : Integer;
      for i:=0 to gc_obj_explmedia_count-1 do
      self.customexplosions[i].Create;
      self.syncweaponslist.Create(SizeOf(Integer));
   end;
   
   constructor TWeapon; begin Create; end;
   
   procedure Destroy;
   begin
      self.childs.Destroy;
      var i : Integer;
      for i:=0 to gc_obj_explmedia_count-1 do
      self.customexplosions[i].Destroy;
      self.syncweaponslist.Destroy;
   end;
   
   destructor TWeapon; begin Destroy; end;
end;

type TProj = class
   baseid : Integer;
   weaponid : Integer;
   owner : Integer;
   trg : Integer;
   px : Float;
   py : Float;
   pz : Float;
   dx : Float;
   dy : Float;
   dz : Float;
   ownerpl : Integer
   ownercid : Integer;
   ownerid : Integer;
   ownerweaponind : Integer;
   uniqrnd : Float;
end;

type TProjSimulatePoint = class
   weaponid : Integer;
   owner : Integer;
   px : Float;
   pz : Float;
   damagetime : Float;
end;

type TProjSimulatePoints = class
   curind : Integer;
   maxdamagetime : Float;
   projsim : array [0..gc_MaxProjSimulatePoints-1] of TProjSimulatePoint;
end;

type TLogMessage = class
   id : Integer;
   s1 : String;
   s2 : String;
   time : Float;
   ballowdublicates : Boolean;
end;

type TLogMessages = class
   count : Integer;
   msg : array [0..gc_MaxLogMessageCount-1] of TLogMessage;
public
   procedure Clear(ind : Integer);
   begin
      msg[ind].id := gc_logmessagesid_none;
      msg[ind].s1 := '';
      msg[ind].s2 := '';
      msg[ind].time := 0;
      msg[ind].ballowdublicates := False;
   end;
   procedure CalcCount();
   begin
      count := 0;
      var i : Integer;
      for i:=0 to gc_MaxLogMessageCount-1 do
      begin
         if (msg[i].id<>gc_logmessagesid_none) then
         count := count+1;
      end;
   end;
   procedure Sort();
   begin
      var i : Integer;
      for i:=0 to gc_MaxLogMessageCount-2 do
      begin
         if (msg[i].id=gc_logmessagesid_none) and (msg[i+1].id<>gc_logmessagesid_none) then
         begin
            msg[i] := msg[i+1];
            Clear(i+1);
         end;
      end;
   end;
   procedure Remove(ind : Integer);
   begin
      Clear(ind);
      count := count-1;
      if count<0 then
      begin
         count := 0;
         ErrorLog('TLogMessages count<0');
      end;
      Sort;
   end;
   procedure ClearAll;
   begin
      var i : Integer;
      for i:=0 to gc_MaxLogMessageCount-1 do
      self.Clear(i);
      count := 0;
   end;
end;

type TChatMessage = class
   sfrom : String;
   sto : String;
   text : String;
   mode : Integer;
   time : Float;
end;

type TChatMessages = class
   count : Integer;
   msg : array [0..gc_MaxChatMessageCount-1] of TChatMessage;
public
   procedure Clear(ind : Integer);
   begin
      msg[ind].sfrom := '';
      msg[ind].sto := '';
      msg[ind].text := '';
      msg[ind].mode := 0;
      msg[ind].time := 0;
   end;
   procedure CalcCount();
   begin
      count := 0;
      var i : Integer;
      for i:=0 to gc_MaxChatMessageCount-1 do
      begin
         if (msg[i].time<>0) then
         count := count+1;
      end;
   end;
   procedure Sort();
   begin
      var i : Integer;
      for i:=0 to gc_MaxChatMessageCount-2 do
      begin
         if (msg[i].time=0) then
         begin
            msg[i] := msg[i+1];
            Clear(i+1);
         end;
      end;
   end;
   procedure Add(const mode : Integer; var sfrom, sto, text : String);
   begin
      if (count>=gc_MaxChatMessageCount) then
      begin
         const maxcount = (gc_MaxChatMessageCount div 4);
         var i : Integer;
         for i:=0 to gc_MaxChatMessageCount-1 do
         begin
            if (i>=maxcount) then
            msg[i-maxcount] := msg[i];
            if (i<maxcount) or (i>=(gc_MaxChatMessageCount-maxcount)) then
            Clear(i);
         end;
         CalcCount;
      end;
      var ind : Integer = count;
      msg[ind].mode := mode;
      msg[ind].sfrom := sfrom;
      msg[ind].sto := sto;
      msg[ind].text := text;
      msg[ind].time := GetCurrentTime;
      count := count+1;
   end;
   procedure Remove(ind : Integer);
   begin
      Clear(ind);
      count := count-1;
      if count<0 then
      begin
         count := 0;
         ErrorLog('TChatMessages count<0');
      end;
      Sort;
   end;
   procedure ClearAll;
   begin
      var i : Integer;
      for i:=0 to gc_MaxChatMessageCount-1 do
      self.Clear(i);
      count := 0;
   end;
end;

type TTopZone = class
   danger : array[0..gc_MaxPlayerCount] of Float;
   attract : array[0..gc_MaxPlayerCount] of Float;
   changeDanger : array[0..gc_MaxPlayerCount] of Float;
   changeAttract : array[0..gc_MaxPlayerCount] of Float;
   centerDist : array[0..gc_MaxPlayerCount] of Integer;
   changed : Boolean;
end;

const gc_font_none = 0;
const gc_font_default = 1;
const gc_font_default_sans = 2;
const gc_font_default_serif = 3;
const gc_font_sans_10 = 4;
const gc_font_sans_11 = 5;
const gc_font_sans_12 = 6;
const gc_font_sans_13 = 7;
const gc_font_sans_14 = 8;
const gc_font_sans_15 = 9;
const gc_font_sans_16 = 10;
const gc_font_sans_17 = 11;
const gc_font_sans_18 = 12;
const gc_font_sans_19 = 13;
const gc_font_sans_21 = 14;
const gc_font_serif_10 = 15;
const gc_font_serif_11 = 16;
const gc_font_serif_12 = 17;
const gc_font_serif_13 = 18;
const gc_font_serif_14 = 19;
const gc_font_serif_15 = 20;
const gc_font_serif_16 = 21;
const gc_font_serif_17 = 22;
const gc_font_serif_18 = 23;
const gc_font_serif_19 = 24;
const gc_font_serif_21 = 25;
const gc_font_opensans_12 = 26;
const gc_font_spectrum_16 = 27;
const gc_font_spacer_1 = 28;
const gc_font_spacer_4 = 29;
const gc_font_spacer_6 = 30;
const gc_font_pass_14 = 31;

const gc_halDefault = 0;
const gc_halLeft = 1;
const gc_halRight = 2;
const gc_halMiddle = 3;
const gc_halRightWidth = 4;
const gc_halParentLeft = 5;
const gc_halParentRight = 6;
const gc_halParentMiddle = 7;
const gc_halParentRightWidth = 8;
const gc_valDefault = 0;
const gc_valTop = 1;
const gc_valBottom = 2;
const gc_valMiddle = 3;
const gc_valBottomHeight = 4;
const gc_valParentTop = 5;
const gc_valParentBottom = 6;
const gc_valParentMiddle = 7;
const gc_valParentBottomHeight = 8;

type TConst = class
private
   const cLogMessageMaxID = 32;
   const cloglocale = 'gui|log.';
public
   logmessage : array [0..cLogMessageMaxID-1] of String;
   procedure Init;
   begin
      logmessage[gc_logmessagesid_none] := cloglocale+'none';
      logmessage[gc_logmessagesid_famine] := cloglocale+'famine';
      logmessage[gc_logmessagesid_terrainpreventsshot] := cloglocale+'terrainpreventsshot';
      logmessage[gc_logmessagesid_obstaclepreventshot] := cloglocale+'obstaclepreventshot';
      logmessage[gc_logmessagesid_notenoughres] := cloglocale+'notenoughres';
      logmessage[gc_logmessagesid_notenoughfarms] := cloglocale+'notenoughfarms';
      logmessage[gc_logmessagesid_populationlimitreached] := cloglocale+'populationlimitreached';
      logmessage[gc_logmessagesid_cantshootnogold] := cloglocale+'cantshootnogold';
      logmessage[gc_logmessagesid_cantshootnocoaloriron] := cloglocale+'cantshootnocoaloriron';
      logmessage[gc_logmessagesid_nogold] := cloglocale+'nogold';
      logmessage[gc_logmessagesid_upgradedone] := cloglocale+'upgradedone';
      logmessage[gc_logmessagesid_captured] := cloglocale+'captured';
      logmessage[gc_logmessagesid_buildingdestroyed] := cloglocale+'buildingdestroyed';
      logmessage[gc_logmessagesid_buildingbuilt] := cloglocale+'buildingbuilt';
      logmessage[gc_logmessagesid_shipbuilt] := cloglocale+'shipbuilt';
      logmessage[gc_logmessagesid_custom] := cloglocale+'none';
      logmessage[gc_logmessagesid_lowfood] := cloglocale+'lowfood';
      logmessage[gc_logmessagesid_lowgold] := cloglocale+'lowgold';
      logmessage[gc_logmessagesid_lowshoot] := cloglocale+'lowshoot';
      logmessage[gc_logmessagesid_unitinfo] := cloglocale+'none';
      logmessage[gc_logmessagesid_alarmattack] := 'misc|log.alarmattack';
      logmessage[gc_logmessagesid_alarmcapture] := 'misc|log.alarmcapture';
      logmessage[gc_logmessagesid_alarmattacked] := 'misc|log.alarmattacked';
      logmessage[gc_logmessagesid_alarmcaptured] := 'misc|log.alarmcaptured';
   end;
end;

type TUIConst = class
private
   const cHAlignCount = 9;
   const cVAlignCount = 9;
   const cFontCount = 32;
   const cFontStyleLeft = '%style(';
   const cFontStyleRight = ')%';
public
   halign : array [0..cHAlignCount-1] of String; // halLeft, halRight, halMiddle, halRightWidth, halParentLeft, halParentRight, halParentMiddle, halParentRightWidth
   valign : array [0..cVAlignCount-1] of String; // valTop, valBottom, valMiddle, valBottomHeight, valParentTop, valParentBottom, valParentMiddle, valParentBottomHeight
   font : array [0..cFontCount-1] of String;
   fontstyle : array [0..cFontCount-1] of String;
   function GetHAlignIndexByName(const shalign : String) : Integer;
   begin
      Result := 0;
      var i : Integer;
      for i:=0 to cHAlignCount-1 do
      if (halign[i]=shalign) then
      begin
         Result := i;
         break;
      end;
   end;
   function GetVAlignIndexByName(const svalign : String) : Integer;
   begin
      Result := 0;
      var i : Integer;
      for i:=0 to cVAlignCount-1 do
      if (valign[i]=svalign) then
      begin
         Result := i;
         break;
      end;
   end;
   function GetFontIndexByName(const sfont : String) : Integer;
   begin
      Result := 0;
      var i : Integer;
      for i:=0 to cFontCount-1 do
      if (font[i]=sfont) then
      begin
         Result := i;
         break;
      end;
   end;
   function GetFontStyleIndexByName(const sfontstyle : String) : Integer;
   begin
      Result := 0;
      var i : Integer;
      for i:=0 to cFontCount-1 do
      if (fontstyle[i]=sfontstyle) then
      begin
         Result := i;
         break;
      end;
   end;
   function GetFontCount() : Integer;
   begin
      Result := cFontCount;
   end;
   procedure Init;
   begin
      halign[gc_halDefault] := '';
      halign[gc_halLeft] := 'halLeft';
      halign[gc_halRight] := 'halRight';
      halign[gc_halMiddle] := 'halMiddle';
      halign[gc_halRightWidth] := 'halRightWidth';
      halign[gc_halParentleft] := 'halParentleft';
      halign[gc_halParentRight] := 'halParentRight';
      halign[gc_halParentMiddle] := 'halParentMiddle';
      halign[gc_halParentRightWidth] := 'halParentRightWidth';
      halign[gc_valDefault] := '';
      valign[gc_valTop] := 'valTop';
      valign[gc_valBottom] := 'valBottom';
      valign[gc_valMiddle] := 'valMiddle';
      valign[gc_valBottomHeight] := 'valBottomHeight';
      valign[gc_valParentTop] := 'valParentTop';
      valign[gc_valParentBottom] := 'valParentBottom';
      valign[gc_valParentMiddle] := 'valParentMiddle';
      valign[gc_valParentBottomHeight] := 'valParentBottomHeight';
      
      font[gc_font_none] := '';
      font[gc_font_default] := 'sans12';
      font[gc_font_default_sans] := 'sans12';
      font[gc_font_default_serif] := 'serif12';
      font[gc_font_sans_10] := 'sans10';
      font[gc_font_sans_11] := 'sans11';
      font[gc_font_sans_12] := 'sans12';
      font[gc_font_sans_13] := 'sans13';
      font[gc_font_sans_14] := 'sans14';
      font[gc_font_sans_15] := 'sans15';
      font[gc_font_sans_16] := 'sans16';
      font[gc_font_sans_17] := 'sans17';
      font[gc_font_sans_18] := 'sans18';
      font[gc_font_sans_19] := 'sans19';
      font[gc_font_sans_21] := 'sans21';
      font[gc_font_serif_10] := 'serif10';
      font[gc_font_serif_11] := 'serif11';
      font[gc_font_serif_12] := 'serif12';
      font[gc_font_serif_13] := 'serif13';
      font[gc_font_serif_14] := 'serif14';
      font[gc_font_serif_15] := 'serif15';
      font[gc_font_serif_16] := 'serif16';
      font[gc_font_serif_17] := 'serif17';
      font[gc_font_serif_18] := 'serif18';
      font[gc_font_serif_19] := 'serif19';
      font[gc_font_serif_21] := 'serif21';
      font[gc_font_opensans_12] := 'opensans12';
      font[gc_font_spectrum_16] := 'spectrum16';
      font[gc_font_spacer_1] := 'spacer1';
      font[gc_font_spacer_4] := 'spacer4';
      font[gc_font_spacer_6] := 'spacer6';
      font[gc_font_pass_14] := 'pass14';
      
      fontstyle[gc_font_none] := cFontStyleLeft+font[gc_font_none]+cFontStyleRight;
      fontstyle[gc_font_default] := cFontStyleLeft+font[gc_font_default]+cFontStyleRight;
      fontstyle[gc_font_default_sans] := cFontStyleLeft+font[gc_font_default_sans]+cFontStyleRight;
      fontstyle[gc_font_default_serif] := cFontStyleLeft+font[gc_font_default_serif]+cFontStyleRight;
      fontstyle[gc_font_sans_10] := cFontStyleLeft+font[gc_font_sans_10]+cFontStyleRight;
      fontstyle[gc_font_sans_11] := cFontStyleLeft+font[gc_font_sans_11]+cFontStyleRight;
      fontstyle[gc_font_sans_12] := cFontStyleLeft+font[gc_font_sans_12]+cFontStyleRight;
      fontstyle[gc_font_sans_13] := cFontStyleLeft+font[gc_font_sans_13]+cFontStyleRight;
      fontstyle[gc_font_sans_14] := cFontStyleLeft+font[gc_font_sans_14]+cFontStyleRight;
      fontstyle[gc_font_sans_15] := cFontStyleLeft+font[gc_font_sans_15]+cFontStyleRight;
      fontstyle[gc_font_sans_16] := cFontStyleLeft+font[gc_font_sans_16]+cFontStyleRight;
      fontstyle[gc_font_sans_17] := cFontStyleLeft+font[gc_font_sans_17]+cFontStyleRight;
      fontstyle[gc_font_sans_18] := cFontStyleLeft+font[gc_font_sans_18]+cFontStyleRight;
      fontstyle[gc_font_sans_19] := cFontStyleLeft+font[gc_font_sans_19]+cFontStyleRight;
      fontstyle[gc_font_sans_21] := cFontStyleLeft+font[gc_font_sans_21]+cFontStyleRight;
      fontstyle[gc_font_serif_10] := cFontStyleLeft+font[gc_font_serif_10]+cFontStyleRight;
      fontstyle[gc_font_serif_11] := cFontStyleLeft+font[gc_font_serif_11]+cFontStyleRight;
      fontstyle[gc_font_serif_12] := cFontStyleLeft+font[gc_font_serif_12]+cFontStyleRight;
      fontstyle[gc_font_serif_13] := cFontStyleLeft+font[gc_font_serif_13]+cFontStyleRight;
      fontstyle[gc_font_serif_14] := cFontStyleLeft+font[gc_font_serif_14]+cFontStyleRight;
      fontstyle[gc_font_serif_15] := cFontStyleLeft+font[gc_font_serif_15]+cFontStyleRight;
      fontstyle[gc_font_serif_16] := cFontStyleLeft+font[gc_font_serif_16]+cFontStyleRight;
      fontstyle[gc_font_serif_17] := cFontStyleLeft+font[gc_font_serif_17]+cFontStyleRight;
      fontstyle[gc_font_serif_18] := cFontStyleLeft+font[gc_font_serif_18]+cFontStyleRight;
      fontstyle[gc_font_serif_19] := cFontStyleLeft+font[gc_font_serif_19]+cFontStyleRight;
      fontstyle[gc_font_serif_21] := cFontStyleLeft+font[gc_font_serif_21]+cFontStyleRight;
      fontstyle[gc_font_opensans_12] := cFontStyleLeft+font[gc_font_opensans_12]+cFontStyleRight;
      fontstyle[gc_font_spectrum_16] := cFontStyleLeft+font[gc_font_spectrum_16]+cFontStyleRight;
      fontstyle[gc_font_spacer_1] := cFontStyleLeft+font[gc_font_spacer_1]+cFontStyleRight;
      fontstyle[gc_font_spacer_4] := cFontStyleLeft+font[gc_font_spacer_4]+cFontStyleRight;
      fontstyle[gc_font_spacer_6] := cFontStyleLeft+font[gc_font_spacer_6]+cFontStyleRight;
      fontstyle[gc_font_pass_14] := cFontStyleLeft+font[gc_font_pass_14]+cFontStyleRight;
   end;
end;

type TAIConst = class
public
   armyMinCount : array [0..gc_ai_maxarmytypes-1] of Integer;
   defArmyMinCount : array [0..gc_ai_maxarmytypes-1] of Integer;
   armyMaxCount : array [0..gc_ai_maxarmytypes-1] of Integer;
   maxDivers : array [0..gc_ai_MaxDiverArmies-1] of Integer;
   
   procedure Init;
   begin
      armyMinCount[gc_ai_armytype_lightinfantry] := 36;
      armyMinCount[gc_ai_armytype_shootinfantry] := 16;
      armyMinCount[gc_ai_armytype_fasthorse] := 16;
      armyMinCount[gc_ai_armytype_hardhorse] := 16;
      armyMinCount[gc_ai_armytype_horseshooter] := 16;
      armyMinCount[gc_ai_armytype_cannon] := 1;
      armyMinCount[gc_ai_armytype_grenadier] := 16;
      armyMinCount[gc_ai_armytype_peasant] := 16;
      armyMinCount[gc_ai_armytype_weakunit] := 25;
      armyMinCount[gc_ai_armytype_supermortar] := 4;
      armyMinCount[gc_ai_armytype_mortar] := 1;
      armyMinCount[gc_ai_armytype_archerinfantry] := 16;
      armyMinCount[gc_ai_armytype_galley] := 1;
      armyMinCount[gc_ai_armytype_frigate] := 1;
      armyMinCount[gc_ai_armytype_battleship] := 1;
      armyMinCount[gc_ai_armytype_transport] := 1;
      
      defArmyMinCount[gc_ai_armytype_lightinfantry] := 4;
      defArmyMinCount[gc_ai_armytype_shootinfantry] := 16;
      defArmyMinCount[gc_ai_armytype_fasthorse] := 16;
      defArmyMinCount[gc_ai_armytype_hardhorse] := 16;
      defArmyMinCount[gc_ai_armytype_horseshooter] := 16;
      defArmyMinCount[gc_ai_armytype_cannon] := 1;
      defArmyMinCount[gc_ai_armytype_grenadier] := 16;
      defArmyMinCount[gc_ai_armytype_peasant] := 16;
      defArmyMinCount[gc_ai_armytype_weakunit] := 25;
      defArmyMinCount[gc_ai_armytype_supermortar] := 4;
      defArmyMinCount[gc_ai_armytype_mortar] := 1;
      defArmyMinCount[gc_ai_armytype_archerinfantry] := 16;
      defArmyMinCount[gc_ai_armytype_galley] := 1;
      defArmyMinCount[gc_ai_armytype_frigate] := 1;
      defArmyMinCount[gc_ai_armytype_battleship] := 1;
      defArmyMinCount[gc_ai_armytype_transport] := 1;
      
      armyMaxCount[gc_ai_armytype_lightinfantry] := 256;
      armyMaxCount[gc_ai_armytype_shootinfantry] := 256;
      armyMaxCount[gc_ai_armytype_fasthorse] := 256;
      armyMaxCount[gc_ai_armytype_hardhorse] := 256;
      armyMaxCount[gc_ai_armytype_horseshooter] := 256;
      armyMaxCount[gc_ai_armytype_cannon] := 36;
      armyMaxCount[gc_ai_armytype_grenadier] := 256;
      armyMaxCount[gc_ai_armytype_peasant] := 16;
      armyMaxCount[gc_ai_armytype_weakunit] := 256;
      armyMaxCount[gc_ai_armytype_supermortar] := 256;
      armyMaxCount[gc_ai_armytype_mortar] := 36;
      armyMaxCount[gc_ai_armytype_archerinfantry] := 256;
      armyMaxCount[gc_ai_armytype_galley] := 1;
      armyMaxCount[gc_ai_armytype_frigate] := 1;
      armyMaxCount[gc_ai_armytype_battleship] := 1;
      armyMaxCount[gc_ai_armytype_transport] := 1;
      
      if gc_ai_MaxDiverArmies > 0 then
      maxDivers[0] := 5;
      if gc_ai_MaxDiverArmies > 1 then
      maxDivers[1] := 8;
      if gc_ai_MaxDiverArmies > 2 then
      maxDivers[2] := 12;
      var i : Integer;
      for i := 3 to gc_ai_MaxDiverArmies-1 do
      maxDivers[i] := 15;
   end;
end;

type TArcherCell = class
   weight : Float;
   trgHnd : Integer;
end;

type TGeneratorBitmapMask = class
   r, g, b : Float;
   terrain : Integer;
   water : Integer;
   forest : Integer;
   bsmooth : Boolean;
   broad : Boolean;
end;

type TAch = class
   achid : Integer;
   achsid : String;
   goal : Integer;
   cur : Integer;
   status : Integer;
   stringtag : String;
   bbitcoded : Boolean;
end;

type TAchs = class
   binit : Boolean;
   ach : array [0..gc_ach_maxcount-1] of TAch;
   
   procedure ClearProgress;
   begin
      var i : Integer;
      for i:=0 to gc_ach_maxcount-1 do
      begin
         self.ach[i].cur := 0;
         self.ach[i].status := 0;
         self.ach[i].stringtag := '';
         self.ach[i].bbitcoded := False;
      end;
   end;
   procedure Clear;
   begin
      var i : Integer;
      for i:=0 to gc_ach_maxcount-1 do
      begin
         self.ach[i].achid := 0;
         self.ach[i].achsid := '';
         self.ach[i].goal := 0;
         self.ach[i].cur := 0;
         self.ach[i].status := 0;
         self.ach[i].stringtag := '';
         self.ach[i].bbitcoded := False;
      end;
   end;
   function IndexOf(const updach : String) : Integer;
   begin
      result := -1;
      var i : Integer;
      for i:=0 to gc_ach_maxcount-1 do
      if (self.ach[i].achsid=updach) then
      begin
         result := i;
         break;
      end;
   end;
   procedure Init;
   begin
      self.Clear;
      binit := True;
      var i : Integer;
      for i:=0 to gc_ach_existscount-1 do
      case i of
         gc_ach_skirmish_win1 : ach[i].goal := 1;
         gc_ach_skirmish_win10 : ach[i].goal := 10;
         gc_ach_skirmish_win100 : ach[i].goal := 100;
         gc_ach_multiplayer_win1 : ach[i].goal := 1;
         gc_ach_multiplayer_win10 : ach[i].goal := 10;
         gc_ach_multiplayer_win100 : ach[i].goal := 100;
         gc_ach_campaign_finalaus : begin
            ach[i].goal := 5; ach[i].bbitcoded := True;
         end;
         gc_ach_campaign_finaleng : begin
            ach[i].goal := 5;
            ach[i].bbitcoded := True;
         end;
         gc_ach_campaign_finalfra : begin
            ach[i].goal := 5;
            ach[i].bbitcoded := True;
         end;
         gc_ach_campaign_finalukr : begin
            ach[i].goal := 5;
            ach[i].bbitcoded := True;
         end;
         gc_ach_campaign_finalrus : begin
            ach[i].goal := 5;
            ach[i].bbitcoded := True;
         end;
         gc_ach_campaign_finalaus_impossible : begin
            ach[i].goal := 5;
            ach[i].bbitcoded := True;
         end;
         gc_ach_campaign_finaleng_impossible : begin
            ach[i].goal := 5;
            ach[i].bbitcoded := True;
         end;
         gc_ach_campaign_finalfra_impossible : begin
            ach[i].goal := 5;
            ach[i].bbitcoded := True;
         end;
         gc_ach_campaign_finalukr_impossible : begin
            ach[i].goal := 5;
            ach[i].bbitcoded := True;
         end;
         gc_ach_campaign_finalrus_impossible : begin
            ach[i].goal := 5;
            ach[i].bbitcoded := True;
         end;
         gc_ach_skirmish_win1_1vs2_impossible : ach[i].goal := 1;
         gc_ach_skirmish_win1_1vs4_impossible : ach[i].goal := 1;
         gc_ach_skirmish_win1_1vs6_impossible : ach[i].goal := 1;
         gc_ach_skirmish_win1_islands : ach[i].goal := 1;
         gc_ach_skirmish_win10_islands : ach[i].goal := 10;
         gc_ach_skirmish_win100_islands : ach[i].goal := 100;
         gc_ach_upgrade_18century : ach[i].goal := 1;
         gc_ach_economy_millionres_20min : ach[i].goal := 1;
         gc_ach_upgrade_18century_20min : ach[i].goal := 1;
         gc_ach_upgrade_all_acablamil : ach[i].goal := 1;
         gc_ach_upgrade_mine95 : ach[i].goal := 1;
         gc_ach_skirmish_ferryunload : ach[i].goal := 1;
         gc_ach_randommap_teamai : ach[i].goal := 1;
         gc_ach_multiplayer_lose10 : ach[i].goal := 10;
         gc_ach_economy_millionres_sell : ach[i].goal := 1;
         gc_ach_units_dragoon18dip_100 : ach[i].goal := 1;
         gc_ach_upgrade_pikeman_3att3def : ach[i].goal := 1;
         gc_ach_upgrade_serdiuk_att : ach[i].goal := 1;
         gc_ach_upgrade_acashotdamage : ach[i].goal := 1;
         gc_ach_upgrade_cavbuildattack : ach[i].goal := 1;
         gc_ach_upgrade_mill1 : ach[i].goal := 1;
         gc_ach_upgrade_building_def : ach[i].goal := 1;
         gc_ach_units_cannons20 : ach[i].goal := 1;
         gc_ach_units_mortar50 : ach[i].goal := 1;
         gc_ach_units_battleship : ach[i].goal := 1;
         gc_ach_upgrade_balloon : ach[i].goal := 1;
         gc_ach_units_fishboat100 : ach[i].goal := 1;
         gc_ach_upgrade_wingedhussar_6att6def : ach[i].goal := 1;
         gc_ach_learn_createsquad : ach[i].goal := 1;
         gc_ach_economy_population2000 : ach[i].goal := 1;
         gc_ach_economy_population4000 : ach[i].goal := 1;
         gc_ach_units_priest50 : ach[i].goal := 1;
         gc_ach_units_tower10 : ach[i].goal := 1;
         gc_ach_units_wall50 : ach[i].goal := 1;
         gc_ach_upgrade_acawoodstoneeff : ach[i].goal := 1;
         gc_ach_skirmish_win1hour : ach[i].goal := 1;
         gc_ach_units_archerdip500 : ach[i].goal := 1;
         gc_ach_units_lightinfantrydip500 : ach[i].goal := 1;
         gc_ach_units_cossacksichdip50 : ach[i].goal := 1;
         gc_ach_upgrade_towerattspeed : ach[i].goal := 1;
         gc_ach_upgrade_roundshier_5att5def : ach[i].goal := 1;
         gc_ach_units_kingmusketeer100 : ach[i].goal := 1;
         gc_ach_units_highlander200 : ach[i].goal := 1;
         gc_ach_units_vityaz200 : ach[i].goal := 1;
         gc_ach_units_hetman3 : ach[i].goal := 1;
         gc_ach_units_bldcen5 : ach[i].goal := 1;
         gc_ach_units_bldhou100 : ach[i].goal := 1;
         gc_ach_units_reiterswe200 : ach[i].goal := 1;
         gc_ach_units_musketeerpru200 : ach[i].goal := 1;
         gc_ach_units_janissary200 : ach[i].goal := 1;
         gc_ach_units_archer1000 : ach[i].goal := 1;
         gc_ach_units_croat200 : ach[i].goal := 1;
         gc_ach_learn_fillsquadhalfdead10 : ach[i].goal := 10;
         gc_ach_randommap_buildcen_capturednation : ach[i].goal := 1;
         gc_ach_capture_cannons20 : ach[i].goal := 1;
         gc_ach_destroy_buildings10 : ach[i].goal := 10;
         gc_ach_destroy_buildings100 : ach[i].goal := 100;
         gc_ach_units_bldba2_5 : ach[i].goal := 1;
         gc_ach_units_bldpor10 : ach[i].goal := 1;
         gc_ach_camp_tutmis1_fin : ach[i].goal := 1;
         gc_ach_camp_tutmis2_fin : ach[i].goal := 1;
         gc_ach_camp_secretplace10 : begin
            ach[i].goal := 10;
            ach[i].bbitcoded := True;
         end;
         gc_ach_camp_good5 : begin
            ach[i].goal := 5;
            ach[i].bbitcoded := True;
         end;
         gc_ach_camp_evil5 : begin
            ach[i].goal := 5;
            ach[i].bbitcoded := True;
         end;
         gc_ach_camp_findchest : ach[i].goal := 1;
         gc_ach_camp_hirerobbers : ach[i].goal := 1;
         gc_ach_camp_engmis1_alltowns : ach[i].goal := 1;
         gc_ach_camp_engmis2_peasantdialogwell : ach[i].goal := 1;
         gc_ach_camp_ausmis1_capturepackhorses : ach[i].goal := 1;
         gc_ach_camp_ausmis2_killallunitsblack : ach[i].goal := 1;
         gc_ach_camp_ausmis3_mantuyunorebels : ach[i].goal := 1;
         gc_ach_camp_framis1_trophyshipsuccess : ach[i].goal := 1;
         gc_ach_camp_framis2_donotharmlocals : ach[i].goal := 1;
         gc_ach_camp_framis2_freeslaves : ach[i].goal := 1;
         gc_ach_camp_framis3_palermokillgarrison : ach[i].goal := 1;
         gc_ach_camp_ukrmis1_free100slaves : ach[i].goal := 1;
         gc_ach_camp_ukrmis1_destroy2turkforts : ach[i].goal := 1;
         gc_ach_camp_ukrmis2_destroywhitestonebld : ach[i].goal := 1;
         gc_ach_camp_ukrmis3_destroykafabld : ach[i].goal := 1;
         gc_ach_camp_ukrmis4_killrusflankstealth : ach[i].goal := 1;
         gc_ach_camp_ukrmis5_allkingguardssurvived : ach[i].goal := 1;
         gc_ach_camp_rusmis1_saveorel : ach[i].goal := 1;
         gc_ach_camp_rusmis2_caravanwontpass : ach[i].goal := 1;
         gc_ach_camp_rusmis3_blitzkrieg : ach[i].goal := 1;
         gc_ach_camp_rusmis4_beatallboforeswewar : ach[i].goal := 1;
         gc_ach_camp_tutmis1_executeofficer : ach[i].goal := 1;
         gc_ach_camp_tutmis1_mercyofficer : ach[i].goal := 1;
      end;
   end;
   procedure Create;
   begin
      Clear;
      var i : Integer;
      for i:=0 to gc_ach_existscount-1 do
      begin
         self.ach[i].achid := i;
         self.ach[i].achsid := 'ach'+IntToStr(i);
         self.ach[i].goal := 1;
         self.ach[i].cur := 0;
         self.ach[i].status := 0;
         self.ach[i].stringtag := '';
      end;
      Init;
   end;
   
   procedure _misc_CheckCampaignAch(const updach : String);
   begin
      var i : Integer;
      //ach : array [0..gc_usercampaign_maxachcount-1] of String;
      //if (updach='') then
   end;
   
   procedure _misc_ModifyCampaignAch(const ach, uniqsid : String);
   begin
      _misc_CheckCampaignAch(ach);
   end;
end;

type TProfile = class
   name : String;
   sndmute : Integer;
   sndmaster : Float;
   sndmusic : Float;
   sndambient : Float;
   sndfx : Float;
   sndvoice : Float;
   sndinterface : Float;
   keyscrollspeed : Float;
   mousescrollspeed : Float;
   middlemousescrollspeed : Float;
   wheelspeed : Float;
   igamespeed : Integer;
   lastcampaignsave : String;
   lastcustomsave : String;
   netemail : String;
   netpass : String;
   netcdkey : String;
   bautosave : Boolean;
   bbrushunderunit : Boolean;
   bclipmouse : Boolean;
   bsearchenemyinfront : Boolean;
   binfiniteonleftclick : Boolean;
   bselectallunitsonz : Boolean;
   bfreezoom : Boolean;
   bDbgPreloadPatterns : Boolean;
   bDbgLogPointerNil : Boolean;
   bDbgVisualizePlayOnceProjFX : Boolean;
   bShowPause : Boolean;
   bShowMiniMap : Boolean;
   bcenterfirstrow : Boolean;
   bFamine : Boolean;
   bRestrictCamera : Boolean;
   bDbgCamera : Boolean;
   bDbgNoBrushes : Boolean;
   bDbgHotkeys : Boolean;
   bDbgUnitDebugInfo : Boolean;
   bDemo : Boolean;
   bNewMapOnMainMenu : Boolean;
   bCheatPlaceUnits : Boolean;
   bCheatNoFogOfWar : Boolean;
   bCheatSwitchPlayer : Boolean;
   bCheatPeaceMode : Boolean;
   bCheatNoSpeedLimit : Boolean;
   bCheatCopyPaste : Boolean;
   bCheatMoveUnits : Boolean;
   bCheatShowAllMissions : Boolean;
   bShowFPS : Boolean;
   bNoAchs : Boolean;
   bDevMode : Boolean;
   lang : String;
   displaymode : String;
   dontshowannouncement : Integer;
   lastknowndlcs : Integer;
   purchaseunixtime : Integer;
end;

type TCampaignProgress = class
   bexists : Boolean;
   campname : String;
   missname : String;
   benabled : Boolean;
   bvisible : Boolean;
   started : Integer;
   finished : Integer;
   lose : Integer;
   maxfinishdifficulty : Integer;
   storestring : String;
   
   procedure Clear();
   begin
      bexists := False;
      campname := '';
      missname := '';
      benabled := False;
      bvisible := False;
      started := 0;
      finished := 0;
      lose := 0;
      maxfinishdifficulty := 0;
      storestring := '';
   end;
   destructor TCampaignProgress;
   begin
      Clear;
   end;
end;

type TUserCampaign = class
   difficulty : Integer;
   campprogress : array [0..gc_usercampaign_maxcampprogress-1] of TCampaignProgress;
   procedure Clear();
   begin
      difficulty := 0;
      var i : Integer;
      for i:=0 to gc_usercampaign_maxcampprogress-1 do
      self.campprogress[i].Clear;
   end;
end;

type TProfileUserStruct = class
   custommap : TMap;
   campaign : TUserCampaign;
   achs : TAchs;
   adviserdefault : TAdviser;
   procedure Create();
   begin
      self.achs.Create;
   end;
   procedure Clear();
   begin
      var mapclean : TMap;
      self.custommap := mapclean;
      self.campaign.Clear;
   end;
end;

type TInternetShellSession = class
   gamename : String;
   mapname : String;
   maxplayers : Integer;
   masterid : Integer;
   clientscount : Integer;
   blocked : Boolean;
   bclosed : Boolean;
end;

type TInternetShellClientStates = class
   bplay : Boolean;
   bmaster : Boolean;
   bsession : Boolean;
   bonline : Boolean;
end;

type TInternetShellClient = class
   lanid : Integer;
   nick : String;
   country : String;
   score : Integer;
   dlc : Integer;
   pur : Integer;
   ram : Integer;
   prevscore : Integer;
   prevgamesplayed : Integer;
   prevgameswin : Integer;
   state : Integer;
   gamesplayed : Integer;
   gameswin : Integer;
   lastgame : String;
   info : String;
   ping : Integer;
   bmute : Boolean;
   states : TInternetShellClientStates;
   sic : Integer;
   si1 : Integer;
   si2 : Integer;
   si3 : Integer;
   snc : String;
   sn1 : String;
   sn2 : String;
   sn3 : String;
   
   procedure ApplyDataFromInfo();
   begin
      dlc := -1;
      pur := -1;
      ram := 0;
      sic := 0;
      si1 := 0;
      si2 := 0;
      si3 := 0;
      snc := '';
      sn1 := '';
      sn2 := '';
      sn3 := '';
      if (info<>'') and IsDelimiterCharExists(info, gc_gui_delimiterchar) then
      begin
         var count : Integer = GetDelimiterStringCount(info, gc_gui_delimiterchar, gc_gui_quotechar);
         var text, key, sval : String;
         var ival : Integer;
         var bisinteger : Boolean;
         var i : Integer;
         for i:=0 to count-1 do
         begin
            text := GetDelimiterStringByIndex(info, gc_gui_delimiterchar, gc_gui_quotechar, i);
            case text of
               'dlc' : key := text;
               'pur' : key := text;
               'ram' : key := text;
               'sic' : key := text;
               'si1' : key := text;
               'si2' : key := text;
               'si3' : key := text;
               'snc' : key := text;
               'sn1' : key := text;
               'sn2' : key := text;
               'sn3' : key := text;
               else
               begin
                  sval := text;
                  ival := StrToInt(sval);
                  bisinteger := (IntToStr(ival)=sval);
                  if (bisinteger) then
                  begin
                     case key of
                        'dlc' : dlc := ival;
                        'pur' : pur := ival;
                        'ram' : ram := ival;
                        'sic' : sic := ival;
                        'si1' : si1 := ival;
                        'si2' : si2 := ival;
                        'si3' : si3 := ival;
                     end;
                  end
                  else
                  begin
                     case key of
                        'snc' : snc := sval;
                        'sn1' : sn1 := sval;
                        'sn2' : sn2 := sval;
                        'sn3' : sn3 := sval;
                        else
                        key := text;
                     end;
                  end;
               end;
            end;
         end;
      end;

      if (country<>'') and IsDelimiterCharExists(country, gc_gui_delimiterchar) then
      begin
         var count : Integer = GetDelimiterStringCount(country, gc_gui_delimiterchar, gc_gui_quotechar);
         var text, key, sval : String;
         var ival : Integer;
         var bvalid : Boolean;
         var i : Integer;
         for i:=0 to count-1 do
         begin
            text := GetDelimiterStringByIndex(country, gc_gui_delimiterchar, gc_gui_quotechar, i);
            var signpos : Integer = StrPos('=', text);
            var part : String = SubStr(text, 1, signpos-1);
            var sval : String = SubStr(text, signpos+1, StrLength(text)-signpos);
            var ival : Integer = StrToInt(sval);
            bvalid := (IntToStr(ival)=sval);
            if (bvalid) then
            case part of
               'ps' : begin
                  ival := StrToInt(sval);
                  prevscore := ival;
               end;
               'pw' : begin
                  ival := StrToInt(sval);
                  prevgameswin := ival;
               end;
               'pg' : begin
                  ival := StrToInt(sval);
                  prevgamesplayed := ival;
               end;
            end;
         end;
      end;
   end;
end;

type TInternetShellRanking = class
   ind : Integer;
   lanid : Integer;
   nick : String;
   score : Integer;
   rank : Integer;
   wins : Integer;
   games : Integer;
   states : TInternetShellClientStates;
end;

type TInternetShellRankings = class
private
   fList : TPtrList;
public
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   constructor TInternetShellRankings;
   begin
      Create;
   end;
   procedure Clear;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         FreeMem(p);
      end;
      self.fList.Clear;
   end;
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   destructor TInternetShellRankings;
   begin
      Destroy;
   end;
   function GetCount: Integer;
   begin
      result := self.fList.GetCount;
   end;
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<GetCount) then
      result:=self.fList.GetPointer(index)
      else begin
         result := nil;
         ErrorLog('TInternetShellRankings.Get : index = '+IntToStr(index)+' pointer=nil');
      end;
   end;
   function Add: Pointer;
   begin
      GetMem(result, SizeOf(TInternetShellRanking));
      FillMem(result, SizeOf(TInternetShellRanking), 0);
      self.fList.AddPointer(result);
   end;
   function AddSetup(ind, lanid : Integer; var nick : String; score, rank, wins, games : Integer; bplay, bmaster, bsession, bonline : Boolean): Pointer;
   begin
      result:=Add;
      TInternetShellRanking(result).ind := ind;
      TInternetShellRanking(result).lanid := lanid;
      TInternetShellRanking(result).nick := nick;
      TInternetShellRanking(result).score := score;
      TInternetShellRanking(result).rank := rank;
      TInternetShellRanking(result).wins := wins;
      TInternetShellRanking(result).games := games;
      TInternetShellRanking(result).states.bplay := bplay;
      TInternetShellRanking(result).states.bmaster := bmaster;
      TInternetShellRanking(result).states.bsession := bsession;
      TInternetShellRanking(result).states.bonline := bonline;
   end;
   function RemoveByLanID(lanid: Integer): Pointer;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=self.fList.GetCount-1 downto 0 do begin
         p:=self.fList.GetPointer(i);
         if (TInternetShellRanking(p).lanid=lanid) then
         begin
            FreeMem(p);
            self.fList.Delete(i);
         end;
      end;
   end;
   function GetByLanID(lanid: Integer): Pointer;
   begin
      result := nil;
      var i: Integer;
      var p: Pointer;
      for i:=self.fList.GetCount-1 downto 0 do begin
         p:=self.fList.GetPointer(i);
         if (TInternetShellRanking(p).lanid=lanid) then
         begin
            result:=p;
            break
         end;
      end;
   end;
   function GetByNick(var nick: String): Pointer;
   begin
      result := nil;
      var i: Integer;
      var p: Pointer;
      for i:=self.fList.GetCount-1 downto 0 do begin
         p:=self.fList.GetPointer(i);
         if (SameText(TInternetShellRanking(p).nick, nick)) then
         begin
            result:=p;
            break
         end;
      end;
   end;
   function AddOrUpdateFromParser(parser: Integer): Pointer;
   begin
      result := nil;
      if (parser<>0) then
      begin
         var lanid : Integer = ParserGetIntValueByKeyByHandle(parser, 'ID');
         var i: Integer;
         var p: Pointer;
         for i:=self.fList.GetCount-1 downto 0 do begin
            p:=self.fList.GetPointer(i);
            if (TInternetShellRanking(p).lanid=lanid) then
            begin
               result:=p;
               break
            end;
         end;
         if (result=nil) then
         result:=Add;
         TInternetShellRanking(result).ind := ParserGetIntValueByKeyByHandle(parser, 'Index');
         TInternetShellRanking(result).lanid := lanid;
         TInternetShellRanking(result).nick := ParserGetValueByKeyByHandle(parser, 'Nick');
         TInternetShellRanking(result).score := ParserGetIntValueByKeyByHandle(parser, 'Score');
         TInternetShellRanking(result).rank := ParserGetIntValueByKeyByHandle(parser, 'Rank');
         TInternetShellRanking(result).wins := ParserGetIntValueByKeyByHandle(parser, 'Wins');
         TInternetShellRanking(result).games := ParserGetIntValueByKeyByHandle(parser, 'Games');
         //TInternetShellRanking(result).states.bplay := bplay;
         //TInternetShellRanking(result).states.bmaster := bmaster;
         //TInternetShellRanking(result).states.bsession := bsession;
         //TInternetShellRanking(result).states.bonline := bonline;
      end
      else
      ErrorLog('TInternetShellRanking : parser=0');
   end;
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then
      begin
         var p: Pointer=self.fList.GetPointer(index);
         if p<>nil then
         FreeMem(p);
         self.fList.Delete(index);
      end;
   end;
end;

type TInternetShellClients = class
private
   fList : TPtrList;
public
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   constructor TInternetShellClients;
   begin
      Create;
   end;
   procedure Clear;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         FreeMem(p);
      end;
      self.fList.Clear;
   end;
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   destructor TInternetShellClients;
   begin
      Destroy;
   end;
   function GetCount: Integer;
   begin
      result := self.fList.GetCount;
   end;
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<GetCount) then
      result:=self.fList.GetPointer(index)
      else begin
         result := nil;
         ErrorLog('TInternetShellClients.Get : index = '+IntToStr(index)+' pointer=nil');
      end;
   end;
   function Add: Pointer;
   begin
      GetMem(result, SizeOf(TInternetShellClient));
      FillMem(result, SizeOf(TInternetShellClient), 0);
      self.fList.AddPointer(result);
   end;
   function AddSetup(lanid : Integer; var nick, country : String; score, gamesplayed, gameswin : Integer; var lastgame, info : String; ping : Float; bplay, bmaster, bsession, bonline : Boolean): Pointer;
   begin
      result:=Add;
      TInternetShellClient(result).lanid:=lanid;
      TInternetShellClient(result).nick:=nick;
      TInternetShellClient(result).country:=country;
      TInternetShellClient(result).score:=score;
      TInternetShellClient(result).gamesplayed:=gamesplayed;
      TInternetShellClient(result).gameswin:=gameswin;
      TInternetShellClient(result).lastgame:=lastgame;
      TInternetShellClient(result).info:=info;
      //TInternetShellClient(result).dlc:=dlc;
      //TInternetShellClient(result).pur:=pur;
      TInternetShellClient(result).ApplyDataFromInfo; // .dlc, .pur prepare
      TInternetShellClient(result).ping := round(ping);
      TInternetShellClient(result).states.bplay := bplay;
      TInternetShellClient(result).states.bmaster := bmaster;
      TInternetShellClient(result).states.bsession := bsession;
      TInternetShellClient(result).states.bonline := bonline;
   end;
   function RemoveByLanID(lanid: Integer): Pointer;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=self.fList.GetCount-1 downto 0 do begin
         p:=self.fList.GetPointer(i);
         if (TInternetShellClient(p).lanid=lanid) then
         begin
            FreeMem(p);
            self.fList.Delete(i);
         end;
      end;
   end;
   function GetByLanID(lanid: Integer): Pointer;
   begin
      result := nil;
      var i: Integer;
      var p: Pointer;
      for i:=self.fList.GetCount-1 downto 0 do begin
         p:=self.fList.GetPointer(i);
         if (TInternetShellClient(p).lanid=lanid) then
         begin
            result:=p;
            break
         end;
      end;
   end;
   function GetByNick(var nick: String): Pointer;
   begin
      result := nil;
      var i: Integer;
      var p: Pointer;
      for i:=self.fList.GetCount-1 downto 0 do begin
         p:=self.fList.GetPointer(i);
         if (SameText(TInternetShellClient(p).nick, nick)) then
         begin
            result:=p;
            break
         end;
      end;
   end;
   function AddOrUpdateFromParser(parser: Integer): Pointer;
   begin
      result := nil;
      if (parser<>0) then
      begin
         var lanid : Integer = ParserGetIntValueByKeyByHandle(parser, 'ID');
         var i: Integer;
         var p: Pointer;
         for i:=self.fList.GetCount-1 downto 0 do begin
            p:=self.fList.GetPointer(i);
            if (TInternetShellClient(p).lanid=lanid) then
            begin
               result:=p;
               break
            end;
         end;
         if (result=nil) then
         result:=Add;
         TInternetShellClient(result).lanid := lanid;
         TInternetShellClient(result).nick := ParserGetValueByKeyByHandle(parser, 'Nick');
         TInternetShellClient(result).country := ParserGetValueByKeyByHandle(parser, 'Country');
         TInternetShellClient(result).score := ParserGetIntValueByKeyByHandle(parser, 'Score');
         TInternetShellClient(result).gamesplayed := ParserGetIntValueByKeyByHandle(parser, 'GamesPlayed');
         TInternetShellClient(result).gameswin := ParserGetIntValueByKeyByHandle(parser, 'GamesWin');
         TInternetShellClient(result).lastgame := ParserGetValueByKeyByHandle(parser, 'LastGame');
         TInternetShellClient(result).info := ParserGetValueByKeyByHandle(parser, 'Info');
         TInternetShellClient(result).ApplyDataFromInfo; // .dlc, .pur prepare
         TInternetShellClient(result).ping := Round(ParserGetFloatValueByKeyByHandle(parser, 'Pingtime'));
         TInternetShellClient(result).states.bplay := ParserGetBoolValueByKeyByHandle(parser, 'States.scsPlayed');
         TInternetShellClient(result).states.bmaster := ParserGetBoolValueByKeyByHandle(parser, 'States.scsMaster');
         TInternetShellClient(result).states.bsession := ParserGetBoolValueByKeyByHandle(parser, 'States.scsSession');
         TInternetShellClient(result).states.bonline := ParserGetBoolValueByKeyByHandle(parser, 'States.scsOnline');
         //Log('id = '+IntToStr(lanid)+' bplay = '+BoolToStr(TInternetShellClient(result).states.bplay)+' bmaster = '+BoolToStr(TInternetShellClient(result).states.bmaster)+' bsession = '+BoolToStr(TInternetShellClient(result).states.bsession)+' bonline = '+BoolToStr(TInternetShellClient(result).states.bonline));
      end
      else
      ErrorLog('TInternetShellClients : parser=0');
   end;
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then
      begin
         var p: Pointer=self.fList.GetPointer(index);
         if p<>nil then
         FreeMem(p);
         self.fList.Delete(index);
      end;
   end;
end;

type TInternetShellSessions = class
private
   fList : TPtrList;
public
   procedure Create;
   begin
      if self.fList.GetItemSize=0 then
      self.fList.Create;
   end;
   constructor TInternetShellSessions;
   begin
      Create;
   end;
   procedure Clear;
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         FreeMem(p);
      end;
      self.fList.Clear;
   end;
   procedure Destroy;
   begin
      Clear;
      self.fList.Destroy;
   end;
   destructor TInternetShellSessions;
   begin
      Destroy;
   end;
   function GetCount: Integer;
   begin
      result := self.fList.GetCount;
   end;
   function GetByMasterID(const id: Integer): Pointer;
   begin
      result:=nil;
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         if (id=TInternetShellSession(p).masterid) then
         begin
            result := p;
            break;
         end;
      end;
   end;
   function Get(index: Integer): Pointer;
   begin
      if (index>=0) and (index<GetCount) then
      result:=self.fList.GetPointer(index)
      else begin
         result := nil;
         ErrorLog('TInternetShellSessions.Get : index = '+IntToStr(index)+' pointer=nil');
      end;
   end;
   function Add: Pointer;
   begin
      GetMem(result, SizeOf(TInternetShellSession));
      FillMem(result, SizeOf(TInternetShellSession), 0);
      self.fList.AddPointer(result);
   end;
   function AddSetup(var gamename, mapname : String; maxplayers, masterid, clientscount : Integer; blocked, bclosed : Boolean): Pointer;
   begin
      result:=Add;
      TInternetShellSession(result).gamename:=gamename;
      TInternetShellSession(result).mapname:=mapname;
      TInternetShellSession(result).maxplayers:=maxplayers;
      TInternetShellSession(result).masterid:=masterid;
      TInternetShellSession(result).clientscount:=clientscount;
      TInternetShellSession(result).blocked:=blocked;
      TInternetShellSession(result).bclosed:=bclosed;
   end;
   function AddFromParser(parser: Integer): Pointer;
   begin
      result := nil;
      if (parser<>0) then
      begin
         if (not ParserGetBoolValueByKeyByHandle(parser, 'Locked')) or (gbool_lan_debug) then
         begin
            result:=Add;
            TInternetShellSession(result).gamename := ParserGetValueByKeyByHandle(parser, 'GameName');
            TInternetShellSession(result).mapname := ParserGetValueByKeyByHandle(parser, 'MapName');
            TInternetShellSession(result).maxplayers := ParserGetIntValueByKeyByHandle(parser, 'MaxPlayers');
            TInternetShellSession(result).masterid := ParserGetIntValueByKeyByHandle(parser, 'Master');
            TInternetShellSession(result).clientscount := ParserGetIntValueByKeyByHandle(parser, 'Clients');
            TInternetShellSession(result).blocked := ParserGetBoolValueByKeyByHandle(parser, 'Locked');
            TInternetShellSession(result).bclosed := ParserGetBoolValueByKeyByHandle(parser, 'Closed');
         end;
      end
      else
      ErrorLog('TInternetShellSessions : parser=0');
   end;
   procedure Delete(const index: Integer);
   begin
      if (index>=0) and (index<GetCount) then
      begin
         var p: Pointer=self.fList.GetPointer(index);
         if p<>nil then
         FreeMem(p);
         self.fList.Delete(index);
      end;
   end;
   procedure DeleteByMasterID(const id: Integer);
   begin
      var i: Integer;
      var p: Pointer;
      for i:=0 to self.fList.GetCount-1 do begin
         p:=self.fList.GetPointer(i);
         if (id=TInternetShellSession(p).masterid) then
         begin
            self.fList.Delete(i);
            break;
         end;
      end;
   end;
end;

type TInternetShellProfile = class
   lanid : Integer;
   nick : String;
   mail : String;
   pass : String;
   info : String;
   country : String;
   
   procedure Clear;
   begin
      lanid := 0;
      nick := '';
      mail := '';
      pass := '';
      info := '';
      country := '';
   end;
end;

type TInternetShell = class
   lanid : Integer;
   nick : String;
   mail : String;
   pass : String;
   key : String;
   selclientid : Integer;
   selsessionid : Integer;
   selrankingid : Integer;
   currentpage : Integer;
   currentsessionid : Integer;
   currentsession : TInternetShellSession;
   currentrankingpage : Integer;
   profile : TInternetShellProfile;
   profiletmp : TInternetShellProfile;
   clients : TInternetShellClients;
   sessions : TInternetShellSessions;
   rankings : TInternetShellRankings;
   rankingslastupdate : Float;
   chatind : Integer;
   chat : array [0..gc_gui_maxinternetchatlog-1] of String;
   roomchatind : Integer;
   roomchat : array [0..gc_gui_maxinternetchatlog-1] of String;
   historyscrollind : Integer;
   historyind : Integer;
   history : array [0..gc_gui_maxinternetchatlog-1] of String;
   bshowroom : Boolean;
   bshowranking : Boolean;
   bshowprofile : Boolean;
   bjoin : Boolean;
   bprivate : Boolean;
   bratingroom : Boolean;
   bhistoricalbattle : Boolean;
   bauth : Boolean;
   bautosearch : Boolean;
   bcustomautosearch : Boolean;
   autosearchstarttime : Float;
   autosearchtotaltime : Float;
   autosearchhistorytotaltime : Float;
   autosearchroomtime : Float;
   autosearchrealstarttime : Float;
   autosearchsettings : Integer;
   autosearchantispamlasttime : Float;
   qprequeststart : Float;
   qprequeststop : Float;
   qprequeststartgame : Float;
   qprequeststartgameplcount : Integer;
   qprequeststartgamename : String;
   qprequestterminate : Float;
   qprequestcloseroom : Float;
   qprequestsleep : Float;
   qprequestsign : Float;
   qprequestmakematch : Float;
   qprequestmapname : String;
   qprankedgame : Boolean;
   mirrormatchscore : Integer;
   showrankingseasonoffset : Integer;
   
   autosearchteam : Integer;
   prevstate : Integer;
   roomdata : String;
   autosearchdocreate : String;
   
   procedure ClearCurrentSession;
   begin
      var empty : TInternetShellSession;
      self.currentsessionid := 0;
      self.currentsession := empty;
   end;
   procedure ClearChat(bShell, bSession : Boolean);
   begin
      var i : Integer;
      if (bShell) then
      begin
         chatind := 0;
         for i:=0 to gc_gui_maxinternetchatlog-1 do
         chat[i] := '';
      end;
      if (bSession) then
      begin
         roomchatind := 0;
         for i:=0 to gc_gui_maxinternetchatlog-1 do
         roomchat[i] := '';
      end;
   end;
   procedure ClearHistory;
   begin
      historyind := 0;
      historyscrollind := 0;
      var i : Integer;
      for i:=0 to gc_gui_maxinternetchatlog-1 do
      history[i] := '';
   end;
   procedure ClearQuickPlay;
   begin
      bautosearch := False;
      bcustomautosearch := False;
      autosearchdocreate := '';
      autosearchantispamlasttime := 0;
      
      qprequeststart := 0;
      qprequeststop := 0;
      qprequeststartgame := 0;
      qprequeststartgameplcount := 0;
      qprequeststartgamename := '';
      qprequestterminate := 0;
      qprequestcloseroom := 0;
      qprequestsleep := 0;
      qprequestsign := 0;
      qprequestmakematch := 0;
      qprequestmapname := '';
   end;
   procedure Clear(bClearChat : Boolean);
   begin
      lanid := 0;
      nick := '';
      mail := '';
      pass := '';
      key := '';
      selclientid := 0;
      selsessionid := 0;
      selrankingid := 0;
      currentpage := 0;
      currentsessionid := 0;
      currentrankingpage := 0;
      self.clients.Clear;
      self.sessions.Clear;
      self.rankings.Clear;
      self.profile.Clear;
      self.profiletmp.Clear;

      bautosearch := False;
      bcustomautosearch := False;
      autosearchstarttime := 0;
      autosearchtotaltime := 0;
      autosearchroomtime := 0;
      autosearchrealstarttime := 0;
      autosearchhistorytotaltime := 0;
      autosearchsettings := 0;
      autosearchantispamlasttime := 0;
      autosearchteam := 0;
      prevstate := 0;
      roomdata := '';
      autosearchdocreate := '';
      
      ClearQuickPlay;
      if (bClearChat) then
      ClearChat(True, True);
      ClearHistory;
      roomdata := '';
   end;
   procedure Destroy;
   begin
      Clear(True);
      self.clients.Destroy;
      self.sessions.Destroy;
      self.rankings.Destroy
   end;
   destructor TInternetShell;
   begin
      Destroy;
   end;
end;

type TSoundGrid = class
   sndrequests : array [0..gc_snd_ind_count-1] of Integer;
   bsndrequestexist : Boolean;
end;

type TSoundManagerCounter = class
   walkinfcloth : Integer;
   walkinfmetal : Integer;
   walkartwheels : Integer;
   walkartmortar : Integer;
   walkcavfast : Integer;
   walkcavslow : Integer;
   walkpeastone : Integer;
   walkpea : Integer;
   
   walkdstinfcloth : Float;
   walkdstinfmetal : Float;
   walkdstartwheels : Float;
   walkdstartmortar : Float;
   walkdstcavfast : Float;
   walkdstcavslow : Float;
   walkdstpeastone : Float;
   walkdstpea : Float;
end;

type TSoundManager = class
   bprocess : Boolean;
   lastprogresstime : Float;
   emitterhnd : Integer;
   frustumltx : Float;
   frustumlty : Float;
   frustumrtx : Float;
   frustumrty : Float;
   frustumrbx : Float;
   frustumrby : Float;
   frustumlbx : Float;
   frustumlby : Float;
   frustumcenx : Float;
   frustumceny : Float;
   frustumrad : Float;
   counter : TSoundManagerCounter;
   
   procedure CalcFrustum();
   begin
      const border = 8;
      var y : Float;
      GetCameraFrustumQuadrangle(frustumltx,y,frustumlty,frustumrtx,y,frustumrty,frustumrbx,y,frustumrby,frustumlbx,y,frustumlby);
      if (border<>0) then
      begin
         frustumltx := frustumltx-border;
         frustumlty := frustumlty-border;
         frustumrbx := frustumrbx+border;
         frustumrby := frustumrby+border;
      end;
      frustumcenx := (frustumltx+frustumrtx+frustumrbx+frustumlbx)/4;
      frustumceny := (frustumlty+frustumrty+frustumrby+frustumlby)/4;
      frustumrad := ((frustumrtx-frustumltx)+(frustumrby-frustumlty))/4;
   end;
   function IsObjInFrustum(const goHnd : Integer) : Boolean;
   begin
      var px, py, pz : Float;
      GetGameObjectAbsolutePositionByHandle(goHnd, px, py, pz);
      if (VectorDistance(px, 0, pz, frustumcenx, 0, frustumceny)<frustumrad) then
      Result := True
      else
      Result := False;
   end;
   function GetObjectArgData(goHnd, argInd : Integer) : Pointer;
   begin
      Result := nil;
      if goHnd <> 0 then
      begin
         var hnd : Integer = GetGameObjectStateMachineHandle(goHnd);
         if hnd <> 0 then
         Result := StateMachineGetArgDataByInd(hnd, argInd);
      end;
   end;
   function RemoveObj(const goHnd : Integer; var counter : Integer; var counterdst : Float) : Boolean;
   begin
      Result := False;
      var pobj : Pointer = GetObjectArgData(goHnd, gc_argunit_obj);
      if (pobj<>nil) then
      begin
         if (TObj(pobj).sndwalkproc) then
         begin
            counter := counter-1;
            counterdst := counterdst-TObj(pobj).sndwalkdst;
            TObj(pobj).sndwalkproc := False;
            TObj(pobj).sndwalkdst := 0;
            Result := True;
         end;
      end;
   end;
   function AddObjIfInFrustum(const goHnd : Integer; var counter : Integer; var counterdst : Float) : Boolean;
   begin
      var pobj : Pointer = GetObjectArgData(goHnd, gc_argunit_obj);
      if (pobj<>nil) and GetGameObjectVisibleByHandle(goHnd) then
      begin
         var px, py, pz : Float;
         GetGameObjectAbsolutePositionByHandle(goHnd, px, py, pz);
         var dst : Float = VectorDistance(px, 0, pz, frustumcenx, 0, frustumceny);
         if (dst<frustumrad) then
         begin
            if (TObj(pobj).sndwalkproc) then
            counterdst := counterdst-TObj(pobj).sndwalkdst
            else
            counter := counter+1;
            TObj(pobj).sndwalkproc := True;
            TObj(pobj).sndwalkdst := dst;
            Result := True;
            counterdst := counterdst+dst;
         end
         else
         begin
            RemoveObj(goHnd, counter, counterdst);
            Result := False;
         end;
      end;
   end;
end;

type TIslandInfo = class
   index : Integer;
   shore : Boolean;
   checked : Boolean;
end;

type TIslands = class
   count : Integer;
   grid : array [0..gc_MaxColMapWidth-1] of array [0..gc_MaxColMapHeight-1] of TIslandInfo;
end;

type TSettingsVideo = class
   preset : String;
   resolution : Integer;
   antialiasing : String;
   fxaa : Boolean;
   ssao : Boolean;
   hdrtype : String;
   vsyncmode : String;
   shadowenabled : Boolean;
   shadowmap : String;
   texturequality : String;
   texturefilter : String;
   shadertype : String;
   waterreflection : String;
   lightpreset : Integer;
   advanced : Boolean;
end;

type TSettings = class
   video : TSettingsVideo;
end;

type TArmyInfoCell = class
   infantry : Integer;
   shooters : Integer;
   cavalry  : Integer;
   cannons  : Integer;
   mortars  : Integer;
   mcannons : Integer;
   towers   : Integer;
   ships    : Integer;
   force    : Integer;
   army     : Integer;
   tag      : Integer;
   checked  : Boolean;
   region   : Integer;
   
   regions  : TIntegerList;
   
   procedure Create;
   begin
      region := -1;
      self.regions.Create(SizeOf(Integer));
   end;
   
   procedure Reset;
   begin
      infantry := 0;
      shooters := 0;
      cavalry := 0;
      cannons := 0;
      mortars := 0;
      mcannons := 0;
      towers := 0;
      ships := 0;
      force := 0;
      army := -1;
      tag := 0;
      checked := false;
      region := -1;
   end;
   
   constructor TArmyInfoCell;
   begin
      Create;
   end;
   
   procedure Destroy;
   begin
      self.regions.Destroy;
   end;
   
   destructor TArmyInfoCell;
   begin
      Destroy;
   end;
end;

type TWriteNewRequest = class
   plind : Integer;
   racename : String;
   basename : String;
   cid : Integer;
   uid : Integer;
   posx : Float;
   posz : Float;
   itype : Integer;
   bpush : Boolean;
end;

const gc_scenario_maxflags = 256;
const gc_scenario_maxcounters = 256;
const gc_scenario_maxtimers = 32;
const gc_scenario_maxgroups = 256-1; //(using Byte type)
const gc_scenario_maxzones = 256;
const gc_scenario_maxtriggers = 512;
const gc_scenario_maxobjectives = 32;
const gc_scenario_maxqueries = 256;
const gc_scenario_maxresults = 256;

const gc_trigger_maxconditions = 8;
const gc_trigger_maxactions = 16;

const gc_scenario_maxresulttriggers = 64;
const gc_scenario_maxqueryresults = 8;
const gc_scenario_maximages = 64;

const gc_scenario_ui_helpertype_none = 0;
const gc_scenario_ui_helpertype_mapsettings = 1;
const gc_scenario_ui_helpertype_players = 2;
const gc_scenario_ui_helpertype_flags = 3;
const gc_scenario_ui_helpertype_counters = 4;
const gc_scenario_ui_helpertype_timers = 5;
const gc_scenario_ui_helpertype_groups = 6;
const gc_scenario_ui_helpertype_zones = 7;
const gc_scenario_ui_helpertype_objectives = 8;
const gc_scenario_ui_helpertype_queries = 9;
const gc_scenario_ui_helpertype_images = 10;
const gc_scenario_ui_helpertype_results = 11;
const gc_scenario_ui_helpertype_resulttriggers = 12;
const gc_scenario_ui_helpertype_triggers = 13;
const gc_scenario_ui_helpertype_conditions = 14;
const gc_scenario_ui_helpertype_actions = 15;

const gc_trigger_condition_sign_count = 7;
const gc_trigger_condition_sign_none = 0;
const gc_trigger_condition_sign_equal = 1;
const gc_trigger_condition_sign_notequal = 2;
const gc_trigger_condition_sign_more = 3;
const gc_trigger_condition_sign_moreequal = 4;
const gc_trigger_condition_sign_less = 5;
const gc_trigger_condition_sign_lessequal = 6;

// scenario editor
// add new to end, to contain compatibility with older maps
const gc_trigger_condition_count = 32;
const gc_trigger_condition_none = 0;
const gc_trigger_condition_undefined = 1;
const gc_trigger_condition_triggerIsActive = 2;
const gc_trigger_condition_triggerIsNotActive = 3;
const gc_trigger_condition_flagActive = 4;
const gc_trigger_condition_flagNotActive = 5;
const gc_trigger_condition_timerFinished = 6;
const gc_trigger_condition_timerNotFinished = 7;
const gc_trigger_condition_difficulty = 8;
const gc_trigger_condition_playerResources = 9;
const gc_trigger_condition_units_CountOfUnitsInGroup = 10;
const gc_trigger_condition_units_CountOfGroupUnitsInZone = 11;
const gc_trigger_condition_units_CountOfPlayerUnitsInZone = 12;
const gc_trigger_condition_triggerIsActiveTime = 13;
const gc_trigger_condition_counterValue = 14;
const gc_trigger_condition_resultExecutedCount = 15;
const gc_trigger_condition_executeState = 16;
const gc_trigger_condition_units_CountOfPlayerUnits = 17;
const gc_trigger_condition_units_CountOfPlayerUnitsInGroup = 18;
const gc_trigger_condition_units_CountOfActingUnitsInGroup = 19;
const gc_trigger_condition_logical_OR = 20;
const gc_trigger_condition_net_IsServer = 21;
const gc_trigger_condition_net_IsClient = 22;
const gc_trigger_condition_net_IsOnline = 23;
const gc_trigger_condition_net_IsOffline = 24;
const gc_trigger_condition_net_IsQueryDialogVisible = 25;
const gc_trigger_condition_net_CurrentPlayer = 26;
const gc_trigger_condition_net_CurrentPlayerTeam = 27;
const gc_trigger_condition_net_CurrentPlayerPosition = 28; // same as current player.
const gc_trigger_condition_net_CountOfPlayersInTeam = 29;
const gc_trigger_condition_units_CountOfUnitsInZone = 30;
const gc_trigger_condition_units_CountOfPlayersTeamUnitsInZone = 31;

// add new to end, to contain compatibility with older maps
const gc_trigger_action_count = 99;
const gc_trigger_action_none = 0;
const gc_trigger_action_undefined = 1;
const gc_trigger_action_service_timerStart = 2;
const gc_trigger_action_service_timerStop = 3;
const gc_trigger_action_service_flagSetActive = 4;
const gc_trigger_action_service_flagSetNotActive = 5;
const gc_trigger_action_resource_resourceAdd = 6;
const gc_trigger_action_resource_resourceRemove = 7;
const gc_trigger_action_resource_resourceSet = 8;
const gc_trigger_action_objective_objectiveShow = 9;
const gc_trigger_action_objective_objectiveHide = 10;
const gc_trigger_action_objective_objectiveSetDone = 11;
const gc_trigger_action_map_cameraMoveToZone = 12;
const gc_trigger_action_map_revealZone = 13;
const gc_trigger_action_query_queryShow = 14;
const gc_trigger_action_selection_selectionAddGroup = 15;
const gc_trigger_action_selection_selectionAddUnitsInZone = 16;
const gc_trigger_action_selection_selectionClear = 17;
const gc_trigger_action_player_playerSetAsEnemy = 18;
const gc_trigger_action_player_playerSetAsAlly = 19;
const gc_trigger_action_player_playerSetAsNeutral = 20;
const gc_trigger_action_player_unitsChangePlayer = 21;
const gc_trigger_action_units_DestroyGroup = 22;
const gc_trigger_action_units_KillGroup = 23;
const gc_trigger_action_units_CreateUnits = 24;
const gc_trigger_action_units_MakeUnplayable = 25;
const gc_trigger_action_units_MakePlayable = 26;
const gc_trigger_action_country_unitProduceEnable = 27;
const gc_trigger_action_country_unitProduceDisable = 28;
const gc_trigger_action_country_upgradeEnable = 29;
const gc_trigger_action_country_upgradeDisable = 30;
const gc_trigger_action_country_makeUpgradeInstanly = 31;
const gc_trigger_action_order_attackGroup = 32;
const gc_trigger_action_order_attackZone = 33;
const gc_trigger_action_order_standground = 34;
const gc_trigger_action_order_cancelStandGround = 35;
const gc_trigger_action_order_enableSearchEnemy = 36;
const gc_trigger_action_order_disableSearchEnemy = 37;
const gc_trigger_action_order_moveToZone = 38;
const gc_trigger_action_order_runUpgrade = 39;
const gc_trigger_action_order_doExtractFood = 40;
const gc_trigger_action_order_doExtractWood = 41;
const gc_trigger_action_order_doExtractStone = 42;
const gc_trigger_action_order_goInside = 43;
const gc_trigger_action_gate_gateOpen = 44;
const gc_trigger_action_gate_gateClose = 45;
const gc_trigger_action_ferry_goOutside = 46;
const gc_trigger_action_endgame_win = 47;
const gc_trigger_action_endgame_lose = 48;
const gc_trigger_action_advanced_ifThenElse = 49;
const gc_trigger_action_advanced_enableTrigger = 50;
const gc_trigger_action_advanced_disableTrigger = 51;
const gc_trigger_action_advanced_enableThisTrigger = 52;
const gc_trigger_action_save_save = 53;
const gc_trigger_action_save_load = 54;
const gc_trigger_action_service_timerStartCustom = 55;
const gc_trigger_action_service_DebugLog = 56;
const gc_trigger_action_service_DebugMessage = 57;
const gc_trigger_action_order_setDirectionToZone = 58;
const gc_trigger_action_order_setUnitsVisible = 59;
const gc_trigger_action_order_setUnitsUnvisible = 60;
const gc_trigger_action_service_counterAddValue = 61;
const gc_trigger_action_service_counterRemoveValue = 62;
const gc_trigger_action_service_counterSetValue = 63;
const gc_trigger_action_order_moveToZoneInstanly = 64;
const gc_trigger_action_advanced_executeState = 65;
const gc_trigger_action_group_Clear = 66;
const gc_trigger_action_group_AddUnitsOfGroupToGroup = 67;
const gc_trigger_action_group_AddUnitsInZoneToGroup = 68;
const gc_trigger_action_group_AddUnitsOfGroupInZoneToGroup = 69;
const gc_trigger_action_group_AddUnitsOfPlayerInZoneToGroup = 70;
const gc_trigger_action_order_setDirectionToAngle = 71;
const gc_trigger_action_order_GroupProduceUnit = 72;
const gc_trigger_action_order_GroupCancelProduceUnit = 73;
const gc_trigger_action_order_SetRallyPointToZone = 74;
const gc_trigger_action_group_CreateSquad = 75;
const gc_trigger_action_group_AddIdlePeasantsToGroup = 76;
const gc_trigger_action_group_AddIdleMinesToGroup = 77;
const gc_trigger_action_group_AddUnitsOfPlayerOfGroupToGroup = 78;
const gc_trigger_action_advanced_ExecuteTriggerActions = 79;
const gc_trigger_action_player_enableAI = 80;
const gc_trigger_action_player_disableAI = 81;
const gc_trigger_action_group_AddSpecificUnitsOfPlayerInZoneToGroup = 82;
const gc_trigger_action_order_findAndAttackEnemyInZone = 83;
const gc_trigger_action_order_dismissSquadsInGroup = 84;
const gc_trigger_action_order_ArtilleryAttackZoneCenterWithDispertion = 85;
const gc_trigger_action_objective_PlaceMarkerInZone = 86;
const gc_trigger_action_objective_RemoveMarkerInZone = 87;
const gc_trigger_action_objective_AddMarkerToGroup = 88;
const gc_trigger_action_objective_RemoveMarkerFromGroup = 89;
const gc_trigger_action_net_SetTriggerActive = 90;
const gc_trigger_action_net_SetFlagState = 91;
const gc_trigger_action_net_SetCounterValue = 92;
const gc_trigger_action_net_ExecuteTriggerActions = 93;
const gc_trigger_action_net_SetCounterSpecialValue = 94;
const gc_trigger_action_net_TeamSetTriggerActive = 95;
const gc_trigger_action_net_TeamSetFlagState = 96;
const gc_trigger_action_net_TeamSetCounterValue = 97;
const gc_trigger_action_net_TeamExecuteTriggerActions = 98;

// markers
const gc_scenario_marker_count = 13;
const gc_scenario_marker_id_none = 0;
const gc_scenario_marker_id_attention = 1;
const gc_scenario_marker_id_flag = 2;
const gc_scenario_marker_id_crown = 3;
const gc_scenario_marker_id_round = 4;
const gc_scenario_marker_id_shield = 5;
const gc_scenario_marker_id_circle = 6;
const gc_scenario_marker_id_arrowdown = 7;
const gc_scenario_marker_id_bui = 8;
const gc_scenario_marker_id_rallypoint = 9;
const gc_scenario_marker_id_movearrow = 10;
const gc_scenario_marker_id_shield2 = 11;
const gc_scenario_marker_id_crown2 = 12;
const gc_scenario_marker_none = '';
const gc_scenario_marker_attention = 'misattention';
const gc_scenario_marker_flag = 'misflag';
const gc_scenario_marker_crown = 'miscrown';
const gc_scenario_marker_round = 'misround';
const gc_scenario_marker_shield = 'misshield';
const gc_scenario_marker_circle = 'miscircle';
const gc_scenario_marker_arrowdown = 'misarrowdown';
const gc_scenario_marker_bui = 'bui';
const gc_scenario_marker_rallypoint = 'rallypoint';
const gc_scenario_marker_movearrow = 'movearrow';
const gc_scenario_marker_shield2 = 'misshield2';
const gc_scenario_marker_crown2 = 'miscrown2';
global gConstScenarioMarkers : array [0..gc_scenario_marker_count-1] of String;
gConstScenarioMarkers[gc_scenario_marker_id_none] := gc_scenario_marker_none;
gConstScenarioMarkers[gc_scenario_marker_id_attention] := gc_scenario_marker_attention;
gConstScenarioMarkers[gc_scenario_marker_id_flag] := gc_scenario_marker_flag;
gConstScenarioMarkers[gc_scenario_marker_id_crown] := gc_scenario_marker_crown;
gConstScenarioMarkers[gc_scenario_marker_id_round] := gc_scenario_marker_round;
gConstScenarioMarkers[gc_scenario_marker_id_shield] := gc_scenario_marker_shield;
gConstScenarioMarkers[gc_scenario_marker_id_circle] := gc_scenario_marker_circle;
gConstScenarioMarkers[gc_scenario_marker_id_arrowdown] := gc_scenario_marker_arrowdown;
gConstScenarioMarkers[gc_scenario_marker_id_bui] := gc_scenario_marker_bui;
gConstScenarioMarkers[gc_scenario_marker_id_rallypoint] := gc_scenario_marker_rallypoint;
gConstScenarioMarkers[gc_scenario_marker_id_movearrow] := gc_scenario_marker_movearrow;
gConstScenarioMarkers[gc_scenario_marker_id_shield2] := gc_scenario_marker_shield2;
gConstScenarioMarkers[gc_scenario_marker_id_crown2] := gc_scenario_marker_crown2;

type TScenarioPlayer = class
   name : String;
   cid : Integer;
   team : Integer;
   difficulty : Integer;
   resources : array [0..gc_ResCount-1] of Integer;
   bai : Boolean;
end;

type TScenarioSettings = class
   filename : String;
   preview : String;
   langfilename : String;
   langtable : String;
   name : String;
   description : String;
end;

type TScenarioCounter = class
   name : String;
   value : Integer;
end;

type TScenarioFlag = class
   name : String;
   bactive : Boolean;
end;

type TScenarioTimer = class
   name : String;
   bstarted : Boolean;
   bfinished : Boolean;
   time : Float;
   starttime : Float;
end;

type TScenarioGroup = class
   name : String;
   unitslist : TIntegerList;
end;

type TScenarioZone = class
   name : String;
   x : Float;
   y : Float;
   w : Float;
   h : Float;
   bactive : Boolean;
   player : Integer;
   group : Integer;
   blinkgroup : Boolean;
   buseaverage : Boolean;
   addradius : Float;
   linkgroup : Integer;
   bonlyungroup : Boolean;
   unitslist : TIntegerList;
end;

type TScenarioTriggerCondition = class
   id : Integer;
   triggerid : Integer;
   flagid : Integer;
   counterid : Integer;
   timerid : Integer;
   groupid : Integer;
   zoneid : Integer;
   resultid : Integer;
   
   bactive : Boolean;
   bstate : Boolean;
   usecounterid : Integer;
   busecounterasamount : Boolean;
   busecounterastime : Boolean;
   player : Integer;
   difficulty : Integer;
   resource : Integer;
   amount : Integer;
   sign : Integer;
   time : Float;
   executestate : String;
   stringregister : String;
   
   procedure Clear();
   begin
      var clear : TScenarioTriggerCondition;
      self := clear;
   end;
   procedure Create();
   begin
      bactive := True;
   end;
   constructor TScenarioTriggerCondition;
   begin
      Create();
   end;
end;

type TScenarioTriggerAction = class
   id : Integer;
   triggerid : Integer;
   flagid : Integer;
   counterid : Integer;
   timerid : Integer;
   groupid : Integer;
   groupidtrg : Integer;
   zoneid : Integer;
   objectiveid : Integer;
   queryid : Integer;
   
   bactive : Boolean;
   bstate : Boolean;
   usecounterid : Integer;
   busecounterasamount : Boolean;
   busecounterastime : Boolean;
   player : Integer;
   playertrg : Integer;
   resource : Integer;
   amount : Integer;
   time : Float;
   radius : Float;
   countryid : Integer;
   unitid : Integer;
   direction : Float;
   upgradeid : Integer;
   formationid : Integer;
   executestate : String;
   stringregister : String;
   marker : Integer;
   procedure Clear();
   begin
      var clear : TScenarioTriggerAction;
      self := clear;
   end;
   procedure Create();
   begin
      bactive := True;
   end;
   constructor TScenarioTriggerAction;
   begin
      Create();
   end;
end;

type TScenarioTrigger = class
   name : String;
   bactive : Boolean;
   bexists : Boolean;
   btriggertreeopen : Boolean;
   bconditionstreeopen : Boolean;
   bactionstreeopen : Boolean;
   conditions : array [0..gc_trigger_maxconditions-1] of TScenarioTriggerCondition;
   actions : array [0..gc_trigger_maxactions-1] of TScenarioTriggerAction;
   activetime : Float;
   procedure Clear(const index : Integer);
   begin
      var clear : TScenarioTrigger;
      self := clear;
      name := 'trigger'+IntToStr(index);
   end;
   function ClampInt(const val, min, max : Integer) : Integer;
   begin
      if (val<min) then Result := min
      else
      if (val>max) then Result := max
      else
      Result := val;
   end;
   procedure SortConditions();
   begin
      var i : Integer;
      for i:=0 to gc_trigger_maxconditions-2 do
      begin
         if (conditions[i].id=gc_trigger_condition_none) and (conditions[i+1].id<>gc_trigger_condition_none) then
         begin
            conditions[i] := conditions[i+1];
            self.conditions[i+1].Clear;
         end;
      end;
   end;
   procedure SortActions();
   begin
      var i : Integer;
      for i:=0 to gc_trigger_maxactions-2 do
      begin
         if (actions[i].id=gc_trigger_condition_none) and (actions[i+1].id<>gc_trigger_condition_none) then
         begin
            actions[i] := actions[i+1];
            self.actions[i+1].Clear;
         end;
      end;
   end;
   function AddCondition() : Integer;
   begin
      var i : Integer;
      for i:=0 to gc_trigger_maxconditions-1 do
      begin
         if (conditions[i].id=gc_trigger_condition_none) then
         begin
            conditions[i].id := gc_trigger_condition_undefined;
            Result := i;
            break;
         end;
      end;
   end;
   function AddAction() : Integer;
   begin
      var i : Integer;
      for i:=0 to gc_trigger_maxactions-1 do
      begin
         if (actions[i].id=gc_trigger_action_none) then
         begin
            actions[i].id := gc_trigger_action_undefined;
            Result := i;
            break;
         end;
      end;
   end;
   function ActionMove(const actionind, relativepos : Integer) : Integer;
   begin
      Result := actionind;
      if (relativepos=0) then exit;
      var bUp : Boolean = (relativepos<0);
      var iDir : Integer;
      if (bUp) then
      iDir := -1
      else
      iDir := 1;
      var i : Integer;
      for i:=0 to Abs(relativepos)-1 do
      begin
         if ((bUp) and (Result>0)) or ((not bUp) and (Result<(gc_trigger_maxactions-2)) and (actions[Result+iDir].id<>gc_trigger_action_none)) then
         begin
            var tmp : TScenarioTriggerAction;
            tmp := actions[Result+iDir];
            actions[Result+iDir] := actions[Result];
            actions[Result] := tmp;
            Result := Result+iDir;
         end;
      end;
      Result := ClampInt(Result, 0, gc_trigger_maxactions-1);
   end;
   function ConditionMove(const conditionind, relativepos : Integer) : Integer;
   begin
      Result := conditionind;
      if (relativepos=0) then exit;
      var bUp : Boolean = (relativepos<0);
      var iDir : Integer;
      if (bUp) then
      iDir := -1
      else
      iDir := 1;
      var i : Integer;
      for i:=0 to Abs(relativepos)-1 do
      begin
         if ((bUp) and (Result>0)) or ((not bUp) and (Result<(gc_trigger_maxconditions-2)) and (conditions[Result+iDir].id<>gc_trigger_condition_none)) then
         begin
            var tmp : TScenarioTriggerCondition;
            tmp := conditions[Result+iDir];
            conditions[Result+iDir] := conditions[Result];
            conditions[Result] := tmp;
            Result := Result+iDir;
         end;
      end;
      Result := ClampInt(Result, 0, gc_trigger_maxconditions-1);
   end;
end;

type TScenarioObjective = class
   name : String;
   langtable : String;
   langkey : String;
   gametime : Float;
   bvisible : Boolean;
   bdone : Boolean;
end;

type TScenarioImage = class
   name : String;
   material : String;
end;

type TScenarioResult = class
   name : String;
   langtable : String;
   langkey : String;
   nextqueryid : Integer;
   resulttriggerid : Integer;
   executecount : Integer;
end;

type TScenarioQuery = class
   name : String;
   langtable : String;
   langkey : String;
   imageid : Integer;
   results : array [0..gc_scenario_maxqueryresults-1] of Integer;
end;

type TScenarioUI = class
   helpertype : Integer;
   bshowui : Boolean;
   beditresulttrigger : Boolean;
   pickedtriggerindex : Integer;
   pickedconditionindex : Integer;
   pickedactionindex : Integer;
   pickedplayerindex : Integer;
   pickedflagindex : Integer;
   pickedcounterindex : Integer;
   pickedtimerindex : Integer;
   pickedgroupindex : Integer;
   pickedzoneindex : Integer;
   pickedobjectiveindex : Integer;
   pickedqueryindex : Integer;
   pickedimageindex : Integer;
   pickedresultindex : Integer;
   pickedresulttriggerindex : Integer;
   pickedresulttriggeractionindex : Integer;
   pickedresulttriggerconditionindex : Integer;
   copytriggercache : TScenarioTrigger;
   copyactioncache : TScenarioTriggerAction;
   copyconditioncache : TScenarioTriggerCondition;
end;

type TScenario = class
   bactive : Boolean;
   bexists : Boolean;
   campname : String;
   missname : String;
   difficulty : Integer;
   settings : TScenarioSettings;
   ui : TScenarioUI;
   players : array [0..gc_MaxPlayerCount-1] of TScenarioPlayer;
   flags : array [0..gc_scenario_maxflags-1] of TScenarioFlag;
   counters : array [0..gc_scenario_maxcounters-1] of TScenarioCounter;
   timers : array [0..gc_scenario_maxtimers-1] of TScenarioTimer;
   groups : array [0..gc_scenario_maxgroups-1] of TScenarioGroup;
   zones : array [0..gc_scenario_maxzones-1] of TScenarioZone;
   triggers : array [0..gc_scenario_maxtriggers-1] of TScenarioTrigger;
   objectives : array [0..gc_scenario_maxobjectives-1] of TScenarioObjective;
   queries : array [0..gc_scenario_maxqueries-1] of TScenarioQuery;
   images : array [0..gc_scenario_maximages-1] of TScenarioImage;
   results : array [0..gc_scenario_maxresults-1] of TScenarioResult;
   resulttriggers : array [0..gc_scenario_maxresulttriggers-1] of TScenarioTrigger;
   function ClampInt(const val, min, max : Integer) : Integer;
   begin
      if (val<min) then Result := min
      else
      if (val>max) then Result := max
      else
      Result := val;
   end;
   procedure FixTriggersLinks(index : Integer; bMoveUp, bMoveDown, bDelete : Boolean);
   begin
      var indPrimary : Integer = index;
      var indSecondary : Integer;
      if (bMoveUp) then
      indSecondary := indPrimary-1
      else
      indSecondary := indPrimary+1;
      
      var i, j : Integer;
      // Fix Result Triggers
      for i:=0 to gc_scenario_maxresulttriggers-1 do
      begin
         for j:=0 to gc_trigger_maxconditions-1 do
         begin
            var tid : Integer = self.resulttriggers[i].conditions[j].triggerid;
            if (tid<>-1) then
            begin
               if (bDelete) then
               begin
                  if (tid=indPrimary) then
                  self.resulttriggers[i].conditions[j].triggerid := 0;
               end
               else
               if (tid=indPrimary) then
               begin
                  if (bMoveUp) then
                  self.resulttriggers[i].conditions[j].triggerid := indSecondary
                  else
                  if (bMoveDown) then
                  self.resulttriggers[i].conditions[j].triggerid := indSecondary
               end
               else
               if (tid=indSecondary) then
               begin
                  if (bMoveUp) then
                  self.resulttriggers[i].conditions[j].triggerid := indPrimary
                  else
                  if (bMoveDown) then
                  self.resulttriggers[i].conditions[j].triggerid := indPrimary
               end;
            end;
         end;
         for j:=0 to gc_trigger_maxactions-1 do
         begin
            var tid : Integer = self.resulttriggers[i].actions[j].triggerid;
            if (tid<>-1) then
            begin
               if (bDelete) then
               begin
                  if (tid=indPrimary) then
                  self.resulttriggers[i].actions[j].triggerid := 0;
               end
               else
               if (tid=indPrimary) then
               begin
                  if (bMoveUp) then
                  self.resulttriggers[i].actions[j].triggerid := indSecondary
                  else
                  if (bMoveDown) then
                  self.resulttriggers[i].actions[j].triggerid := indSecondary
               end
               else
               if (tid=indSecondary) then
               begin
                  if (bMoveUp) then
                  self.resulttriggers[i].actions[j].triggerid := indPrimary
                  else
                  if (bMoveDown) then
                  self.resulttriggers[i].actions[j].triggerid := indPrimary
               end;
            end;
         end;
      end;
      
      // Fix Common Triggers
      for i:=0 to gc_scenario_maxtriggers-1 do
      begin
         for j:=0 to gc_trigger_maxconditions-1 do
         begin
            var tid : Integer = self.triggers[i].conditions[j].triggerid;
            if (tid<>-1) then
            begin
               if (bDelete) then
               begin
                  if (tid=indPrimary) then
                  self.triggers[i].conditions[j].triggerid := 0;
               end
               else
               if (tid=indPrimary) then
               begin
                  if (bMoveUp) then
                  self.triggers[i].conditions[j].triggerid := indSecondary
                  else
                  if (bMoveDown) then
                  self.triggers[i].conditions[j].triggerid := indSecondary
               end
               else
               if (tid=indSecondary) then
               begin
                  if (bMoveUp) then
                  self.triggers[i].conditions[j].triggerid := indPrimary
                  else
                  if (bMoveDown) then
                  self.triggers[i].conditions[j].triggerid := indPrimary
               end;
            end;
         end;
         for j:=0 to gc_trigger_maxactions-1 do
         begin
            var tid : Integer = self.triggers[i].actions[j].triggerid;
            if (tid<>-1) then
            begin
               if (bDelete) then
               begin
                  if (tid=indPrimary) then
                  self.triggers[i].actions[j].triggerid := 0;
               end
               else
               if (tid=indPrimary) then
               begin
                  if (bMoveUp) then
                  self.triggers[i].actions[j].triggerid := indSecondary
                  else
                  if (bMoveDown) then
                  self.triggers[i].actions[j].triggerid := indSecondary
               end
               else
               if (tid=indSecondary) then
               begin
                  if (bMoveUp) then
                  self.triggers[i].actions[j].triggerid := indPrimary
                  else
                  if (bMoveDown) then
                  self.triggers[i].actions[j].triggerid := indPrimary
               end;
            end;
         end;
      end;
   end;
   procedure SortTriggers();
   begin
      var i : Integer;
      for i:=0 to gc_scenario_maxtriggers-2 do
      begin
         if (not triggers[i].bexists) and (triggers[i+1].bexists) then
         begin
            FixTriggersLinks(i+1, True, False, False);
            triggers[i] := triggers[i+1];
            self.triggers[i+1].Clear(i+1);
         end;
      end;
   end;
   procedure := (var scenario : TScenario);
   begin
      self.bexists := scenario.bexists;
      self.bactive := scenario.bactive;
      self.settings := scenario.settings;
      self.ui := scenario.ui;
      self.players := scenario.players;
      self.flags := scenario.flags;
      self.counters := scenario.counters;
      self.timers := scenario.timers;
      self.groups := scenario.groups;
      self.zones := scenario.zones;
      self.triggers := scenario.triggers;
      self.objectives := scenario.objectives;
      self.queries := scenario.queries;
      self.images := scenario.images;
      self.results := scenario.results;
      self.resulttriggers := scenario.resulttriggers;
   end;
   function AddTrigger() : Integer;
   begin
      Result := 0;
      var i : Integer;
      for i:=0 to gc_scenario_maxtriggers-1 do
      begin
         if (not triggers[i].bexists) then
         begin
            Result := i;
            triggers[Result].bexists := True;
            triggers[Result].bactive := False;
            triggers[Result].btriggertreeopen := True;
            break;
         end;
      end;
   end;
   function TriggerMove(const triggerind, relativepos : Integer) : Integer;
   begin
      Result := triggerind;
      if (relativepos=0) then exit;
      var bUp : Boolean = (relativepos<0);
      var iDir : Integer;
      if (bUp) then
      iDir := -1
      else
      iDir := 1;
      var i : Integer;
      for i:=0 to Abs(relativepos)-1 do
      begin
         if ((bUp) and (Result>0)) or ((not bUp) and (Result<(gc_scenario_maxtriggers-2)) and (triggers[Result+iDir].bexists)) then
         begin
            var tmp : TScenarioTrigger;
            tmp := triggers[Result+iDir];
            triggers[Result+iDir] := triggers[Result];
            triggers[Result] := tmp;
            FixTriggersLinks(Result, bUp, (not bUp), False);
            Result := Result+iDir;
         end;
      end;
      Result := ClampInt(Result, 0, gc_scenario_maxtriggers-1);
   end;
   procedure Create(const mapname : String);
   begin
      const cAddRadius = 3;
      var i, j : Integer;
      for i:=0 to gc_MaxPlayerCount-1 do
      begin
         players[i].name := 'AI player'+IntToStr(i);
         players[i].cid := 0;
         players[i].team := 0;
         players[i].difficulty := 1;
         for j:=0 to gc_ResCount-1 do
         players[i].resources[j] := 50000;
      end;
      players[0].name := 'human';
      for i:=0 to gc_scenario_maxflags-1 do
      flags[i].name := 'flag'+IntToStr(i);
      for i:=0 to gc_scenario_maxcounters-1 do
      counters[i].name := 'counter'+IntToStr(i);
      for i:=0 to gc_scenario_maxtimers-1 do
      timers[i].name := 'timer'+IntToStr(i);
      for i:=0 to gc_scenario_maxgroups-1 do
      begin
         groups[i].name := 'group'+IntToStr(i);
         self.groups[i].unitslist.Create(SizeOf(Integer));
      end;
      for i:=0 to gc_scenario_maxzones-1 do
      begin
         zones[i].name := 'zone'+IntToStr(i);
         self.zones[i].unitslist.Create(SizeOf(Integer));
         zones[i].player := -1;
         zones[i].group := -1;
         zones[i].linkgroup := -1;
         zones[i].addradius := cAddRadius;
      end;
      for i:=0 to gc_scenario_maxtriggers-1 do
      triggers[i].name := 'trigger'+IntToStr(i);
      for i:=0 to gc_scenario_maxobjectives-1 do
      begin
         objectives[i].name := 'objective'+IntToStr(i);
         objectives[i].langtable := settings.langtable;
         objectives[i].langkey := 'objective'+IntToStr(i);
      end;
      
      settings.filename := mapname+'.map';
      settings.preview := mapname+'.bmp';
      settings.langfilename := mapname+'.lng';
      settings.langtable := mapname;
      settings.name := mapname+'.name';
      settings.description := mapname+'.description';
      
      for i:=0 to gc_scenario_maximages-1 do
      images[i].name := 'image'+IntToStr(i);
      for i:=0 to gc_scenario_maxresulttriggers-1 do
      resulttriggers[i].name := 'resulttrigger'+IntToStr(i);
      for i:=0 to gc_scenario_maxresults-1 do
      begin
         results[i].name := 'result'+IntToStr(i);
         results[i].langtable := settings.langtable;
         results[i].langkey := results[i].name;
         results[i].nextqueryid := -1;
         results[i].resulttriggerid := -1;
      end;
      for i:=0 to gc_scenario_maxqueries-1 do
      begin
         for j:=0 to gc_scenario_maxqueryresults-1 do
         queries[i].results[j] := -1;
         queries[i].name := 'query'+IntToStr(i);
         queries[i].langtable := settings.langtable;
         queries[i].langkey := queries[i].name;
         queries[i].imageid := -1;
      end;
   end;
   constructor TScenario;
   begin
      Create('');
   end;
end;

global gInternetShell : TInternetShell;
global gInternetShellTmp : TInternetShell;

const gc_ReconnectManager_MaxAttemptsCount = 4;
const gc_ReconnectManager_AttemptTimeOut = 2;
type TReconnectManager = class
   bsessionstarted : Boolean;
   btryreconnect : Boolean;
   bimpossibletoreconnect : Boolean;
   bsuccessreconnectserver : Boolean;
   bsuccessreconnectroom : Boolean;
   bconnectedserver : Boolean;
   bconnectedroom : Boolean;
   sessionstarttime : Float;
   sessionendtime : Float;
   attempt : Integer;
   lastattempttime : Float;
   prevstate : Integer;

   procedure Init(bStartProcess : Boolean);
   begin
      Log('gReconnectManager.Init');
      attempt := 0;
      lastattempttime := 0;
      if (not bStartProcess) then
      sessionstarttime := 0;
      sessionendtime := 0;
      if (not bStartProcess) then
      begin
         bsessionstarted := False;
         btryreconnect := False;
      end;
      bimpossibletoreconnect := False;
      bsuccessreconnectserver := False;
      bsuccessreconnectroom := False;
      bconnectedserver := False;
      bconnectedroom := False;
      prevstate := 0;
   end;

   function IsOnServer() : Boolean;
   begin
      Result := IsLanPublicServerMode and (gInternetShell.bauth);
   end;

   function IsOnline() : Boolean;
   begin
      Result := (GetLanMode>0);
   end;
end;

type TQuickPlayer = class
   playerid : Integer;
   sessionid : Integer;
   weight : Float;
   score : Integer;
   rank : Float;
   games : Integer;
   wins : Integer;
   waittime : Float;
   ram : Integer;
   tolerance : Float;
   winrate : Float;
   checksum : String;
end;
type TQuickSession = class
   players : array [0..gc_MaxPlayerCount-2] of TQuickPlayer;
end;
const gc_MaxQuickPlayers = 256;
const gc_MaxQuickSessions = 64; // only custom ranked sessions
type TQuickPlayers = class
   player : array [0..gc_MaxQuickPlayers-1] of TQuickPlayer;
   count : Integer;
   session : array [0..gc_MaxQuickSessions-1] of TQuickSession;
   sessioncount : Integer;
   sessionplayercount : Integer;

   function IndexOf(src : Integer) : Integer;
   begin
      result:=-1;
      var i: Integer;
      for i:=0 to count-1 do begin
         if player[i].playerid=src then begin
            result:=i;
            break;
         end;
      end;
   end;
   function GetRankByScoreELO(score : Integer) : Integer;
   begin
      if (score<800) then Result := 0
      else
      if (score<900) then Result := 1
      else
      if (score<1001) then Result := 2
      else
      if (score<1100) then Result := 3
      else
      if (score<1150) then Result := 4
      else
      if (score<1230) then Result := 5
      else
      if (score<1300) then Result := 6
      else
      if (score<1380) then Result := 7
      else
      if (score<1425) then Result := 8
      else
      if (score<1475) then Result := 9
      else
      if (score<1600) then Result := 10
      else
      if (score<1900) then Result := 11
      else
      Result := 11; // 12 Emperor is not here yet
      {if (score<800) then Result := 0
      else
      if (score<900) then Result := 1
      else
      if (score<1001) then Result := 2
      else
      if (score<1100) then Result := 3
      else
      if (score<1200) then Result := 4
      else
      if (score<1300) then Result := 5
      else
      if (score<1400) then Result := 6
      else
      if (score<1500) then Result := 7
      else
      if (score<1600) then Result := 8
      else
      if (score<1700) then Result := 9
      else
      if (score<1800) then Result := 10
      else
      if (score<1900) then Result := 11
      else
      Result := 11; // 12 Emperor is not here yet}
      {if (score<1100) then Result := 0
      else
      if (score<1200) then Result := 1
      else
      if (score<1400) then Result := 2
      else
      if (score<1600) then Result := 3
      else
      if (score<1800) then Result := 4
      else
      if (score<2000) then Result := 5
      else
      if (score<2200) then Result := 6
      else
      if (score<2400) then Result := 7
      else
      if (score<2700) then Result := 8
      else
      Result := 9;}
   end;
   function GetRankByScore(score : Integer) : Integer;
   begin
      if (score<200) then Result := 0
      else
      if (score<600) then Result := 1
      else
      if (score<1200) then Result := 2
      else
      if (score<2000) then Result := 3
      else
      if (score<3000) then Result := 4
      else
      if (score<4400) then Result := 5
      else
      if (score<6400) then Result := 6
      else
      if (score<9200) then Result := 7
      else
      if (score<13600) then Result := 8
      else
      Result := 9;
   end;

   function GetMaxScoreByRankELO(rank : Integer) : Integer;
   begin
      case rank of
         -1 : Result := 800;
         0 : Result := 900;
         1 : Result := 1001;
         2 : Result := 1100;
         3 : Result := 1150;
         4 : Result := 1230;
         5 : Result := 1300;
         6 : Result := 1380;
         7 : Result := 1425;
         8 : Result := 1475;
         9 : Result := 1600;
         10 : Result := 1900;
         else
         Result := 20000;
      end;

      {case rank of
         -1 : Result := 1100;
         0 : Result := 1200;
         1 : Result := 1400;
         2 : Result := 1600;
         3 : Result := 1800;
         4 : Result := 2000;
         5 : Result := 2200;
         6 : Result := 2400;
         7 : Result := 2700;
         else
         Result := 20000;
      end;}
   end;
   function GetMaxScoreByRank(rank : Integer) : Integer;
   begin
      case rank of
         -1 : Result := 0;
         0 : Result := 200;
         1 : Result := 600;
         2 : Result := 1200;
         3 : Result := 2000;
         4 : Result := 3000;
         5 : Result := 4400;
         6 : Result := 9200;
         7 : Result := 13600;
         else
         Result := 20000;
      end;
   end;
   procedure AddSession(masterid, clientid1, clientid2, waittime : Integer);
   begin
      if sessioncount>=gc_MaxQuickSessions then
      exit;
      if (masterid>0) and ((gint_quickplay_countofplayers<6) or ((clientid1>0) or (clientid2>0))) then
      begin
         var i : Integer;
         for i:=0 to (gint_quickplay_countofplayers div 2)-1 do
         begin
            var playerid : Integer;
            case i of
               0 : playerid := masterid;
               1 : playerid := clientid1;
               2 : playerid := clientid2;
            end;
            var pclient : Pointer = gInternetShell.clients.GetByLanID(playerid);
            if (pclient<>nil) then
            begin
               session[sessioncount].players[i].playerid := playerid;
               session[sessioncount].players[i].score := TInternetShellClient(pclient).score;
               //session[sessioncount].players[i].rank := GetRankByScoreELO(session[sessioncount].players[i].score);
               var addprevscore : Integer;
               if (TInternetShellClient(pclient).prevscore<>0) and (session[sessioncount].players[i].score<TInternetShellClient(pclient).prevscore) then
               addprevscore := TInternetShellClient(pclient).prevscore-session[sessioncount].players[i].score;
               var midscores : Integer = session[sessioncount].players[i].score+(addprevscore div 2);
               session[sessioncount].players[i].rank := GetRankByScoreELO(midscores);
               session[sessioncount].players[i].games := TInternetShellClient(pclient).gamesplayed;
               session[sessioncount].players[i].wins := TInternetShellClient(pclient).gameswin;
               session[sessioncount].players[i].waittime := waittime;
               if (session[sessioncount].players[i].games>0) then
               session[sessioncount].players[i].winrate := session[sessioncount].players[i].wins/session[sessioncount].players[i].games;
               // ping
               // fpsperf
               session[sessioncount].players[i].weight := (100000/(100000+playerid));//*(1/(ping div 40))*(fpsperf/50);
               //var psession : Pointer = gInternetShell.sessions.GetByMasterID(playerid);
               //if (psession<>nil) then
               //begin
               //end;
               sessionplayercount := sessionplayercount+1;
            end;
         end;
         sessioncount := sessioncount+1;
      end;
   end;

   procedure AddPlayer(playerid, time : Integer; checksum : String);
   begin
      if count>=gc_MaxQuickPlayers then
      exit;
      var pclient : Pointer = gInternetShell.clients.GetByLanID(playerid);
      if (pclient<>nil) then
      begin
         player[count].playerid := playerid;
         player[count].score := TInternetShellClient(pclient).score;
         //player[count].rank := GetRankByScoreELO(player[count].score);
         var addprevscore : Integer;
         if (TInternetShellClient(pclient).prevscore<>0) and (TInternetShellClient(pclient).score<TInternetShellClient(pclient).prevscore) then
         addprevscore := TInternetShellClient(pclient).prevscore-TInternetShellClient(pclient).score;
         var midscores : Integer = TInternetShellClient(pclient).score+(addprevscore div 2);
         player[count].rank := GetRankByScoreELO(midscores);
         player[count].games := TInternetShellClient(pclient).gamesplayed;
         player[count].wins := TInternetShellClient(pclient).gameswin;
         player[count].waittime := time;
         player[count].checksum := checksum;
         const waittimestep = 30;
         var ranktolerance : Integer;
         case time div waittimestep of
            0,1 : ranktolerance := 0;
            2,3,4 : ranktolerance := 1;
            5,6 : ranktolerance := 2;
            7,8 : ranktolerance := 3;
            9,10 : ranktolerance := 4;
            11  : ranktolerance := 5;
            12  : ranktolerance := 6;
            13  : ranktolerance := 7;
            14  : ranktolerance := 8;
            15  : ranktolerance := 9;
            else
            ranktolerance := 10;
         end;
         if (player[count].games>0) then
         player[count].winrate := player[count].wins/player[count].games;
         player[count].ram := TInternetShellClient(pclient).ram;
         player[count].tolerance := ranktolerance;
         var smin : Integer = GetMaxScoreByRankELO(floor(player[count].rank)-1);
         var smax : Integer = GetMaxScoreByRankELO(floor(player[count].rank));
         var frank : Float = (player[count].score-smin)/((smax+1)-smin);
         player[count].rank := Clamp(player[count].rank+frank, 0, 9);
         player[count].weight := 1+player[count].rank+player[count].tolerance;
         count := count+1;
      end;
   end;
end;

const gc_lanmode_server = 2;
const gc_lanmode_client = 1;

const gc_LAN_GENERATE = 1;
//const gc_LAN_READYSTART = 2;
const gc_LAN_START = 3;
const gc_LAN_ROOM_READY = 4;
//const gc_LAN_ROOM_START = 5;
const gc_LAN_ROOM_CLIENT_CHANGES = 6;
const gc_LAN_GAME_READY = 7;
const gc_LAN_GAME_ANSWER_READY = 8;
const gc_LAN_GAME_START = 9;
const gc_LAN_GAME_SURRENDER = 10;
const gc_LAN_GAME_SURRENDER_CONFIRM = 11;
const gc_LAN_GAME_SERVER_LEAVE = 12;
const gc_LAN_GAME_SESSION_RESULTS = 13;
const gc_LAN_GAME_SYNC_REQUEST = 14;
const gc_LAN_GAME_SYNC_DATA = 15;
const gc_LAN_GAME_SYNC_GAMETIME = 16;
const gc_LAN_GAME_SYNC_ALIVE = 17;

const gc_LAN_ROOM_SERVER_DATASYNC = 100;
const gc_LAN_ROOM_SERVER_DATACHANGE = 101;
const gc_LAN_ROOM_CLIENT_DATACHANGE = 102;
const gc_LAN_ROOM_CLIENT_LEAVE = 103;

const gc_LAN_MODS_MODSYNC_REQUEST = 200;
const gc_LAN_MODS_MODSYNC_PARSER = 201;
const gc_LAN_MODS_CHECKSUM_REQUEST = 202; // may be unused
const gc_LAN_MODS_CHECKSUM_ANSWER = 203; // may be unused
const gc_LAN_MODS_CHECKSUM_REQUESTCANJOIN = 204; // may be unused
const gc_LAN_MODS_CHECKSUM_ANSWERCANJOIN = 205; // may be unused
const gc_LAN_MODS_CHECKSUM_ANSWERCANNOTJOIN = 206; // may be unused

const gc_LAN_ADVISER_CLIENT_DATACHANGE = 300;

// Debug fonts start. remove after setting up fonts
type TChar = class
   text : String;
   width : Integer;
   offx : Integer;
   addwidth : Integer;
   addoffx : Integer;
end;

type TFonts = class
   font : String;
   chars : array [0..255] of TChar;
end;

global gFonts : array [0..31] of TFonts;
// Debug fonts finish. remove after setting up fonts

global gMap : TMap;
global gMapHistoricalBattle : TMap;
global gScanGrid : array [0..gc_scangrid_countx-1] of array [0..gc_scangrid_county-1] of TGridList;
global gScanGridUnits : array [0..gc_MaxPlayerCount-1] of array [0..gc_scangrid_countx-1] of array [0..gc_scangrid_county-1] of TIntegerList;
global gResGrid : array [0..gc_resgrid_countx-1] of array [0..gc_resgrid_county-1] of TResGridList;
global gIdleGrid : array [0..gc_idlegrid_countx-1] of array [0..gc_idlegrid_county-1] of Integer;
global gIslands : TIslands;
global gSoundGrid : array [0..gc_soundgrid_countx-1] of array [0..gc_soundgrid_county-1] of TSoundGrid;
global gSoundManager : TSoundManager;
global gIntegerList : TIntegerList;
global gUnderCursorList : TIntegerList;
global gDoRoundDamageList : TIntegerList;
global gDestructListBuilding : TIntegerList;
global gDestructListShip : TIntegerList;
global gPrimitives : TIntegerList;
global gTmpIntList : TIntegerList;
global gTmpIntListFindUnits : TIntegerList;
global gTmpIntListFillSquad : TIntegerList;
global gTmpIntListUpgrades : TIntegerList;
global gTmpIntListMembers : TIntegerList;
global gTmpIntListGetBuildPoints : TIntegerList;
global gTmpIntListObjectsInRadius : TIntegerList;
global gTmpIntListOnSelection : TIntegerList;
global gTmpIntListGUIInsideList : TIntegerList;
global gTmpIntListGUIDrummerList : TIntegerList;
global gTmpIntListGUISquadList : TIntegerList;
global gIntListWriteSquadAction : TIntegerList;
global gEmptyList : TIntegerList;
global gOrderUnitsList : TIntegerList;
global gOrderUnitsListTmp : TIntegerList;
global gLanSyncUnitsParamsUIDList : TIntegerList;
global gGOPathList : TIntegerList;
global gWaterPathList : TIntegerList;
global gProgress : TProgress;
global gGuiUpdateHighlights : TIntegerList;
global gGuiSelectionHelper : TIntegerList;
global gSelectedObjects : TIntegerList;
global gRegionsList : TIntegerList;
global gShellSortOnlineMarked : TIntegerList;
global gShellSortOnline : TIntegerList;
global gShellSortPlayMarked : TIntegerList;
global gShellSortPlay : TIntegerList;
global gRepairSessionList : TIntegerList;
global gSyncRes : TIntegerList;
global gDeathUnits : TIntegerList;
global gPlayer : array [0..gc_MaxPlayerCount-1] of TPlayer;
global gObjProp : array [0..gc_MaxCountryCount-1] of array [0..gc_country_maxmembers-1] of TObjProp;
global gEnemyInfoList : TEnemyInfoList;
global gCtrlGroups : array [0..gc_MaxPlayerCount-1] of array [0..9] of TIntegerList;
global gPatternMask : array [0..gc_terraindata_mapwidth-1] of array [0..gc_terraindata_mapheight-1] of Boolean;
global gMapMask : array [0..gc_terraindata_mapwidth-1] of array [0..gc_terraindata_mapheight-1] of TGeneratorBitmapMask;
global gCustomObjPoints : array [0..gc_MaxCountryCount-1] of array [0..gc_country_maxmembers-1] of TCustomObjPoints;
global gCustomBuildPointsWall : array [0..gc_MaxWallVariationCount-1] of TCustomBuildPointsWall;
global gWeapons : array[0..gc_weapon_maxmembers-1] of TWeapon;
global gLogMessages : TLogMessages;
global gChatMessages : TChatMessages;
global gArcherGrid : array [0..gc_ai_MaxArcherGridSize-1] of array [0..gc_ai_MaxArcherGridSize-1] of TArcherCell;
global gProjSimulatePoints : TProjSimulatePoints;
global gProfile : TProfile;
global gProfileTmp : TProfile;
global gProfileUserStruct : TProfileUserStruct;
global gProfileUserStructTmp : TProfileUserStruct;
global gSettings : TSettings;
global gSettingsTmp : TSettings;
global gSelectedSquads : TPtrList;
global gPtrList : TPtrList;
global gWriteSquadPtrList : TPtrList;
global gWriteNewRequestList : TPtrList;
global gArmyInfoCells : array [0..gc_scangrid_countx-1] of array [0..gc_scangrid_county-1] of TArmyInfoCell;

global gUIConst : TUIConst;
global gConst : TConst;
global gAIConst : TAIConst;
global gKeyColor : array [0..gc_MaxKeyColorCount-1] of array [0..2] of Float;

global gWallCluster : TWallCluster;
global gWallClusterCreate : TWallCluster;
global gWallSystem : TWallSystem;

global gUpdateAreas : array [0..gc_top_MaxUpdateAreas-1] of TRect;

//global gAchs : TAchs;
global gAchLastUpgrades : TIntegerList;
global gSVNVersion : Integer;

global gScenario : TScenario;
global gScenarioTmp : TScenario;

global gQuickPlay : TQuickPlayers;
global gQuickPlayHideIcon : TIntegerList;

global gReconnectManager : TReconnectManager;

// Debug fonts start. remove after setting up fonts
procedure InitFonts();
begin
   var i, j : Integer;
   for i:=0 to 31 do
   begin
      gFonts[i].font := gUIConst.font[i];
      if (gFonts[i].font<>'') then
      for j:=0 to 255 do
      begin
         var width : Integer = GetGUIFontCharWidth(gFonts[i].font, j);
         var offx : Integer = GetGUIFontCharOffsetX(gFonts[i].font, j);
         gFonts[i].chars[j].width := width;
         gFonts[i].chars[j].offx := offx;
         gFonts[i].chars[j].addwidth := 0;
         gFonts[i].chars[j].addoffx := 0;
      end;
      gFonts[i].chars[0].text := #0;
      gFonts[i].chars[1].text := #1;
      gFonts[i].chars[2].text := #2;
      gFonts[i].chars[3].text := #3;
      gFonts[i].chars[4].text := #4;
      gFonts[i].chars[5].text := #5;
      gFonts[i].chars[6].text := #6;
      gFonts[i].chars[7].text := #7;
      gFonts[i].chars[8].text := #8;
      gFonts[i].chars[9].text := #9;
      gFonts[i].chars[10].text := #10;
      gFonts[i].chars[11].text := #11;
      gFonts[i].chars[12].text := #12;
      gFonts[i].chars[13].text := #13;
      gFonts[i].chars[14].text := #14;
      gFonts[i].chars[15].text := #15;
      gFonts[i].chars[16].text := #16;
      gFonts[i].chars[17].text := #17;
      gFonts[i].chars[18].text := #18;
      gFonts[i].chars[19].text := #19;
      gFonts[i].chars[20].text := #20;
      gFonts[i].chars[21].text := #21;
      gFonts[i].chars[22].text := #22;
      gFonts[i].chars[23].text := #23;
      gFonts[i].chars[24].text := #24;
      gFonts[i].chars[25].text := #25;
      gFonts[i].chars[26].text := #26;
      gFonts[i].chars[27].text := #27;
      gFonts[i].chars[28].text := #28;
      gFonts[i].chars[29].text := #29;
      gFonts[i].chars[30].text := #30;
      gFonts[i].chars[31].text := #31;
      gFonts[i].chars[32].text := #32;
      gFonts[i].chars[33].text := #33;
      gFonts[i].chars[34].text := #34;
      gFonts[i].chars[35].text := #35;
      gFonts[i].chars[36].text := #36;
      gFonts[i].chars[37].text := #37;
      gFonts[i].chars[38].text := #38;
      gFonts[i].chars[39].text := #39;
      gFonts[i].chars[40].text := #40;
      gFonts[i].chars[41].text := #41;
      gFonts[i].chars[42].text := #42;
      gFonts[i].chars[43].text := #43;
      gFonts[i].chars[44].text := #44;
      gFonts[i].chars[45].text := #45;
      gFonts[i].chars[46].text := #46;
      gFonts[i].chars[47].text := #47;
      gFonts[i].chars[48].text := #48;
      gFonts[i].chars[49].text := #49;
      gFonts[i].chars[50].text := #50;
      gFonts[i].chars[51].text := #51;
      gFonts[i].chars[52].text := #52;
      gFonts[i].chars[53].text := #53;
      gFonts[i].chars[54].text := #54;
      gFonts[i].chars[55].text := #55;
      gFonts[i].chars[56].text := #56;
      gFonts[i].chars[57].text := #57;
      gFonts[i].chars[58].text := #58;
      gFonts[i].chars[59].text := #59;
      gFonts[i].chars[60].text := #60;
      gFonts[i].chars[61].text := #61;
      gFonts[i].chars[62].text := #62;
      gFonts[i].chars[63].text := #63;
      gFonts[i].chars[64].text := #64;
      gFonts[i].chars[65].text := #65;
      gFonts[i].chars[66].text := #66;
      gFonts[i].chars[67].text := #67;
      gFonts[i].chars[68].text := #68;
      gFonts[i].chars[69].text := #69;
      gFonts[i].chars[70].text := #70;
      gFonts[i].chars[71].text := #71;
      gFonts[i].chars[72].text := #72;
      gFonts[i].chars[73].text := #73;
      gFonts[i].chars[74].text := #74;
      gFonts[i].chars[75].text := #75;
      gFonts[i].chars[76].text := #76;
      gFonts[i].chars[77].text := #77;
      gFonts[i].chars[78].text := #78;
      gFonts[i].chars[79].text := #79;
      gFonts[i].chars[80].text := #80;
      gFonts[i].chars[81].text := #81;
      gFonts[i].chars[82].text := #82;
      gFonts[i].chars[83].text := #83;
      gFonts[i].chars[84].text := #84;
      gFonts[i].chars[85].text := #85;
      gFonts[i].chars[86].text := #86;
      gFonts[i].chars[87].text := #87;
      gFonts[i].chars[88].text := #88;
      gFonts[i].chars[89].text := #89;
      gFonts[i].chars[90].text := #90;
      gFonts[i].chars[91].text := #91;
      gFonts[i].chars[92].text := #92;
      gFonts[i].chars[93].text := #93;
      gFonts[i].chars[94].text := #94;
      gFonts[i].chars[95].text := #95;
      gFonts[i].chars[96].text := #96;
      gFonts[i].chars[97].text := #97;
      gFonts[i].chars[98].text := #98;
      gFonts[i].chars[99].text := #99;
      gFonts[i].chars[100].text := #100;
      gFonts[i].chars[101].text := #101;
      gFonts[i].chars[102].text := #102;
      gFonts[i].chars[103].text := #103;
      gFonts[i].chars[104].text := #104;
      gFonts[i].chars[105].text := #105;
      gFonts[i].chars[106].text := #106;
      gFonts[i].chars[107].text := #107;
      gFonts[i].chars[108].text := #108;
      gFonts[i].chars[109].text := #109;
      gFonts[i].chars[110].text := #110;
      gFonts[i].chars[111].text := #111;
      gFonts[i].chars[112].text := #112;
      gFonts[i].chars[113].text := #113;
      gFonts[i].chars[114].text := #114;
      gFonts[i].chars[115].text := #115;
      gFonts[i].chars[116].text := #116;
      gFonts[i].chars[117].text := #117;
      gFonts[i].chars[118].text := #118;
      gFonts[i].chars[119].text := #119;
      gFonts[i].chars[120].text := #120;
      gFonts[i].chars[121].text := #121;
      gFonts[i].chars[122].text := #122;
      gFonts[i].chars[123].text := #123;
      gFonts[i].chars[124].text := #124;
      gFonts[i].chars[125].text := #125;
      gFonts[i].chars[126].text := #126;
      gFonts[i].chars[127].text := #127;
      gFonts[i].chars[128].text := #128;
      gFonts[i].chars[129].text := #129;
      gFonts[i].chars[130].text := #130;
      gFonts[i].chars[131].text := #131;
      gFonts[i].chars[132].text := #132;
      gFonts[i].chars[133].text := #133;
      gFonts[i].chars[134].text := #134;
      gFonts[i].chars[135].text := #135;
      gFonts[i].chars[136].text := #136;
      gFonts[i].chars[137].text := #137;
      gFonts[i].chars[138].text := #138;
      gFonts[i].chars[139].text := #139;
      gFonts[i].chars[140].text := #140;
      gFonts[i].chars[141].text := #141;
      gFonts[i].chars[142].text := #142;
      gFonts[i].chars[143].text := #143;
      gFonts[i].chars[144].text := #144;
      gFonts[i].chars[145].text := #145;
      gFonts[i].chars[146].text := #146;
      gFonts[i].chars[147].text := #147;
      gFonts[i].chars[148].text := #148;
      gFonts[i].chars[149].text := #149;
      gFonts[i].chars[150].text := #150;
      gFonts[i].chars[151].text := #151;
      gFonts[i].chars[152].text := #152;
      gFonts[i].chars[153].text := #153;
      gFonts[i].chars[154].text := #154;
      gFonts[i].chars[155].text := #155;
      gFonts[i].chars[156].text := #156;
      gFonts[i].chars[157].text := #157;
      gFonts[i].chars[158].text := #158;
      gFonts[i].chars[159].text := #159;
      gFonts[i].chars[160].text := #160;
      gFonts[i].chars[161].text := #161;
      gFonts[i].chars[162].text := #162;
      gFonts[i].chars[163].text := #163;
      gFonts[i].chars[164].text := #164;
      gFonts[i].chars[165].text := #165;
      gFonts[i].chars[166].text := #166;
      gFonts[i].chars[167].text := #167;
      gFonts[i].chars[168].text := #168;
      gFonts[i].chars[169].text := #169;
      gFonts[i].chars[170].text := #170;
      gFonts[i].chars[171].text := #171;
      gFonts[i].chars[172].text := #172;
      gFonts[i].chars[173].text := #173;
      gFonts[i].chars[174].text := #174;
      gFonts[i].chars[175].text := #175;
      gFonts[i].chars[176].text := #176;
      gFonts[i].chars[177].text := #177;
      gFonts[i].chars[178].text := #178;
      gFonts[i].chars[179].text := #179;
      gFonts[i].chars[180].text := #180;
      gFonts[i].chars[181].text := #181;
      gFonts[i].chars[182].text := #182;
      gFonts[i].chars[183].text := #183;
      gFonts[i].chars[184].text := #184;
      gFonts[i].chars[185].text := #185;
      gFonts[i].chars[186].text := #186;
      gFonts[i].chars[187].text := #187;
      gFonts[i].chars[188].text := #188;
      gFonts[i].chars[189].text := #189;
      gFonts[i].chars[190].text := #190;
      gFonts[i].chars[191].text := #191;
      gFonts[i].chars[192].text := #192;
      gFonts[i].chars[193].text := #193;
      gFonts[i].chars[194].text := #194;
      gFonts[i].chars[195].text := #195;
      gFonts[i].chars[196].text := #196;
      gFonts[i].chars[197].text := #197;
      gFonts[i].chars[198].text := #198;
      gFonts[i].chars[199].text := #199;
      gFonts[i].chars[200].text := #200;
      gFonts[i].chars[201].text := #201;
      gFonts[i].chars[202].text := #202;
      gFonts[i].chars[203].text := #203;
      gFonts[i].chars[204].text := #204;
      gFonts[i].chars[205].text := #205;
      gFonts[i].chars[206].text := #206;
      gFonts[i].chars[207].text := #207;
      gFonts[i].chars[208].text := #208;
      gFonts[i].chars[209].text := #209;
      gFonts[i].chars[210].text := #210;
      gFonts[i].chars[211].text := #211;
      gFonts[i].chars[212].text := #212;
      gFonts[i].chars[213].text := #213;
      gFonts[i].chars[214].text := #214;
      gFonts[i].chars[215].text := #215;
      gFonts[i].chars[216].text := #216;
      gFonts[i].chars[217].text := #217;
      gFonts[i].chars[218].text := #218;
      gFonts[i].chars[219].text := #219;
      gFonts[i].chars[220].text := #220;
      gFonts[i].chars[221].text := #221;
      gFonts[i].chars[222].text := #222;
      gFonts[i].chars[223].text := #223;
      gFonts[i].chars[224].text := #224;
      gFonts[i].chars[225].text := #225;
      gFonts[i].chars[226].text := #226;
      gFonts[i].chars[227].text := #227;
      gFonts[i].chars[228].text := #228;
      gFonts[i].chars[229].text := #229;
      gFonts[i].chars[230].text := #230;
      gFonts[i].chars[231].text := #231;
      gFonts[i].chars[232].text := #232;
      gFonts[i].chars[233].text := #233;
      gFonts[i].chars[234].text := #234;
      gFonts[i].chars[235].text := #235;
      gFonts[i].chars[236].text := #236;
      gFonts[i].chars[237].text := #237;
      gFonts[i].chars[238].text := #238;
      gFonts[i].chars[239].text := #239;
      gFonts[i].chars[240].text := #240;
      gFonts[i].chars[241].text := #241;
      gFonts[i].chars[242].text := #242;
      gFonts[i].chars[243].text := #243;
      gFonts[i].chars[244].text := #244;
      gFonts[i].chars[245].text := #245;
      gFonts[i].chars[246].text := #246;
      gFonts[i].chars[247].text := #247;
      gFonts[i].chars[248].text := #248;
      gFonts[i].chars[249].text := #249;
      gFonts[i].chars[250].text := #250;
      gFonts[i].chars[251].text := #251;
      gFonts[i].chars[252].text := #252;
      gFonts[i].chars[253].text := #253;
      gFonts[i].chars[254].text := #254;
      gFonts[i].chars[255].text := #255;
   end;
end;
// Debug fonts finish. remove after setting up fonts

// run after all registers. Do not move procedure upper.
procedure _classes_RunConstructors();
begin
   procedure GetKeyColorByPlayerIndex(const plInd : Integer; var r, g, b : Float);
   begin
      var koef : Float = 1.1;
      var ir, ig, ib : Integer;
      case plInd of
         0 : begin ir := 164; ig := 0; ib := 0; koef := 1.15; end;
         1 : begin ir := 15; ig := 60+25; ib := 192; koef := 1.2; end;
         2 : begin ir := 44; ig := 180; ib := 148; koef := 1.15; end;
         //3 : begin ir := 255; ig := 90; ib := 0; koef := 1.2; end;
         //4 : begin ir := 230; ig := 142; ib := 0; end;
         3 : begin ir := 127; ig := 50; ib := 190; end;
         4 : begin ir := 255; ig := 90; ib := 0; koef := 1.2; end;
         //5 : begin ir := 50; ig := 50; ib := 68; koef := 1.2; end; // black
         //5 : begin ir := 70; ig := 180; ib := 35; koef := 1.1; end; // black to green
         5 : begin ir := 0; ig := 164; ib := 0; koef := 1; end; // black to green
         6 : begin ir := 140; ig := 228; ib := 255; koef := 1.2; end;
         7 : begin ir := 255; ig := 80; ib := 140; koef := 1.0; end;
         8 : begin ir := 255; ig := 190; ib := 0; koef := 1.0; end;
         9 : begin ir := 0; ig := 128; ib := 140; koef := 1.0; end;
         10 : begin ir := 140; ig := 190; ib := 80; koef := 1.0; end;
         11 : begin ir := 110; ig := 90; ib := 35; koef := 1.1; end;
         12 : begin ir := 101; ig := 52; ib := 0; koef := 1.1; end;
         13 : begin ir := 50; ig := 50; ib := 68; koef := 1.2; end; // black
         else
         begin ir := 255; ig := 255; ib := 255; end;
      end;
      r := ir*koef/255;
      g := ig*koef/255;
      b := ib*koef/255;
   end;
   
   //procedure GetKeyColorByPlayerIndex(const plInd : Integer; var r, g, b : Float);
   //begin
      //   const cKoef = 1.3;
      //   var ir, ig, ib : Integer;
      //   case plInd of
         //      0 : begin ir := 164; ig := 0; ib := 0; end;
         //      1 : begin ir := 0; ig := 60; ib := 192; end;
         //      2 : begin ir := 44; ig := 180; ib := 148; end;
         //      3 : begin ir := 147; ig := 59; ib := 174; end;
         //      4 : begin ir := 240; ig := 132; ib := 20; end;
         //      5 : begin ir := 50; ig := 50; ib := 68; end;
         //      6 : begin ir := 200; ig := 200; ib := 200; end;
         //      7 : begin ir := 101; ig := 52; ib := 0; end;
         //      else
         //      begin ir := 255; ig := 255; ib := 255; end;
      //   end;
      //   r := ir*cKoef/255;
      //   g := ig*cKoef/255;
      //   b := ib*cKoef/255;
   //end;
   
   var i, j, k : Integer;
   for i:=0 to gc_scangrid_countx-1 do
   for j:=0 to gc_scangrid_county-1 do begin
      for k:=0 to gc_MaxPlayerCount-1 do
      gScanGridUnits[k,i,j].Create(SizeOf(Integer));
      gScanGrid[i,j].Create(SizeOf(Integer));
      gResGrid[i,j].Create(SizeOf(Integer));
   end;
   gIntegerList.Create(SizeOf(Integer));
   gUnderCursorList.Create(SizeOf(Integer));
   gDoRoundDamageList.Create(SizeOf(Integer));
   gDestructListBuilding.Create(SizeOf(Integer));
   gDestructListShip.Create(SizeOf(Integer));
   gPrimitives.Create(SizeOf(Integer));
   gTmpIntList.Create(SizeOf(Integer));
   gTmpIntListFindUnits.Create(SizeOf(Integer));
   gTmpIntListFillSquad.Create(SizeOf(Integer));
   gTmpIntListUpgrades.Create(SizeOf(Integer));
   gTmpIntListMembers.Create(SizeOf(Integer));
   gTmpIntListGetBuildPoints.Create(SizeOf(Integer));
   gTmpIntListObjectsInRadius.Create(SizeOf(Integer));
   gTmpIntListOnSelection.Create(SizeOf(Integer));
   gTmpIntListGUIInsideList.Create(SizeOf(Integer));
   gTmpIntListGUIDrummerList.Create(SizeOf(Integer));
   gTmpIntListGUISquadList.Create(SizeOf(Integer));
   gLanSyncUnitsParamsUIDList.Create(SizeOf(Integer));
   gShellSortOnlineMarked.Create(SizeOf(Integer));
   gShellSortOnline.Create(SizeOf(Integer));
   gShellSortPlayMarked.Create(SizeOf(Integer));
   gShellSortPlay.Create(SizeOf(Integer));
   gRepairSessionList.Create(SizeOf(Integer));
   gSyncRes.Create(SizeOf(Integer));
   gEmptyList.Create(SizeOf(Integer));
   gDeathUnits.Create(SizeOf(Integer));
   gIntListWriteSquadAction.Create(SizeOf(Integer));
   gOrderUnitsList.Create(SizeOf(Integer));
   gOrderUnitsListTmp.Create(SizeOf(Integer));
   gGOPathList.Create(SizeOf(Integer));
   gWaterPathList.Create(SizeOf(Integer));
   gGuiUpdateHighlights.Create(SizeOf(Integer));
   gGuiSelectionHelper.Create(SizeOf(Integer));
   gSelectedObjects.Create(SizeOf(Integer));
   gRegionsList.Create(SizeOf(Integer));
   gAchLastUpgrades.Create(SizeOf(Integer));
   gQuickPlayHideIcon.Create(SizeOf(Integer));
   for i:=0 to gc_MaxPlayerCount-1 do
   begin
      gPlayer[i].Create;
      for j:=0 to 9 do
      gCtrlGroups[i, j].Create(SizeOf(Integer));
   end;
   for i:=0 to gc_MaxKeyColorCount-1 do
   GetKeyColorByPlayerIndex(i, gKeyColor[i][0], gKeyColor[i][1], gKeyColor[i][2]);
   for i:=0 to gc_MaxCountryCount-1 do
   for j:=0 to gc_country_maxmembers-1 do
   gObjProp[i][j].Create;
   gEnemyInfoList.Create;
   for i:=0 to gc_weapon_maxmembers-1 do
   gWeapons[i].Create;
   gUIConst.Init;
   gConst.Init;
   gAIConst.Init;
   
   gWallCluster.Create;
   gWallSystem.Create;
   gWallClusterCreate.Create;
   
   gInternetShell.clients.Create;
   gInternetShell.sessions.Create;
   gInternetShell.rankings.Create;
   
   gSelectedSquads.Create;
   gPtrList.Create;
   gWriteSquadPtrList.Create;
   gWriteNewRequestList.Create;
   
   //gAchs.Create;
   
   // debug fonts start. remove after finish font fix
   InitFonts;
end;

// run after all registers. Do not move procedure upper.
_classes_RunConstructors();