Rework harvester resource claiming:
* Maintains lists of claims, and only restricts reservations for friendly units. * Removes OnNotifyResourceClaimLost; it's not clear whether that is still useful, and it prevents future necessary cleanups. * Moves other code without changing behaviour. This fixed stale claims from dead units and enemy claims from preventing otherwise valid harvest activities.
This commit is contained in:
@@ -263,8 +263,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
public Player Player { get; private set; }
|
public Player Player { get; private set; }
|
||||||
|
|
||||||
readonly DomainIndex domainIndex;
|
readonly DomainIndex domainIndex;
|
||||||
readonly ResourceLayer resLayer;
|
readonly ResourceClaimLayer claimLayer;
|
||||||
readonly ResourceClaimLayer territory;
|
|
||||||
readonly IPathFinder pathfinder;
|
readonly IPathFinder pathfinder;
|
||||||
|
|
||||||
readonly Func<Actor, bool> isEnemyUnit;
|
readonly Func<Actor, bool> isEnemyUnit;
|
||||||
@@ -311,8 +310,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
domainIndex = World.WorldActor.Trait<DomainIndex>();
|
domainIndex = World.WorldActor.Trait<DomainIndex>();
|
||||||
resLayer = World.WorldActor.Trait<ResourceLayer>();
|
claimLayer = World.WorldActor.TraitOrDefault<ResourceClaimLayer>();
|
||||||
territory = World.WorldActor.TraitOrDefault<ResourceClaimLayer>();
|
|
||||||
pathfinder = World.WorldActor.Trait<IPathFinder>();
|
pathfinder = World.WorldActor.Trait<IPathFinder>();
|
||||||
|
|
||||||
isEnemyUnit = unit =>
|
isEnemyUnit = unit =>
|
||||||
@@ -769,19 +767,22 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
return targets.MinByOrDefault(target => (target.Actor.CenterPosition - capturer.CenterPosition).LengthSquared);
|
return targets.MinByOrDefault(target => (target.Actor.CenterPosition - capturer.CenterPosition).LengthSquared);
|
||||||
}
|
}
|
||||||
|
|
||||||
CPos FindNextResource(Actor harvester)
|
CPos FindNextResource(Actor actor, Harvester harv)
|
||||||
{
|
{
|
||||||
var harvInfo = harvester.Info.TraitInfo<HarvesterInfo>();
|
var mobileInfo = actor.Info.TraitInfo<MobileInfo>();
|
||||||
var mobileInfo = harvester.Info.TraitInfo<MobileInfo>();
|
|
||||||
var passable = (uint)mobileInfo.GetMovementClass(World.Map.Rules.TileSet);
|
var passable = (uint)mobileInfo.GetMovementClass(World.Map.Rules.TileSet);
|
||||||
|
|
||||||
|
Func<CPos, bool> isValidResource = cell =>
|
||||||
|
domainIndex.IsPassable(actor.Location, cell, mobileInfo, passable) &&
|
||||||
|
harv.CanHarvestCell(actor, cell) &&
|
||||||
|
claimLayer.CanClaimCell(actor, cell);
|
||||||
|
|
||||||
var path = pathfinder.FindPath(
|
var path = pathfinder.FindPath(
|
||||||
PathSearch.Search(World, mobileInfo, harvester, true,
|
PathSearch.Search(World, mobileInfo, actor, true, isValidResource)
|
||||||
loc => domainIndex.IsPassable(harvester.Location, loc, mobileInfo, passable) && harvester.CanHarvestAt(loc, resLayer, harvInfo, territory))
|
|
||||||
.WithCustomCost(loc => World.FindActorsInCircle(World.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius)
|
.WithCustomCost(loc => World.FindActorsInCircle(World.Map.CenterOfCell(loc), Info.HarvesterEnemyAvoidanceRadius)
|
||||||
.Where(u => !u.IsDead && harvester.Owner.Stances[u.Owner] == Stance.Enemy)
|
.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)))
|
.Sum(u => Math.Max(WDist.Zero.Length, Info.HarvesterEnemyAvoidanceRadius.Length - (World.Map.CenterOfCell(loc) - u.CenterPosition).Length)))
|
||||||
.FromPoint(harvester.Location));
|
.FromPoint(actor.Location));
|
||||||
|
|
||||||
if (path.Count == 0)
|
if (path.Count == 0)
|
||||||
return CPos.Zero;
|
return CPos.Zero;
|
||||||
@@ -809,7 +810,7 @@ namespace OpenRA.Mods.Common.AI
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Tell the idle harvester to quit slacking:
|
// Tell the idle harvester to quit slacking:
|
||||||
var newSafeResourcePatch = FindNextResource(harvester);
|
var newSafeResourcePatch = FindNextResource(harvester, harv);
|
||||||
BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(harvester, newSafeResourcePatch));
|
BotDebug("AI: Harvester {0} is idle. Ordering to {1} in search for new resources.".F(harvester, newSafeResourcePatch));
|
||||||
QueueOrder(new Order("Harvest", harvester, false) { TargetLocation = newSafeResourcePatch });
|
QueueOrder(new Order("Harvest", harvester, false) { TargetLocation = newSafeResourcePatch });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
readonly HarvesterInfo harvInfo;
|
readonly HarvesterInfo harvInfo;
|
||||||
readonly Mobile mobile;
|
readonly Mobile mobile;
|
||||||
readonly MobileInfo mobileInfo;
|
readonly MobileInfo mobileInfo;
|
||||||
readonly ResourceLayer resLayer;
|
readonly ResourceClaimLayer claimLayer;
|
||||||
readonly ResourceClaimLayer territory;
|
|
||||||
readonly IPathFinder pathFinder;
|
readonly IPathFinder pathFinder;
|
||||||
readonly DomainIndex domainIndex;
|
readonly DomainIndex domainIndex;
|
||||||
|
|
||||||
@@ -37,8 +36,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
harvInfo = self.Info.TraitInfo<HarvesterInfo>();
|
harvInfo = self.Info.TraitInfo<HarvesterInfo>();
|
||||||
mobile = self.Trait<Mobile>();
|
mobile = self.Trait<Mobile>();
|
||||||
mobileInfo = self.Info.TraitInfo<MobileInfo>();
|
mobileInfo = self.Info.TraitInfo<MobileInfo>();
|
||||||
resLayer = self.World.WorldActor.Trait<ResourceLayer>();
|
claimLayer = self.World.WorldActor.Trait<ResourceClaimLayer>();
|
||||||
territory = self.World.WorldActor.TraitOrDefault<ResourceClaimLayer>();
|
|
||||||
pathFinder = self.World.WorldActor.Trait<IPathFinder>();
|
pathFinder = self.World.WorldActor.Trait<IPathFinder>();
|
||||||
domainIndex = self.World.WorldActor.Trait<DomainIndex>();
|
domainIndex = self.World.WorldActor.Trait<DomainIndex>();
|
||||||
}
|
}
|
||||||
@@ -90,8 +88,8 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Attempt to claim a resource as ours
|
// Attempt to claim the target cell
|
||||||
if (territory != null && !territory.ClaimResource(self, closestHarvestablePosition.Value))
|
if (!claimLayer.TryClaimCell(self, closestHarvestablePosition.Value))
|
||||||
return ActivityUtils.SequenceActivities(new Wait(25), this);
|
return ActivityUtils.SequenceActivities(new Wait(25), this);
|
||||||
|
|
||||||
// If not given a direct order, assume ordered to the first resource location we find:
|
// If not given a direct order, assume ordered to the first resource location we find:
|
||||||
@@ -115,7 +113,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
CPos? ClosestHarvestablePos(Actor self)
|
CPos? ClosestHarvestablePos(Actor self)
|
||||||
{
|
{
|
||||||
if (self.CanHarvestAt(self.Location, resLayer, harvInfo, territory))
|
if (harv.CanHarvestCell(self, self.Location) && claimLayer.CanClaimCell(self, self.Location))
|
||||||
return self.Location;
|
return self.Location;
|
||||||
|
|
||||||
// Determine where to search from and how far to search:
|
// Determine where to search from and how far to search:
|
||||||
@@ -126,8 +124,8 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
// Find any harvestable resources:
|
// Find any harvestable resources:
|
||||||
var passable = (uint)mobileInfo.GetMovementClass(self.World.Map.Rules.TileSet);
|
var passable = (uint)mobileInfo.GetMovementClass(self.World.Map.Rules.TileSet);
|
||||||
List<CPos> path;
|
List<CPos> path;
|
||||||
using (var search = PathSearch.Search(self.World, mobileInfo, self, true,
|
using (var search = PathSearch.Search(self.World, mobileInfo, self, true, loc =>
|
||||||
loc => domainIndex.IsPassable(self.Location, loc, mobileInfo, passable) && self.CanHarvestAt(loc, resLayer, harvInfo, territory))
|
domainIndex.IsPassable(self.Location, loc, mobileInfo, passable) && harv.CanHarvestCell(self, loc) && claimLayer.CanClaimCell(self, loc))
|
||||||
.WithCustomCost(loc =>
|
.WithCustomCost(loc =>
|
||||||
{
|
{
|
||||||
if ((avoidCell.HasValue && loc == avoidCell.Value) ||
|
if ((avoidCell.HasValue && loc == avoidCell.Value) ||
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
readonly Harvester harv;
|
readonly Harvester harv;
|
||||||
readonly HarvesterInfo harvInfo;
|
readonly HarvesterInfo harvInfo;
|
||||||
readonly IFacing facing;
|
readonly IFacing facing;
|
||||||
readonly ResourceClaimLayer territory;
|
readonly ResourceClaimLayer claimLayer;
|
||||||
readonly ResourceLayer resLayer;
|
readonly ResourceLayer resLayer;
|
||||||
readonly BodyOrientation body;
|
readonly BodyOrientation body;
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
harvInfo = self.Info.TraitInfo<HarvesterInfo>();
|
harvInfo = self.Info.TraitInfo<HarvesterInfo>();
|
||||||
facing = self.Trait<IFacing>();
|
facing = self.Trait<IFacing>();
|
||||||
body = self.Trait<BodyOrientation>();
|
body = self.Trait<BodyOrientation>();
|
||||||
territory = self.World.WorldActor.TraitOrDefault<ResourceClaimLayer>();
|
claimLayer = self.World.WorldActor.Trait<ResourceClaimLayer>();
|
||||||
resLayer = self.World.WorldActor.Trait<ResourceLayer>();
|
resLayer = self.World.WorldActor.Trait<ResourceLayer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,8 +38,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
{
|
{
|
||||||
if (IsCanceled)
|
if (IsCanceled)
|
||||||
{
|
{
|
||||||
if (territory != null)
|
claimLayer.RemoveClaim(self);
|
||||||
territory.UnclaimByActor(self);
|
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,8 +46,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
|
|
||||||
if (harv.IsFull)
|
if (harv.IsFull)
|
||||||
{
|
{
|
||||||
if (territory != null)
|
claimLayer.RemoveClaim(self);
|
||||||
territory.UnclaimByActor(self);
|
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,8 +62,7 @@ namespace OpenRA.Mods.Common.Activities
|
|||||||
var resource = resLayer.Harvest(self.Location);
|
var resource = resLayer.Harvest(self.Location);
|
||||||
if (resource == null)
|
if (resource == null)
|
||||||
{
|
{
|
||||||
if (territory != null)
|
claimLayer.RemoveClaim(self);
|
||||||
territory.UnclaimByActor(self);
|
|
||||||
return NextActivity;
|
return NextActivity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -122,32 +122,6 @@ namespace OpenRA.Mods.Common
|
|||||||
NotifyBlocker(self, positions.SelectMany(p => self.World.ActorMap.GetActorsAt(p)));
|
NotifyBlocker(self, positions.SelectMany(p => self.World.ActorMap.GetActorsAt(p)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool CanHarvestAt(this Actor self, CPos pos, ResourceLayer resLayer, HarvesterInfo harvInfo,
|
|
||||||
ResourceClaimLayer territory)
|
|
||||||
{
|
|
||||||
// Resources only exist in the ground layer
|
|
||||||
if (pos.Layer != 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var resType = resLayer.GetResource(pos);
|
|
||||||
if (resType == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Can the harvester collect this kind of resource?
|
|
||||||
if (!harvInfo.Resources.Contains(resType.Info.Type))
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CPos ClosestCell(this Actor self, IEnumerable<CPos> cells)
|
public static CPos ClosestCell(this Actor self, IEnumerable<CPos> cells)
|
||||||
{
|
{
|
||||||
return cells.MinByOrDefault(c => (self.Location - c).LengthSquared);
|
return cells.MinByOrDefault(c => (self.Location - c).LengthSquared);
|
||||||
|
|||||||
@@ -527,7 +527,6 @@
|
|||||||
<Compile Include="Pathfinder\BasePathSearch.cs" />
|
<Compile Include="Pathfinder\BasePathSearch.cs" />
|
||||||
<Compile Include="Traits\World\PlayerPaletteFromCurrentTileset.cs" />
|
<Compile Include="Traits\World\PlayerPaletteFromCurrentTileset.cs" />
|
||||||
<Compile Include="Traits\World\RadarPings.cs" />
|
<Compile Include="Traits\World\RadarPings.cs" />
|
||||||
<Compile Include="Traits\World\ResourceClaim.cs" />
|
|
||||||
<Compile Include="Traits\World\ResourceClaimLayer.cs" />
|
<Compile Include="Traits\World\ResourceClaimLayer.cs" />
|
||||||
<Compile Include="Traits\World\ResourceLayer.cs" />
|
<Compile Include="Traits\World\ResourceLayer.cs" />
|
||||||
<Compile Include="Traits\World\ShroudPalette.cs" />
|
<Compile Include="Traits\World\ShroudPalette.cs" />
|
||||||
|
|||||||
@@ -78,10 +78,12 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public class Harvester : IIssueOrder, IResolveOrder, IPips,
|
public class Harvester : IIssueOrder, IResolveOrder, IPips,
|
||||||
IExplodeModifier, IOrderVoice, ISpeedModifier, ISync, INotifyCreated,
|
IExplodeModifier, IOrderVoice, ISpeedModifier, ISync, INotifyCreated,
|
||||||
INotifyResourceClaimLost, INotifyIdle, INotifyBlockingMove, INotifyBuildComplete
|
INotifyIdle, INotifyBlockingMove, INotifyBuildComplete
|
||||||
{
|
{
|
||||||
public readonly HarvesterInfo Info;
|
public readonly HarvesterInfo Info;
|
||||||
readonly Mobile mobile;
|
readonly Mobile mobile;
|
||||||
|
readonly ResourceLayer resLayer;
|
||||||
|
readonly ResourceClaimLayer claimLayer;
|
||||||
Dictionary<ResourceTypeInfo, int> contents = new Dictionary<ResourceTypeInfo, int>();
|
Dictionary<ResourceTypeInfo, int> contents = new Dictionary<ResourceTypeInfo, int>();
|
||||||
bool idleSmart = true;
|
bool idleSmart = true;
|
||||||
int idleDuration;
|
int idleDuration;
|
||||||
@@ -108,6 +110,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
{
|
{
|
||||||
Info = info;
|
Info = info;
|
||||||
mobile = self.Trait<Mobile>();
|
mobile = self.Trait<Mobile>();
|
||||||
|
resLayer = self.World.WorldActor.Trait<ResourceLayer>();
|
||||||
|
claimLayer = self.World.WorldActor.Trait<ResourceClaimLayer>();
|
||||||
|
|
||||||
self.QueueActivity(new CallFunc(() => ChooseNewProc(self, null)));
|
self.QueueActivity(new CallFunc(() => ChooseNewProc(self, null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,8 +216,10 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public void AcceptResource(ResourceType type)
|
public void AcceptResource(ResourceType type)
|
||||||
{
|
{
|
||||||
if (!contents.ContainsKey(type.Info)) contents[type.Info] = 1;
|
if (!contents.ContainsKey(type.Info))
|
||||||
else contents[type.Info]++;
|
contents[type.Info] = 1;
|
||||||
|
else
|
||||||
|
contents[type.Info]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UnblockRefinery(Actor self)
|
public void UnblockRefinery(Actor self)
|
||||||
@@ -309,6 +316,20 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return contents.Count == 0;
|
return contents.Count == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool CanHarvestCell(Actor self, CPos cell)
|
||||||
|
{
|
||||||
|
// Resources only exist in the ground layer
|
||||||
|
if (cell.Layer != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var resType = resLayer.GetResource(cell);
|
||||||
|
if (resType == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Can the harvester collect this kind of resource?
|
||||||
|
return Info.Resources.Contains(resType.Info.Type);
|
||||||
|
}
|
||||||
|
|
||||||
public IEnumerable<IOrderTargeter> Orders
|
public IEnumerable<IOrderTargeter> Orders
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -354,21 +375,9 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
CPos? loc;
|
CPos? loc;
|
||||||
if (order.TargetLocation != CPos.Zero)
|
if (order.TargetLocation != CPos.Zero)
|
||||||
{
|
|
||||||
loc = order.TargetLocation;
|
|
||||||
|
|
||||||
var territory = self.World.WorldActor.TraitOrDefault<ResourceClaimLayer>();
|
|
||||||
if (territory != null)
|
|
||||||
{
|
{
|
||||||
// Find the nearest claimable cell to the order location (useful for group-select harvest):
|
// Find the nearest claimable cell to the order location (useful for group-select harvest):
|
||||||
loc = mobile.NearestCell(loc.Value, p => mobile.CanEnterCell(p) && territory.ClaimResource(self, p), 1, 6);
|
loc = mobile.NearestCell(order.TargetLocation, p => mobile.CanEnterCell(p) && claimLayer.TryClaimCell(self, p), 1, 6);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Find the nearest cell to the order location (useful for group-select harvest):
|
|
||||||
var taken = new HashSet<CPos>();
|
|
||||||
loc = mobile.NearestCell(loc.Value, p => mobile.CanEnterCell(p) && taken.Add(p), 1, 6);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -423,15 +432,6 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnNotifyResourceClaimLost(Actor self, ResourceClaim claim, Actor claimer)
|
|
||||||
{
|
|
||||||
if (self == claimer) return;
|
|
||||||
|
|
||||||
// Our claim on a resource was stolen, find more unclaimed resources:
|
|
||||||
self.CancelActivity();
|
|
||||||
self.QueueActivity(new FindResources(self));
|
|
||||||
}
|
|
||||||
|
|
||||||
PipType GetPipAt(int i)
|
PipType GetPipAt(int i)
|
||||||
{
|
{
|
||||||
var n = i * Info.Capacity / Info.PipCount;
|
var n = i * Info.Capacity / Info.PipCount;
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2017 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
|
|
||||||
|
|
||||||
namespace OpenRA.Mods.Common.Traits
|
|
||||||
{
|
|
||||||
public sealed class ResourceClaim
|
|
||||||
{
|
|
||||||
public readonly Actor Claimer;
|
|
||||||
public CPos Cell;
|
|
||||||
|
|
||||||
public ResourceClaim(Actor claimer, CPos cell) { Claimer = claimer; Cell = cell; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using OpenRA.Graphics;
|
using OpenRA.Graphics;
|
||||||
using OpenRA.Traits;
|
using OpenRA.Traits;
|
||||||
|
|
||||||
@@ -18,112 +19,55 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
[Desc("Allows harvesters to coordinate their operations. Attach this to the world actor.")]
|
[Desc("Allows harvesters to coordinate their operations. Attach this to the world actor.")]
|
||||||
public sealed class ResourceClaimLayerInfo : TraitInfo<ResourceClaimLayer> { }
|
public sealed class ResourceClaimLayerInfo : TraitInfo<ResourceClaimLayer> { }
|
||||||
|
|
||||||
public sealed class ResourceClaimLayer : IWorldLoaded
|
public sealed class ResourceClaimLayer
|
||||||
{
|
{
|
||||||
Dictionary<CPos, ResourceClaim> claimByCell;
|
readonly Dictionary<CPos, List<Actor>> claimByCell = new Dictionary<CPos, List<Actor>>(32);
|
||||||
Dictionary<Actor, ResourceClaim> claimByActor;
|
readonly Dictionary<Actor, CPos> claimByActor = new Dictionary<Actor, CPos>(32);
|
||||||
|
|
||||||
private void MakeClaim(Actor claimer, CPos cell)
|
|
||||||
{
|
|
||||||
UnclaimByActor(claimer);
|
|
||||||
UnclaimByCell(cell, claimer);
|
|
||||||
claimByActor[claimer] = claimByCell[cell] = new ResourceClaim(claimer, cell);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Unclaim(ResourceClaim claim, Actor claimer)
|
|
||||||
{
|
|
||||||
if (claimByActor.Remove(claim.Claimer) & claimByCell.Remove(claim.Cell))
|
|
||||||
{
|
|
||||||
if (claim.Claimer.Disposed) return;
|
|
||||||
if (!claim.Claimer.IsInWorld) return;
|
|
||||||
if (claim.Claimer.IsDead) return;
|
|
||||||
|
|
||||||
claim.Claimer.Trait<INotifyResourceClaimLost>().OnNotifyResourceClaimLost(claim.Claimer, claim, claimer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WorldLoaded(World w, WorldRenderer wr)
|
|
||||||
{
|
|
||||||
// 32 seems a sane default initial capacity for the total # of harvesters in a game. Purely a guesstimate.
|
|
||||||
claimByCell = new Dictionary<CPos, ResourceClaim>(32);
|
|
||||||
claimByActor = new Dictionary<Actor, ResourceClaim>(32);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to claim the resource at the cell for the given actor.
|
/// Attempt to reserve the resource in a cell for the given actor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="claimer"></param>
|
public bool TryClaimCell(Actor claimer, CPos cell)
|
||||||
/// <param name="cell"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool ClaimResource(Actor claimer, CPos cell)
|
|
||||||
{
|
{
|
||||||
// Has anyone else claimed this point?
|
var claimers = claimByCell.GetOrAdd(cell);
|
||||||
ResourceClaim claim;
|
|
||||||
if (claimByCell.TryGetValue(cell, out claim))
|
|
||||||
{
|
|
||||||
// Same claimer:
|
|
||||||
if (claim.Claimer == claimer) return true;
|
|
||||||
|
|
||||||
// This is to prevent in-fighting amongst friendly harvesters:
|
// Clean up any stale claims
|
||||||
if (claimer.Owner == claim.Claimer.Owner) return false;
|
claimers.RemoveAll(a => a.IsDead);
|
||||||
if (claimer.Owner.Stances[claim.Claimer.Owner] == Stance.Ally) return false;
|
|
||||||
|
|
||||||
// If an enemy/neutral claimed this, don't respect that claim:
|
// Prevent harvesters from the player or their allies fighting over the same cell
|
||||||
}
|
if (claimers.Any(c => c != claimer && claimer.Owner.IsAlliedWith(c.Owner)))
|
||||||
|
return false;
|
||||||
|
|
||||||
// Either nobody else claims this point or an enemy/neutral claims it:
|
// Remove the actor's last claim, if it has one
|
||||||
MakeClaim(claimer, cell);
|
CPos lastClaim;
|
||||||
|
if (claimByActor.TryGetValue(claimer, out lastClaim))
|
||||||
|
claimByCell.GetOrAdd(lastClaim).Remove(claimer);
|
||||||
|
|
||||||
|
claimers.Add(claimer);
|
||||||
|
claimByActor[claimer] = cell;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Release the last resource claim made on this cell.
|
/// Returns false if the cell is already reserved by an allied actor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="cell"></param>
|
public bool CanClaimCell(Actor claimer, CPos cell)
|
||||||
public void UnclaimByCell(CPos cell, Actor claimer)
|
|
||||||
{
|
{
|
||||||
ResourceClaim claim;
|
return !claimByCell.GetOrAdd(cell)
|
||||||
if (claimByCell.TryGetValue(cell, out claim))
|
.Any(c => c != claimer && !c.IsDead && claimer.Owner.IsAlliedWith(c.Owner));
|
||||||
Unclaim(claim, claimer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Release the last resource claim made by this actor.
|
/// Release the last resource claim made by this actor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="claimer"></param>
|
/// <param name="claimer"></param>
|
||||||
public void UnclaimByActor(Actor claimer)
|
public void RemoveClaim(Actor claimer)
|
||||||
{
|
{
|
||||||
ResourceClaim claim;
|
CPos lastClaim;
|
||||||
if (claimByActor.TryGetValue(claimer, out claim))
|
if (claimByActor.TryGetValue(claimer, out lastClaim))
|
||||||
Unclaim(claim, claimer);
|
claimByCell.GetOrAdd(lastClaim).Remove(claimer);
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
claimByActor.Remove(claimer);
|
||||||
/// Is the cell location <paramref name="cell"/> claimed for harvesting by any other actor?
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="self"></param>
|
|
||||||
/// <param name="cell"></param>
|
|
||||||
/// <returns>true if already claimed by an ally that isn't <paramref name="self"/>; false otherwise.</returns>
|
|
||||||
public bool IsClaimedByAnyoneElse(Actor self, CPos cell, out ResourceClaim claim)
|
|
||||||
{
|
|
||||||
if (claimByCell.TryGetValue(cell, out claim))
|
|
||||||
{
|
|
||||||
// Same claimer:
|
|
||||||
if (claim.Claimer == self) return false;
|
|
||||||
|
|
||||||
// This is to prevent in-fighting amongst friendly harvesters:
|
|
||||||
if (self.Owner == claim.Claimer.Owner) return true;
|
|
||||||
if (self.Owner.Stances[claim.Claimer.Owner] == Stance.Ally) return true;
|
|
||||||
|
|
||||||
// If an enemy/neutral claimed this, don't respect that claim and fall through:
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// No claim.
|
|
||||||
claim = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,11 +28,6 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
int QuantizedBodyFacings(ActorInfo ai, SequenceProvider sequenceProvider, string race);
|
int QuantizedBodyFacings(ActorInfo ai, SequenceProvider sequenceProvider, string race);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface INotifyResourceClaimLost
|
|
||||||
{
|
|
||||||
void OnNotifyResourceClaimLost(Actor self, ResourceClaim claim, Actor claimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IPlaceBuildingDecorationInfo : ITraitInfo
|
public interface IPlaceBuildingDecorationInfo : ITraitInfo
|
||||||
{
|
{
|
||||||
IEnumerable<IRenderable> Render(WorldRenderer wr, World w, ActorInfo ai, WPos centerPosition);
|
IEnumerable<IRenderable> Render(WorldRenderer wr, World w, ActorInfo ai, WPos centerPosition);
|
||||||
|
|||||||
Reference in New Issue
Block a user