public static bool Prefix(LineOfSight __instance, ref LineOfFireLevel __result, CombatGameState ___Combat, AbstractActor source, Vector3 sourcePosition, ICombatant target, Vector3 targetPosition, Quaternion targetRotation, out Vector3 collisionWorldPos) { Mod.Log.Trace?.Write($"LOS:GLOFU entered. "); Vector3 forward = targetPosition - sourcePosition; forward.y = 0f; Quaternion rotation = Quaternion.LookRotation(forward); Vector3[] lossourcePositions = source.GetLOSSourcePositions(sourcePosition, rotation); Vector3[] lostargetPositions = target.GetLOSTargetPositions(targetPosition, targetRotation); List <AbstractActor> allActors = new List <AbstractActor>(___Combat.AllActors); allActors.Remove(source); AbstractActor abstractActor = target as AbstractActor; string targetedBuildingGuid = null; if (abstractActor != null) { allActors.Remove(abstractActor); } else { targetedBuildingGuid = target.GUID; } LineSegment lineSegment = new LineSegment(sourcePosition, targetPosition); // Sort the target actors by distance from the source allActors.Sort((AbstractActor x, AbstractActor y) => Vector3.Distance(x.CurrentPosition, sourcePosition).CompareTo(Vector3.Distance(y.CurrentPosition, sourcePosition)) ); float targetPositionDistance = Vector3.Distance(sourcePosition, targetPosition); for (int i = allActors.Count - 1; i >= 0; i--) { if (allActors[i].IsDead || Vector3.Distance(allActors[i].CurrentPosition, sourcePosition) > targetPositionDistance || lineSegment.DistToPoint(allActors[i].CurrentPosition) > allActors[i].Radius * 5f) { // If the actor is // 1) dead // 2) the distance from actor to source is greater than targetPos distance // 3) the distance to the actor is greater than the radious of all actors (?!?) // remove the actor from consideration allActors.RemoveAt(i); } } float sourcePositionsWithLineOfFireToTargetPositions = 0f; // num2 float losTargetPositionsCount = 0f; // num3 float weaponsWithUnobstructedLOF = 0f; // num4 collisionWorldPos = targetPosition; float shortestDistanceFromVectorToIteratedActor = 999999.9f; // num5 Weapon longestRangeWeapon = source.GetLongestRangeWeapon(false, false); float maximumWeaponRangeForSource = (longestRangeWeapon != null) ? longestRangeWeapon.MaxRange : 0f; // MY CHANGE: float adjustedSpotterRange = ___Combat.LOS.GetAdjustedSpotterRange(source, abstractActor); float adjustedSensorRange = ___Combat.LOS.GetAdjustedSensorRange(source, abstractActor); //LowVisibility.Logger.Log($"LineOfSight:GetLineOfFireUncached:pre - using sensorRange:{adjustedSensorRange} instead of spotterRange:{adjustedSpotterRange}. Max weapon range is:{maximumWeaponRangeForSource} "); maximumWeaponRangeForSource = Mathf.Max(maximumWeaponRangeForSource, adjustedSensorRange, adjustedSpotterRange); for (int j = 0; j < lossourcePositions.Length; j++) { // Iterate the source positions (presumably each weapon has different source locations) for (int k = 0; k < lostargetPositions.Length; k++) { // Iterate the target positions (presumably each build/mech has differnet locations) losTargetPositionsCount += 1f; float distanceFromSourceToTarget = Vector3.Distance(lossourcePositions[j], lostargetPositions[k]); if (distanceFromSourceToTarget <= maximumWeaponRangeForSource) { // Possible match, check for collisions lineSegment = new LineSegment(lossourcePositions[j], lostargetPositions[k]); bool canUseDirectAttack = false; Vector3 vector; if (targetedBuildingGuid == null) { // Not a building, so check for compatible actors for (int l = 0; l < allActors.Count; l++) { if (lineSegment.DistToPoint(allActors[l].CurrentPosition) < allActors[l].Radius) { vector = NvMath.NearestPointStrict(lossourcePositions[j], lostargetPositions[k], allActors[l].CurrentPosition); float distanceFromVectorToIteratedActor = Vector3.Distance(vector, allActors[l].CurrentPosition); if (distanceFromVectorToIteratedActor < allActors[l].HighestLOSPosition.y) { // TODO: Could I have this flipped, and .y is the highest y in the path? This is checking for indirect fire? // If the height of the attack is less than the HighestLOSPosition.y value, we have found the match? canUseDirectAttack = true; weaponsWithUnobstructedLOF += 1f; if (distanceFromVectorToIteratedActor < shortestDistanceFromVectorToIteratedActor) { shortestDistanceFromVectorToIteratedActor = distanceFromVectorToIteratedActor; collisionWorldPos = vector; } break; } } } } // If there is a source position with LOS to the target, record it if (__instance.HasLineOfFire(lossourcePositions[j], lostargetPositions[k], targetedBuildingGuid, maximumWeaponRangeForSource, out vector)) { sourcePositionsWithLineOfFireToTargetPositions += 1f; if (targetedBuildingGuid != null) { break; } } else { // There is no LineOfFire between the source and targert position if (canUseDirectAttack) { weaponsWithUnobstructedLOF -= 1f; } float distanceFromVectorToSourcePosition = Vector3.Distance(vector, sourcePosition); if (distanceFromVectorToSourcePosition < shortestDistanceFromVectorToIteratedActor) { shortestDistanceFromVectorToIteratedActor = distanceFromVectorToSourcePosition; // There is a collection somewhere in the path (MAYBE?) collisionWorldPos = vector; } } } } if (targetedBuildingGuid != null && sourcePositionsWithLineOfFireToTargetPositions > 0.5f) { break; } } // If a building, ignore the various positions (WHY?) float ratioSourcePosToTargetPos = (targetedBuildingGuid != null) ? sourcePositionsWithLineOfFireToTargetPositions : (sourcePositionsWithLineOfFireToTargetPositions / losTargetPositionsCount); // "MinRatioFromActors": 0.2, float b = ratioSourcePosToTargetPos - ___Combat.Constants.Visibility.MinRatioFromActors; float ratioDirectAttacksToTargetPositions = Mathf.Min(weaponsWithUnobstructedLOF / losTargetPositionsCount, b); if (ratioDirectAttacksToTargetPositions > 0.001f) { ratioSourcePosToTargetPos -= ratioDirectAttacksToTargetPositions; } //LowVisibility.Logger.Log($"LineOfSight:GetLineOfFireUncached:pre - ratio is:{ratioSourcePosToTargetPos} / direct:{ratioDirectAttacksToTargetPositions} / b:{b}"); // "RatioFullVis": 0.79, // "RatioObstructedVis": 0.41, if (ratioSourcePosToTargetPos >= ___Combat.Constants.Visibility.RatioFullVis) { __result = LineOfFireLevel.LOFClear; } else if (ratioSourcePosToTargetPos >= ___Combat.Constants.Visibility.RatioObstructedVis) { __result = LineOfFireLevel.LOFObstructed; } else { __result = LineOfFireLevel.LOFBlocked; } Mod.Log.Trace?.Write($"LOS:GLOFU LOS result is:{__result}"); return(false); }
public static Vector3 GetBuildingHitPosition(this LineOfSight LOS, AbstractActor attacker, BattleTech.Building target, Vector3 attackPosition, float weaponRange, Vector3 origHitPosition) { Vector3 a = origHitPosition; Vector3 vector3_1 = attackPosition + attacker.HighestLOSPosition; string guid = target.GUID; Vector3 collisionWorldPos = Vector3.zero; bool flag = false; if ((UnityEngine.Object)target.BuildingRep == (UnityEngine.Object)null) { return(a); } foreach (Collider allRaycastCollider in target.GameRep.AllRaycastColliders) { if (LOS.HasLineOfFire(vector3_1, allRaycastCollider.bounds.center, guid, weaponRange, out collisionWorldPos)) { a = allRaycastCollider.bounds.center; flag = true; break; } } for (int index1 = 0; index1 < target.LOSTargetPositions.Length; ++index1) { if (LOS.HasLineOfFire(vector3_1, target.LOSTargetPositions[index1], guid, weaponRange, out collisionWorldPos)) { if (flag) { Vector3 end = Vector3.Lerp(a, target.LOSTargetPositions[index1], UnityEngine.Random.Range(0.0f, 0.15f)); if (LOS.HasLineOfFire(vector3_1, end, guid, weaponRange, out collisionWorldPos)) { a = end; } } else { Vector3 vector3_2 = a; for (int index2 = 0; index2 < 10; ++index2) { vector3_2 = Vector3.Lerp(vector3_2, target.LOSTargetPositions[index1], UnityEngine.Random.Range(0.1f, 0.6f)); if (LOS.HasLineOfFire(vector3_1, vector3_2, guid, weaponRange, out collisionWorldPos)) { a = vector3_2; flag = true; break; } } if (!flag) { a = target.LOSTargetPositions[index1]; flag = true; } } } } Ray ray = new Ray(vector3_1, a - vector3_1); foreach (Collider allRaycastCollider in target.GameRep.AllRaycastColliders) { GameObject gameObject = allRaycastCollider.gameObject; bool activeSelf = gameObject.activeSelf; gameObject.SetActive(true); RaycastHit hitInfo; if (allRaycastCollider.Raycast(ray, out hitInfo, 1000f)) { gameObject.SetActive(activeSelf); return(hitInfo.point); } gameObject.SetActive(activeSelf); } return(a); }