diff --git a/.vs/SAIN/FileContentIndex/4a974a1c-f76a-407b-81eb-a0a899857539.vsidx b/.vs/SAIN/FileContentIndex/4a974a1c-f76a-407b-81eb-a0a899857539.vsidx deleted file mode 100644 index a80ddd922..000000000 Binary files a/.vs/SAIN/FileContentIndex/4a974a1c-f76a-407b-81eb-a0a899857539.vsidx and /dev/null differ diff --git a/.vs/SAIN/FileContentIndex/8bd65465-315d-4aea-a8b5-9bbaa26b2eea.vsidx b/.vs/SAIN/FileContentIndex/8bd65465-315d-4aea-a8b5-9bbaa26b2eea.vsidx new file mode 100644 index 000000000..1ad8b1b5c Binary files /dev/null and b/.vs/SAIN/FileContentIndex/8bd65465-315d-4aea-a8b5-9bbaa26b2eea.vsidx differ diff --git a/.vs/SAIN/FileContentIndex/919a002a-4520-4ac9-8e6c-7bcc66e771e7.vsidx b/.vs/SAIN/FileContentIndex/919a002a-4520-4ac9-8e6c-7bcc66e771e7.vsidx new file mode 100644 index 000000000..cc3ea889c Binary files /dev/null and b/.vs/SAIN/FileContentIndex/919a002a-4520-4ac9-8e6c-7bcc66e771e7.vsidx differ diff --git a/Components/SAIN Bot Component/Classes/Objects/SAINEnemyClass.cs b/Components/SAIN Bot Component/Classes/Objects/SAINEnemyClass.cs index eb88d61de..ee9d337a9 100644 --- a/Components/SAIN Bot Component/Classes/Objects/SAINEnemyClass.cs +++ b/Components/SAIN Bot Component/Classes/Objects/SAINEnemyClass.cs @@ -1,5 +1,7 @@ -using EFT; +using BepInEx.Logging; +using EFT; using SAIN.Components; +using static SAIN.UserSettings.VisionConfig; using SAIN.Helpers; using UnityEngine; using UnityEngine.AI; @@ -15,60 +17,208 @@ public SAINEnemy(BotOwner bot, IAIDetails person) { BotOwner = bot; Person = person; + Logger = BepInEx.Logging.Logger.CreateLogSource(GetType().Name); } + private readonly ManualLogSource Logger; + public void Update() { + UpdateDistance(); UpdateVisible(); UpdatePath(); } + private float RayCastFreq + { + get + { + const float CloseCap = 10f; + const float FarCap = 90f; + const float baseTime = 0.1f; + const float baseTimeAdd = 0.2f; + const float maxTime = 0.5f; + + float distance = DistanceFromLastSeen; + + float clampedClose = Mathf.Clamp(distance, 0f, CloseCap); + float scaled = clampedClose / CloseCap; + scaled *= baseTimeAdd; + scaled += baseTime; + + float result = scaled; + + if (distance > CloseCap) + { + float clampedFar = Mathf.Clamp(distance - CloseCap, 0f, FarCap); + float farScale = clampedFar / FarCap; + farScale *= baseTimeAdd; + result += farScale; + } + + float clampedResult = Mathf.Clamp(result, 0f, maxTime); + + if (DebugTimer < Time.time && DebugVision.Value && Person.GetPlayer.ProfileId == Plugin.MainPlayer.ProfileId) + { + DebugTimer = Time.time + 1f; + Logger.LogDebug($"RayCast Frequency result: [{clampedResult}]"); + } + return clampedResult; + } + } + + private float DebugTimer = 0f; + + private void UpdateDistance() + { + if (DistanceTimer < Time.time) + { + DistanceTimer = Time.time + 0.25f; + float distance = GetMagnitudeToBot(Position); + RealDistance = distance; + LastSeenDistance = IsVisible ? distance : GetMagnitudeToBot(PositionLastSeen); + DistanceFromLastSeen = IsVisible ? 0f : (PositionLastSeen - Position).magnitude; + } + } + + public float DistanceFromLastSeen { get; private set; } + public Vector3 Position => Person.Position; + + private float GetMagnitudeToBot(Vector3 point) + { + return (BotOwner.Position - point).magnitude; + } + + public float RealDistance { get; private set; } + public float LastSeenDistance { get; private set; } + public Vector3 PositionLastSeen { get; private set; } + public float TimeSinceSeen { get; private set; } + public float TimeSeen { get; private set; } + public bool Seen { get; private set; } + public float TimeFirstSeen { get; private set; } + public float TimeLastSeen { get; private set; } + + private float DistanceTimer = 0f; + private bool Reacting = false; + private float ReactionTimer = 0f; + private void UpdateVisible() { if (CheckVisibleTimer < Time.time) { - CheckVisibleTimer = Time.time + 0.15f; - - bool lineOfSight = CheckEnemyVisible(); + CheckVisibleTimer = Time.time + RayCastFreq; + bool lineOfSight = false; bool visible = false; - if (lineOfSight == true && GoalEnemy?.IsVisible == true) + bool wasVisible = IsVisible; + + if (CheckPartsVisible()) { - visible = BotOwner.LookSensor.IsPointInVisibleSector(Person.Position); + lineOfSight = true; + if (GoalEnemy?.IsVisible == true && BotOwner.LookSensor.IsPointInVisibleSector(Person.Position)) + { + if (!Reacting) + { + Reacting = true; + ReactionTimer = Time.time + 0.2f; + } + else + { + if (ReactionTimer < Time.time) + { + visible = true; + } + } + } + } + if (visible == true) + { + TimeSeen = Time.time; + if (!Seen) + { + TimeFirstSeen = Time.time; + Seen = true; + } + } + if (visible == false) + { + if (wasVisible) + { + TimeLastSeen = Time.time; + PositionLastSeen = Person.Position; + } + if (Seen) + { + TimeSinceSeen = Time.time - TimeLastSeen; + } + Reacting = false; } - IsVisible = visible; InLineOfSight = lineOfSight; + IsVisible = visible; } } - private bool CheckEnemyVisible() + private bool CheckPartsVisible() { Vector3 HeadPosition = BotOwner.LookSensor._headPoint; float distance = (Person.Position - BotOwner.Position).magnitude; + float maxDistance = BotOwner.Settings.Current.CurrentVisibleDistance; - LayerMask Mask; - - if (distance < 10f) - { - Mask = LayerMaskClass.HighPolyWithTerrainMask; - } - else + if (distance >= maxDistance) { - Mask = LayerMaskClass.HighPolyWithTerrainMaskAI; + return false; } + var NoFoliageMask = LayerMaskClass.HighPolyWithTerrainMask; + var FoliageMask = LayerMaskClass.HighPolyWithTerrainMaskAI; + LayerMask Mask = distance < 5f ? NoFoliageMask : FoliageMask; + foreach (var part in Person.MainParts.Values) { Vector3 partPos = part.Position; Vector3 Direction = partPos - HeadPosition; - if (!Physics.Raycast(HeadPosition, Direction, Direction.magnitude, Mask)) + float visionCap = Mathf.Clamp(Direction.magnitude, 0f, BotOwner.Settings.Current.CurrentVisibleDistance); + if (!Physics.Raycast(HeadPosition, Direction, out var hit, visionCap, Mask)) { var weapon = BotOwner.WeaponRoot.position; Direction = partPos - weapon; CanShoot = !Physics.Raycast(weapon, Direction, Direction.magnitude, Mask); + + if (DebugVision.Value && Mask == NoFoliageMask) + { + DebugGizmos.SingleObjects.Line(HeadPosition, EnemyChestPosition, Color.red, 0.01f, true, 1f, true); + Logger.LogDebug($"[{BotOwner.name}] can see through foliage because distance < 5f"); + } + return true; } + else + { + if (Mask == LayerMaskClass.HighPolyWithTerrainMaskAI) + { + Vector3 hitPos = hit.point; + Vector3 hitToTarget = part.Position - hitPos; + if (hitToTarget.magnitude < 1f) + { + Vector3 directionAwayFromTarget = -hitToTarget.normalized * 0.1f; + Vector3 newRayPos = hitPos + directionAwayFromTarget; + Direction = part.Position - newRayPos; + if (!Physics.Raycast(newRayPos, Direction.normalized, Direction.magnitude, LayerMaskClass.HighPolyWithTerrainMask)) + { + if (DebugVision.Value) + { + DebugGizmos.SingleObjects.Line(newRayPos, EnemyChestPosition, Color.red, 0.01f, true, 1f, true); + Logger.LogDebug($"[{BotOwner.name}] can see through foliage because distance < 1f"); + } + + return true; + } + } + + //Vector3 StartToHit = hitPos - HeadPosition; + } + } } return false; } diff --git a/Components/SAIN Bot Component/Classes/Objects/SearchMoveObject.cs b/Components/SAIN Bot Component/Classes/Objects/SearchMoveObject.cs index 247d050db..159ec194e 100644 --- a/Components/SAIN Bot Component/Classes/Objects/SearchMoveObject.cs +++ b/Components/SAIN Bot Component/Classes/Objects/SearchMoveObject.cs @@ -17,10 +17,12 @@ public class SearchMoveObject : SAINBot public SearchMoveObject(BotOwner bot, NavMeshAgent agent) : base(bot) { - Logger = BepInEx.Logging.Logger.CreateLogSource(GetType().Name); Agent = agent; + Logger = BepInEx.Logging.Logger.CreateLogSource(GetType().Name); } + private readonly ManualLogSource Logger; + public bool Update(float reachDist = -1f) { if (CornerDestination != null && Destination != null) @@ -287,8 +289,6 @@ private float GetSignedAngle(Vector3 dirCenter, Vector3 dirOther, Vector3? axis public Vector3[] PathWay { get; private set; } public int PathWayIndex { get; private set; } public int PathWayIndexMax { get; private set; } - - private readonly ManualLogSource Logger; } public class MoveDangerPoint diff --git a/Components/SAIN Bot Component/SAINComponent.cs b/Components/SAIN Bot Component/SAINComponent.cs index c4cac1d39..3214aa4a2 100644 --- a/Components/SAIN Bot Component/SAINComponent.cs +++ b/Components/SAIN Bot Component/SAINComponent.cs @@ -87,33 +87,6 @@ private void UpdateEnemy() public SAINEnemy Enemy { get; private set; } - public bool ShiftAwayFromCloseWall(Vector3 target, out Vector3 newPos) - { - const float closeDist = 0.75f; - - if (CheckTooCloseToWall(target, out var rayHit, closeDist)) - { - var direction = (BotOwner.Position - rayHit.point).normalized * 0.8f; - direction.y = 0f; - var movePoint = BotOwner.Position + direction; - if (NavMesh.SamplePosition(movePoint, out var hit, 0.1f, -1)) - { - newPos = hit.position; - return true; - } - } - newPos = Vector3.zero; - return false; - } - - public bool CheckTooCloseToWall(Vector3 target, out RaycastHit rayHit, float checkDist = 0.75f) - { - Vector3 botPos = BotOwner.Position; - Vector3 direction = target - botPos; - botPos.y = WeaponRoot.y; - return Physics.Raycast(BotOwner.Position, direction, out rayHit, checkDist, LayerMaskClass.HighPolyWithTerrainMask); - } - public void Dispose() { StopAllCoroutines(); @@ -138,25 +111,11 @@ public void Dispose() public Vector3? GoalTargetPos => BotOwner.Memory.GoalTarget?.GoalTarget?.Position; public Vector3? GoalEnemyPos => BotOwner.Memory.GoalEnemy?.CurrPosition; - public Vector3 MidPoint(Vector3 target, float lerpVal = 0.5f) - { - return Vector3.Lerp(BotOwner.Position, target, lerpVal); - } - public bool BotIsAtPoint(Vector3 point, float reachDist = 1f) - { - return DistanceToDestination(point) < reachDist; - } - - public float DistanceToDestination(Vector3 point) - { - return Vector3.Distance(point, BotOwner.Transform.position); - } - public bool BotHasStamina => BotOwner.GetPlayer.Physical.Stamina.NormalValue > 0f; public Vector3 UnderFireFromPosition { get; set; } - public bool HasEnemyAndCanShoot => BotOwner.Memory.GoalEnemy?.CanShoot == true && BotOwner.Memory.GoalEnemy?.IsVisible == true; + public bool HasEnemyAndCanShoot => Enemy?.IsVisible == true; public AILimitClass AILimit { get; private set; } diff --git a/Config/VisionConfig.cs b/Config/VisionConfig.cs index 41524fdc6..6ae8a86bb 100644 --- a/Config/VisionConfig.cs +++ b/Config/VisionConfig.cs @@ -7,6 +7,7 @@ internal class VisionConfig public static ConfigEntry AbsoluteMaxVisionDistance { get; private set; } public static ConfigEntry NoGlobalFog { get; private set; } public static ConfigEntry DebugWeather { get; private set; } + public static ConfigEntry DebugVision { get; private set; } public static void Init(ConfigFile Config) { @@ -22,7 +23,12 @@ public static void Init(ConfigFile Config) new AcceptableValueRange(100f, 800.0f), new ConfigurationManagerAttributes { IsAdvanced = true, Order = 2 })); - DebugWeather = Config.Bind(debugmode, "Debug Logs", false, + DebugWeather = Config.Bind(debugmode, "Debug Weather", false, + new ConfigDescription("", + null, + new ConfigurationManagerAttributes { IsAdvanced = true, Order = 1 })); + + DebugVision = Config.Bind(debugmode, "Debug Vision", false, new ConfigDescription("", null, new ConfigurationManagerAttributes { IsAdvanced = true, Order = 1 }));