From 60b0335a597ad8b3d2331ee7e9d3c3e1312b9beb Mon Sep 17 00:00:00 2001 From: Solarint Date: Wed, 26 Apr 2023 18:09:24 -0500 Subject: [PATCH] Bug Fixed Flashlights 2.0. Release --- SAIN Combat/Patches/RateofFirePatch.cs | 7 +- SAIN Combat/Patches/RecoilPatch.cs | 13 +- SAIN Combat/Plugin.cs | 2 +- .../Components/FlashlightDetection.cs | 232 ++++++++++++++---- .../Components/SAIN_Flashlight_Component.cs | 23 +- SAIN Flashlights/Config/DazzleConfig.cs | 30 ++- SAIN Flashlights/Helpers/FlashlightHelpers.cs | 92 +++---- .../Patches/CheckFlashlightPatch.cs | 14 ++ SAIN Flashlights/Patches/DazzlePatch.cs | 2 +- SAIN Flashlights/Plugin.cs | 2 +- SAIN Flashlights/Properties/AssemblyInfo.cs | 4 +- SAIN Flashlights/SAIN Flashlights.csproj | 2 +- .../Helpers/{Math.cs => SAIN_Math.cs} | 4 +- SAIN Helpers/SAIN Helpers.csproj | 2 +- SAIN Movement/Patches/BotMemoryPatch.cs | 1 - SAIN Movement/Patches/DogFightPatch.cs | 33 ++- SAIN Movement/Patches/MovementPatch.cs | 146 +---------- SAIN-Helpers.dll | Bin 22528 -> 22528 bytes 18 files changed, 292 insertions(+), 317 deletions(-) rename SAIN Helpers/Helpers/{Math.cs => SAIN_Math.cs} (99%) diff --git a/SAIN Combat/Patches/RateofFirePatch.cs b/SAIN Combat/Patches/RateofFirePatch.cs index f64adcdc6..ddb2a5dba 100644 --- a/SAIN Combat/Patches/RateofFirePatch.cs +++ b/SAIN Combat/Patches/RateofFirePatch.cs @@ -48,14 +48,9 @@ public static bool PatchPrefix(ref BotOwner ___botOwner_0, ref float ___float_0) } public class SemiAutoPatch : ModulePatch { - private static PropertyInfo _WeaponManagerPI; - private static PropertyInfo _WeaponAIPresetPI; - protected override MethodBase GetTargetMethod() { - _WeaponManagerPI = AccessTools.Property(typeof(BotOwner), "WeaponManager"); - _WeaponAIPresetPI = AccessTools.Property(_WeaponManagerPI.PropertyType, "WeaponAIPreset"); - return AccessTools.Method(_WeaponAIPresetPI.PropertyType, "method_1"); + return AccessTools.Method(typeof(GClass363), "method_1"); } [PatchPrefix] diff --git a/SAIN Combat/Patches/RecoilPatch.cs b/SAIN Combat/Patches/RecoilPatch.cs index 3418bd0e6..f13d185c9 100644 --- a/SAIN Combat/Patches/RecoilPatch.cs +++ b/SAIN Combat/Patches/RecoilPatch.cs @@ -13,18 +13,23 @@ namespace SAIN_Audio.Combat.Patches { public class AimOffsetPatch : ModulePatch { - private static PropertyInfo _AimingDataPI; protected override MethodBase GetTargetMethod() { - _AimingDataPI = AccessTools.Property(typeof(BotOwner), "AimingData"); - return AccessTools.Method(_AimingDataPI.PropertyType, "method_13"); + // Note: Can't find this based on the property type of AimingData? Is this because of it being an interface? + return AccessTools.Method(typeof(GClass544), "method_13"); } [PatchPrefix] public static bool PatchPrefix(GClass544 __instance, ref BotOwner ___botOwner_0, ref Vector3 ___vector3_5, ref Vector3 ___vector3_4, ref float ___float_13) { - __instance.EndTargetPoint = __instance.RealTargetPoint + ___vector3_5 + ___float_13 * (___vector3_4 + (___botOwner_0.RecoilData.RecoilOffset * ScatterMultiplier.Value)); + __instance.EndTargetPoint = __instance.RealTargetPoint + + ___vector3_5 + + ___float_13 + + * (___vector3_4 + + (___botOwner_0.RecoilData.RecoilOffset + * ScatterMultiplier.Value)); return false; } diff --git a/SAIN Combat/Plugin.cs b/SAIN Combat/Plugin.cs index d3eecef9a..e081e3d75 100644 --- a/SAIN Combat/Plugin.cs +++ b/SAIN Combat/Plugin.cs @@ -6,7 +6,7 @@ namespace SAIN_Audio.Combat { - [BepInPlugin("me.sol.sain", "SAIN Combat", "1.6")] + [BepInPlugin("me.sol.sain", "SAIN Combat", "1.1")] public class CombatPlugin : BaseUnityPlugin { private void Awake() diff --git a/SAIN Flashlights/Components/FlashlightDetection.cs b/SAIN Flashlights/Components/FlashlightDetection.cs index d66e36c71..66b0716f4 100644 --- a/SAIN Flashlights/Components/FlashlightDetection.cs +++ b/SAIN Flashlights/Components/FlashlightDetection.cs @@ -1,8 +1,11 @@ using BepInEx.Logging; using Comfort.Common; using EFT; +using SAIN_Helpers; using System.Collections; using UnityEngine; +using UnityEngine.AI; +using static Mono.Security.X509.X520; using static SAIN_Flashlights.Config.DazzleConfig; namespace SAIN_Flashlights.Components @@ -15,6 +18,13 @@ public class FlashlightDetection private static Player LocalPlayer { get; set; } protected static ManualLogSource Logger { get; private set; } + private float SearchTime; + + private float RayCastFrequencyTime; + + /// + /// Finds the local player and the SAIN_Flashlight_Component if it is not already set. + /// public void FindYourPlayer() { if (LocalPlayer == null) @@ -27,14 +37,17 @@ public void FindYourPlayer() } } } + + /// + /// Creates detection points for the flashlight detection system. + /// + /// The player to create the detection points for. public void CreateDetectionPoints(Player player) { if (Logger == null) - { Logger = BepInEx.Logging.Logger.CreateLogSource(nameof(FlashlightDetection)); - } - if (player == null || player.IsAI || !player.HealthController.IsAlive) + if (player == null || !player.HealthController.IsAlive) return; Vector3 playerPosition = player.WeaponRoot.position; @@ -43,95 +56,229 @@ public void CreateDetectionPoints(Player player) float detectionDistance = 60f; - if (Physics.Raycast(playerPosition, playerLookDirection, out RaycastHit hit, detectionDistance, LayerMaskClass.HighPolyWithTerrainMask)) + // Define the cone angle (in degrees) + float coneAngle = 10f; + + // Generate random angles within the cone range for yaw and pitch + float randomYawAngle = UnityEngine.Random.Range(-coneAngle * 0.5f, coneAngle * 0.5f); + float randomPitchAngle = UnityEngine.Random.Range(-coneAngle * 0.5f, coneAngle * 0.5f); + + // Create a Quaternion rotation based on the random yaw and pitch angles + Quaternion randomRotation = Quaternion.Euler(randomPitchAngle, randomYawAngle, 0); + + // Rotate the player's look direction by the Quaternion rotation + Vector3 rotatedLookDirection = randomRotation * playerLookDirection; + + if (Physics.Raycast(playerPosition, rotatedLookDirection, out RaycastHit hit, detectionDistance, LayerMaskClass.HighPolyWithTerrainMask)) { FlashLightPoint = hit.point; - ExpireDetectionPoint(0.25f); + ExpireDetectionPoint(0.1f); - PlayerPosition = playerPosition; - ExpirePlayerPoint(0.25f); + PlayerPosition = player.Transform.position; + ExpirePlayerPoint(0.1f); + + if (DebugFlash.Value) + DebugDrawer.Sphere(hit.point, 0.1f, Color.red, 0.25f); } } - public void DetectPoints(Player player) + /// + /// Detects and investigates a flashlight for bots. + /// + public void DetectAndInvestigateFlashlight(Player player) { if (Logger == null) - { Logger = BepInEx.Logging.Logger.CreateLogSource(nameof(FlashlightDetection)); - } if (!player.IsAI || player?.AIData?.BotOwner == null) - { return; - } FindYourPlayer(); if (PlayerFlashComponent == null) { - Logger.LogWarning($"Could not find flashlight component for local player"); + Logger.LogError($"Could not find flashlight component for local player"); return; } BotOwner bot = player.AIData.BotOwner; + Vector3 playerPos = PlayerPosition; + Vector3 flashPos = FlashLightPoint; - if (FlashLightPoint != Vector3.zero && PlayerPosition != Vector3.zero) + if (flashPos != Vector3.zero && playerPos != Vector3.zero) { - if (bot.LookSensor.IsPointInVisibleSector(FlashLightPoint)) + Vector3 botPos = bot.MyHead.position; + + if ((botPos - flashPos).magnitude < 100f) { - Vector3 botPosition = bot.MyHead.position; + if (CanISeeFlashlight(bot, flashPos)) + { + Vector3 estimatedPosition = EstimateSearchPosition(playerPos, flashPos, botPos, 5f); - Vector3 direction = (FlashLightPoint - botPosition).normalized; + TryToInvestigate(bot, estimatedPosition, out Vector3 debugHitPos); - float rayLength = (FlashLightPoint - botPosition).magnitude; + DebugSearchPosition(bot.name, debugHitPos, flashPos, botPos, 5f, 0.15f, 0.05f); + } + } + } + } + + /// + /// Checks if the bot can see the flashlight based on the bot's position, the player's position, and the flashlight's position. + /// + /// The bot owner. + /// The player's position. + /// The flashlight's position. + /// True if the bot can see the flashlight, false otherwise. + private bool CanISeeFlashlight(BotOwner bot, Vector3 flashPos) + { + if (bot.LookSensor.IsPointInVisibleSector(flashPos)) + { + Vector3 botPos = bot.Transform.position; + + Vector3 direction = (flashPos - botPos).normalized; + + float rayLength = (flashPos - botPos).magnitude - 0.1f; - if (!Physics.Raycast(botPosition, direction, rayLength, LayerMaskClass.HighPolyWithTerrainMask)) + if (RayCastFrequencyTime < Time.time && !Physics.Raycast(botPos, direction, rayLength, LayerMaskClass.HighPolyWithTerrainMask)) + { + SetRayCastTimer(0.25f); + + if (SearchTime < Time.time) { - float dispersion = rayLength / 10f; + return true; + } + } + } - float num2 = MathHelpers.Random(-dispersion, dispersion); - float num3 = MathHelpers.Random(-dispersion, dispersion); + return false; + } - Vector3 vector = new Vector3(PlayerPosition.x + num2, PlayerPosition.y, PlayerPosition.z + num3); + /// + /// Tries to investigate a visible flashlight beam by estimating a search position and adding it to the bot's group search points. + /// + /// The bot owner. + /// The player's position. + /// The flashbang's position. + /// The bot's position. + private void TryToInvestigate(BotOwner bot, Vector3 estimatedPosition, out Vector3 debugHitPos) + { + debugHitPos = Vector3.zero; - //var target = new GClass270(vector, PlaceForCheckType.suspicious); + if (NavMesh.SamplePosition(estimatedPosition, out NavMeshHit hit, 1.0f, -1)) + { + NavMeshPath searchpath = new NavMeshPath(); + NavMesh.CalculatePath(bot.Transform.position, hit.position, -1, searchpath); - bot.BotsGroup.AddPointToSearch(vector, 10f, bot, true); + if (searchpath.status == NavMeshPathStatus.PathInvalid) + { + SetSearchTimer(1f); + SetRayCastTimer(1f); - //bot.Memory.GoalTarget.SetTarget(target); + if (DebugFlash.Value) + Logger.LogDebug($"Path Invalid"); + } + else + { + SetSearchTimer(5f); + SetRayCastTimer(5f); - if (DebugFlash.Value) - { - Helpers.DebugDrawer.Line(vector, botPosition, 0.005f, Color.red, 3f); - Helpers.DebugDrawer.Line(FlashLightPoint, botPosition, 0.005f, Color.red, 3f); - } - } + debugHitPos = hit.position; + + bot.BotsGroup.AddPointToSearch(hit.position, 20f, bot, true); } } } + /// + /// Estimates a search position based on the player, flash, and bot positions, and a dispersion value. + /// + /// The position of the player. + /// The position of the flashlight point. + /// The position of the bot. + /// What the distance to enemy is divided by to produce dispersion. Higher is more accurate + /// The estimated search position. + private Vector3 EstimateSearchPosition(Vector3 playerPos, Vector3 flashPos, Vector3 botPos, float dispersion) + { + Vector3 estimatedPosition = Vector3.Lerp(playerPos, flashPos, Random.Range(0.0f, 0.25f)); + + float distance = (playerPos - botPos).magnitude; + + float maxDispersion = Mathf.Clamp(distance, 0f, 20f); + + float positionDispersion = maxDispersion / dispersion; + + float x = SAIN_Math.Random(-positionDispersion, positionDispersion); + float z = SAIN_Math.Random(-positionDispersion, positionDispersion); + + return new Vector3(estimatedPosition.x + x, estimatedPosition.y, estimatedPosition.z + z); + } + + /// + /// Sets the search timer to the current time plus the given duration. + /// + /// The duration to add to the current time. + private void SetSearchTimer(float duration) + { + SearchTime = Time.time + duration; + } + + /// + /// Sets the RayCastFrequencyTime to the current time plus the given duration. + /// + /// The duration to add to the current time. + private void SetRayCastTimer(float duration) + { + RayCastFrequencyTime = Time.time + duration; + } + + /// + /// Draws debug lines and spheres to visualize the search position of a bot. + /// + /// Name of the bot. + /// Position of the hit. + /// Position of the flashlight. + /// Position of the bot. + /// Time until the debug lines and spheres expire. + /// Size of the debug spheres. + /// Size of the debug lines. + private void DebugSearchPosition(string name, Vector3 hitpos, Vector3 flashpos, Vector3 botpos, float expiretime, float spheresize, float linesize) + { + if (DebugFlash.Value) + { + Logger.LogDebug($"{name} Is Investigating Flashlight Beam"); + + Helpers.DebugDrawer.Sphere(hitpos, spheresize, Color.red, expiretime); + Helpers.DebugDrawer.Sphere(flashpos, spheresize, Color.red, expiretime); + + Helpers.DebugDrawer.Line(flashpos, botpos, linesize, Color.red, expiretime); + Helpers.DebugDrawer.Line(hitpos, botpos, linesize, Color.red, expiretime); + Helpers.DebugDrawer.Line(hitpos, flashpos, linesize, Color.red, expiretime); + } + } + + /// + /// Waits for a specified amount of time before setting the FlashLightPoint to Vector3.zero. + /// + /// The amount of time to wait before setting the FlashLightPoint. + /// An IEnumerator object. private IEnumerator ExpireDetectionPoint(float delay) { yield return new WaitForSeconds(delay); FlashLightPoint = Vector3.zero; } + /// + /// Waits for a specified delay and then sets the PlayerPosition to Vector3.zero. + /// + /// The delay to wait before setting the PlayerPosition. + /// An IEnumerator object. private IEnumerator ExpirePlayerPoint(float delay) { yield return new WaitForSeconds(delay); PlayerPosition = Vector3.zero; } } - public static class MathHelpers - { - public static float Random(float a, float b) - { - float num = (float)random_0.NextDouble(); - return a + (b - a) * num; - } - - private static readonly System.Random random_0 = new System.Random(); - } public class TimedVector3 { public TimedVector3(Vector3 point, float timestamp) @@ -143,5 +290,4 @@ public TimedVector3(Vector3 point, float timestamp) public Vector3 Point { get; set; } public float Timestamp { get; set; } } - } \ No newline at end of file diff --git a/SAIN Flashlights/Components/SAIN_Flashlight_Component.cs b/SAIN Flashlights/Components/SAIN_Flashlight_Component.cs index 98bed359a..137d8af6b 100644 --- a/SAIN Flashlights/Components/SAIN_Flashlight_Component.cs +++ b/SAIN Flashlights/Components/SAIN_Flashlight_Component.cs @@ -16,14 +16,8 @@ public class SAIN_Flashlight_Component : MonoBehaviour private Player Player { get; set; } protected static ManualLogSource Logger { get; private set; } - public List LightComponents { get; set; } - private FlashlightDetection _lightDetection; - public int SelectedMode { get; set; } - public bool IsActive { get; set; } - public Item FlashlightItem { get; set; } - private void Start() { Player = GetComponent(); @@ -58,17 +52,20 @@ private IEnumerator LightDetection() yield return new WaitForSeconds(1f); } - if (Player.AIData.UsingLight && WhiteLight) + if (WhiteLight) { - _lightDetection.CreateDetectionPoints(Player); - } + if (Player.IsAI) + { + _lightDetection.DetectAndInvestigateFlashlight(Player); + } - if (Player.IsAI) - { - _lightDetection.DetectPoints(Player); + if (Player.IsYourPlayer) + { + _lightDetection.CreateDetectionPoints(Player); + } } - yield return new WaitForSeconds(0.1f); + yield return new WaitForSeconds(0.05f); } } diff --git a/SAIN Flashlights/Config/DazzleConfig.cs b/SAIN Flashlights/Config/DazzleConfig.cs index e827b6703..3a383dff5 100644 --- a/SAIN Flashlights/Config/DazzleConfig.cs +++ b/SAIN Flashlights/Config/DazzleConfig.cs @@ -4,8 +4,8 @@ namespace SAIN_Flashlights.Config { internal class DazzleConfig { - public static ConfigEntry EnableMod { get; private set; } - public static ConfigEntry Angle { get; private set; } + public static ConfigEntry EnableDetection { get; private set; } + public static ConfigEntry FasterDetection { get; private set; } public static ConfigEntry Effectiveness { get; private set; } public static ConfigEntry MaxDazzleRange { get; private set; } public static ConfigEntry DebugFlash { get; private set; } @@ -15,22 +15,27 @@ public static void Init(ConfigFile Config) { string debugmode = "Settings"; - Effectiveness = Config.Bind(debugmode, "Dazzle Intensity", 1f, + EnableDetection = Config.Bind(debugmode, "Enable Player Flashlight Detection", true, + new ConfigDescription("Bots will notice flashlights being shined around and investigate the source", + null, + new ConfigurationManagerAttributes { IsAdvanced = false, Order = 10 })); + + FasterDetection = Config.Bind(debugmode, "Faster Detection from Flashlights", true, + new ConfigDescription("Bots will notice you faster if you are shining a light at them", + null, + new ConfigurationManagerAttributes { IsAdvanced = false, Order = 9 })); + + Effectiveness = Config.Bind(debugmode, "Dazzle Intensity", 3f, new ConfigDescription("Intensifies the dazzle effect", - new AcceptableValueRange(0.1f, 3.0f), + new AcceptableValueRange(0.1f, 10.0f), new ConfigurationManagerAttributes { IsAdvanced = false, Order = 6 })); - Angle = Config.Bind(debugmode, "Angle Modifier", 1f, - new ConfigDescription("Lower value is equal to wider flashlight angle. The wider the angle, the easier it is for bots to be dazzled.", - new AcceptableValueRange(0.8f, 1.0f), - new ConfigurationManagerAttributes { IsAdvanced = true, Order = 5 })); - MaxDazzleRange = Config.Bind(debugmode, "Max Dazzle Range", 25f, new ConfigDescription("Maximum possible distance to affect AI aim", new AcceptableValueRange(5.0f, 100.0f), new ConfigurationManagerAttributes { IsAdvanced = true, Order = 4 })); - AIHatesFlashlights = Config.Bind(debugmode, "Random Voicelines", true, + AIHatesFlashlights = Config.Bind(debugmode, "Random Voicelines", false, new ConfigDescription("Small Chance to play a random voiceline when being dazzled", null, new ConfigurationManagerAttributes { IsAdvanced = false, Order = 3 })); @@ -40,9 +45,8 @@ public static void Init(ConfigFile Config) null, new ConfigurationManagerAttributes { IsAdvanced = false, Order = 2 })); - - DebugFlash = Config.Bind(debugmode, "Debug Logs", false, - new ConfigDescription("", + DebugFlash = Config.Bind(debugmode, "Debug Mode", false, + new ConfigDescription("Draws Lines and Spheres to visualize flashlight detection. Logs information about dazzle.", null, new ConfigurationManagerAttributes { IsAdvanced = true, Order = 1 })); } diff --git a/SAIN Flashlights/Helpers/FlashlightHelpers.cs b/SAIN Flashlights/Helpers/FlashlightHelpers.cs index b5eea3e4f..2bcebce24 100644 --- a/SAIN Flashlights/Helpers/FlashlightHelpers.cs +++ b/SAIN Flashlights/Helpers/FlashlightHelpers.cs @@ -3,15 +3,12 @@ using System; using UnityEngine; using static SAIN_Flashlights.Config.DazzleConfig; +using SAIN_Helpers; namespace SAIN_Flashlights.Helpers { public class FlashLight { - private GameObject _flashlight; - - private GameObject[] _modes; - protected static ManualLogSource Logger { get; private set; } /// @@ -34,20 +31,20 @@ public static void EnemyWithFlashlight(BotOwner bot, IAIDetails person) { if (!Physics.Raycast(weaponRoot, (position - weaponRoot).normalized, (position - weaponRoot).magnitude, LayerMaskClass.HighPolyWithTerrainMask)) { + DebugDraw(weaponRoot, position); + if (SillyMode.Value) { - NonStaticHelpers voice = new NonStaticHelpers(); - voice.FunnyMode(bot, position); + FunnyHelpers.FunnyMode(bot, position); return; } float gainSight = GetGainSightModifier(enemyDist); float dazzlemodifier = 1f; + if (enemyDist < MaxDazzleRange.Value) - { dazzlemodifier = GetDazzleModifier(bot, person); - } ApplyDazzle(dazzlemodifier, gainSight, bot); @@ -77,8 +74,7 @@ public static void EnemyWithLaser(BotOwner bot, IAIDetails person) { if (SillyMode.Value) { - NonStaticHelpers voice = new NonStaticHelpers(); - voice.FunnyMode(bot, position); + FunnyHelpers.FunnyMode(bot, position); return; } @@ -130,13 +126,13 @@ private static void DebugLogs(BotOwner bot, float dazzlemod, float gainsightmod, /// The position of the enemy. /// The weapon root of the enemy. /// Whether the enemy is looking at the player. - private static void DebugDraw(Vector3 position, Vector3 weaponRoot, bool enemylookatme) + private static void DebugDraw(Vector3 position, Vector3 weaponRoot) { - if (DebugFlash.Value && enemylookatme) + if (DebugFlash.Value) { - DebugDrawer.Sphere(position - weaponRoot, 0.05f, Color.white, 1f); - DebugDrawer.Line(position, weaponRoot, 0.01f, Color.green, 1f); - DebugDrawer.Line(weaponRoot, position, 0.01f, Color.red, 1f); + SAIN_Helpers.DebugDrawer.Sphere(position - weaponRoot, 0.05f, Color.white, 1f); + SAIN_Helpers.DebugDrawer.Line(position, weaponRoot, 0.01f, Color.green, 1f); + SAIN_Helpers.DebugDrawer.Line(weaponRoot, position, 0.01f, Color.red, 1f); } } @@ -150,18 +146,23 @@ private static void ApplyDazzle(float dazzleModif, float gainSightModif, BotOwne { GClass557 modif = new GClass557 { - PrecicingSpeedCoef = Mathf.Clamp(dazzleModif, 1f, 2f) * Effectiveness.Value, - AccuratySpeedCoef = Mathf.Clamp(dazzleModif, 1f, 2f) * Effectiveness.Value, + PrecicingSpeedCoef = Mathf.Clamp(dazzleModif, 1f, 5f) * Effectiveness.Value, + AccuratySpeedCoef = Mathf.Clamp(dazzleModif, 1f, 5f) * Effectiveness.Value, LayChanceDangerCoef = 1f, VisibleDistCoef = 1f, GainSightCoef = gainSightModif, - ScatteringCoef = Mathf.Clamp(dazzleModif, 1f, 1.5f) * Effectiveness.Value, - PriorityScatteringCoef = Mathf.Clamp(dazzleModif, 1f, 1.5f) * Effectiveness.Value, + ScatteringCoef = Mathf.Clamp(dazzleModif, 1f, 2.5f) * Effectiveness.Value, + PriorityScatteringCoef = Mathf.Clamp(dazzleModif, 1f, 2.5f) * Effectiveness.Value, HearingDistCoef = 1f, TriggerDownDelay = 1f, WaitInCoverCoef = 1f }; + if (AIHatesFlashlights.Value) + { + FunnyHelpers.RandomVoiceLine(bot); + } + bot.Settings.Current.Apply(modif, 0.1f); } @@ -176,10 +177,8 @@ private static bool FlashLightVisionCheck(BotOwner bot, IAIDetails person) Vector3 position = bot.MyHead.position; Vector3 weaponRoot = person.WeaponRoot.position; - float flashAngle = Mathf.Clamp(0.9770526f * Angle.Value, 0.8f, 1f); - bool enemylookatme = IsAngLessNormalized(NormalizeFastSelf(position - weaponRoot), person.LookDirection, flashAngle); - - DebugDraw(position, weaponRoot, enemylookatme); + float flashAngle = Mathf.Clamp(0.9770526f, 0.8f, 1f); + bool enemylookatme = SAIN_Math.IsAngLessNormalized(SAIN_Math.NormalizeFastSelf(position - weaponRoot), person.LookDirection, flashAngle); return enemylookatme; } @@ -195,40 +194,12 @@ private static bool LaserVisionCheck(BotOwner bot, IAIDetails person) Vector3 position = bot.MyHead.position; Vector3 weaponRoot = person.WeaponRoot.position; - float laserAngle = 0.999f; - bool enemylookatme = IsAngLessNormalized(NormalizeFastSelf(position - weaponRoot), person.LookDirection, laserAngle); - - DebugDraw(position, weaponRoot, enemylookatme); + float laserAngle = 0.990f; + bool enemylookatme = SAIN_Math.IsAngLessNormalized(SAIN_Math.NormalizeFastSelf(position - weaponRoot), person.LookDirection, laserAngle); return enemylookatme; } - /// - /// Checks if the angle between two vectors is less than a given cosine value. - /// - /// The first vector. - /// The second vector. - /// The cosine value. - /// True if the angle between the two vectors is less than the given cosine value, false otherwise. - private static bool IsAngLessNormalized(Vector3 a, Vector3 b, float cos) - { - return a.x * b.x + a.y * b.y + a.z * b.z > cos; - } - - /// - /// Normalizes the vector in a less performance heavy way than normal - /// - /// The vector to normalize. - /// The normalized vector. - private static Vector3 NormalizeFastSelf(Vector3 v) - { - float num = (float)Math.Sqrt((double)(v.x * v.x + v.y * v.y + v.z * v.z)); - v.x /= num; - v.y /= num; - v.z /= num; - return v; - } - /// /// Calculates the dazzle modifier for a given BotOwner and Enemy. /// @@ -243,20 +214,13 @@ private static float GetDazzleModifier(BotOwner ___botOwner_0, IAIDetails person float enemyDist = (position - weaponRoot).magnitude; float dazzlemodifier = 1f - (enemyDist / MaxDazzleRange.Value); - dazzlemodifier = Mathf.Clamp(dazzlemodifier, 0.1f, 1.0f); - dazzlemodifier += 1.33f; + dazzlemodifier = (2 * dazzlemodifier) + 1f; if (bot.NightVision.UsingNow) { dazzlemodifier *= 1.5f; } - if (AIHatesFlashlights.Value) - { - NonStaticHelpers voice = new NonStaticHelpers(); - voice.RandomVoiceLine(bot); - } - return dazzlemodifier; } @@ -274,11 +238,11 @@ private static float GetGainSightModifier(float enemyDist) } } - public class NonStaticHelpers + public class FunnyHelpers { private static float funnytimer = 0f; - public void FunnyMode(BotOwner bot, Vector3 position) + public static void FunnyMode(BotOwner bot, Vector3 position) { if (funnytimer < Time.time) { @@ -332,7 +296,7 @@ public void FunnyMode(BotOwner bot, Vector3 position) } } - public void RandomVoiceLine(BotOwner bot) + public static void RandomVoiceLine(BotOwner bot) { float randomphrase = UnityEngine.Random.value; if (randomphrase > 0.97f) diff --git a/SAIN Flashlights/Patches/CheckFlashlightPatch.cs b/SAIN Flashlights/Patches/CheckFlashlightPatch.cs index ddd4e8af7..168b97685 100644 --- a/SAIN Flashlights/Patches/CheckFlashlightPatch.cs +++ b/SAIN Flashlights/Patches/CheckFlashlightPatch.cs @@ -3,15 +3,20 @@ using HarmonyLib; using SAIN_Flashlights.Components; using System.Reflection; +using static SAIN_Flashlights.Config.DazzleConfig; namespace SAIN_Flashlights.Patches { public class CheckFlashlightPatch : ModulePatch { private static FieldInfo _tacticalModesField; + private static MethodInfo _UsingLight; protected override MethodBase GetTargetMethod() { + _UsingLight = AccessTools.PropertySetter(typeof(AiDataClass), "UsingLight"); + _tacticalModesField = AccessTools.Field(typeof(TacticalComboVisualController), "list_0"); + return AccessTools.Method(typeof(Player.FirearmController), "SetLightsState"); } @@ -19,9 +24,18 @@ protected override MethodBase GetTargetMethod() public static void PatchPostfix(ref Player ____player) { SAIN_Flashlight_Component flashlightComponent = ____player.gameObject.GetComponent(); + if (flashlightComponent != null) { flashlightComponent.CheckDevice(____player, _tacticalModesField); + + if (!flashlightComponent.WhiteLight && !flashlightComponent.Laser) + { + _UsingLight.Invoke(____player.AIData, new object[] { false }); + + if (DebugFlash.Value) + Logger.LogDebug($"Updated Using Light for {____player.Profile.Nickname}, now set to {____player.AIData.UsingLight}"); + } } else { diff --git a/SAIN Flashlights/Patches/DazzlePatch.cs b/SAIN Flashlights/Patches/DazzlePatch.cs index be75fd8fe..c7848c2df 100644 --- a/SAIN Flashlights/Patches/DazzlePatch.cs +++ b/SAIN Flashlights/Patches/DazzlePatch.cs @@ -25,7 +25,7 @@ public static void Prefix(ref BotOwner ___botOwner_0, IAIDetails person, ref flo SAIN_Flashlight_Component flashlightComponent = person.GetPlayer.gameObject.GetComponent(); if (flashlightComponent == null) { - Logger.LogError("flashlightComponent is null"); + Logger.LogError("SAIN Flashlight Dazzle: flashlightComponent is null"); return; } diff --git a/SAIN Flashlights/Plugin.cs b/SAIN Flashlights/Plugin.cs index cb5ee3f3c..dda204fb9 100644 --- a/SAIN Flashlights/Plugin.cs +++ b/SAIN Flashlights/Plugin.cs @@ -6,7 +6,7 @@ namespace SAIN_Flashlights { - [BepInPlugin("me.sol.sainflash", "SAIN Flashlights", "1.1")] + [BepInPlugin("me.sol.sainflash", "SAIN Flashlights", "2.0")] public class FlashlightsPlugin : BaseUnityPlugin { private void Awake() diff --git a/SAIN Flashlights/Properties/AssemblyInfo.cs b/SAIN Flashlights/Properties/AssemblyInfo.cs index f0dc09338..4c5970035 100644 --- a/SAIN Flashlights/Properties/AssemblyInfo.cs +++ b/SAIN Flashlights/Properties/AssemblyInfo.cs @@ -32,6 +32,6 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.1.0.0")] -[assembly: AssemblyFileVersion("1.1.0.0")] +[assembly: AssemblyVersion("2.0.0.0")] +[assembly: AssemblyFileVersion("2.0.0.0")] [assembly: TarkovVersion(22617)] diff --git a/SAIN Flashlights/SAIN Flashlights.csproj b/SAIN Flashlights/SAIN Flashlights.csproj index 3707f2a96..0268e1135 100644 --- a/SAIN Flashlights/SAIN Flashlights.csproj +++ b/SAIN Flashlights/SAIN Flashlights.csproj @@ -48,7 +48,7 @@ false - SAIN.Flashlights + SAIN_Flashlights diff --git a/SAIN Helpers/Helpers/Math.cs b/SAIN Helpers/Helpers/SAIN_Math.cs similarity index 99% rename from SAIN Helpers/Helpers/Math.cs rename to SAIN Helpers/Helpers/SAIN_Math.cs index 17b4f05ea..0a4e742e3 100644 --- a/SAIN Helpers/Helpers/Math.cs +++ b/SAIN Helpers/Helpers/SAIN_Math.cs @@ -10,7 +10,7 @@ namespace SAIN_Helpers { - public static class Math + public static class SAIN_Math { /// /// Calculates the inverse of a value using a logistic function. @@ -1057,7 +1057,7 @@ public static bool IsOdd(int value) /// The Vector3 to rotate. /// The direction to rotate. /// The rotated Vector3. - public static Vector3 Rotate90(Vector3 n, Math.SideTurn side) + public static Vector3 Rotate90(Vector3 n, SAIN_Math.SideTurn side) { if (side == SideTurn.left) { diff --git a/SAIN Helpers/SAIN Helpers.csproj b/SAIN Helpers/SAIN Helpers.csproj index f71c104cc..e87b1547e 100644 --- a/SAIN Helpers/SAIN Helpers.csproj +++ b/SAIN Helpers/SAIN Helpers.csproj @@ -154,7 +154,7 @@ - + diff --git a/SAIN Movement/Patches/BotMemoryPatch.cs b/SAIN Movement/Patches/BotMemoryPatch.cs index eb7a18bce..f05165c5a 100644 --- a/SAIN Movement/Patches/BotMemoryPatch.cs +++ b/SAIN Movement/Patches/BotMemoryPatch.cs @@ -15,7 +15,6 @@ public class SainMemory : MonoBehaviour public bool NeedtoHeal { get; set; } = false; public bool NeedtoReload { get; set; } = false; public float DodgeTimer { get; set; } = 0f; - public bool GoingToNewCover { get; set; } } public class AddComponentPatch : ModulePatch diff --git a/SAIN Movement/Patches/DogFightPatch.cs b/SAIN Movement/Patches/DogFightPatch.cs index 57ebccaf2..83a1c5e31 100644 --- a/SAIN Movement/Patches/DogFightPatch.cs +++ b/SAIN Movement/Patches/DogFightPatch.cs @@ -8,12 +8,12 @@ using UnityEngine.AI; using static SAIN_Audio.Movement.Config.DebugConfig; using static SAIN_Audio.Movement.Config.DogFighterConfig; +using SAIN_Helpers; namespace SAIN_Audio.Movement.Patches { public class DogFight { - private static Type _DogFightType; public class Start : ModulePatch { private static PropertyInfo _DogFightProperty; @@ -21,11 +21,12 @@ public class Start : ModulePatch protected override MethodBase GetTargetMethod() { _DogFightProperty = AccessTools.Property(typeof(BotOwner), "DogFight"); - _DogFightType = _DogFightProperty.PropertyType; - _DogFightState = AccessTools.PropertySetter(_DogFightType, "DogFightState"); - return AccessTools.Method(_DogFightType, "ShallStartCauseHavePlace"); + _DogFightState = AccessTools.PropertySetter(_DogFightProperty.PropertyType, "DogFightState"); + + return AccessTools.Method(_DogFightProperty.PropertyType, "ShallStartCauseHavePlace"); } + [PatchPrefix] public static bool PatchPrefix(ref BotOwner ___botOwner_0, ref NavMeshPath ___navMeshPath_0) { @@ -48,9 +49,10 @@ public class ManualUpdate : ModulePatch protected override MethodBase GetTargetMethod() { _DogFightProperty = AccessTools.Property(typeof(BotOwner), "DogFight"); - _DogFightType = _DogFightProperty.PropertyType; - _DogFightState = AccessTools.PropertySetter(_DogFightType, "DogFightState"); - return AccessTools.Method(_DogFightType, "ManualUpdate"); + + _DogFightState = AccessTools.PropertySetter(_DogFightProperty.PropertyType, "DogFightState"); + + return AccessTools.Method(_DogFightProperty.PropertyType, "ManualUpdate"); } [PatchPrefix] public static bool PatchPrefix(ref BotOwner ___botOwner_0, ref NavMeshPath ___navMeshPath_0, ref float ___float_2) @@ -115,9 +117,10 @@ public class Fight : ModulePatch protected override MethodBase GetTargetMethod() { _DogFightProperty = AccessTools.Property(typeof(BotOwner), "DogFight"); - _DogFightType = _DogFightProperty.PropertyType; - _DogFightState = AccessTools.PropertySetter(_DogFightType, "DogFightState"); - return AccessTools.Method(_DogFightType, "Fight"); + + _DogFightState = AccessTools.PropertySetter(_DogFightProperty.PropertyType, "DogFightState"); + + return AccessTools.Method(_DogFightProperty.PropertyType, "Fight"); } [PatchPrefix] public static bool PatchPrefix(ref BotOwner ___botOwner_0, ref float ___float_1, ref bool ___bool_0, ref NavMeshPath ___navMeshPath_0) @@ -192,7 +195,7 @@ private static bool CheckPathLength(NavMeshPath path, float straighDist) // Method_1 public static bool Backup(BotOwner bot, NavMeshPath navMeshPath_0, out Vector3 trgPos) { - Vector3 a = -NormalizeFastSelf(bot.Memory.GoalEnemy.Direction); + Vector3 a = -SAIN_Math.NormalizeFastSelf(bot.Memory.GoalEnemy.Direction); trgPos = Vector3.zero; float num = 0f; NavMeshHit navMeshHit; @@ -223,13 +226,5 @@ public static bool Backup(BotOwner bot, NavMeshPath navMeshPath_0, out Vector3 t } return false; } - public static Vector3 NormalizeFastSelf(Vector3 v) - { - float num = (float)Math.Sqrt((double)(v.x * v.x + v.y * v.y + v.z * v.z)); - v.x /= num; - v.y /= num; - v.z /= num; - return v; - } } } diff --git a/SAIN Movement/Patches/MovementPatch.cs b/SAIN Movement/Patches/MovementPatch.cs index b19fa437b..3394f07f5 100644 --- a/SAIN Movement/Patches/MovementPatch.cs +++ b/SAIN Movement/Patches/MovementPatch.cs @@ -10,7 +10,6 @@ public class MovementSpeed : ModulePatch { protected override MethodBase GetTargetMethod() { - // AccessTools lets us get methods easily return AccessTools.Method(typeof(BotOwner), "UpdateManual"); } [PatchPostfix] @@ -67,147 +66,4 @@ public static void PatchPostfix(BotOwner __instance) bot.GetPlayer.ChangePose(Pose); } } -} -/* - public class MovementSpeedPatch1 : ModulePatch - { - static float VisibleTimer = 0f; - protected override MethodBase GetTargetMethod() - { - return typeof(GClass406)?.GetMethod("GoToByWay", BindingFlags.Instance | BindingFlags.Public); - } - [PatchPostfix] - public static void PatchPostfix(ref BotOwner ___botOwner_0) - { - if (VisibleTimer < Time.time) - { - VisibleTimer = Time.time + 0.25f; - - if (___botOwner_0.Memory.HaveEnemy && !___botOwner_0.Memory.GoalEnemy.CanShoot && ___botOwner_0.Memory.GoalEnemy.PersonalLastSeenTime > Time.time + 5f) - { - ___botOwner_0.SetTargetMoveSpeed(0.1f); - ___botOwner_0.Mover.SetTargetMoveSpeed(0.1f); - ___botOwner_0.SetPose(0.1f); - ___botOwner_0.GetPlayer.ChangePose(0.1f); - ___botOwner_0.GetPlayer.MovementContext.SetAimingSlowdown(false, 1f); - //Logger.LogInfo($"Movement: [{___botOwner_0.name}] Enemy Visible but not Shootable, Rat mode engaged"); - return; - } - //Move speed is set to 0.6 by default, this changes that to 1.0 (max walk speed) - if (___botOwner_0.AimingData.LastDist2Target < 20f || !___botOwner_0.WeaponManager.HaveBullets) - { - ___botOwner_0.SetTargetMoveSpeed(1.0f); - ___botOwner_0.Mover.SetTargetMoveSpeed(1.0f); - ___botOwner_0.SetPose(1.0f); - ___botOwner_0.GetPlayer.ChangePose(1.0f); - ___botOwner_0.GetPlayer.MovementContext.SetAimingSlowdown(false, 1f); - //Logger.LogInfo($"Movement: [{___botOwner_0.name}] Enemy Close!"); - return; - } - if (___botOwner_0.AimingData.LastDist2Target > 40f && ___botOwner_0.Memory.GoalEnemy.CanShoot) - { - ___botOwner_0.SetTargetMoveSpeed(0.6f); - ___botOwner_0.Mover.SetTargetMoveSpeed(0.6f); - ___botOwner_0.SetPose(0.6f); - ___botOwner_0.GetPlayer.ChangePose(0.6f); - ___botOwner_0.GetPlayer.MovementContext.SetAimingSlowdown(true, 0.6f); - //Logger.LogInfo($"Movement: [{___botOwner_0.name}] Enemy Visible but is far away, so I'm aiming"); - return; - } - } - } - } - public class MovementSpeedPatch2 : ModulePatch - { - static float VisibleTimer = 0f; - protected override MethodBase GetTargetMethod() - { - return typeof(GClass327)?.GetMethod("ManualUpdate", BindingFlags.Instance | BindingFlags.Public); - } - [PatchPostfix] - public static void PatchPostfix(ref BotOwner ___botOwner_0) - { - if (VisibleTimer < Time.time) - { - VisibleTimer = Time.time + 0.25f; - if (___botOwner_0.Memory.HaveEnemy && !___botOwner_0.Memory.GoalEnemy.CanShoot && ___botOwner_0.Memory.GoalEnemy.PersonalLastSeenTime > Time.time + 5f) - { - ___botOwner_0.SetTargetMoveSpeed(0.1f); - ___botOwner_0.Mover.SetTargetMoveSpeed(0.1f); - ___botOwner_0.SetPose(0.1f); - ___botOwner_0.GetPlayer.ChangePose(0.1f); - ___botOwner_0.GetPlayer.MovementContext.SetAimingSlowdown(false, 1f); - //Logger.LogInfo($"Movement: [{___botOwner_0.name}] Enemy Visible but not Shootable, Rat mode engaged"); - return; - } - //Move speed is set to 0.6 by default, this changes that to 1.0 (max walk speed) - if (___botOwner_0.AimingData.LastDist2Target < 20f || !___botOwner_0.WeaponManager.HaveBullets) - { - ___botOwner_0.SetTargetMoveSpeed(1.0f); - ___botOwner_0.Mover.SetTargetMoveSpeed(1.0f); - ___botOwner_0.SetPose(1.0f); - ___botOwner_0.GetPlayer.ChangePose(1.0f); - ___botOwner_0.GetPlayer.MovementContext.SetAimingSlowdown(false, 1f); - //Logger.LogInfo($"Movement: [{___botOwner_0.name}] Enemy Close!"); - return; - } - if (___botOwner_0.AimingData.LastDist2Target > 30f && ___botOwner_0.Memory.GoalEnemy.CanShoot) - { - ___botOwner_0.SetTargetMoveSpeed(0.6f); - ___botOwner_0.Mover.SetTargetMoveSpeed(0.6f); - ___botOwner_0.SetPose(0.6f); - ___botOwner_0.GetPlayer.ChangePose(0.6f); - ___botOwner_0.GetPlayer.MovementContext.SetAimingSlowdown(true, 0.6f); - //Logger.LogInfo($"Movement: [{___botOwner_0.name}] Enemy Visible but is far away, so I'm aiming"); - return; - } - } - } - } - public class MovementSpeedPatch3 : ModulePatch - { - static float VisibleTimer = 0f; - protected override MethodBase GetTargetMethod() - { - return typeof(GClass473)?.GetMethod("CheckLookEnemy", BindingFlags.Instance | BindingFlags.Public); - } - [PatchPostfix] - public static void PatchPostfix(ref BotOwner ___botOwner_0) - { - if (VisibleTimer < Time.time) - { - VisibleTimer = Time.time + 0.25f; - if (___botOwner_0.Memory.HaveEnemy && !___botOwner_0.Memory.GoalEnemy.CanShoot && ___botOwner_0.Memory.GoalEnemy.PersonalLastSeenTime > Time.time + 5f) - { - ___botOwner_0.SetTargetMoveSpeed(0.1f); - ___botOwner_0.Mover.SetTargetMoveSpeed(0.1f); - ___botOwner_0.SetPose(0.1f); - ___botOwner_0.GetPlayer.ChangePose(0.1f); - ___botOwner_0.GetPlayer.MovementContext.SetAimingSlowdown(false, 1f); - //Logger.LogInfo($"Movement: [{___botOwner_0.name}] Enemy Visible but not Shootable, Rat mode engaged"); - return; - } - //Move speed is set to 0.6 by default, this changes that to 1.0 (max walk speed) - if (___botOwner_0.AimingData.LastDist2Target < 20f || !___botOwner_0.WeaponManager.HaveBullets) - { - ___botOwner_0.SetTargetMoveSpeed(1.0f); - ___botOwner_0.Mover.SetTargetMoveSpeed(1.0f); - ___botOwner_0.SetPose(1.0f); - ___botOwner_0.GetPlayer.ChangePose(1.0f); - ___botOwner_0.GetPlayer.MovementContext.SetAimingSlowdown(false, 1f); - //Logger.LogInfo($"Movement: [{___botOwner_0.name}] Enemy Close!"); - return; - } - if (___botOwner_0.AimingData.LastDist2Target > 30f && ___botOwner_0.Memory.GoalEnemy.CanShoot) - { - ___botOwner_0.SetTargetMoveSpeed(0.6f); - ___botOwner_0.Mover.SetTargetMoveSpeed(0.6f); - ___botOwner_0.SetPose(0.6f); - ___botOwner_0.GetPlayer.ChangePose(0.6f); - ___botOwner_0.GetPlayer.MovementContext.SetAimingSlowdown(true, 0.6f); - //Logger.LogInfo($"Movement: [{___botOwner_0.name}] Enemy Visible but is far away, so I'm aiming"); - return; - } - } - } - }*/ \ No newline at end of file +} \ No newline at end of file diff --git a/SAIN-Helpers.dll b/SAIN-Helpers.dll index b3f1d033016b89c8b602f856b8dbdad917765a96..6a98110223d84912c0ec41d5e9e966f50d5721d4 100644 GIT binary patch delta 3474 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 T-FHRd!|&_}O+U?2BR&5E`V_F% delta 3466 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