anchor.diff:  {$I KaM_Remake.inc}
   interface
   uses
  -  KM_CommonClasses, KM_Defaults, KM_Points;
  +  KM_CommonClasses, KM_CommonTypes, KM_Defaults, KM_Points,
  +  KM_Units, KM_UnitGroups,
  +  KM_NavMesh, KM_NavMeshInfluences;
   
   
   type
  +
     //Collection of influence maps
     TKMInfluences = class
     private
  -    fMapX: Word;
  -    fMapY: Word;
  -    fUpdatePlayerId: TKMHandIndex; //Player we will be updating next
  -    //This is cache for CanOwn in gTerrain.Land
  -    Ownable: array of array of Boolean;
  -
  -    //Stored in 1D arrays for optimisation, accessed through property
  -    fInfluence: array of Byte; //Each players area of influence
  -    fOwnership: array of Byte;
  -
  -    function GetInfluence(aPlayer: Byte; Y,X: Word): Byte; inline;
  -    procedure SetInfluence(aPlayer: Byte; Y,X: Word; aInfluence: Byte); inline;
  -    function GetOwnership(aPlayer: Byte; Y,X: Word): Byte; inline;
  -    procedure SetOwnership(aPlayer: Byte; Y,X: Word; aOwnership: Byte); inline;
  -
  -    property Influence[aPlayer: Byte; Y,X: Word]: Byte read GetInfluence write SetInfluence;
  -
  -    procedure InitInfluenceAvoid;
  -    procedure InitInfluenceOwnable;
  -    procedure UpdateDirectInfluence(aIndex: TKMHandIndex);
  -    procedure UpdateOwnershipInfluence(aIndex: TKMHandIndex);
  +    fMapX, fMapY, fPolygons: Word; // Limits of arrays
  +
  +    fUpdateCityIdx, fUpdateArmyIdx: TKMHandIndex; // Update index
  +    fPresence: TKMWordArray; // Military presence
  +    fOwnership: TKMByteArray; // City mark the space around itself
  +
  +    fFloodFill: TKMInfluenceFloodFill;
  +    fInfluenceSearch: TNavMeshInfluenceSearch;
  +    fNavMesh: TKMNavMesh;
  +
  +    // Avoid building
  +    procedure InitAvoidBuilding();
  +    // Army presence
  +    function GetAllPresences(const aPL: TKMHandIndex; aIdx: Word): Word; inline;
  +    function GetPresence(const aPL: TKMHandIndex; const aIdx: Word; const aGT: TGroupType): Word; inline;
  +    procedure SetPresence(const aPL: TKMHandIndex; const aIdx: Word; const aGT: TGroupType; const aPresence: Word); inline;
  +    procedure SetIncPresence(const aPL: TKMHandIndex; aIdx: Word; const aGT: TGroupType; const aPresence: Word); inline;
  +    procedure UpdateMilitaryPresence(const aPL: TKMHandIndex);
  +    // City influence
  +    function GetOwnership(const aPL: TKMHandIndex; const aIdx: Word): Byte; inline;
  +    procedure SetOwnership(const aPL: TKMHandIndex; const aIdx: Word; const aOwnership: Byte); inline;
  +    function GetOwnershipFromPoint(const aPL: TKMHandIndex; aY, aX: Word): Byte; inline; // For property -> aY, aX are switched!
  +    procedure SetOwnershipFromPoint(const aPL: TKMHandIndex; aY, aX: Word; const aOwnership: Byte); inline; // For property -> aY, aX are switched!
  +    procedure UpdateOwnership(const aPL: TKMHandIndex);
  +    // Common
  +    procedure InitArrays();
     public
  -    //Common map of areas where building is undesired (around Store, Mines, Woodcutters)
  -    AvoidBuilding: array of array of Byte;
  -
  -    //Tiles best owner and his influence
  -    property Ownership[aPlayer:Byte; Y,X: Word]: Byte read GetOwnership write SetOwnership;
  -    //Tension: array of array of array of SmallInt;
  -
  -    procedure AddAvoidBuilding(X,Y: Word; aRad: Single; aValue: Byte = 255);
  -    procedure RemAvoidBuilding(aArea: TKMRect);
  -    function GetBestOwner(X, Y: Word): TKMHandIndex;
  -    function GetBestAllianceOwnership(aIndex: TKMHandIndex; X,Y: Word; aAllianceType: TAllianceType): Byte;
  -    function OtherOwnerships(aPlayer: Byte; X, Y: Word): Word;
  -    procedure Init;
  -    procedure ExportInfluenceMaps;
  -
  +    AvoidBuilding: TKMByte2Array; //Common map of areas where building is undesired (around Store, Mines, Woodcutters)
  +
  +    constructor Create(aNavMesh: TKMNavMesh);
  +    destructor Destroy(); override;
       procedure Save(SaveStream: TKMemoryStream);
       procedure Load(LoadStream: TKMemoryStream);
  +
  +    // Avoid building
  +    // Army presence
  +    property PresenceAllGroups[aPL: TKMHandIndex; aIdx: Word]: Word read GetAllPresences;
  +    property Presence[aPL: TKMHandIndex; aIdx: Word; aGT: TGroupType]: Word read GetPresence write SetPresence;
  +    property IncPresence[aPL: TKMHandIndex; aIdx: Word; aGT: TGroupType]: Word write SetIncPresence;
  +    // City influence
  +    property Ownership[aPL: TKMHandIndex; aY,aX: Word]: Byte read GetOwnershipFromPoint write SetOwnershipFromPoint; // To secure compatibility with old AI
  +    property OwnPoly[aPL: TKMHandIndex; aIdx: Word]: Byte read GetOwnership write SetOwnership;
  +    // Common
  +    property InfluenceSearch: TNavMeshInfluenceSearch read fInfluenceSearch write fInfluenceSearch;
  +
  +    // Avoid building
  +    procedure AddAvoidBuilding(aX,aY: Word; aRad: Single; aValue: Byte = 255);
  +    procedure RemAvoidBuilding(aArea: TKMRect);
  +    // Army presence
  +    // City influence
  +    function GetBestOwner(aX,aY: Word): TKMHandIndex; overload;
  +    function GetBestOwner(const aIdx: Word): TKMHandIndex; overload;
  +    function GetBestAllianceOwnership(const aPL: TKMHandIndex; const aIdx: Word; const aAllianceType: TAllianceType): Byte;
  +    function GetOtherOwnerships(const aPL: TKMHandIndex; const aX, aY: Word): Word;
  +
  +
  +
  +    procedure AfterMissionInit();
       procedure UpdateState(aTick: Cardinal);
       procedure Paint(aRect: TKMRect);
     end;
  
positive.code: //Render debug symbols
  procedure TKMInfluences.Paint(aRect: TKMRect);
  var
    I, K, J: Integer;
    Col: Cardinal;
  begin
    if not AI_GEN_INFLUENCE_MAPS then Exit;
  
    if OVERLAY_INFLUENCE then
      for I := aRect.Top to aRect.Bottom do
      for K := aRect.Left to aRect.Right do
      begin
        Col := $80000000;
        J := GetBestOwner(K,I);
        if J <> PLAYER_NONE then
          Col := (gHands[J].FlagColor and $FFFFFF) or (Influence[J,I,K] shl 24);
        gRenderAux.Quad(K, I, Col);
      end;
  
    if OVERLAY_OWNERSHIP then
      for I := aRect.Top to aRect.Bottom do
      for K := aRect.Left to aRect.Right do
      begin
        Col := $80000000;
        J := GetBestOwner(K,I);
        if J <> PLAYER_NONE then
          Col := (gHands[J].FlagColor and $FFFFFF)
                  or (Ownership[J,I,K] shl 24)
                  or ((Byte(InRange(Ownership[J,I,K], OWN_THRESHOLD, OWN_MARGIN)) * 255) shl 24);
        gRenderAux.Quad(K, I, Col);
      end;
  
    if OVERLAY_AVOID then
      for I := aRect.Top to aRect.Bottom do
      for K := aRect.Left to aRect.Right do
      begin
        Col := AvoidBuilding[I,K] * 65793 or $80000000;
        gRenderAux.Quad(K, I, Col);
      end;
  end;
  
  
  end.
  
negative.code:       end;
    end;
  var
    H: TKMHouse;
    I,X,Y: Integer;
  begin
    for Y := 0 to fMapY - 1 do
    for X := 0 to fMapX - 1 do
      AvoidBuilding[Y,X] := 0;
  
    //Avoid areas where Gold/Iron mines should be
    for Y := 3 to fMapY - 2 do
    for X := 2 to fMapX - 2 do
      if gTerrain.CanPlaceHouse(KMPoint(X,Y), ht_IronMine) then
        CheckAndMarkMine(X,Y, ht_IronMine)
      else if gTerrain.CanPlaceHouse(KMPoint(X,Y), ht_GoldMine) then
        CheckAndMarkMine(X,Y, ht_GoldMine);
  
    //Avoid Coal fields
    for Y := 1 to fMapY - 1 do
    for X := 1 to fMapX - 1 do
     AvoidBuilding[Y,X] := AvoidBuilding[Y,X] or (Byte(gTerrain.TileIsCoal(X, Y) > 1) * $FF);
  
    //Leave free space BELOW all players Stores
    for I := 0 to gHands.Count - 1 do
    begin
      H := gHands[I].FindHouse(ht_Store);
      if (H <> nil) then
      for Y := Max(H.Entrance.Y + 1, 1) to Min(H.Entrance.Y + 2, fMapY - 1) do
      for X := Max(H.Entrance.X - 1, 1) to Min(H.Entrance.X + 1, fMapX - 1) do
        AvoidBuilding[Y,X] := AvoidBuilding[Y,X] or $FF;
    end;
  end;
  
  
  
  
  function TKMInfluences.GetAllPresences(const aPL: TKMHandIndex; aIdx: Word): Word;
  var
    GT: TGroupType;
  begin
    Result := 0;
    aIdx := (aPL*fPolygons + aIdx) shl 2;
    for GT := Low(TGroupType) to High(TGroupType) do
      Result := Result + fPresence[aIdx + Byte(GT)];
  end;
  
  
  function TKMInfluences.GetPresence(const aPL: TKMHandIndex; const aIdx: Word; const aGT: TGroupType): Word;
  begin
    Result := fPresence[((aPL*fPolygons + aIdx) shl 2) + Byte(aGT)];
  end;
  
  
  procedure TKMInfluences.SetPresence(const aPL: TKMHandIndex; const aIdx: Word; const aGT: TGroupType; const aPresence: Word);
  begin
    fPresence[((aPL*fPolygons + aIdx) shl 2) + Byte(aGT)] := aPresence;
  end;
  
  
  procedure TKMInfluences.SetIncPresence(const aPL: TKMHandIndex; aIdx: Word; const aGT: TGroupType; const aPresence: Word);
  begin
    aIdx := ((aPL*fPolygons + aIdx) shl 2) + Byte(aGT);
    fPresence[aIdx] := fPresence[aIdx] + aPresence;
  end;
  
  
  procedure TKMInfluences.UpdateMilitaryPresence(const aPL: TKMHandIndex);
  const
    EACH_X_MEMBER_COEF = 10;
  var
    I, K, Cnt: Integer;
    GT: TGroupType;
    G: TKMUnitGroup;
    U: TKMUnit;
    PointArr: TKMWordArray;
  begin
    InitArrays();
  
    SetLength(PointArr,16);
    for I := 0 to fPolygons-1 do
      for GT in TGroupType do
        Presence[aPL,I,GT] := 0;
  
    for I := 0 to gHands[aPL].UnitGroups.Count-1 do
    begin
      G := gHands[aPL].UnitGroups.Groups[I];
      if (G = nil) OR G.IsDead then
        continue;
      K := 0;
      Cnt := 0;
      while (K < G.Count) do
      begin
        U := G.Members[K];
        if (U <> nil) AND not U.IsDeadOrDying then
        begin
          if (Length(PointArr) <= Cnt) then
            SetLength(PointArr, Cnt + 16);
          PointArr[Cnt] := gAIFields.NavMesh.FindClosestPolygon(U.GetPosition);
          Cnt := Cnt + 1;
        end;
        K := K + EACH_X_MEMBER_COEF; // Pick each X member (Huge groups cover large areas so be sure that influence will be accurate)
      end;
  
      if (Cnt > 0) then
        fFloodFill.MilitaryPresence(aPL, 80, 30, Cnt-1, G.GroupType, PointArr);
    end;
  end;
  
  
  
  
  
  
  function TKMInfluences.GetOwnership(const aPL: TKMHandIndex; const aIdx: Word): Byte;
  begin
    Result := fOwnership[aPL * fPolygons + aIdx];
  end;
  
  
  procedure TKMInfluences.SetOwnership(const aPL: TKMHandIndex; const aIdx: Word; const aOwnership: Byte);
  begin
    fOwnership[aPL * fPolygons + aIdx] := aOwnership;
  end;
  
  
  function TKMInfluences.GetOwnershipFromPoint(const aPL: TKMHandIndex; aY, aX: Word): Byte;
  var
    POM: Word;
  begin
    POM := fNavMesh.FindClosestPolygon(KMPoint(aX,aY));
    Result := GetOwnership(aPL, POM);
  end;
  
  
  procedure TKMInfluences.SetOwnershipFromPoint(const aPL: TKMHandIndex; aY, aX: Word; const aOwnership: Byte);
  begin
    aX := fNavMesh.FindClosestPolygon(KMPoint(aX,aY));
    SetOwnership(aPL, aX, aOwnership);
  end;
  
  
  function TKMInfluences.GetBestOwner(aX,aY: Word): TKMHandIndex;
  begin
    aX := fNavMesh.FindClosestPolygon(KMPoint(aX,aY));
    Result := GetBestOwner(aX);
  end;
  
  
  function TKMInfluences.GetBestOwner(const aIdx: Word): TKMHandIndex;
  var
    PL: TKMHandIndex;
    Best: Integer;
  begin
    Result := PLAYER_NONE;
    if not AI_GEN_INFLUENCE_MAPS OR (aIdx = High(Word)) then
      Exit;
  
    Best := 0;
    for PL := 0 to gHands.Count - 1 do
      if (OwnPoly[PL,aIdx] > Best) then
      begin
        Best := OwnPoly[PL,aIdx];
        Result := PL;
      end;
  end;
  
  
  function TKMInfluences.GetBestAllianceOwnership(const aPL: TKMHandIndex; const aIdx: Word; const aAllianceType: TAllianceType): Byte;
  var
    PL: TKMHandIndex;
  begin
    Result := 0;
    if not AI_GEN_INFLUENCE_MAPS OR (aIdx = High(Word)) then
      Exit;
  
    for PL := 0 to gHands.Count - 1 do
      if (aPL <> PL) AND (gHands[aPL].Alliances[PL] = aAllianceType) AND (OwnPoly[PL,aIdx] > Result) then
        Result := OwnPoly[PL,aIdx];
  end;
  
  
  function TKMInfluences.GetOtherOwnerships(const aPL: TKMHandIndex; const aX, aY: Word): Word;
  var
    PL: TKMHandIndex;
    Idx: Word;
  begin
    Result := 0;
    Idx := fNavMesh.FindClosestPolygon(KMPoint(aX,aY));
    if not AI_GEN_INFLUENCE_MAPS OR (Idx = High(Word)) then
      Exit;
  
    Result := 0;
    for PL := 0 to gHands.Count - 1 do
      if (PL <> aPL) then
        Result := Result + OwnPoly[PL,Idx];
  end;
  
  
  // Here is the main reason for reworking influences: only 1 flood fill for city per a update + ~20x less elements in array
  procedure TKMInfluences.UpdateOwnership(const aPL: TKMHandIndex);
  var
    I, Idx, Cnt: Integer;
    H: TKMHouse;
    IdxArray: TKMWordArray;
  begin
    InitArrays();
  
    //Clear array
    for Idx := 0 to fPolygons - 1 do
      OwnPoly[aPL, Idx] := 0;
  
    // Create array of polygon indexes
    SetLength(IdxArray, gHands[aPL].Houses.Count);
    Cnt := 0;
    for I := 0 to gHands[aPL].Houses.Count - 1 do
    begin
      H := gHands[aPL].Houses[I];
      if not H.IsDestroyed AND (H.HouseType <> ht_WatchTower) then  // Ignore watchtower?????????????????????????????????????????????
      begin
        Idx := fNavMesh.FindClosestPolygon( H.GetPosition );
        if (Idx <> High(Word)) then
        begin
          IdxArray[Cnt] := Idx;
          Cnt := Cnt + 1;
        end;
      end;
    end;
  
    if (Cnt > 0) then
      fFloodFill.HouseInfluence(aPL, INIT_HOUSE_INFLUENCE, MAX_INFLUENCE_DISTANCE, Cnt - 1, IdxArray);
  end;
  
  
  
  
  procedure TKMInfluences.InitArrays();
  var
    I: Integer;
  begin
    if (fPolygons <> Length(gAIFields.NavMesh.Polygons)) then
    begin
      fPolygons := Length(gAIFields.NavMesh.Polygons);
      SetLength(fPresence, gHands.Count * fPolygons * GROUPS);
      SetLength(fOwnership, gHands.Count * fPolygons);
      for I := 0 to Length(fPresence) - 1 do
        fPresence[I] := 0;
      for I := 0 to Length(fOwnership) - 1 do
        fOwnership[I] := 0;
    end;
  end;
  
  
  procedure TKMInfluences.AfterMissionInit();
  var
    PL: TKMHandIndex;
  begin
    fMapX := gTerrain.MapX;
    fMapY := gTerrain.MapY;
    SetLength(AvoidBuilding, fMapY, fMapX);
    InitAvoidBuilding();
    InitArrays();
  
    if AI_GEN_INFLUENCE_MAPS then
      for PL := 0 to gHands.Count - 1 do
        UpdateOwnership(PL);
  end;
  
  
  procedure TKMInfluences.UpdateState(aTick: Cardinal);
  begin
    // City:
    if aTick mod 150 = 15 then // Update every 15 sec 1 player
    begin
      fUpdateCityIdx := (fUpdateCityIdx + 1) mod gHands.Count;
      UpdateOwnership(fUpdateCityIdx);
    end;
    // Army:
    if (aTick mod 5 = 0) then // Update every 0.5 sec 1 player
    begin
      fUpdateArmyIdx := (fUpdateArmyIdx + 1) mod gHands.Count;
      UpdateMilitaryPresence(fUpdateArmyIdx);
    end;
  end;
  
  