diff --git a/OpenRA.Mods.Common/AI/AIHarvesterManager.cs b/OpenRA.Mods.Common/AI/AIHarvesterManager.cs new file mode 100644 index 0000000000..986ef65402 --- /dev/null +++ b/OpenRA.Mods.Common/AI/AIHarvesterManager.cs @@ -0,0 +1,98 @@ +#region Copyright & License Information +/* + * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenRA.Mods.Common.Activities; +using OpenRA.Mods.Common.Pathfinder; +using OpenRA.Mods.Common.Traits; +using OpenRA.Support; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.AI +{ + class AIHarvesterManager + { + readonly HackyAI ai; + readonly World world; + readonly IPathFinder pathfinder; + readonly DomainIndex domainIndex; + readonly ResourceLayer resLayer; + readonly ResourceClaimLayer claimLayer; + + public AIHarvesterManager(HackyAI ai, Player p) + { + this.ai = ai; + world = p.World; + pathfinder = world.WorldActor.Trait(); + domainIndex = world.WorldActor.Trait(); + resLayer = world.WorldActor.TraitOrDefault(); + claimLayer = world.WorldActor.TraitOrDefault(); + } + + CPos FindNextResource(Actor actor, Harvester harv) + { + var mobileInfo = actor.Info.TraitInfo(); + var passable = (uint)mobileInfo.GetMovementClass(world.Map.Rules.TileSet); + + Func isValidResource = cell => + domainIndex.IsPassable(actor.Location, cell, mobileInfo, passable) && + harv.CanHarvestCell(actor, cell) && + claimLayer.CanClaimCell(actor, cell); + + var path = pathfinder.FindPath( + PathSearch.Search(world, mobileInfo, actor, true, isValidResource) + .WithCustomCost(loc => world.FindActorsInCircle(world.Map.CenterOfCell(loc), ai.Info.HarvesterEnemyAvoidanceRadius) + .Where(u => !u.IsDead && actor.Owner.Stances[u.Owner] == Stance.Enemy) + .Sum(u => Math.Max(WDist.Zero.Length, ai.Info.HarvesterEnemyAvoidanceRadius.Length - (world.Map.CenterOfCell(loc) - u.CenterPosition).Length))) + .FromPoint(actor.Location)); + + if (path.Count == 0) + return CPos.Zero; + + return path[0]; + } + + public void Tick(List activeUnits) + { + if (resLayer == null || resLayer.IsResourceLayerEmpty) + return; + + // Find idle harvesters and give them orders: + foreach (var harvester in activeUnits) + { + var harv = harvester.TraitOrDefault(); + if (harv == null) + continue; + + if (!harv.IsEmpty) + continue; + + if (!harvester.IsIdle) + { + var act = harvester.CurrentActivity; + if (!harv.LastSearchFailed || act.NextActivity == null || act.NextActivity.GetType() != typeof(FindResources)) + continue; + } + + var para = harvester.TraitOrDefault(); + if (para != null && para.IsInAir) + continue; + + // Tell the idle harvester to quit slacking: + var newSafeResourcePatch = FindNextResource(harvester, harv); + HackyAI.BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(harvester, newSafeResourcePatch)); + ai.QueueOrder(new Order("Harvest", harvester, Target.FromCell(world, newSafeResourcePatch), false)); + } + } + } +} diff --git a/OpenRA.Mods.Common/AI/HackyAI.cs b/OpenRA.Mods.Common/AI/HackyAI.cs index 67d469db34..075852f13b 100644 --- a/OpenRA.Mods.Common/AI/HackyAI.cs +++ b/OpenRA.Mods.Common/AI/HackyAI.cs @@ -265,11 +265,6 @@ namespace OpenRA.Mods.Common.AI public List Squads = new List(); public Player Player { get; private set; } - readonly DomainIndex domainIndex; - readonly ResourceLayer resLayer; - readonly ResourceClaimLayer claimLayer; - readonly IPathFinder pathfinder; - readonly Func isEnemyUnit; readonly Predicate unitCannotBeOrdered; Dictionary waitingPowers = new Dictionary(); @@ -284,6 +279,8 @@ namespace OpenRA.Mods.Common.AI BitArray resourceTypeIndices; + AIHarvesterManager harvManager; + List builders = new List(); List unitsHangingAroundTheBase = new List(); @@ -314,11 +311,6 @@ namespace OpenRA.Mods.Common.AI if (World.Type == WorldType.Editor) return; - domainIndex = World.WorldActor.Trait(); - resLayer = World.WorldActor.TraitOrDefault(); - claimLayer = World.WorldActor.TraitOrDefault(); - pathfinder = World.WorldActor.Trait(); - isEnemyUnit = unit => Player.Stances[unit.Owner] == Stance.Enemy && !unit.Info.HasTraitInfo() @@ -348,6 +340,8 @@ namespace OpenRA.Mods.Common.AI playerResource = p.PlayerActor.Trait(); frozenLayer = p.PlayerActor.Trait(); + harvManager = new AIHarvesterManager(this, p); + foreach (var building in Info.BuildingQueues) builders.Add(new BaseBuilder(this, building, p, playerPower, playerResource)); foreach (var defense in Info.DefenseQueues) @@ -626,8 +620,7 @@ namespace OpenRA.Mods.Common.AI if (--assignRolesTicks <= 0) { assignRolesTicks = Info.AssignRolesInterval; - if (resLayer != null && !resLayer.IsResourceLayerEmpty) - GiveOrdersToIdleHarvesters(); + harvManager.Tick(activeUnits); FindNewUnits(self); InitializeBase(self, true); } @@ -748,59 +741,6 @@ namespace OpenRA.Mods.Common.AI return targets.MinByOrDefault(target => (target.Actor.CenterPosition - capturer.CenterPosition).LengthSquared); } - CPos FindNextResource(Actor actor, Harvester harv) - { - var mobileInfo = actor.Info.TraitInfo(); - var passable = (uint)mobileInfo.GetMovementClass(World.Map.Rules.TileSet); - - Func isValidResource = cell => - domainIndex.IsPassable(actor.Location, cell, mobileInfo, passable) && - harv.CanHarvestCell(actor, cell) && - claimLayer.CanClaimCell(actor, cell); - - var path = pathfinder.FindPath( - PathSearch.Search(World, mobileInfo, actor, true, isValidResource) - .WithCustomCost(loc => World.FindActorsInCircle(World.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius) - .Where(u => !u.IsDead && actor.Owner.Stances[u.Owner] == Stance.Enemy) - .Sum(u => Math.Max(WDist.Zero.Length, Info.HarvesterEnemyAvoidanceRadius.Length - (World.Map.CenterOfCell(loc) - u.CenterPosition).Length))) - .FromPoint(actor.Location)); - - if (path.Count == 0) - return CPos.Zero; - - return path[0]; - } - - void GiveOrdersToIdleHarvesters() - { - // Find idle harvesters and give them orders: - foreach (var harvester in activeUnits) - { - var harv = harvester.TraitOrDefault(); - if (harv == null) - continue; - - if (!harv.IsEmpty) - continue; - - if (!harvester.IsIdle) - { - var act = harvester.CurrentActivity; - if (!harv.LastSearchFailed || act.NextActivity == null || act.NextActivity.GetType() != typeof(FindResources)) - continue; - } - - var para = harvester.TraitOrDefault(); - if (para != null && para.IsInAir) - continue; - - // Tell the idle harvester to quit slacking: - var newSafeResourcePatch = FindNextResource(harvester, harv); - BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(harvester, newSafeResourcePatch)); - QueueOrder(new Order("Harvest", harvester, Target.FromCell(World, newSafeResourcePatch), false)); - } - } - void FindNewUnits(Actor self) { var newUnits = self.World.ActorsHavingTrait() @@ -809,9 +749,7 @@ namespace OpenRA.Mods.Common.AI foreach (var a in newUnits) { - if (a.Info.HasTraitInfo()) - QueueOrder(new Order("Harvest", a, false)); - else + if (!a.Info.HasTraitInfo()) unitsHangingAroundTheBase.Add(a); if (a.Info.HasTraitInfo() && a.Info.HasTraitInfo()) diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 60199610aa..46a69f055c 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -126,6 +126,7 @@ +