Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions SAIN Combat/Patches/RateofFirePatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
13 changes: 9 additions & 4 deletions SAIN Combat/Patches/RecoilPatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion SAIN Combat/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
232 changes: 189 additions & 43 deletions SAIN Flashlights/Components/FlashlightDetection.cs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;

/// <summary>
/// Finds the local player and the SAIN_Flashlight_Component if it is not already set.
/// </summary>
public void FindYourPlayer()
{
if (LocalPlayer == null)
Expand All @@ -27,14 +37,17 @@ public void FindYourPlayer()
}
}
}

/// <summary>
/// Creates detection points for the flashlight detection system.
/// </summary>
/// <param name="player">The player to create the detection points for.</param>
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;
Expand All @@ -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)
/// <summary>
/// Detects and investigates a flashlight for bots.
/// </summary>
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);
}
}
}
}

/// <summary>
/// Checks if the bot can see the flashlight based on the bot's position, the player's position, and the flashlight's position.
/// </summary>
/// <param name="bot">The bot owner.</param>
/// <param name="playerPos">The player's position.</param>
/// <param name="flashPos">The flashlight's position.</param>
/// <returns>True if the bot can see the flashlight, false otherwise.</returns>
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);
/// <summary>
/// Tries to investigate a visible flashlight beam by estimating a search position and adding it to the bot's group search points.
/// </summary>
/// <param name="bot">The bot owner.</param>
/// <param name="playerPos">The player's position.</param>
/// <param name="flashPos">The flashbang's position.</param>
/// <param name="botPos">The bot's position.</param>
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);
}
}
}

/// <summary>
/// Estimates a search position based on the player, flash, and bot positions, and a dispersion value.
/// </summary>
/// <param name="playerPos">The position of the player.</param>
/// <param name="flashPos">The position of the flashlight point.</param>
/// <param name="botPos">The position of the bot.</param>
/// <param name="dispersion">What the distance to enemy is divided by to produce dispersion. Higher is more accurate</param>
/// <returns>The estimated search position.</returns>
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);
}

/// <summary>
/// Sets the search timer to the current time plus the given duration.
/// </summary>
/// <param name="duration">The duration to add to the current time.</param>
private void SetSearchTimer(float duration)
{
SearchTime = Time.time + duration;
}

/// <summary>
/// Sets the RayCastFrequencyTime to the current time plus the given duration.
/// </summary>
/// <param name="duration">The duration to add to the current time.</param>
private void SetRayCastTimer(float duration)
{
RayCastFrequencyTime = Time.time + duration;
}

/// <summary>
/// Draws debug lines and spheres to visualize the search position of a bot.
/// </summary>
/// <param name="name">Name of the bot.</param>
/// <param name="hitpos">Position of the hit.</param>
/// <param name="flashpos">Position of the flashlight.</param>
/// <param name="botpos">Position of the bot.</param>
/// <param name="expiretime">Time until the debug lines and spheres expire.</param>
/// <param name="spheresize">Size of the debug spheres.</param>
/// <param name="linesize">Size of the debug lines.</param>
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);
}
}

/// <summary>
/// Waits for a specified amount of time before setting the FlashLightPoint to Vector3.zero.
/// </summary>
/// <param name="delay">The amount of time to wait before setting the FlashLightPoint.</param>
/// <returns>An IEnumerator object.</returns>
private IEnumerator ExpireDetectionPoint(float delay)
{
yield return new WaitForSeconds(delay);
FlashLightPoint = Vector3.zero;
}

/// <summary>
/// Waits for a specified delay and then sets the PlayerPosition to Vector3.zero.
/// </summary>
/// <param name="delay">The delay to wait before setting the PlayerPosition.</param>
/// <returns>An IEnumerator object.</returns>
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)
Expand All @@ -143,5 +290,4 @@ public TimedVector3(Vector3 point, float timestamp)
public Vector3 Point { get; set; }
public float Timestamp { get; set; }
}

}
Loading