diff --git a/Source/CombatExtended/CombatExtended/Comps/CompSuppressable.cs b/Source/CombatExtended/CombatExtended/Comps/CompSuppressable.cs index b50014a225..322125000f 100644 --- a/Source/CombatExtended/CombatExtended/Comps/CompSuppressable.cs +++ b/Source/CombatExtended/CombatExtended/Comps/CompSuppressable.cs @@ -7,6 +7,7 @@ using Verse.AI; using UnityEngine; using CombatExtended.AI; +using CombatExtended.Compatibility; namespace CombatExtended { @@ -243,6 +244,10 @@ public void AddSuppression(float amount, IntVec3 origin) } } } + public bool IgnoreSuppresion(IntVec3 origin) + { + return BlockerRegistry.PawnUnsuppresableFromCallback(parent as Pawn, origin) || SuppressionUtility.InterceptorZonesFor((Pawn)parent).Where(x => x.Contains(parent.Position)).Any(x => !x.Contains(origin)); + } public override void CompTick() { diff --git a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs index 6c7bc7c71e..8dc14eb59b 100644 --- a/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs +++ b/Source/CombatExtended/CombatExtended/Projectiles/ProjectileCE.cs @@ -1084,7 +1084,8 @@ protected void ApplySuppression(Pawn pawn, float suppressionMultiplier = 1f) var compSuppressable = pawn.TryGetComp(); if (compSuppressable != null && pawn.Faction != launcher?.Faction - && (shield == null || shield.ShieldState == ShieldState.Resetting)) + && (shield == null || shield.ShieldState == ShieldState.Resetting) + && !compSuppressable.IgnoreSuppresion(OriginIV3)) { suppressionAmount = def.projectile.damageAmountBase * suppressionMultiplier; diff --git a/Source/CombatExtended/CombatExtended/SuppressionUtility.cs b/Source/CombatExtended/CombatExtended/SuppressionUtility.cs index d190764a49..0fdb81e4e8 100644 --- a/Source/CombatExtended/CombatExtended/SuppressionUtility.cs +++ b/Source/CombatExtended/CombatExtended/SuppressionUtility.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using CombatExtended.AI; +using CombatExtended.Compatibility; using CombatExtended.Utilities; using RimWorld; using UnityEngine; @@ -23,7 +24,7 @@ public static class SuppressionUtility private static DangerTracker dangerTracker; - private static List interceptors; + 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) { @@ -91,7 +92,6 @@ private static bool GetCoverPositionFrom(Pawn pawn, IntVec3 fromPosition, float { List cellList = new List(GenRadial.RadialCellsAround(pawn.Position, maxDist, true)); IntVec3 bestPos = pawn.Position; - interceptors = pawn.Map.listerThings.ThingsInGroup(ThingRequestGroup.ProjectileInterceptor).Select(t => t.TryGetComp()).ToList(); lightingTracker = pawn.Map.GetLightingTracker(); dangerTracker = pawn.Map.GetDangerTracker(); @@ -203,14 +203,8 @@ private static float GetCellCoverRatingForPawn(Pawn pawn, IntVec3 cell, IntVec3 cellRating += 10f - (bonusCellRating * 10f); - for (int i = 0; i < interceptors.Count; i++) - { - CompProjectileInterceptor interceptor = interceptors[i]; - if (interceptor.Active && interceptor.parent.Position.DistanceTo(cell) < interceptor.Props.radius) - { - cellRating += 15f; - } - } + // 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; // Avoid bullets and other danger sources; // Yet do not discard cover that is extremely good, even if it may be dangerous @@ -246,7 +240,20 @@ private static float GetCellCoverRatingForPawn(Pawn pawn, IntVec3 cell, IntVec3 } return cellRating; } - + 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) + { + result = result.Union(compatibilityZones); + } + return result; + } + private static bool IsOccupiedByEnemies(IEnumerable cells, Pawn pawn) + { + return cells.Any(cell => pawn.Map.thingGrid.ThingsListAt(cell).Any(thing => (thing.HostileTo(pawn)))); + } private static float GetCoverRating(Thing cover) { // Higher values mean more effective at being considered cover. diff --git a/Source/CombatExtended/Compatibility/BlockerRegistry.cs b/Source/CombatExtended/Compatibility/BlockerRegistry.cs index 484f205492..f0f3fdf7cd 100644 --- a/Source/CombatExtended/Compatibility/BlockerRegistry.cs +++ b/Source/CombatExtended/Compatibility/BlockerRegistry.cs @@ -15,10 +15,14 @@ public static class BlockerRegistry private static bool enabledCFC = false; private static bool enabledIS = false; private static bool enabledBCW = false; + private static bool enabledPUF = false; + private static bool enabledSZ = false; private static List> checkForCollisionBetweenCallbacks; private static List> checkCellForCollisionCallbacks; private static List> impactSomethingCallbacks; private static List> beforeCollideWithCallbacks; + private static List> pawnUnsuppresableFromCallback; + private static List>>> shieldZonesCallback; private static void EnableCB() { @@ -35,6 +39,16 @@ private static void EnableCFC() enabledCFC = true; checkCellForCollisionCallbacks = new List>(); } + private static void EnableSZ() + { + enabledSZ = true; + shieldZonesCallback = new List>>>(); + } + private static void EnablePUF() + { + enabledPUF = true; + pawnUnsuppresableFromCallback = new List>(); + } private static void EnableBCW() { enabledBCW = true; @@ -90,6 +104,23 @@ public static bool CheckForCollisionBetweenCallback(ProjectileCE projectile, Vec return false; } + public static void RegisterShieldZonesCallback(Func>> f) + { + if (!enabledSZ) + { + EnableSZ(); + } + shieldZonesCallback.Add(f); + } + public static void RegisterUnsuppresableFromCallback(Func f) + { + if (!enabledPUF) + { + EnablePUF(); + } + pawnUnsuppresableFromCallback.Add(f); + } + public static bool CheckCellForCollisionCallback(ProjectileCE projectile, IntVec3 cell, Thing launcher) { if (!enabledCFC) @@ -105,7 +136,6 @@ public static bool CheckCellForCollisionCallback(ProjectileCE projectile, IntVec } return false; } - public static bool ImpactSomethingCallback(ProjectileCE projectile, Thing launcher) { if (!enabledIS) @@ -121,7 +151,29 @@ public static bool ImpactSomethingCallback(ProjectileCE projectile, Thing launch } return false; } - + public static IEnumerable> ShieldZonesCallback(Thing thing) + { + if (!enabledSZ) + { + return null; + } + return shieldZonesCallback.SelectMany(cb => cb(thing)); + } + public static bool PawnUnsuppresableFromCallback(Pawn pawn, IntVec3 origin) + { + if (!enabledPUF) + { + return false; + } + foreach (var cb in pawnUnsuppresableFromCallback) + { + if (cb(pawn, origin)) + { + return true; + } + } + return false; + } public static bool BeforeCollideWithCallback(ProjectileCE projectile, Thing collideWith) { if (!enabledBCW) @@ -137,7 +189,6 @@ public static bool BeforeCollideWithCallback(ProjectileCE projectile, Thing coll } return false; } - public static Vector3 GetExactPosition(Vector3 origin, Vector3 curPosition, Vector3 shieldPosition, float radiusSq) { Vector3 velocity = curPosition - origin; diff --git a/Source/CombatExtended/Compatibility/EDShields.cs b/Source/CombatExtended/Compatibility/EDShields.cs index 4889f5b6af..c317515e2d 100644 --- a/Source/CombatExtended/Compatibility/EDShields.cs +++ b/Source/CombatExtended/Compatibility/EDShields.cs @@ -35,9 +35,12 @@ public void Install() { BlockerRegistry.RegisterCheckForCollisionBetweenCallback(EDShields.CheckForCollisionBetweenCallback); BlockerRegistry.RegisterImpactSomethingCallback(EDShields.ImpactSomethingCallback); + BlockerRegistry.RegisterShieldZonesCallback(EDShields.ShieldZonesCallback); Type t = Type.GetType("Jaxxa.EnhancedDevelopment.Shields.Shields.ShieldManagerMapComp, ED-Shields"); HitSoundDef = (SoundDef)t.GetField("HitSoundDef", BindingFlags.Static | BindingFlags.Public).GetValue(null); } + + public IEnumerable GetCompatList() { yield break; @@ -84,7 +87,6 @@ public static bool CheckForCollisionBetweenCallback(ProjectileCE projectile, Vec continue; } - int fieldRadiusSq = fieldRadius * fieldRadius; Quaternion shieldProjAng = Quaternion.LookRotation(from - shieldPosition2D); if ((Quaternion.Angle(targetAngle, shieldProjAng) > 90)) { @@ -156,5 +158,31 @@ public static void getShields(Map map) } } + private static IEnumerable> ShieldZonesCallback(Thing pawnToSuppress) + { + Map map = pawnToSuppress.Map; + getShields(map); + List> result = new List>(); + foreach (Building building in shields) + { + var shield = building as Building_Shield; + var generator = shield.GetComp(); + bool isActive = generator.IsActive(); + if (!isActive) + { + continue; + } + bool blockDirect = generator.BlockDirect_Active(); + if (!blockDirect) + { + continue; + } + //Is there no shields that doesn't intercept ingoing friendly projectiles? + int fieldRadius = (int)generator.FieldRadius_Active(); + result.Add(GenRadial.RadialCellsAround(shield.Position, fieldRadius, true)); + } + return result; + } + } } diff --git a/Source/CombatExtended/Compatibility/Rimatomics.cs b/Source/CombatExtended/Compatibility/Rimatomics.cs index c9e156cfed..d894714815 100644 --- a/Source/CombatExtended/Compatibility/Rimatomics.cs +++ b/Source/CombatExtended/Compatibility/Rimatomics.cs @@ -32,6 +32,7 @@ public void Install() { BlockerRegistry.RegisterCheckForCollisionBetweenCallback(Rimatomics.CheckForCollisionBetweenCallback); BlockerRegistry.RegisterImpactSomethingCallback(Rimatomics.ImpactSomethingCallback); + BlockerRegistry.RegisterShieldZonesCallback(Rimatomics.ShieldZonesCallback); } public IEnumerable GetCompatList() @@ -149,6 +150,28 @@ public static bool ImpactSomethingCallback(ProjectileCE projectile, Thing launch return false; } + private static IEnumerable> ShieldZonesCallback(Thing pawnToSuppress) + { + Map map = pawnToSuppress.Map; + getShields(map); + List> result = new List>(); + foreach (CompRimatomicsShield shield in shields) + { + if (!shield.Active || shield.ShieldState != ShieldState.Active) + { + continue; + } + if (GenHostility.HostileTo(pawnToSuppress, shield.parent) && !shield.debugInterceptNonHostileProjectiles && !shield.Props.interceptNonHostileProjectiles) + { + // Avoid hostile shields because they aren't intercepting friendly projectiles + continue; + } + + int fieldRadius = (int)shield.Radius; + result.Add(GenRadial.RadialCellsAround(shield.parent.Position, fieldRadius, true)); + } + return result; + } public static void getShields(Map map) { diff --git a/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs b/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs index bbe5a8c331..a3d939db83 100644 --- a/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs +++ b/Source/CombatExtended/Compatibility/VanillaExpandedFramework.cs @@ -26,7 +26,28 @@ IEnumerable IPatch.GetCompatList() void IPatch.Install() { BlockerRegistry.RegisterCheckForCollisionCallback(CheckIntercept); + BlockerRegistry.RegisterShieldZonesCallback(ShieldZonesCallback); } + + private IEnumerable> ShieldZonesCallback(Thing pawnToSuppress) + { + IEnumerable interceptors = CompShieldField.ListerShieldGensActiveIn(pawnToSuppress.Map).ToList(); + List> result = new List>(); + if (!interceptors.Any()) + { + return result; + } + foreach (var interceptor in interceptors) + { + if (!interceptor.CanFunction) + { + continue; + } + result.Add(GenRadial.RadialCellsAround(interceptor.HostThing.Position, interceptor.ShieldRadius, true)); + } + return result; + } + private static bool CheckIntercept(ProjectileCE projectile, IntVec3 cell, Thing launcher) { if (projectile.def.projectile.flyOverhead) diff --git a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs index 9cc5d19976..e0ff231ef2 100644 --- a/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs +++ b/Source/CombatExtended/Compatibility/VanillaPsycastExpanded.cs @@ -26,12 +26,35 @@ public IEnumerable GetCompatList() } public void Install() { + BlockerRegistry.RegisterImpactSomethingCallback(ImpactSomething); //temp commented BlockerRegistry.RegisterBeforeCollideWithCallback(BeforeCollideWith); BlockerRegistry.RegisterCheckForCollisionCallback(Hediff_Overshield_InterceptCheck); BlockerRegistry.RegisterCheckForCollisionBetweenCallback(AOE_CheckIntercept); + BlockerRegistry.RegisterShieldZonesCallback(ShieldZones); + BlockerRegistry.RegisterUnsuppresableFromCallback(Unsuppresable); + } + private static Dictionary>> shieldZones; + private static int shieldZonesCacheTick = -1; + private static IEnumerable> ShieldZones(Thing thing) + { + IEnumerable> result = null; + var currentTick = GenTicks.TicksGame; + if (shieldZonesCacheTick != currentTick) + { + shieldZonesCacheTick = currentTick; + shieldZones = new Dictionary>>(); + } + if (!shieldZones.TryGetValue(thing.Map, out result)) + { + result = thing.Map.listerThings.ThingsInGroup(ThingRequestGroup.Pawn).Cast().SelectMany(x => x.health.hediffSet.hediffs).Where(x => x is Hediff_Overshield).Select(x => { var ho = x as Hediff_Overshield; return GenRadial.RadialCellsAround(ho.pawn.Position, ho.OverlaySize, true); }).ToList(); + shieldZones.Add(thing.Map, result); + } + return result; } + private static bool Unsuppresable(Pawn pawn, IntVec3 origin) => pawn.health.hediffSet.hediffs.Any(x => x.GetType() == typeof(Hediff_Overshield)); + private static bool BeforeCollideWith(ProjectileCE projectile, Thing collideWith) { if (collideWith is Pawn pawn)