Skip to content

Commit e3aa2dc

Browse files
anvilvapreMailaender
authored andcommitted
HitShape, query trait via actor cached targetable positions.
1 parent f88b6d7 commit e3aa2dc

File tree

6 files changed

+61
-29
lines changed

6 files changed

+61
-29
lines changed

OpenRA.Game/Actor.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public Activity CurrentActivity
6868
public IEffectiveOwner EffectiveOwner { get; }
6969
public IOccupySpace OccupiesSpace { get; }
7070
public ITargetable[] Targetables { get; }
71+
public IEnumerable<ITargetablePositions> EnabledTargetablePositions { get; private set; }
7172

7273
public bool IsIdle => CurrentActivity == null;
7374
public bool IsDead => Disposed || (health != null && health.IsDead);
@@ -114,7 +115,6 @@ class ConditionState
114115
readonly IDefaultVisibility defaultVisibility;
115116
readonly INotifyBecomingIdle[] becomingIdles;
116117
readonly INotifyIdle[] tickIdles;
117-
readonly IEnumerable<ITargetablePositions> enabledTargetablePositions;
118118
readonly IEnumerable<WPos> enabledTargetableWorldPositions;
119119
bool created;
120120

@@ -192,8 +192,8 @@ internal Actor(World world, string name, TypeDictionary initDict)
192192
tickIdles = tickIdlesList.ToArray();
193193
Targetables = targetablesList.ToArray();
194194
var targetablePositions = targetablePositionsList.ToArray();
195-
enabledTargetablePositions = targetablePositions.Where(Exts.IsTraitEnabled);
196-
enabledTargetableWorldPositions = enabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this));
195+
EnabledTargetablePositions = targetablePositions.Where(Exts.IsTraitEnabled);
196+
enabledTargetableWorldPositions = EnabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this));
197197
SyncHashes = syncHashesList.ToArray();
198198
}
199199
}
@@ -530,7 +530,7 @@ public bool IsTargetableBy(Actor byActor)
530530

531531
public IEnumerable<WPos> GetTargetablePositions()
532532
{
533-
if (enabledTargetablePositions.Any())
533+
if (EnabledTargetablePositions.Any())
534534
return enabledTargetableWorldPositions;
535535

536536
return new[] { CenterPosition };

OpenRA.Mods.Common/Projectiles/Bullet.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
using System;
1313
using System.Collections.Generic;
14-
using System.Linq;
1514
using OpenRA.GameRules;
1615
using OpenRA.Graphics;
1716
using OpenRA.Mods.Common.Effects;
@@ -336,9 +335,11 @@ bool AnyValidTargetsInRadius(World world, WPos pos, WDist radius, Actor firedBy,
336335
continue;
337336

338337
// If the impact position is within any actor's HitShape, we have a direct hit
339-
var activeShapes = victim.TraitsImplementing<HitShape>().Where(Exts.IsTraitEnabled);
340-
if (activeShapes.Any(i => i.DistanceFromEdge(victim, pos).Length <= 0))
341-
return true;
338+
// PERF: Avoid using TraitsImplementing<HitShape> that needs to find the actor in the trait dictionary.
339+
foreach (var targetPos in victim.EnabledTargetablePositions)
340+
if (targetPos is HitShape h)
341+
if (h.DistanceFromEdge(victim, pos).Length <= 0)
342+
return true;
342343
}
343344

344345
return false;

OpenRA.Mods.Common/Warheads/DamageWarhead.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,14 @@ public override void DoImpact(in Target target, WarheadArgs args)
5050
if (!IsValidAgainst(victim, firedBy))
5151
return;
5252

53-
var closestActiveShape = victim.TraitsImplementing<HitShape>().Where(Exts.IsTraitEnabled)
54-
.MinByOrDefault(t => t.DistanceFromEdge(victim, victim.CenterPosition));
53+
// PERF: Avoid using TraitsImplementing<HitShape> that needs to find the actor in the trait dictionary.
54+
var closestActiveShape = (HitShape)victim.EnabledTargetablePositions.MinByOrDefault(t =>
55+
{
56+
if (t is HitShape h)
57+
return h.DistanceFromEdge(victim, victim.CenterPosition);
58+
else
59+
return WDist.MaxValue;
60+
});
5561

5662
// Cannot be damaged without an active HitShape
5763
if (closestActiveShape == null)

OpenRA.Mods.Common/Warheads/SpreadDamageWarhead.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,32 @@ protected override void DoImpact(WPos pos, Actor firedBy, WarheadArgs args)
6363
if (!IsValidAgainst(victim, firedBy))
6464
continue;
6565

66-
var closestActiveShape = victim.TraitsImplementing<HitShape>()
67-
.Where(Exts.IsTraitEnabled)
68-
.Select(s => (HitShape: s, Distance: s.DistanceFromEdge(victim, pos)))
69-
.MinByOrDefault(s => s.Distance);
66+
HitShape closestActiveShape = null;
67+
var closestDistance = int.MaxValue;
68+
69+
// PERF: Avoid using TraitsImplementing<HitShape> that needs to find the actor in the trait dictionary.
70+
foreach (var targetPos in victim.EnabledTargetablePositions)
71+
{
72+
if (targetPos is HitShape h)
73+
{
74+
var distance = h.DistanceFromEdge(victim, pos).Length;
75+
if (distance < closestDistance)
76+
{
77+
closestDistance = distance;
78+
closestActiveShape = h;
79+
}
80+
}
81+
}
7082

7183
// Cannot be damaged without an active HitShape.
72-
if (closestActiveShape.HitShape == null)
84+
if (closestActiveShape == null)
7385
continue;
7486

7587
var falloffDistance = 0;
7688
switch (DamageCalculationType)
7789
{
7890
case DamageCalculationType.HitShape:
79-
falloffDistance = closestActiveShape.Distance.Length;
91+
falloffDistance = closestDistance;
8092
break;
8193
case DamageCalculationType.ClosestTargetablePosition:
8294
falloffDistance = victim.GetTargetablePositions().Select(x => (x - pos).Length).Min();
@@ -108,7 +120,7 @@ protected override void DoImpact(WPos pos, Actor firedBy, WarheadArgs args)
108120
ImpactOrientation = impactOrientation,
109121
};
110122

111-
InflictDamage(victim, firedBy, closestActiveShape.HitShape, updatedWarheadArgs);
123+
InflictDamage(victim, firedBy, closestActiveShape, updatedWarheadArgs);
112124
}
113125
}
114126

OpenRA.Mods.Common/Warheads/TargetDamageWarhead.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
*/
1010
#endregion
1111

12-
using System.Linq;
1312
using OpenRA.GameRules;
1413
using OpenRA.Mods.Common.Traits;
1514
using OpenRA.Traits;
@@ -36,20 +35,32 @@ protected override void DoImpact(WPos pos, Actor firedBy, WarheadArgs args)
3635
if (!IsValidAgainst(victim, firedBy))
3736
continue;
3837

39-
var closestActiveShape = victim.TraitsImplementing<HitShape>()
40-
.Where(Exts.IsTraitEnabled)
41-
.Select(s => (HitShape: s, Distance: s.DistanceFromEdge(victim, pos)))
42-
.MinByOrDefault(s => s.Distance);
38+
HitShape closestActiveShape = null;
39+
var closestDistance = int.MaxValue;
40+
41+
// PERF: Avoid using TraitsImplementing<HitShape> that needs to find the actor in the trait dictionary.
42+
foreach (var targetPos in victim.EnabledTargetablePositions)
43+
{
44+
if (targetPos is HitShape hitshape)
45+
{
46+
var distance = hitshape.DistanceFromEdge(victim, pos).Length;
47+
if (distance < closestDistance)
48+
{
49+
closestDistance = distance;
50+
closestActiveShape = hitshape;
51+
}
52+
}
53+
}
4354

4455
// Cannot be damaged without an active HitShape.
45-
if (closestActiveShape.HitShape == null)
56+
if (closestActiveShape == null)
4657
continue;
4758

4859
// Cannot be damaged if HitShape is outside Spread.
49-
if (closestActiveShape.Distance > Spread)
60+
if (closestDistance > Spread.Length)
5061
continue;
5162

52-
InflictDamage(victim, firedBy, closestActiveShape.HitShape, args);
63+
InflictDamage(victim, firedBy, closestActiveShape, args);
5364
}
5465
}
5566
}

OpenRA.Mods.Common/WorldExtensions.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
*/
1010
#endregion
1111

12+
using System;
1213
using System.Collections.Generic;
13-
using System.Linq;
1414
using OpenRA.Mods.Common.Traits;
1515

1616
namespace OpenRA.Mods.Common
@@ -49,9 +49,11 @@ public static IEnumerable<Actor> FindActorsOnLine(this World world, WPos lineSta
4949
foreach (var currActor in actorsInSquare)
5050
{
5151
var actorWidth = 0;
52-
var shapes = currActor.TraitsImplementing<HitShape>().Where(Exts.IsTraitEnabled);
53-
if (shapes.Any())
54-
actorWidth = shapes.Max(h => h.Info.Type.OuterRadius.Length);
52+
53+
// PERF: Avoid using TraitsImplementing<HitShape> that needs to find the actor in the trait dictionary.
54+
foreach (var targetPos in currActor.EnabledTargetablePositions)
55+
if (targetPos is HitShape hitshape)
56+
actorWidth = Math.Max(actorWidth, hitshape.Info.Type.OuterRadius.Length);
5557

5658
var projection = lineStart.MinimumPointLineProjection(lineEnd, currActor.CenterPosition);
5759
var distance = (currActor.CenterPosition - projection).HorizontalLength;

0 commit comments

Comments
 (0)