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
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified .vs/SAIN/v17/.suo
Binary file not shown.
266 changes: 266 additions & 0 deletions Components/EnemyLineOfSightManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
using BepInEx.Logging;
using Comfort.Common;
using EFT;
using SAIN.Components;
using System.Collections.Generic;
using System.Linq;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.AI;
using static SAIN.UserSettings.VisionConfig;
using SAIN.Helpers;

public class EnemyLineOfSightManager : MonoBehaviour
{
private float SpherecastRadius = 0.05f;
private LayerMask SightLayers => LayerMaskClass.HighPolyWithTerrainMask;
private int MinJobSize = 1;
private List<Player> RegisteredPlayers = Singleton<GameWorld>.Instance.RegisteredPlayers;
private Dictionary<string, SAINComponent> SAINComponents = new Dictionary<string, SAINComponent>();
private List<SAINComponent> SAINComponentsList = new List<SAINComponent>();

private int Frames = 0;

private void Awake()
{
Logger = BepInEx.Logging.Logger.CreateLogSource(GetType().Name);
}

private ManualLogSource Logger;

private void Update()
{
var game = Singleton<IBotGame>.Instance;
if (game == null)
{
return;
}
if (!EnableVisionJobs.Value)
{
return;
}

Frames++;
if (Frames == CheckFrameCount.Value)
{
Frames = 0;

FindBotComponents();
GlobalRaycastJob();
EnemyRaycastJob();
}
}

private void FindBotComponents()
{
for (int i = 0; i < RegisteredPlayers.Count; i++)
{
var player = RegisteredPlayers[i];
string Id = player.ProfileId;
if (SAINComponents.ContainsKey(Id))
{
if (player.HealthController?.IsAlive == false || SAINComponents[Id] == null)
{
SAINComponents.Remove(Id);
}
}
else
{
if (player.HealthController?.IsAlive == true && player.IsAI)
{
var bot = player.AIData?.BotOwner;
if (bot != null && bot.Profile.Info.Settings.Role != WildSpawnType.marksman)
{
SAINComponents.Add(Id, bot.GetOrAddComponent<SAINComponent>());
}
}
}
}

SAINComponentsList = SAINComponents.Values.ToList();
}

private Vector3 HeadPos(Player player)
{
return player.MainParts[BodyPartType.head].Position;
}

private Vector3 BodyPos(Player player)
{
return player.MainParts[BodyPartType.body].Position;
}

private Vector3[] Parts(Player player)
{
return player.MainParts.Values.Select(item => item.Position).ToArray();
}

private void EnemyRaycastJob()
{
List<SAINComponent> botsWithEnemy = new List<SAINComponent>();
foreach (var bot in SAINComponentsList)
{
if (bot.Enemy != null)
{
botsWithEnemy.Add(bot);
}
}

NativeArray<SpherecastCommand> spherecastCommands = new NativeArray<SpherecastCommand>(
botsWithEnemy.Count * 6,
Allocator.TempJob
);
NativeArray<RaycastHit> raycastHits = new NativeArray<RaycastHit>(
botsWithEnemy.Count * 6,
Allocator.TempJob
);

for (int i = 0; i < botsWithEnemy.Count; i++)
{
var bot = botsWithEnemy[i];
Player enemy = bot.Enemy.EnemyPlayer;
Vector3 head = HeadPos(bot.BotOwner.GetPlayer);
var bodyParts = Parts(enemy);
for (int j = 0; j < bodyParts.Length - 1; j++)
{
Vector3 target = bodyParts[j];
Vector3 direction = target - head;
float max = bot.BotOwner.Settings.Current.CurrentVisibleDistance;
float rayDistance = Mathf.Clamp(direction.magnitude, 0f, max);
spherecastCommands[j] = new SpherecastCommand(
head,
SpherecastRadius,
direction.normalized,
rayDistance,
SightLayers
);
}
}

JobHandle spherecastJob = SpherecastCommand.ScheduleBatch(
spherecastCommands,
raycastHits,
MinJobSize
);

spherecastJob.Complete();
int visiblecount = 0;

for (int i = 0; i < botsWithEnemy.Count; i++)
{
bool visible = false;
var bot = botsWithEnemy[i];
Player enemy = bot.Enemy.EnemyPlayer;
var bodyParts = Parts(enemy);
for (int j = 0; j < bodyParts.Length - 1; j++)
{
if (raycastHits[j].collider == null)
{
visiblecount++;
bot.Enemy?.OnGainSight();
if (DebugVision.Value)
{
DebugGizmos.SingleObjects.Line(bot.HeadPosition, bot.Enemy.EnemyPlayer.MainParts[BodyPartType.body].Position, Color.red, 0.01f, true, 0.1f, true);
}
break;
}
}
if (!visible)
{
bot.Enemy?.OnLoseSight();
}
}

if (DebugTimer < Time.time)
{
DebugTimer = Time.time + 5f;
Logger.LogInfo($"Raycast Job Enemy Complete [{spherecastCommands.Length}] raycasts finished for [{botsWithEnemy.Count}] bots with enemy. Found [{visiblecount}] visible enemies.");
}

spherecastCommands.Dispose();
raycastHits.Dispose();
}

private float DebugTimer = 0f;

private void GlobalRaycastJob()
{
NativeArray<SpherecastCommand> allSpherecastCommands = new NativeArray<SpherecastCommand>(
SAINComponentsList.Count * RegisteredPlayers.Count,
Allocator.TempJob
);
NativeArray<RaycastHit> allRaycastHits = new NativeArray<RaycastHit>(
SAINComponentsList.Count * RegisteredPlayers.Count,
Allocator.TempJob
);

int currentIndex = 0;

for (int i = 0; i < SAINComponentsList.Count; i++)
{
var bot = SAINComponentsList[i];
Vector3 head = HeadPos(bot.BotOwner.GetPlayer);

for (int j = 0; j < RegisteredPlayers.Count; j++)
{
Vector3 target = BodyPos(RegisteredPlayers[j]);
Vector3 direction = target - head;
float max = bot.BotOwner.Settings.Current.CurrentVisibleDistance;
float rayDistance = Mathf.Clamp(direction.magnitude, 0f, max);

allSpherecastCommands[currentIndex] = new SpherecastCommand(
head,
SpherecastRadius,
direction.normalized,
rayDistance,
SightLayers
);

currentIndex++;
}
}

JobHandle spherecastJob = SpherecastCommand.ScheduleBatch(
allSpherecastCommands,
allRaycastHits,
MinJobSize
);
int visiblecount = 0;
spherecastJob.Complete();

for (int i = 0; i < SAINComponentsList.Count; i++)
{
int startIndex = i * RegisteredPlayers.Count;
var visiblePlayers = SAINComponentsList[i].VisiblePlayers;

for (int j = 0; j < RegisteredPlayers.Count; j++)
{
currentIndex = startIndex + j;
if (allRaycastHits[currentIndex].collider != null)
{
if (visiblePlayers.Contains(RegisteredPlayers[j]))
{
visiblePlayers.Remove(RegisteredPlayers[j]);
}
}
else
{
visiblecount++;
if (!visiblePlayers.Contains(RegisteredPlayers[j]))
{
visiblePlayers.Add(RegisteredPlayers[j]);
}
}
}
}

if (DebugTimer < Time.time)
{
Logger.LogInfo($"Raycast Job Complete [{allSpherecastCommands.Length}] raycasts finished. Found [{visiblecount}] visible players.");
}

allSpherecastCommands.Dispose();
allRaycastHits.Dispose();
}
}
2 changes: 1 addition & 1 deletion Components/SAIN Bot Component/Classes/AILimitClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void Update()
float distanceToPlayer = SAIN.DistanceToMainPlayer;
if (distanceToPlayer > 500f)
{
TimeAdd = 3;
TimeAdd = 3f;
}
else if (distanceToPlayer > 300f)
{
Expand Down
20 changes: 15 additions & 5 deletions Components/SAIN Bot Component/Classes/BotSquadClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ public void Update()
FindSquadLeader();
}
}

if ((Leader == null || GroupSize != SquadMembers.Count) && TimeSinceLeaderDied == 0f)
if (SquadMembers != null)
{
GroupSize = SquadMembers.Count;
FindSquadLeader();
if ((Leader == null || GroupSize != SquadMembers.Count) && TimeSinceLeaderDied == 0f)
{
GroupSize = SquadMembers.Count;
FindSquadLeader();
}
}
}

Expand Down Expand Up @@ -97,6 +99,7 @@ private void AddLeader(SAINComponent sain)
LeaderDieTime = 0f;
IAmLeader = sain.ProfileId == BotOwner.ProfileId;
LeaderComponent = sain;
Leader = sain.BotOwner;
if (IAmLeader && SquadID == "None")
{
SquadID = Guid.NewGuid().ToString("N");
Expand All @@ -113,7 +116,8 @@ private void AddLeader(SAINComponent sain)

public bool IAmLeader { get; private set; } = false;

public BotOwner Leader => LeaderComponent.BotOwner;
public BotOwner Leader { get; private set; }

public SAINComponent LeaderComponent { get; private set; }

public bool BotInGroup => BotOwner.BotsGroup.MembersCount > 1;
Expand Down Expand Up @@ -169,6 +173,12 @@ private void UpdateMembers()

SquadPowerLevel = BotOwner.BotsGroup.GroupPower;
SquadMembers = dictionary;

if (Leader == null)
{
GroupSize = SquadMembers.Count;
FindSquadLeader();
}
}

public bool MemberIsFallingBack { get; private set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,12 @@ public class SquadDecisionClass : SAINBot
public SquadDecisionClass(BotOwner bot) : base(bot) { }

protected ManualLogSource Logger => SAIN.Decision.Logger;
private SquadClass Squad => SAIN.BotSquad;

public bool GetDecision(out SAINSquadDecision Decision)
{
Decision = SAINSquadDecision.None;
if (!SAIN.BotSquad.BotInGroup || SAIN.BotSquad.SquadMembers == null || SAIN.BotSquad.Leader?.IsDead == true)
{
return false;
}
if (!SAIN.HasEnemy ||! SAIN.HasGoalEnemy)
if (!Squad.BotInGroup || Squad.Leader?.IsDead == true)
{
return false;
}
Expand All @@ -29,15 +26,17 @@ public bool GetDecision(out SAINSquadDecision Decision)
return false;
}

if (!EnemyDecision(out Decision))
if (EnemyDecision(out Decision))
{
if (StartRegroup())
{
Decision = SAINSquadDecision.Regroup;
}
return true;
}
if (StartRegroup())
{
Decision = SAINSquadDecision.Regroup;
return true;
}

return Decision != SAINSquadDecision.None;
return false;
}

private bool EnemyDecision(out SAINSquadDecision Decision)
Expand Down
Loading