diff --git a/OpenRA.Mods.Common/AI/BaseBuilder.cs b/OpenRA.Mods.Common/AI/BaseBuilder.cs index 7b3d7e63bf..acda9f2710 100644 --- a/OpenRA.Mods.Common/AI/BaseBuilder.cs +++ b/OpenRA.Mods.Common/AI/BaseBuilder.cs @@ -28,13 +28,21 @@ namespace OpenRA.Mods.Common.AI int waitTicks; Actor[] playerBuildings; - bool waterAvailable; - bool checkedWater; int failCount; int failRetryTicks; + int checkForBasesTicks; int cachedBases; int cachedBuildings; + enum Water + { + NotChecked, + EnoughWater, + NotEnoughWater + } + + Water waterState = Water.NotChecked; + public BaseBuilder(HackyAI ai, string category, Player p, PowerManager pm, PlayerResources pr) { this.ai = ai; @@ -55,7 +63,7 @@ namespace OpenRA.Mods.Common.AI .Where(a => a.Actor.Owner == player) .Count(); - var baseProviders = world.ActorsWithTrait() + var baseProviders = world.ActorsWithTrait() .Where(a => a.Actor.Owner == player) .Count(); @@ -68,29 +76,34 @@ namespace OpenRA.Mods.Common.AI failRetryTicks = ai.Info.StructureProductionResumeDelay; } - // Only update once per second or so - if (--waitTicks > 0) - return; - - if (!checkedWater) + if (waterState == Water.NotChecked) { - waterAvailable = ai.EnoughWaterToBuildNaval(); - checkedWater = true; + if (ai.EnoughWaterToBuildNaval()) + waterState = Water.EnoughWater; + else + { + waterState = Water.NotEnoughWater; + checkForBasesTicks = ai.Info.CheckForNewBasesDelay; + } } - if (!waterAvailable) + if (waterState == Water.NotEnoughWater && --checkForBasesTicks <= 0) { - var currentBases = world.ActorsWithTrait() + var currentBases = world.ActorsWithTrait() .Where(a => a.Actor.Owner == player) .Count(); if (currentBases > cachedBases) { cachedBases = currentBases; - checkedWater = false; + waterState = Water.NotChecked; } } + // Only update once per second or so + if (--waitTicks > 0) + return; + playerBuildings = world.ActorsWithTrait() .Where(a => a.Actor.Owner == player) .Select(a => a.Actor) @@ -148,7 +161,7 @@ namespace OpenRA.Mods.Common.AI .Where(a => a.Actor.Owner == player) .Count(); - cachedBases = world.ActorsWithTrait() + cachedBases = world.ActorsWithTrait() .Where(a => a.Actor.Owner == player) .Count(); } @@ -254,8 +267,9 @@ namespace OpenRA.Mods.Common.AI } // Only consider building this if there is enough water inside the base perimeter and there are close enough adjacent buildings - if (waterAvailable && ai.CloseEnoughToWater() - && ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold) + if (waterState == Water.EnoughWater && ai.Info.NewProductionCashThreshold > 0 + && playerResources.Resources > ai.Info.NewProductionCashThreshold + && ai.CloseEnoughToWater()) { var navalproduction = GetProducibleBuilding("NavalProduction", buildableThings); if (navalproduction != null && HasSufficientPowerForActor(navalproduction)) @@ -310,7 +324,7 @@ namespace OpenRA.Mods.Common.AI // TODO: Extend this check to cover any naval structure, not just production. if (ai.Info.BuildingCommonNames.ContainsKey("NavalProduction") && ai.Info.BuildingCommonNames["NavalProduction"].Contains(name) - && (!waterAvailable || !ai.CloseEnoughToWater())) + && (waterState == Water.NotEnoughWater || !ai.CloseEnoughToWater())) continue; // Will this put us into low power? diff --git a/OpenRA.Mods.Common/AI/HackyAI.cs b/OpenRA.Mods.Common/AI/HackyAI.cs index af5647f5ad..39f31480b8 100644 --- a/OpenRA.Mods.Common/AI/HackyAI.cs +++ b/OpenRA.Mods.Common/AI/HackyAI.cs @@ -55,22 +55,27 @@ namespace OpenRA.Mods.Common.AI [Desc("Minimum excess power the AI should try to maintain.")] public readonly int MinimumExcessPower = 0; - [Desc("Minimum delay (in ticks) between structure production checks when there is no active production.")] + [Desc("Delay (in ticks) between structure production checks when there is no active production.", + "A StructureProductionRandomBonusDelay is added to this.")] public readonly int StructureProductionInactiveDelay = 125; - [Desc("Minimum delay (in ticks) between structure production checks when actively building things.")] + [Desc("Delay (in ticks) between structure production checks when actively building things.", + "A StructureProductionRandomBonusDelay is added to this.")] public readonly int StructureProductionActiveDelay = 10; - [Desc("A random delay (in ticks) of up to this is added to production delays.")] + [Desc("A random delay (in ticks) of up to this is added to active/inactive production delays.")] public readonly int StructureProductionRandomBonusDelay = 10; - [Desc("How long to wait (in ticks) until retrying to build structure after the last 3 consecutive attempts failed.")] + [Desc("Delay (in ticks) until retrying to build structure after the last 3 consecutive attempts failed.")] public readonly int StructureProductionResumeDelay = 1500; [Desc("After how many failed attempts to place a structure should AI give up and wait", "for StructureProductionResumeDelay before retrying.")] public readonly int MaximumFailedPlacementAttempts = 3; + [Desc("Delay (in ticks) until rechecking for new BaseProviders.")] + public readonly int CheckForNewBasesDelay = 1500; + [Desc("Minimum range at which to build defensive structures near a combat hotspot.")] public readonly int MinimumDefenseRadius = 5; @@ -268,20 +273,20 @@ namespace OpenRA.Mods.Common.AI // TODO: Possibly give this a more generic name when terrain type is unhardcoded public bool EnoughWaterToBuildNaval() { - var baseBuildings = World.Actors.Where( + var baseProviders = World.Actors.Where( a => a.Owner == Player - && a.HasTrait() + && a.HasTrait() && !a.HasTrait()); - foreach (var b in baseBuildings) + foreach (var b in baseProviders) { // TODO: Unhardcode terrain type var playerWorld = Player.World; var countWaterCells = Map.FindTilesInCircle(b.Location, Info.MaxBaseRadius) .Where(c => playerWorld.Map.Contains(c) - && playerWorld.Map.GetTerrainInfo(c).Type == "Water" + && playerWorld.Map.GetTerrainInfo(c).IsWater && Util.AdjacentCells(playerWorld, Target.FromCell(playerWorld, c)) - .All(a => playerWorld.Map.GetTerrainInfo(a).Type == "Water")) + .All(a => playerWorld.Map.GetTerrainInfo(a).IsWater)) .Count(); if (countWaterCells > 0) @@ -305,9 +310,9 @@ namespace OpenRA.Mods.Common.AI var playerWorld = Player.World; var adjacentWater = Map.FindTilesInCircle(a.Location, Info.CheckForWaterRadius) .Where(c => playerWorld.Map.Contains(c) - && playerWorld.Map.GetTerrainInfo(c).Type == "Water" + && playerWorld.Map.GetTerrainInfo(c).IsWater && Util.AdjacentCells(playerWorld, Target.FromCell(playerWorld, c)) - .All(b => playerWorld.Map.GetTerrainInfo(b).Type == "Water")) + .All(b => playerWorld.Map.GetTerrainInfo(b).IsWater)) .Count(); if (adjacentWater > 0)