Many harvester behavior improvements; summary below.
Implemented Harvester territory marking with a simple resource claim system in ResourceClaimLayer trait added to World. Added customCost for PathSearch to support new Harvester search preferences. Explicit delivery order forces harvester to always deliver to that refinery. Explicit harvest order frees harvester from forced delivery refinery and allows for auto-balancing. Harvesters auto-balance refinery choice such that no more than 3 harvesters are linked to any one refinery at a time. Harvesters try very hard to not block the refinery dock location. Harvesters try to avoid enemy territory when searching for resources. Group-select harvest order intelligently disperses harvesters around the order location. Fixed PathFinder caching to not be a sliding window. This is a correctness issue. Sliding window causes no-route paths to be cached permanently in tight move loops and doesn't allow eventual progress to be made. This may have negative performance implications.
This commit is contained in:
@@ -14,6 +14,7 @@ using System.Linq;
|
||||
using OpenRA.Mods.RA.Move;
|
||||
using OpenRA.Mods.RA.Render;
|
||||
using OpenRA.Traits;
|
||||
using System;
|
||||
|
||||
namespace OpenRA.Mods.RA.Activities
|
||||
{
|
||||
@@ -24,19 +25,85 @@ namespace OpenRA.Mods.RA.Activities
|
||||
if (IsCanceled || NextActivity != null) return NextActivity;
|
||||
|
||||
var harv = self.Trait<Harvester>();
|
||||
|
||||
if (harv.IsFull)
|
||||
return Util.SequenceActivities(new DeliverResources(), NextActivity);
|
||||
|
||||
var harvInfo = self.Info.Traits.Get<HarvesterInfo>();
|
||||
var mobile = self.Trait<Mobile>();
|
||||
var mobileInfo = self.Info.Traits.Get<MobileInfo>();
|
||||
var res = self.World.WorldActor.Trait<ResourceLayer>();
|
||||
var path = self.World.WorldActor.Trait<PathFinder>().FindPath(PathSearch.Search(self.World, mobileInfo, self.Owner, true)
|
||||
.WithHeuristic(loc => (res.GetResource(loc) != null && harvInfo.Resources.Contains(res.GetResource(loc).info.Name)) ? 0 : 1)
|
||||
.FromPoint(self.Location));
|
||||
var resLayer = self.World.WorldActor.Trait<ResourceLayer>();
|
||||
var territory = self.World.WorldActor.Trait<ResourceClaimLayer>();
|
||||
|
||||
// Find harvestable resources nearby:
|
||||
var path = self.World.WorldActor.Trait<PathFinder>().FindPath(
|
||||
PathSearch.Search(self.World, mobileInfo, self.Owner, true)
|
||||
.WithCustomCost(loc =>
|
||||
{
|
||||
// Avoid enemy territory:
|
||||
int safetycost = (
|
||||
// TODO: calculate weapons ranges of units and factor those in instead of hard-coding 8.
|
||||
from u in self.World.FindUnitsInCircle(loc.ToPPos(), Game.CellSize * 8)
|
||||
where !u.Destroyed
|
||||
where self.Owner.Stances[u.Owner] == Stance.Enemy
|
||||
select Math.Max(0, 64 - (loc - u.Location).LengthSquared)
|
||||
).Sum();
|
||||
|
||||
return safetycost;
|
||||
})
|
||||
.WithHeuristic(loc =>
|
||||
{
|
||||
// Don't harvest out of range:
|
||||
int distSquared = (loc - (harv.LastOrderLocation ?? harv.LinkedProc.Location)).LengthSquared;
|
||||
if (distSquared > (12 * 12))
|
||||
return int.MaxValue;
|
||||
|
||||
// Get the resource at this location:
|
||||
var resType = resLayer.GetResource(loc);
|
||||
|
||||
if (resType == null) return 1;
|
||||
// Can the harvester collect this kind of resource?
|
||||
if (!harvInfo.Resources.Contains(resType.info.Name)) return 1;
|
||||
|
||||
// Another harvester has claimed this resource:
|
||||
ResourceClaim claim;
|
||||
if (territory.IsClaimedByAnyoneElse(self, loc, out claim)) return 1;
|
||||
|
||||
// Is anyone covering the location already?
|
||||
// NOTE(jsd): This is required to prevent harvester deadlocking.
|
||||
var unitsAtLoc =
|
||||
from u in self.World.FindUnits(loc.ToPPos(), loc.ToPPos() + PVecInt.OneCell)
|
||||
where u != self
|
||||
select u;
|
||||
if (unitsAtLoc.Any()) return 1;
|
||||
|
||||
return 0;
|
||||
})
|
||||
.FromPoint(self.Location)
|
||||
);
|
||||
|
||||
if (path.Count == 0)
|
||||
return NextActivity;
|
||||
{
|
||||
if (!harv.IsEmpty)
|
||||
return new DeliverResources();
|
||||
else
|
||||
{
|
||||
// Get out of the way if we are:
|
||||
harv.UnblockRefinery(self);
|
||||
if (NextActivity != null)
|
||||
return Util.SequenceActivities(NextActivity, new Wait(90), this);
|
||||
else
|
||||
return Util.SequenceActivities(new Wait(90), this);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to claim a resource as ours:
|
||||
if (!territory.ClaimResource(self, path[0]))
|
||||
return Util.SequenceActivities(new Wait(25), this);
|
||||
|
||||
// If not given a direct order, assume ordered to the first resource location we find:
|
||||
if (harv.LastOrderLocation == null)
|
||||
harv.LastOrderLocation = path[0];
|
||||
|
||||
self.SetTargetLine(Target.FromCell(path[0]), Color.Red, false);
|
||||
return Util.SequenceActivities(mobile.MoveTo(path[0], 1), new HarvestResource(), this);
|
||||
@@ -55,23 +122,38 @@ namespace OpenRA.Mods.RA.Activities
|
||||
public override Activity Tick(Actor self)
|
||||
{
|
||||
if (isHarvesting) return this;
|
||||
if (IsCanceled) return NextActivity;
|
||||
|
||||
var territory = self.World.WorldActor.Trait<ResourceClaimLayer>();
|
||||
if (IsCanceled)
|
||||
{
|
||||
territory.UnclaimByActor(self);
|
||||
return NextActivity;
|
||||
}
|
||||
|
||||
var harv = self.Trait<Harvester>();
|
||||
harv.LastHarvestedCell = self.Location;
|
||||
|
||||
if (harv.IsFull)
|
||||
{
|
||||
territory.UnclaimByActor(self);
|
||||
return NextActivity;
|
||||
}
|
||||
|
||||
var resLayer = self.World.WorldActor.Trait<ResourceLayer>();
|
||||
var resource = resLayer.Harvest(self.Location);
|
||||
if (resource == null)
|
||||
{
|
||||
territory.UnclaimByActor(self);
|
||||
return NextActivity;
|
||||
}
|
||||
|
||||
var renderUnit = self.Trait<RenderUnit>(); /* better have one of these! */
|
||||
var resource = self.World.WorldActor.Trait<ResourceLayer>().Harvest(self.Location);
|
||||
if (resource == null)
|
||||
return NextActivity;
|
||||
|
||||
if (renderUnit.anim.CurrentSequence.Name != "harvest")
|
||||
{
|
||||
isHarvesting = true;
|
||||
renderUnit.PlayCustomAnimation(self, "harvest", () => isHarvesting = false);
|
||||
}
|
||||
|
||||
harv.AcceptResource(resource);
|
||||
return this;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user