diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
index 4f46de9c68..8bdb719fca 100644
--- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
+++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs
@@ -801,6 +801,11 @@ protected bool CheckForCollisionBetween()
return collided;
}
+ ///
+ /// Cache field holding things that a projectile might collide with.
+ ///
+ private static readonly List potentialCollisionCandidates = new List();
+
///
/// Checks whether a collision occurs along flight path within this cell.
///
@@ -814,35 +819,49 @@ protected bool CheckCellForCollision(IntVec3 cell)
}
var roofChecked = false;
- var mainThingList = new List(Map.thingGrid.ThingsListAtFast(cell)).Where(t => t is Pawn || t.def.Fillage != FillCategory.None).ToList();
+ potentialCollisionCandidates.Clear();
+
+ foreach (var thing in Map.thingGrid.ThingsListAtFast(cell))
+ {
+ if (thing is Pawn || thing.def.Fillage != FillCategory.None)
+ {
+ potentialCollisionCandidates.AddDistinct(thing);
+ }
+ }
//Find pawns in adjacent cells and append them to main list
- var adjList = new List();
var rot4 = Rot4.FromAngleFlat(shotRotation);
if (rot4.rotInt > 1)
{
//For some reason south and west returns incorrect adjacent cells collection
rot4 = rot4.Opposite;
}
- adjList.AddRange(GenAdj.CellsAdjacentCardinal(cell, rot4, new IntVec2(collisionCheckSize, 0)).ToList());
+
if (Controller.settings.DebugDrawInterceptChecks)
{
Map.debugDrawer.debugCells.Clear();
Map.debugDrawer.DebugDrawerUpdate();
}
//Iterate through adjacent cells and find all the pawns
- foreach (var curCell in adjList)
+ foreach (var curCell in GenAdj.CellsAdjacentCardinal(cell, rot4, new IntVec2(collisionCheckSize, 0)))
{
- if (curCell != cell && curCell.InBounds(Map))
+ if (curCell == cell || !curCell.InBounds(Map))
{
- mainThingList.AddRange(Map.thingGrid.ThingsListAtFast(curCell)
- .Where(x => x is Pawn));
+ continue;
+ }
- if (Controller.settings.DebugDrawInterceptChecks)
+ foreach (var thing in Map.thingGrid.ThingsListAtFast(curCell))
+ {
+ if (thing is Pawn)
{
- Map.debugDrawer.FlashCell(curCell, 0.7f);
+ potentialCollisionCandidates.AddDistinct(thing);
}
}
+
+ if (Controller.settings.DebugDrawInterceptChecks)
+ {
+ Map.debugDrawer.FlashCell(curCell, 0.7f);
+ }
}
//If the last position is above the wallCollisionHeight, we should check for roof intersections first
@@ -855,8 +874,15 @@ protected bool CheckCellForCollision(IntVec3 cell)
roofChecked = true;
}
- foreach (var thing in mainThingList.Distinct().Where(x => !(x is ProjectileCE)).OrderBy(x => (x.DrawPos - LastPos).sqrMagnitude))
+ potentialCollisionCandidates.SortBy(thing => (thing.DrawPos - LastPos).sqrMagnitude);
+
+ foreach (var thing in potentialCollisionCandidates)
{
+ if (thing is ProjectileCE)
+ {
+ continue;
+ }
+
if ((thing == launcher || thing == mount) && !canTargetSelf)
{
continue;
diff --git a/Source/CombatExtended/CombatExtended/SuppressionUtility.cs b/Source/CombatExtended/CombatExtended/SuppressionUtility.cs
index c88cea0785..6bbbf82560 100644
--- a/Source/CombatExtended/CombatExtended/SuppressionUtility.cs
+++ b/Source/CombatExtended/CombatExtended/SuppressionUtility.cs
@@ -24,8 +24,6 @@ public static class SuppressionUtility
private static DangerTracker dangerTracker;
- private static IEnumerable Interceptors(Thing pawn) => pawn.Map.listerThings.ThingsInGroup(ThingRequestGroup.ProjectileInterceptor).Select(t => t.TryGetComp()).Where(x => x.Props.interceptNonHostileProjectiles || !x.parent.HostileTo(pawn));
-
public static bool TryRequestHelp(Pawn pawn)
{
//TODO: 1.5
@@ -150,7 +148,7 @@ private static float GetCellCoverRatingForPawn(Pawn pawn, IntVec3 cell, IntVec3
}
}
- float cellRating = 0f, bonusCellRating = 1f, distToSuppressor = (pawn.Position - shooterPos).LengthHorizontal,
+ float cellRating = 0f, bonusCellRating = 1f,
pawnHeightFactor = CE_Utility.GetCollisionBodyFactors(pawn).y,
pawnVisibleOverCoverFillPercent = pawnHeightFactor * (1f - CollisionVertical.BodyRegionMiddleHeight) + 0.01f,
pawnLowestCrouchFillPercent = pawnHeightFactor * CollisionVertical.BodyRegionBottomHeight + pawnVisibleOverCoverFillPercent,
@@ -209,7 +207,7 @@ private static float GetCellCoverRatingForPawn(Pawn pawn, IntVec3 cell, IntVec3
cellRating += 10f - (bonusCellRating * 10f);
// If the cell is covered by a shield and there are no enemies inside, then increases by 15 (for each such shield)
- cellRating += InterceptorZonesFor(pawn).Where(x => !IsOccupiedByEnemies(x, pawn)).Count(x => x.Contains(cell)) * 15;
+ cellRating += CalculateShieldRating(cell, pawn);
// Avoid bullets and other danger sources;
// Yet do not discard cover that is extremely good, even if it may be dangerous
@@ -245,19 +243,78 @@ private static float GetCellCoverRatingForPawn(Pawn pawn, IntVec3 cell, IntVec3
}
return cellRating;
}
+
+ ///
+ /// Calculate the additional cover rating from shields covering the given cell.
+ ///
+ /// The cell to compute the cover rating for.
+ /// The pawn seeking cover.
+ /// The computed cover rating (15 for each shield covering the cell).
+ private static int CalculateShieldRating(IntVec3 cell, Pawn pawn)
+ {
+ int rating = 0;
+ foreach (var zone in InterceptorZonesFor(pawn))
+ {
+ foreach (var zoneCell in zone)
+ {
+ if (zoneCell == cell)
+ {
+ if (!IsOccupiedByEnemies(zone, pawn))
+ {
+ rating += 15;
+ }
+
+ break;
+ }
+ }
+ }
+
+ return rating;
+ }
+
+ ///
+ /// Get areas covered by a shield that may be suitable for protecting the given pawn.
+ ///
+ /// The pawn seeking cover.
+ /// An enumerator of areas covered by shields on the map that may protect the pawn.
public static IEnumerable> InterceptorZonesFor(Pawn pawn)
{
- var result = Interceptors(pawn).Where(x => x.Active).Select(x => GenRadial.RadialCellsAround(x.parent.Position, x.Props.radius, true));
- var compatibilityZones = BlockerRegistry.ShieldZonesCallback(pawn);
- if (compatibilityZones != null)
+ foreach (var interceptor in pawn.Map.listerThings.ThingsInGroup(ThingRequestGroup.ProjectileInterceptor))
+ {
+ var comp = interceptor.TryGetComp();
+ if (comp.Active && (comp.Props.interceptNonHostileProjectiles || !interceptor.HostileTo(pawn)))
+ {
+ yield return GenRadial.RadialCellsAround(interceptor.Position, comp.Props.radius, true);
+ }
+ }
+
+ foreach (var zone in BlockerRegistry.ShieldZonesCallback(pawn))
{
- result = result.Union(compatibilityZones);
+ yield return zone;
}
- return result;
}
+
+ ///
+ /// Check whether the given area contains any objects hostile to the given pawn.
+ ///
+ /// The area to scan for hostile objects.
+ /// The pawn.
+ /// true if the area contained any hostile objects, false otherwise.
private static bool IsOccupiedByEnemies(IEnumerable cells, Pawn pawn)
{
- return cells.Any(cell => pawn.Map.thingGrid.ThingsListAt(cell).Any(thing => (thing.HostileTo(pawn))));
+ foreach (var cell in cells)
+ {
+ var things = pawn.Map.thingGrid.ThingsListAt(cell);
+ foreach (var thing in things)
+ {
+ if (thing.HostileTo(pawn))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
}
private static float GetCoverRating(Thing cover)
{
diff --git a/Source/CombatExtended/Compatibility/BlockerRegistry.cs b/Source/CombatExtended/Compatibility/BlockerRegistry.cs
index 5489f34c28..3418a1dbdb 100644
--- a/Source/CombatExtended/Compatibility/BlockerRegistry.cs
+++ b/Source/CombatExtended/Compatibility/BlockerRegistry.cs
@@ -155,9 +155,16 @@ public static IEnumerable> ShieldZonesCallback(Thing thing)
{
if (!enabledSZ)
{
- return null;
+ yield break;
+ }
+
+ foreach (var callback in shieldZonesCallback)
+ {
+ foreach (var zone in callback(thing))
+ {
+ yield return zone;
+ }
}
- return shieldZonesCallback.SelectMany(cb => cb(thing));
}
public static bool PawnUnsuppressableFromCallback(Pawn pawn, IntVec3 origin)
{