From 65a6489ef1cb1cd30c5bba2c7f2b9e1cd8ca643b Mon Sep 17 00:00:00 2001 From: reaperrr Date: Tue, 21 Jul 2015 07:30:14 +0200 Subject: [PATCH] Avoid consecutive attempts to build naval structures If not enough water space can be found inside the base perimeter, stop the AI from trying to build naval production buildings permanently until it deploys another construction yard. If enough water is available within the base perimeter, check whether any building that provides buildable area (for adjacency) is close enough to water, otherwise don't even start producing this naval structure. --- OpenRA.Mods.Common/AI/BaseBuilder.cs | 50 +++++++++++++++++++++++- OpenRA.Mods.Common/AI/HackyAI.cs | 57 ++++++++++++++++++++++++++++ mods/ra/rules/ai.yaml | 12 ++++-- 3 files changed, 114 insertions(+), 5 deletions(-) diff --git a/OpenRA.Mods.Common/AI/BaseBuilder.cs b/OpenRA.Mods.Common/AI/BaseBuilder.cs index 8862c59de3..f17d788b79 100644 --- a/OpenRA.Mods.Common/AI/BaseBuilder.cs +++ b/OpenRA.Mods.Common/AI/BaseBuilder.cs @@ -28,8 +28,11 @@ namespace OpenRA.Mods.Common.AI int waitTicks; Actor[] playerBuildings; + bool waterAvailable; + bool checkedWater; int failCount; int failRetryTicks; + int cachedBases; public BaseBuilder(HackyAI ai, string category, Player p, PowerManager pm, PlayerResources pr) { @@ -52,6 +55,25 @@ namespace OpenRA.Mods.Common.AI if (--waitTicks > 0) return; + if (!checkedWater) + { + waterAvailable = ai.EnoughWaterToBuildNaval(); + checkedWater = true; + } + + if (!waterAvailable) + { + var currentBases = world.ActorsWithTrait() + .Where(a => a.Actor.Owner == player) + .Count(); + + if (currentBases > cachedBases) + { + cachedBases = currentBases; + checkedWater = false; + } + } + playerBuildings = world.ActorsWithTrait() .Where(a => a.Actor.Owner == player) .Select(a => a.Actor) @@ -181,7 +203,7 @@ namespace OpenRA.Mods.Common.AI } } - // Make sure that we can can spend as fast as we are earning + // Make sure that we can spend as fast as we are earning if (ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold) { var production = GetProducibleBuilding("Production", buildableThings); @@ -198,6 +220,24 @@ 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) + { + var navalproduction = GetProducibleBuilding("NavalProduction", buildableThings); + if (navalproduction != null && HasSufficientPowerForActor(navalproduction)) + { + HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (navalproduction)", queue.Actor.Owner, navalproduction.Name); + return navalproduction; + } + + if (power != null && navalproduction != null && !HasSufficientPowerForActor(navalproduction)) + { + HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name); + return power; + } + } + // Create some head room for resource storage if we really need it if (playerResources.AlertSilo) { @@ -232,6 +272,14 @@ namespace OpenRA.Mods.Common.AI if (ai.Info.BuildingLimits.ContainsKey(name) && ai.Info.BuildingLimits[name] <= count) continue; + // If we're considering to build a naval structure, check whether there is enough water inside the base perimeter + // and any structure providing buildable area close enough to that water. + // 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())) + continue; + // Will this put us into low power? var actor = world.Map.Rules.Actors[name]; if (playerPower.ExcessPower < ai.Info.MinimumExcessPower || !HasSufficientPowerForActor(actor)) diff --git a/OpenRA.Mods.Common/AI/HackyAI.cs b/OpenRA.Mods.Common/AI/HackyAI.cs index 15225e8a1c..191db5831c 100644 --- a/OpenRA.Mods.Common/AI/HackyAI.cs +++ b/OpenRA.Mods.Common/AI/HackyAI.cs @@ -92,6 +92,11 @@ namespace OpenRA.Mods.Common.AI [Desc("Radius in cells around the center of the base to expand.")] public readonly int MaxBaseRadius = 20; + [Desc("Radius in cells around each building with ProvideBuildableArea", + "to check for a 3x3 area of water where naval structures can be built.", + "Should match maximum adjacency of naval structures.")] + public readonly int CheckForWaterRadius = 8; + [Desc("Production queues AI uses for producing units.")] public readonly string[] UnitQueues = { "Vehicle", "Infantry", "Plane", "Ship", "Aircraft" }; @@ -257,6 +262,58 @@ namespace OpenRA.Mods.Common.AI resourceTypeIndices.Set(World.TileSet.GetTerrainIndex(t.TerrainType), true); } + // TODO: Possibly give this a more generic name when terrain type is unhardcoded + public bool EnoughWaterToBuildNaval() + { + var baseBuildings = World.Actors.Where( + a => a.Owner == Player + && a.HasTrait() + && !a.HasTrait()); + + foreach (var b in baseBuildings) + { + // 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" + && Util.AdjacentCells(playerWorld, Target.FromCell(playerWorld, c)) + .All(a => playerWorld.Map.GetTerrainInfo(a).Type == "Water")) + .Count(); + + if (countWaterCells > 0) + return true; + } + + return false; + } + + // Check whether we have at least one building providing buildable area close enough to water to build naval structures + public bool CloseEnoughToWater() + { + var areaProviders = World.Actors.Where( + a => a.Owner == Player + && a.HasTrait() + && !a.HasTrait()); + + foreach (var a in areaProviders) + { + // TODO: Unhardcode terrain type + var playerWorld = Player.World; + var adjacentWater = Map.FindTilesInCircle(a.Location, Info.CheckForWaterRadius) + .Where(c => playerWorld.Map.Contains(c) + && playerWorld.Map.GetTerrainInfo(c).Type == "Water" + && Util.AdjacentCells(playerWorld, Target.FromCell(playerWorld, c)) + .All(b => playerWorld.Map.GetTerrainInfo(b).Type == "Water")) + .Count(); + + if (adjacentWater > 0) + return true; + } + + return false; + } + public void QueueOrder(Order order) { orders.Enqueue(order); diff --git a/mods/ra/rules/ai.yaml b/mods/ra/rules/ai.yaml index de49a66fd2..ffccd407cf 100644 --- a/mods/ra/rules/ai.yaml +++ b/mods/ra/rules/ai.yaml @@ -8,7 +8,8 @@ Player: Power: powr,apwr Barracks: barr,tent VehiclesFactory: weap - Production: barr,tent,weap,afld,hpad,spen,syrd + Production: barr,tent,weap,afld,hpad + NavalProduction: spen,syrd Silo: silo UnitsCommonNames: Mcv: mcv @@ -118,7 +119,8 @@ Player: Power: powr,apwr Barracks: barr,tent VehiclesFactory: weap - Production: barr,tent,weap,afld,hpad,spen,syrd + Production: barr,tent,weap,afld,hpad + NavalProduction: spen,syrd Silo: silo UnitsCommonNames: Mcv: mcv @@ -245,7 +247,8 @@ Player: Power: powr,apwr Barracks: barr,tent VehiclesFactory: weap - Production: barr,tent,weap,afld,hpad,spen,syrd + Production: barr,tent,weap,afld,hpad + NavalProduction: spen,syrd Silo: silo UnitsCommonNames: Mcv: mcv @@ -371,7 +374,8 @@ Player: Power: powr,apwr Barracks: barr,tent VehiclesFactory: weap - Production: barr,tent,weap,afld,hpad,spen,syrd + Production: barr,tent,weap,afld,hpad + NavalProduction: spen,syrd Silo: silo UnitsCommonNames: Mcv: mcv