From 0df8b3ba39178dd7be6f12cac6de007357cef7a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Fri, 4 Sep 2015 14:30:12 +0200 Subject: [PATCH] bring back AI search for new resource patches avoiding enemies --- OpenRA.Mods.Common/AI/HackyAI.cs | 39 ++++++++++++++++++- .../Activities/FindResources.cs | 25 +----------- OpenRA.Mods.Common/ActorExts.cs | 21 ++++++++++ 3 files changed, 61 insertions(+), 24 deletions(-) diff --git a/OpenRA.Mods.Common/AI/HackyAI.cs b/OpenRA.Mods.Common/AI/HackyAI.cs index 7004ef9794..2709ed5c03 100644 --- a/OpenRA.Mods.Common/AI/HackyAI.cs +++ b/OpenRA.Mods.Common/AI/HackyAI.cs @@ -13,6 +13,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using OpenRA.Mods.Common.Activities; +using OpenRA.Mods.Common.Pathfinder; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; using OpenRA.Support; @@ -105,6 +106,9 @@ namespace OpenRA.Mods.Common.AI "Should match maximum adjacency of naval structures.")] public readonly int CheckForWaterRadius = 8; + [Desc("Avoid enemy actors nearby when searching for a new resource patch. Should be somewhere near the max weapon range.")] + public readonly WDist HarvesterEnemyAvoidanceRadius = WDist.FromCells(8); + [Desc("Production queues AI uses for producing units.")] public readonly string[] UnitQueues = { "Vehicle", "Infantry", "Plane", "Ship", "Aircraft" }; @@ -171,6 +175,11 @@ namespace OpenRA.Mods.Common.AI public Player Player { get; private set; } + readonly DomainIndex domainIndex; + readonly ResourceLayer resLayer; + readonly ResourceClaimLayer territory; + readonly IPathFinder pathfinder; + readonly Func isEnemyUnit; Dictionary waitingPowers = new Dictionary(); Dictionary powerDecisions = new Dictionary(); @@ -212,6 +221,12 @@ namespace OpenRA.Mods.Common.AI { Info = info; World = init.World; + + domainIndex = World.WorldActor.Trait(); + resLayer = World.WorldActor.Trait(); + territory = World.WorldActor.TraitOrDefault(); + pathfinder = World.WorldActor.Trait(); + isEnemyUnit = unit => Player.Stances[unit.Owner] == Stance.Enemy && !unit.HasTrait() && unit.HasTrait(); @@ -634,6 +649,26 @@ namespace OpenRA.Mods.Common.AI FindAndDeployBackupMcv(self); } + CPos FindNextResource(Actor self) + { + var harvInfo = self.Info.Traits.Get(); + var mobileInfo = self.Info.Traits.Get(); + var passable = (uint)mobileInfo.GetMovementClass(World.TileSet); + + var path = pathfinder.FindPath( + PathSearch.Search(World, mobileInfo, self, true, + loc => domainIndex.IsPassable(self.Location, loc, passable) && self.CanHarvestAt(loc, resLayer, harvInfo, territory)) + .WithCustomCost(loc => World.FindActorsInCircle(World.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius) + .Where(u => !u.IsDead && self.Owner.Stances[u.Owner] == Stance.Enemy) + .Sum(u => Math.Max(WDist.Zero.Length, Info.HarvesterEnemyAvoidanceRadius.Length - (World.Map.CenterOfCell(loc) - u.CenterPosition).Length))) + .FromPoint(self.Location)); + + if (path.Count == 0) + return CPos.Zero; + + return path[0]; + } + void GiveOrdersToIdleHarvesters() { // Find idle harvesters and give them orders: @@ -657,7 +692,9 @@ namespace OpenRA.Mods.Common.AI continue; // Tell the idle harvester to quit slacking: - QueueOrder(new Order("Harvest", a, false)); + var newSafeResourcePatch = FindNextResource(a); + BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(a, newSafeResourcePatch)); + QueueOrder(new Order("Harvest", a, false) { TargetLocation = newSafeResourcePatch }); } } diff --git a/OpenRA.Mods.Common/Activities/FindResources.cs b/OpenRA.Mods.Common/Activities/FindResources.cs index 55fce8ca29..fc35ca2d95 100644 --- a/OpenRA.Mods.Common/Activities/FindResources.cs +++ b/OpenRA.Mods.Common/Activities/FindResources.cs @@ -109,34 +109,13 @@ namespace OpenRA.Mods.Common.Activities } } - bool IsHarvestable(Actor self, CPos pos) - { - var resType = resLayer.GetResource(pos); - if (resType == null) - return false; - - // Can the harvester collect this kind of resource? - if (!harvInfo.Resources.Contains(resType.Info.Name)) - return false; - - if (territory != null) - { - // Another harvester has claimed this resource: - ResourceClaim claim; - if (territory.IsClaimedByAnyoneElse(self as Actor, pos, out claim)) - return false; - } - - return true; - } - /// /// Finds the closest harvestable pos between the current position of the harvester /// and the last order location /// CPos? ClosestHarvestablePos(Actor self) { - if (IsHarvestable(self, self.Location)) + if (self.CanHarvestAt(self.Location, resLayer, harvInfo, territory)) return self.Location; // Determine where to search from and how far to search: @@ -146,7 +125,7 @@ namespace OpenRA.Mods.Common.Activities var passable = (uint)mobileInfo.GetMovementClass(self.World.TileSet); var search = PathSearch.Search(self.World, mobileInfo, self, true, - loc => domainIndex.IsPassable(self.Location, loc, passable) && IsHarvestable(self, loc)) + loc => domainIndex.IsPassable(self.Location, loc, passable) && self.CanHarvestAt(loc, resLayer, harvInfo, territory)) .WithCustomCost(loc => { if ((avoidCell.HasValue && loc == avoidCell.Value) || diff --git a/OpenRA.Mods.Common/ActorExts.cs b/OpenRA.Mods.Common/ActorExts.cs index 1667ac176a..2e95bc5f5f 100644 --- a/OpenRA.Mods.Common/ActorExts.cs +++ b/OpenRA.Mods.Common/ActorExts.cs @@ -117,5 +117,26 @@ namespace OpenRA.Mods.Common { NotifyBlocker(self, positions.SelectMany(p => self.World.ActorMap.GetUnitsAt(p))); } + + public static bool CanHarvestAt(this Actor self, CPos pos, ResourceLayer resLayer, HarvesterInfo harvInfo, ResourceClaimLayer territory) + { + var resType = resLayer.GetResource(pos); + if (resType == null) + return false; + + // Can the harvester collect this kind of resource? + if (!harvInfo.Resources.Contains(resType.Info.Name)) + return false; + + if (territory != null) + { + // Another harvester has claimed this resource: + ResourceClaim claim; + if (territory.IsClaimedByAnyoneElse(self as Actor, pos, out claim)) + return false; + } + + return true; + } } }