Move BlockedByActor, IPositionableInfo, IPositionable to Mods.Common.

Actor previously cached targetable locations for static actors as an optimization. As we can no longer reference the IPositionable interface, move this optimization to HitShape instead. Although we lose some of the efficiency of caching the final result on the actor, we gain some by allowing HitShape to cache the results as long as they have not changed. So instead of being limited to static actors, we can extend the caching to currently stationary actor.
This commit is contained in:
RoosterDragon
2022-01-30 16:43:22 +00:00
committed by abcdefg30
parent d8a4d7fd1d
commit d67f696bd0
8 changed files with 103 additions and 96 deletions

View File

@@ -115,8 +115,7 @@ namespace OpenRA
readonly INotifyBecomingIdle[] becomingIdles;
readonly INotifyIdle[] tickIdles;
readonly IEnumerable<ITargetablePositions> enabledTargetablePositions;
readonly bool setStaticTargetablePositions;
WPos[] staticTargetablePositions;
readonly IEnumerable<WPos> enabledTargetableWorldPositions;
bool created;
internal Actor(World world, string name, TypeDictionary initDict)
@@ -146,7 +145,6 @@ namespace OpenRA
Info = world.Map.Rules.Actors[name];
IPositionable positionable = null;
var resolveOrdersList = new List<IResolveOrder>();
var renderModifiersList = new List<IRenderModifier>();
var rendersList = new List<IRender>();
@@ -168,7 +166,6 @@ namespace OpenRA
// performance-sensitive parts of the core game engine, such as pathfinding, visibility and rendering.
// Note: The blocks are required to limit the scope of the t's, so we make an exception to our normal style
// rules for spacing in order to keep these assignments compact and readable.
{ if (trait is IPositionable t) positionable = t; }
{ if (trait is IOccupySpace t) OccupiesSpace = t; }
{ if (trait is IEffectiveOwner t) EffectiveOwner = t; }
{ if (trait is IFacing t) facing = t; }
@@ -196,9 +193,8 @@ namespace OpenRA
Targetables = targetablesList.ToArray();
var targetablePositions = targetablePositionsList.ToArray();
enabledTargetablePositions = targetablePositions.Where(Exts.IsTraitEnabled);
enabledTargetableWorldPositions = enabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this));
SyncHashes = syncHashesList.ToArray();
setStaticTargetablePositions = positionable == null && targetablePositions.Any() && targetablePositions.All(tp => tp.AlwaysEnabled);
}
}
@@ -233,11 +229,6 @@ namespace OpenRA
foreach (var notify in allObserverNotifiers)
notify(this, readOnlyConditionCache);
// All actors that can move or teleport should have IPositionable, if not it's pretty safe to assume the actor is completely immobile and
// all targetable positions can be cached if all ITargetablePositions have no conditional requirements.
if (setStaticTargetablePositions)
staticTargetablePositions = enabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this)).ToArray();
// TODO: Other traits may need initialization after being notified of initial condition state.
// TODO: A post condition initialization notification phase may allow queueing activities instead.
@@ -539,11 +530,8 @@ namespace OpenRA
public IEnumerable<WPos> GetTargetablePositions()
{
if (staticTargetablePositions != null)
return staticTargetablePositions;
if (enabledTargetablePositions.Any())
return enabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this));
return enabledTargetableWorldPositions;
return new[] { CenterPosition };
}

View File

@@ -36,62 +36,6 @@ namespace OpenRA.Traits
Dead = 32
}
/// <summary>
/// When performing locomotion or pathfinding related checks,
/// determines whether the blocking rules will be applied when encountering other actors.
/// </summary>
public enum BlockedByActor
{
/// <summary>
/// Actors on the map are ignored, as if they were not present.
/// An actor can only be blocked by impassable terrain.
/// An actor can never be blocked by other actors. The blocking rules will never be evaluated.
/// </summary>
None,
/// <summary>
/// Actors on the map that are moving, or moveable &amp; allied are ignored.
/// An actor is Immovable is any of the following applies:
/// <list type="bullet">
/// <item>Lacks the Mobile trait.</item>
/// <item>The Mobile trait is disabled or paused.</item>
/// <item>The Mobile trait property IsImmovable is true.</item>
/// </list>
/// Note the above definition means an actor can be Movable, but may not be Moving, i.e. it is Stationary.
/// Actors are allied if their owners have the <see cref="PlayerRelationship.Ally"/> relationship.
/// An actor can be blocked by impassable terrain.
/// An actor can be blocked by immovable actors *if* they are deemed as blocking by the blocking rules.
/// An actor can be blocked by an actor capable of moving, if it is not an ally and *if* they are deemed as
/// blocking by the blocking rules.
/// An actor can never be blocked by an allied actor capable of moving, even if the other actor is stationary.
/// An actor can never be blocked by a moving actor.
/// </summary>
Immovable,
/// <summary>
/// Actors on the map that are moving are ignored.
/// An actor is moving if both of the following apply:
/// <list type="bullet">
/// <item>It is a Moveable actor (see <see cref="Immovable"/>).</item>
/// <item>The Mobile trait property CurrentMovementTypes contains the flag Horizontal.</item>
/// </list>
/// Otherwise the actor is deemed to be Stationary.
/// An actor can be blocked by impassable terrain.
/// An actor can be blocked by immovable actors and stationary actors *if* they are deemed as blocking by the
/// blocking rules.
/// An actor can never be blocked by a moving actor.
/// </summary>
Stationary,
/// <summary>
/// Actors on the map are not ignored.
/// An actor can be blocked by impassable terrain.
/// An actor can be blocked by immovable actors, stationary actors and moving actors *if* they are deemed as
/// blocking by the blocking rules.
/// </summary>
All
}
/// <summary>
/// Type tag for DamageTypes <see cref="Primitives.BitSet{T}"/>.
/// </summary>
@@ -338,23 +282,6 @@ namespace OpenRA.Traits
public enum SubCell : byte { Invalid = byte.MaxValue, Any = byte.MaxValue - 1, FullCell = 0, First = 1 }
public interface IPositionableInfo : IOccupySpaceInfo
{
bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All);
}
public interface IPositionable : IOccupySpace
{
bool CanExistInCell(CPos location);
bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any);
bool CanEnterCell(CPos location, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All);
SubCell GetValidSubCell(SubCell preferred = SubCell.Any);
SubCell GetAvailableSubCell(CPos location, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All);
void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any);
void SetPosition(Actor self, WPos pos);
void SetCenterPosition(Actor self, WPos pos);
}
public interface ITemporaryBlockerInfo : ITraitInfoInterface { }
[RequireExplicitImplementation]
@@ -566,7 +493,6 @@ namespace OpenRA.Traits
public interface ITargetablePositions
{
IEnumerable<WPos> TargetablePositions(Actor self);
bool AlwaysEnabled { get; }
}
public interface IMoveInfo : ITraitInfoInterface

View File

@@ -11,7 +11,6 @@
using OpenRA.Activities;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Activities
{

View File

@@ -10,7 +10,7 @@
#endregion
using OpenRA.Activities;
using OpenRA.Traits;
using OpenRA.Mods.Common.Traits;
namespace OpenRA.Mods.Common.Activities
{

View File

@@ -13,7 +13,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common.Traits;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Pathfinder
{

View File

@@ -15,7 +15,6 @@ using System.Linq;
using System.Runtime.CompilerServices;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Pathfinder
{

View File

@@ -74,6 +74,14 @@ namespace OpenRA.Mods.Common.Traits
ITargetableCells targetableCells;
Turreted turret;
((CPos Cell, SubCell SubCell)[] targetableCells,
WPos? selfCenterPosition,
WRot? selfOrientation,
WRot? turretLocalOrientation,
WVec? turretOffset) cacheInput;
WPos[] cachedTargetablePositions;
public HitShape(Actor self, HitShapeInfo info)
: base(info)
{
@@ -88,14 +96,28 @@ namespace OpenRA.Mods.Common.Traits
base.Created(self);
}
bool ITargetablePositions.AlwaysEnabled => Info.RequiresCondition == null;
IEnumerable<WPos> ITargetablePositions.TargetablePositions(Actor self)
{
if (IsTraitDisabled)
return Enumerable.Empty<WPos>();
return TargetablePositions(self);
// Check for changes in inputs that affect the result of the TargetablePositions method.
// If the inputs have not changed we can cache and reuse the result for later calls.
// i.e. we are treating the method as a pure function.
var newCacheInput = (
Info.UseTargetableCellsOffsets ? targetableCells?.TargetableCells() : null,
Info.TargetableOffsets.Length > 0 ? self.CenterPosition : (WPos?)null,
Info.TargetableOffsets.Length > 0 ? self.Orientation : (WRot?)null,
Info.TargetableOffsets.Length > 0 ? turret?.LocalOrientation : null,
Info.TargetableOffsets.Length > 0 ? turret?.Offset : null);
if (cachedTargetablePositions == null ||
cacheInput != newCacheInput)
{
cachedTargetablePositions = TargetablePositions(self).ToArray();
cacheInput = newCacheInput;
}
return cachedTargetablePositions;
}
IEnumerable<WPos> TargetablePositions(Actor self)

View File

@@ -715,4 +715,78 @@ namespace OpenRA.Mods.Common.Traits
event Action<CPos> CellEntryChanged;
bool TryGetTerrainColorPair(MPos uv, out (Color Left, Color Right) value);
}
/// <summary>
/// When performing locomotion or pathfinding related checks,
/// determines whether the blocking rules will be applied when encountering other actors.
/// </summary>
public enum BlockedByActor
{
/// <summary>
/// Actors on the map are ignored, as if they were not present.
/// An actor can only be blocked by impassable terrain.
/// An actor can never be blocked by other actors. The blocking rules will never be evaluated.
/// </summary>
None,
/// <summary>
/// Actors on the map that are moving, or moveable &amp; allied are ignored.
/// An actor is Immovable is any of the following applies:
/// <list type="bullet">
/// <item>Lacks the <see cref="Mobile"/> trait.</item>
/// <item>The <see cref="Mobile"/> trait has <see cref="ConditionalTrait{MobileInfo}.IsTraitDisabled"/> or
/// <see cref="PausableConditionalTrait{MobileInfo}.IsTraitPaused"/> as true.</item>
/// <item>The <see cref="Mobile"/> trait has <see cref="Mobile.IsImmovable"/> as true.</item>
/// </list>
/// Note the above definition means an actor can be Movable, but may not be Moving, i.e. it is Stationary.
/// Actors are allied if their owners have the <see cref="PlayerRelationship.Ally"/> relationship.
/// An actor can be blocked by impassable terrain.
/// An actor can be blocked by immovable actors *if* they are deemed as blocking by the blocking rules.
/// An actor can be blocked by an actor capable of moving, if it is not an ally and *if* they are deemed as
/// blocking by the blocking rules.
/// An actor can never be blocked by an allied actor capable of moving, even if the other actor is stationary.
/// An actor can never be blocked by a moving actor.
/// </summary>
Immovable,
/// <summary>
/// Actors on the map that are moving are ignored.
/// An actor is moving if both of the following apply:
/// <list type="bullet">
/// <item>It is a Moveable actor (see <see cref="Immovable"/>).</item>
/// <item><see cref="Mobile.CurrentMovementTypes"/> contains the flag <see cref="MovementType.Horizontal"/>.</item>
/// </list>
/// Otherwise the actor is deemed to be Stationary.
/// An actor can be blocked by impassable terrain.
/// An actor can be blocked by immovable actors and stationary actors *if* they are deemed as blocking by the
/// blocking rules.
/// An actor can never be blocked by a moving actor.
/// </summary>
Stationary,
/// <summary>
/// Actors on the map are not ignored.
/// An actor can be blocked by impassable terrain.
/// An actor can be blocked by immovable actors, stationary actors and moving actors *if* they are deemed as
/// blocking by the blocking rules.
/// </summary>
All
}
public interface IPositionableInfo : IOccupySpaceInfo
{
bool CanEnterCell(World world, Actor self, CPos cell, SubCell subCell = SubCell.FullCell, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All);
}
public interface IPositionable : IOccupySpace
{
bool CanExistInCell(CPos location);
bool IsLeavingCell(CPos location, SubCell subCell = SubCell.Any);
bool CanEnterCell(CPos location, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All);
SubCell GetValidSubCell(SubCell preferred = SubCell.Any);
SubCell GetAvailableSubCell(CPos location, SubCell preferredSubCell = SubCell.Any, Actor ignoreActor = null, BlockedByActor check = BlockedByActor.All);
void SetPosition(Actor self, CPos cell, SubCell subCell = SubCell.Any);
void SetPosition(Actor self, WPos pos);
void SetCenterPosition(Actor self, WPos pos);
}
}