diff --git a/.vs/SAIN/FileContentIndex/1c05c128-fe22-4f21-ad45-84c2ee4b93d7.vsidx b/.vs/SAIN/FileContentIndex/1c05c128-fe22-4f21-ad45-84c2ee4b93d7.vsidx new file mode 100644 index 000000000..d101eafd1 Binary files /dev/null and b/.vs/SAIN/FileContentIndex/1c05c128-fe22-4f21-ad45-84c2ee4b93d7.vsidx differ diff --git a/.vs/SAIN/FileContentIndex/226762c4-f3ac-4f6e-a339-89ff0798266c.vsidx b/.vs/SAIN/FileContentIndex/226762c4-f3ac-4f6e-a339-89ff0798266c.vsidx deleted file mode 100644 index bf1b206ca..000000000 Binary files a/.vs/SAIN/FileContentIndex/226762c4-f3ac-4f6e-a339-89ff0798266c.vsidx and /dev/null differ diff --git a/.vs/SAIN/FileContentIndex/307995a4-3d41-44af-88d4-7a3c003f4e2f.vsidx b/.vs/SAIN/FileContentIndex/307995a4-3d41-44af-88d4-7a3c003f4e2f.vsidx new file mode 100644 index 000000000..bc57766ec Binary files /dev/null and b/.vs/SAIN/FileContentIndex/307995a4-3d41-44af-88d4-7a3c003f4e2f.vsidx differ diff --git a/.vs/SAIN/FileContentIndex/50d9dac4-4219-442c-a540-7e4e3217aa6a.vsidx b/.vs/SAIN/FileContentIndex/50d9dac4-4219-442c-a540-7e4e3217aa6a.vsidx new file mode 100644 index 000000000..79941a24c Binary files /dev/null and b/.vs/SAIN/FileContentIndex/50d9dac4-4219-442c-a540-7e4e3217aa6a.vsidx differ diff --git a/.vs/SAIN/FileContentIndex/7ab1bb2b-755b-4875-9d55-ebf06b941be0.vsidx b/.vs/SAIN/FileContentIndex/7ab1bb2b-755b-4875-9d55-ebf06b941be0.vsidx deleted file mode 100644 index 8a60e94df..000000000 Binary files a/.vs/SAIN/FileContentIndex/7ab1bb2b-755b-4875-9d55-ebf06b941be0.vsidx and /dev/null differ diff --git a/.vs/SAIN/FileContentIndex/98fef9f1-e2eb-470e-bbbe-93588e5190b7.vsidx b/.vs/SAIN/FileContentIndex/98fef9f1-e2eb-470e-bbbe-93588e5190b7.vsidx new file mode 100644 index 000000000..d9ac335fc Binary files /dev/null and b/.vs/SAIN/FileContentIndex/98fef9f1-e2eb-470e-bbbe-93588e5190b7.vsidx differ diff --git a/.vs/SAIN/FileContentIndex/bacff32f-a01e-4af2-862a-75e4e38997db.vsidx b/.vs/SAIN/FileContentIndex/bacff32f-a01e-4af2-862a-75e4e38997db.vsidx deleted file mode 100644 index e747c664c..000000000 Binary files a/.vs/SAIN/FileContentIndex/bacff32f-a01e-4af2-862a-75e4e38997db.vsidx and /dev/null differ diff --git a/.vs/SAIN/v17/.suo b/.vs/SAIN/v17/.suo index c0baf698a..8e564f2e4 100644 Binary files a/.vs/SAIN/v17/.suo and b/.vs/SAIN/v17/.suo differ diff --git a/Components/SAIN Bot Component/Classes/BotSquadClass.cs b/Components/SAIN Bot Component/Classes/BotSquadClass.cs index 38c12fe4d..6f444f20a 100644 --- a/Components/SAIN Bot Component/Classes/BotSquadClass.cs +++ b/Components/SAIN Bot Component/Classes/BotSquadClass.cs @@ -10,6 +10,8 @@ namespace SAIN.Classes { public class SquadClass : SAINBot { + public string SquadID { get; private set; } = "None"; + public SquadClass(BotOwner bot) : base(bot) { Logger = BepInEx.Logging.Logger.CreateLogSource(GetType().Name); @@ -25,63 +27,93 @@ public void Update() UpdateMembers(); - if (Leader == null || !Leader.HealthController.IsAlive || GroupSize != SquadMembers.Count) + if (Leader != null || Leader.IsDead) + { + if (LeaderDieTime == 0f) + { + LeaderDieTime = Time.time; + } + if (TimeSinceLeaderDied > 30f) + { + FindSquadLeader(); + } + } + + if ((Leader == null || GroupSize != SquadMembers.Count) && TimeSinceLeaderDied == 0f) { GroupSize = SquadMembers.Count; FindSquadLeader(); } } + + if (SquadID == "None" && LeaderComponent != null && !IAmLeader) + { + SquadID = LeaderComponent.BotSquad.SquadID; + } } } + public float TimeSinceLeaderDied => LeaderDieTime == 0f ? 0f : Time.time - LeaderDieTime; + public float LeaderDieTime { get; private set; } = 0f; private int GroupSize = 0; private float UpdateMembersTimer = 0f; private void FindSquadLeader() { - // If this bot is a boss type, they are the squad leader - if (SAIN.Info.IsBoss) + // Assign current bot as leader to start + float power = SAIN.Info.PowerLevel; + var leadComponent = SAIN; + + if (SAIN.Info.IAmBoss) { - IsSquadLead = true; - Leader = BotOwner; - LeaderComponent = SAIN; + AddLeader(leadComponent); return; } - // Assign current bot as leader to start - BotOwner newSquadLead = BotOwner; - float power = SAIN.Info.PowerLevel; // Iterate through each bot component in friendly group to see who has the highest power level or if any are bosses foreach (var bot in SquadMembers.Values) { + if (bot == null || bot.IsDead) continue; + // If this bot is a boss type, they are the squad leader - if (bot.Info.IsBoss) + if (bot.Info.IAmBoss) { - newSquadLead = bot.BotOwner; + leadComponent = bot; break; } // else If this bot has a higher power level than the last one we checked, they are the squad leader if (bot.Info.PowerLevel > power) { power = bot.Info.PowerLevel; - newSquadLead = bot.BotOwner; + leadComponent = bot; } } - // If the current bot is the result, mark the IsSquadLead value as true - IsSquadLead = newSquadLead.ProfileId == BotOwner.ProfileId; - Leader = newSquadLead; - LeaderComponent = newSquadLead.GetComponent(); + AddLeader(leadComponent); + } + private void AddLeader(SAINComponent sain) + { + LeaderDieTime = 0f; + IAmLeader = sain.ProfileId == BotOwner.ProfileId; + LeaderComponent = sain; + if (IAmLeader && SquadID == "None") + { + SquadID = Guid.NewGuid().ToString("N"); + } if (DebugBotInfo.Value) - Logger.LogDebug($"For Bot: [{BotOwner.Profile.Nickname}]: [{newSquadLead.Profile.Nickname}] is Squad lead! Power Level = [{power}] Squad Power = [{SquadPowerLevel}] Members Count = [{SquadMembers.Count}]"); + { + Logger.LogDebug($"For Bot: [{SAIN.BotOwner.name}]: [{sain.BotOwner.name}] is Squad lead! " + + $"Lead is Boss? {sain.Info.IAmBoss} Lead Power: [{sain.Info.PowerLevel}] My Power: [{SAIN.Info.PowerLevel}] " + + $"Squad Power = [{SquadPowerLevel}] Members Count = [{SquadMembers.Count}]"); + } } public float SquadPowerLevel { get; private set; } - public bool IsSquadLead { get; private set; } = false; + public bool IAmLeader { get; private set; } = false; - public BotOwner Leader { get; private set; } + public BotOwner Leader => LeaderComponent.BotOwner; public SAINComponent LeaderComponent { get; private set; } public bool BotInGroup => BotOwner.BotsGroup.MembersCount > 1; diff --git a/Components/SAIN Bot Component/Classes/Decision/Classes/SquadDecisionClass.cs b/Components/SAIN Bot Component/Classes/Decision/Classes/SquadDecisionClass.cs index c3c0a0e09..5aba264a9 100644 --- a/Components/SAIN Bot Component/Classes/Decision/Classes/SquadDecisionClass.cs +++ b/Components/SAIN Bot Component/Classes/Decision/Classes/SquadDecisionClass.cs @@ -16,11 +16,11 @@ public SquadDecisionClass(BotOwner bot) : base(bot) { } public bool GetDecision(out SAINSquadDecision Decision) { Decision = SAINSquadDecision.None; - if (!SAIN.BotSquad.BotInGroup || SAIN.BotSquad.SquadMembers == null) + if (!SAIN.BotSquad.BotInGroup || SAIN.BotSquad.SquadMembers == null || SAIN.BotSquad.Leader?.IsDead == true) { return false; } - if (SAIN.Enemy?.IsVisible == true) + if (SAIN.Enemy?.IsVisible == true || BotOwner.Memory.GoalEnemy?.PersonalLastShootTime < 3f) { return false; } @@ -132,7 +132,7 @@ private bool EndHelp(SAINComponent member, float distance) public bool StartRegroup() { var squad = SAIN.BotSquad; - if (squad.IsSquadLead) + if (squad.IAmLeader) { return false; } diff --git a/Components/SAIN Bot Component/Classes/Decision/DecisionClass.cs b/Components/SAIN Bot Component/Classes/Decision/DecisionClass.cs index ea26f95b2..b52cd3cbb 100644 --- a/Components/SAIN Bot Component/Classes/Decision/DecisionClass.cs +++ b/Components/SAIN Bot Component/Classes/Decision/DecisionClass.cs @@ -44,12 +44,13 @@ public void Update() { MainDecision = SAINSoloDecision.None; CurrentSquadDecision = SAINSquadDecision.None; + CurrentSelfDecision = SAINSelfDecision.None; return; } if (DecisionTimer < Time.time) { - DecisionTimer = Time.time + 0.03f; + DecisionTimer = Time.time + 0.05f; if (UpdateEnemyTimer < Time.time) { @@ -63,23 +64,24 @@ public void Update() private void GetDecision() { - SAINSquadDecision squadDecision = SAINSquadDecision.None; - SAINSelfDecision selfDecision = SAINSelfDecision.None; + var soloDecision = SAINSoloDecision.None; + var squadDecision = SAINSquadDecision.None; + var selfDecision = SAINSelfDecision.None; - if (!CheckStuckDecision(out SAINSoloDecision soloDecision)) + if (SelfActionDecisions.GetDecision(out selfDecision)) + { + } + else if (CheckStuckDecision(out soloDecision)) + { + } + else if (SquadDecisions.GetDecision(out squadDecision)) + { + } + else if (EnemyDecisions.GetDecision(out soloDecision)) + { + } + else if (GoalTargetDecisions.GetDecision(out soloDecision)) { - if (!SelfActionDecisions.GetDecision(out selfDecision)) - { - if (!SquadDecisions.GetDecision(out squadDecision)) - { - if (!EnemyDecisions.GetDecision(out soloDecision)) - { - if (!GoalTargetDecisions.GetDecision(out soloDecision)) - { - } - } - } - } } UpdateDecisionProperties(soloDecision, squadDecision, selfDecision); diff --git a/Components/SAIN Bot Component/Classes/InfoClass.cs b/Components/SAIN Bot Component/Classes/InfoClass.cs index 45f6b73fa..54aaf7914 100644 --- a/Components/SAIN Bot Component/Classes/InfoClass.cs +++ b/Components/SAIN Bot Component/Classes/InfoClass.cs @@ -22,7 +22,7 @@ public BotInfoClass(BotOwner bot) : base(bot) private void Init() { - IsBoss = CheckIsBoss(BotType); + IAmBoss = CheckIsBoss(BotType); IsFollower = CheckIsFollower(BotType); IsScav = BotType == WildSpawnType.assault || BotType == WildSpawnType.cursedAssault || BotType == WildSpawnType.marksman; string botTypeString = BotType.ToString(); @@ -54,7 +54,7 @@ public bool CanBotTalk { if (BotOwner.Settings.FileSettings.Mind.CAN_TALK && TalkGlobal.Value) { - if (IsBoss) + if (IAmBoss) { return BossTalk.Value; } @@ -81,7 +81,7 @@ private float CalculateDifficulty(BotOwner bot) if (settings != null) { - return GetDifficultyMod(settings.Role, settings.BotDifficulty, IsBoss, IsFollower); + return GetDifficultyMod(settings.Role, settings.BotDifficulty, IAmBoss, IsFollower); } return 1f; @@ -131,7 +131,7 @@ public void GetTimeBeforeSearch() { searchTime = 3f; } - else if (IsBoss && group.BotInGroup) + else if (IAmBoss && group.BotInGroup) { searchTime = 20f; } @@ -169,7 +169,7 @@ public void GetTimeBeforeSearch() if (DebugBotInfo.Value) { - Logger.LogDebug($"Search Time = [{searchTime}] because: IsBoss? [{IsBoss}] IsFollower? [{IsFollower}] Personality [{BotPersonality}] SquadLead? [{group.IsSquadLead}] Squad Members: [{group.SquadMembers.Count}]"); + Logger.LogDebug($"Search Time = [{searchTime}] because: IsBoss? [{IAmBoss}] IsFollower? [{IsFollower}] Personality [{BotPersonality}] SquadLead? [{group.IAmLeader}] Squad Members: [{group.SquadMembers.Count}]"); } TimeBeforeSearch = searchTime; @@ -330,7 +330,7 @@ private bool CanBeRat public EPlayerSide Faction => BotOwner.Profile.Side; - public bool IsBoss { get; private set; } + public bool IAmBoss { get; private set; } public bool IsFollower { get; private set; } diff --git a/Components/SAIN Bot Component/Classes/Objects/SAINEnemyClass.cs b/Components/SAIN Bot Component/Classes/Objects/SAINEnemyClass.cs index 54c7eb932..88dfa875f 100644 --- a/Components/SAIN Bot Component/Classes/Objects/SAINEnemyClass.cs +++ b/Components/SAIN Bot Component/Classes/Objects/SAINEnemyClass.cs @@ -5,22 +5,68 @@ using SAIN.Helpers; using UnityEngine; using UnityEngine.AI; +using System.Collections.Generic; +using System.Xml.Serialization; +using System.Linq; namespace SAIN.Classes { - public class SAINEnemy + public class SAINEnemy : SAINBot { - public BotOwner BotOwner { get; private set; } public GClass475 GoalEnemy => BotOwner.Memory.GoalEnemy; + public Player EnemyPlayer { get; private set; } + public EnemyComponent EnemyComponent { get; private set; } private readonly float BotDifficultyModifier; - public SAINEnemy(BotOwner bot, IAIDetails person, float BotDifficultyMod) + public SAINEnemy(BotOwner bot, IAIDetails person, float BotDifficultyMod) : base(bot) { - BotOwner = bot; Person = person; + EnemyPlayer = person.GetPlayer; BotDifficultyModifier = BotDifficultyMod; Logger = BepInEx.Logging.Logger.CreateLogSource(GetType().Name); + + if (SAIN.BotSquad.BotInGroup) + { + FindEnemyComponent(); + } + } + + private void FindEnemyComponent() + { + var componentArray = EnemyPlayer.GetComponents(); + int? count = componentArray?.Count(); + if (componentArray == null || count == 0) + { + AddNewComponent(); + } + else + { + if (componentArray != null && count > 0) + { + foreach (var component in componentArray) + { + if (component != null) + { + if (component.OwnerSquadId == SAIN.SquadId) + { + EnemyComponent = component; + break; + } + } + } + } + } + if (EnemyComponent == null) + { + AddNewComponent(); + } + } + + private void AddNewComponent() + { + Logger.LogDebug($"New Enemy Component added for enemy: [{EnemyPlayer.name}] for SquadID: [{SAIN.SquadId}]"); + EnemyComponent = EnemyPlayer.gameObject.AddComponent(); } private readonly ManualLogSource Logger; @@ -99,32 +145,22 @@ private float GetMagnitudeToBot(Vector3 point) public bool Seen { get; private set; } public float TimeFirstSeen { get; private set; } public float TimeLastSeen { get; private set; } - - private ReactionTimeChecker ReactionTime; + public bool CanHearCloseVisible { get; private set; } + public bool EnemyClose { get; private set; } private float DistanceTimer = 0f; private void CheckEnemyVisible() { + bool lineOfSight = InLineOfSight; + bool defaultVisible = GoalEnemy?.IsVisible == true && GoalEnemy?.CanShoot == true; + if (CheckVisibleTimer < Time.time) { - CheckVisibleTimer = Time.time + 0.1f; - - bool lineOfSight = RayCastBodyParts(); - bool visible = false; - if (lineOfSight) - { - if (BotOwner.LookSensor.IsPointInVisibleSector(Person.Position)) - { - if (GoalEnemy?.IsVisible == true) - { - visible = true; - } - } - } - - UpdateVisible(visible, lineOfSight); + CheckVisibleTimer = Time.time + 0.25f; + lineOfSight = RayCastBodyParts(); } + UpdateVisible(defaultVisible, lineOfSight); } private void UpdateVisible(bool isVisible, bool inLineOfSight) @@ -133,8 +169,18 @@ private void UpdateVisible(bool isVisible, bool inLineOfSight) bool wasVisible = IsVisible; IsVisible = isVisible; + float realDistance = RealDistance; + bool close = realDistance < 15f; + + var move = Person.GetPlayer.MovementContext; + bool enemySprinting = move.IsSprintEnabled; + float enemyMoveSpeed = move.ClampedSpeed / move.MaxSpeed; + + bool canHear = (enemySprinting && realDistance < 35f) || close && enemyMoveSpeed > 0.25f; + if (isVisible) { + TimeSinceSeen = 0f; if (!Seen) { TimeFirstSeen = Time.time; @@ -153,6 +199,10 @@ private void UpdateVisible(bool isVisible, bool inLineOfSight) TimeSinceSeen = Time.time - TimeLastSeen; } } + + EnemyClose = close; + CanHearCloseVisible = canHear; + EnemyComponent?.UpdateVisible(SAIN.ProfileId, IsVisible, EnemyClose, CanHearCloseVisible); } private bool RayCastBodyParts() @@ -294,6 +344,240 @@ public bool EnemyLookingAtMe private float CheckVisibleTimer = 0f; } + public class GroupMemberComponent : MonoBehaviour + { + public SAINComponent SAIN { get; private set; } + public Dictionary Members => SAIN.BotSquad.SquadMembers; + public Dictionary VisibleMembers { get; private set; } = new Dictionary(); + + public void Init(SAINComponent sain) + { + SAIN = sain; + } + + private void Awake() + { + + } + + private void Update() + { + if (!SAIN.BotSquad.BotInGroup || SAIN.GameIsEnding || SAIN.IsDead) + { + Dispose(); + return; + } + if (SAIN.BotActive) + { + if (CheckVisTimer < Time.time) + { + CheckVisTimer = Time.time + 1f; + UpdateVisibleMembers(); + } + } + } + + private float CheckVisTimer = 0f; + + private void UpdateVisibleMembers() + { + var memberDict = new Dictionary(Members); + var visibleMembers = new Dictionary(); + foreach (var member in memberDict) + { + if (member.Value == null || member.Value.IsDead) continue; + if (VisibleByMember(member.Value)) + { + if (!VisibleMembers.ContainsKey(member.Key)) + { + visibleMembers.Add(member.Key, member.Value); + } + } + else if (VisibleMembers.ContainsKey(member.Key)) + { + visibleMembers.Remove(member.Key); + } + } + VisibleMembers = visibleMembers; + } + + public bool VisibleByMember(SAINComponent member) + { + Vector3 start = SAIN.HeadPosition; + Vector3 end = member.HeadPosition; + Vector3 direction = end - start; + return !Physics.Raycast(start, direction, direction.magnitude, LayerMaskClass.HighPolyWithTerrainMask); + } + + public void Dispose() + { + StopAllCoroutines(); + VisibleMembers?.Clear(); + Destroy(this); + } + } + + public class EnemyComponent : MonoBehaviour + { + public Dictionary Owners { get; private set; } = new Dictionary(); + public Player EnemyPlayer { get; private set; } + public bool VisibleByGroup { get; private set; } + public bool HeardByGroup { get; private set; } + public bool CloseToGroupMember { get; private set; } + public float EnemyPower { get; private set; } + public ETagStatus EnemyHealthStatus { get; private set; } + public string OwnerSquadId { get; private set; } = "None"; + + public GroupOwner MyInfo(string botProfileID) + { + if (!Owners.ContainsKey(botProfileID)) + { + System.Console.WriteLine($"{botProfileID} does not exist in owners list!"); + return null; + } + return Owners[botProfileID]; + } + + public bool CheckIfComponentIsForGroup(SAINComponent sainComponent) + { + foreach (var item in Owners) + { + if (item.Value == null) continue; + if (item.Value.SAINComponent.ProfileId == sainComponent.ProfileId) + { + return true; + } + if (item.Value.SAINComponent.SquadId == sainComponent.SquadId) + { + return true; + } + } + return false; + } + + private void Awake() + { + EnemyPlayer = GetComponent(); + EnemyPower = EnemyPlayer.AIData?.PowerOfEquipment ?? 50f; + } + + private void Update() + { + if (EnemyPlayer == null || EnemyPlayer.HealthController?.IsAlive == false || Owners.Count == 0) + { + Dispose(); + return; + } + if (UpdateTimer < Time.time) + { + UpdateTimer = Time.time + 0.5f; + ClearDead(); + CheckVisibleStatus(); + } + } + + private void CheckVisibleStatus() + { + bool visible = false; + bool canHear = false; + bool close = false; + + foreach (var item in Owners) + { + if (close && canHear && visible) break; + if (item.Value == null) continue; + if (item.Value.BotOwner.Memory.GoalEnemy?.Person?.GetPlayer != EnemyPlayer) continue; + + if (!visible && item.Value.CanSeeEnemy) + { + visible = true; + } + if (!canHear && item.Value.CanHearEnemy) + { + canHear = true; + } + if (!close && item.Value.CloseToEnemy) + { + close = true; + } + } + + VisibleByGroup = visible; + if (visible) + { + EnemyHealthStatus = EnemyPlayer.HealthStatus; + } + HeardByGroup = canHear; + CloseToGroupMember = close; + } + + private void ClearDead() + { + var owners = new Dictionary(Owners); + foreach (var item in Owners) + { + if (item.Value == null || item.Value.BotIsDead) + { + owners.Remove(item.Key); + } + } + Owners = owners; + } + + private float UpdateTimer = 0f; + + public bool AddOwner(SAINComponent sainComponent) + { + string botID = sainComponent.BotOwner.ProfileId; + if (!Owners.ContainsKey(botID)) + { + if (OwnerSquadId == "None") + { + OwnerSquadId = sainComponent.SquadId; + } + Owners.Add(botID, new GroupOwner(sainComponent)); + return true; + } + return false; + } + + public bool UpdateVisible(string botProfileID, bool visible, bool close, bool canHear) + { + var owner = MyInfo(botProfileID); + if (owner == null) + { + return false; + } + + owner.CanSeeEnemy = visible; + owner.CloseToEnemy = close; + owner.CanHearEnemy = canHear; + return true; + } + + public void Dispose() + { + StopAllCoroutines(); + Owners?.Clear(); + EnemyPlayer = null; + Destroy(this); + } + } + + public class GroupOwner + { + public SAINComponent SAINComponent { get; private set; } + public BotOwner BotOwner => SAINComponent.BotOwner; + public bool CloseToEnemy { get; set; } + public bool CanSeeEnemy { get; set; } + public bool CanHearEnemy { get; set; } + public bool BotIsDead => BotOwner.HealthController?.IsAlive == false; + public GroupOwner(SAINComponent SAIN) + { + SAINComponent = SAIN; + } + } + public class ReactionTimeChecker { public ReactionTimeChecker(float reactionTime) diff --git a/Components/SAIN Bot Component/Classes/Objects/SearchClass.cs b/Components/SAIN Bot Component/Classes/Objects/SearchClass.cs index 8170146df..3ab6edd88 100644 --- a/Components/SAIN Bot Component/Classes/Objects/SearchClass.cs +++ b/Components/SAIN Bot Component/Classes/Objects/SearchClass.cs @@ -1,7 +1,6 @@ using BepInEx.Logging; using EFT; using SAIN.Helpers; -using System.IO; using UnityEngine; using UnityEngine.AI; using static SAIN.UserSettings.DebugConfig; @@ -166,7 +165,6 @@ private void UpdateLean() private float UpdateLeanTimer = 0f; private float UnstuckMoveTimer = 0f; public bool BotIsStuck => SAIN.BotStuck.BotIsStuck; - private float CheckMoveTimer = 0f; public Vector3? TargetPosition { get; private set; } private void Reset() @@ -217,6 +215,10 @@ public NavMeshPathStatus GoToPoint(Vector3 point, bool MustHavePath = true, floa Vector3 blindCorner = Path.corners[i]; Vector3 dangerPoint = Path.corners[i + 1]; Vector3 dirToBlind = blindCorner - Start; + if (dirToBlind.magnitude < 1f) + { + continue; + } Vector3 dirToDanger = dangerPoint - Start; Vector3 startPeekPos = GetPeekStartAndEnd(blindCorner, dangerPoint, dirToBlind, dirToDanger, out var endPeekPos); @@ -246,7 +248,7 @@ private Vector3 GetPeekStartAndEnd(Vector3 blindCorner, Vector3 dangerPoint, Vec { const float maxMagnitude = 5f; const float minMagnitude = 1f; - const float OppositePointMagnitude = 10f; + const float OppositePointMagnitude = 5f; Vector3 directionToStart = BotOwner.Position - blindCorner; @@ -257,7 +259,7 @@ private Vector3 GetPeekStartAndEnd(Vector3 blindCorner, Vector3 dangerPoint, Vec } else if (directionToStart.magnitude < minMagnitude) { - cornerStartDir = directionToStart.normalized; + cornerStartDir = directionToStart.normalized * minMagnitude; } else { diff --git a/Components/SAIN Bot Component/Classes/SteeringClass.cs b/Components/SAIN Bot Component/Classes/SteeringClass.cs index 4fb012ba6..77558f019 100644 --- a/Components/SAIN Bot Component/Classes/SteeringClass.cs +++ b/Components/SAIN Bot Component/Classes/SteeringClass.cs @@ -13,7 +13,7 @@ public SteeringClass(BotOwner bot) : base(bot) Logger = BepInEx.Logging.Logger.CreateLogSource(GetType().Name); } - public bool ManualUpdate() + public bool ManualUpdate(bool useDefaultHear = true) { if (!SAIN.BotActive || SAIN.GameIsEnding) { @@ -42,13 +42,14 @@ public bool ManualUpdate() } else { - BotOwner.LookData.SetLookPointByHearing(); + if (useDefaultHear) + { + BotOwner.LookData.SetLookPointByHearing(); + } return false; } } - private float UpdateSteerTimer = 0f; - public void LookToGoalEnemyPos() { var enemy = BotOwner.Memory.GoalEnemy; diff --git a/Components/SAIN Bot Component/Classes/Talk/GroupTalk.cs b/Components/SAIN Bot Component/Classes/Talk/GroupTalk.cs index 40fea0bfe..2c50a10e6 100644 --- a/Components/SAIN Bot Component/Classes/Talk/GroupTalk.cs +++ b/Components/SAIN Bot Component/Classes/Talk/GroupTalk.cs @@ -124,7 +124,7 @@ public void Update() } } - if (BotOwner.Memory.GoalEnemy != null && SAIN.BotSquad.IsSquadLead) + if (BotOwner.Memory.GoalEnemy != null && SAIN.BotSquad.IAmLeader) { UpdateLeaderCommand(); } @@ -175,7 +175,7 @@ private void UpdateLeaderCommand() { if (LeaderComponent != null) { - if (BotSquad.IsSquadLead && LeaderTimer < Time.time) + if (BotSquad.IAmLeader && LeaderTimer < Time.time) { LeaderTimer = Time.time + LeaderFreq * Randomized; diff --git a/Components/SAIN Bot Component/SAINComponent.cs b/Components/SAIN Bot Component/SAINComponent.cs index c72e62dae..87464e989 100644 --- a/Components/SAIN Bot Component/SAINComponent.cs +++ b/Components/SAIN Bot Component/SAINComponent.cs @@ -15,6 +15,9 @@ private void Awake() Init(BotOwner); } + public string ProfileId => BotOwner.ProfileId; + public string SquadId => BotSquad.SquadID; + private void Init(BotOwner bot) { BotColor = RandomColor; @@ -41,6 +44,10 @@ private void Init(BotOwner bot) private void Update() { + if (GameHasEnded) + { + Dispose(); + } if (BotActive && !GameIsEnding) { var goalEnemy = BotOwner.Memory.GoalEnemy; @@ -75,7 +82,13 @@ private void Update() } } - AILimit.Update(); + if (UpdateHealthTimer < Time.time) + { + UpdateHealthTimer = Time.time + 0.25f; + HealthStatus = BotOwner.GetPlayer.HealthStatus; + } + + //AILimit.Update(); if (AILimit.Enabled) { return; @@ -87,9 +100,53 @@ private void Update() Cover.Update(); Talk.Update(); SelfActions.Update(); + UpdateFriendlyFire(); + } + } + + private void UpdateFriendlyFire() + { + if (CheckFriendlyFire() == FriendlyFireStatus.FriendlyBlock) + { + StopShooting(); + } + } + + public FriendlyFireStatus CheckFriendlyFire(Vector3? target = null) + { + var friendlyFire = FriendlyFireStatus.None; + if (!BotSquad.BotInGroup || !BotOwner.ShootData.Shooting) + { + return friendlyFire; + } + if (target == null) + { + if (BotOwner.AimingData == null) + { + return friendlyFire; + } + target = BotOwner.AimingData.RealTargetPoint; } + if (BotOwner.ShootData.ChecFriendlyFire(WeaponRoot, target.Value)) + { + friendlyFire = FriendlyFireStatus.FriendlyBlock; + } + else + { + friendlyFire = FriendlyFireStatus.Clear; + } + return friendlyFire; + } + + public void StopShooting() + { + BotOwner.ShootData.EndShoot(); + BotOwner.AimingData?.LoseTarget(); } + public FriendlyFireStatus FriendlyFireStatus { get; private set; } + + private float UpdateHealthTimer = 0f; public float DifficultyModifier => Info.DifficultyModifier; public bool HasEnemy => Enemy != null; @@ -100,9 +157,9 @@ private void Update() public void Dispose() { StopAllCoroutines(); - Lean.Dispose(); - Cover.CoverFinder.Dispose(); - FlashLight.Dispose(); + Lean?.Dispose(); + Cover?.CoverFinder?.Dispose(); + FlashLight?.Dispose(); Destroy(this); } @@ -153,28 +210,17 @@ public void Dispose() public SteeringClass Steering { get; private set; } - public bool BotActive => BotOwner.BotState == EBotState.Active && !BotOwner.IsDead && BotOwner.GetPlayer.enabled; - - public bool GameIsEnding - { - get - { - var game = Singleton.Instance; - if (game == null) - { - return false; - } - - return game.Status == GameStatus.Stopping; - } - } + public bool IsDead => BotOwner?.IsDead == true; + public bool BotActive => BotOwner.BotState == EBotState.Active && !IsDead && BotOwner?.GetPlayer?.enabled == true; + public bool GameIsEnding => GameHasEnded || Singleton.Instance?.Status == GameStatus.Stopping; + public bool GameHasEnded => Singleton.Instance == null; public bool Healthy => HealthStatus == ETagStatus.Healthy; public bool Injured => HealthStatus == ETagStatus.Injured; public bool BadlyInjured => HealthStatus == ETagStatus.BadlyInjured; public bool Dying => HealthStatus == ETagStatus.Dying; - public ETagStatus HealthStatus => BotOwner.GetPlayer.HealthStatus; + public ETagStatus HealthStatus { get; private set; } public LastHeardSound LastHeardSound => Hearing.LastHeardSound; diff --git a/Components/SAIN Bot Component/SAINEnum.cs b/Components/SAIN Bot Component/SAINEnum.cs index 0f40b1a9c..233dbb607 100644 --- a/Components/SAIN Bot Component/SAINEnum.cs +++ b/Components/SAIN Bot Component/SAINEnum.cs @@ -31,6 +31,14 @@ public enum SAINSelfDecision Surgery = 6, } + public enum FriendlyFireStatus + { + None, + FriendlyBlock, + FriendlyClose, + Clear, + } + public enum CoverStatus { None = 0, diff --git a/Layers/SAIN Combat/Actions/SearchAction.cs b/Layers/SAIN Combat/Actions/SearchAction.cs index 07cf0d14e..b23bc366b 100644 --- a/Layers/SAIN Combat/Actions/SearchAction.cs +++ b/Layers/SAIN Combat/Actions/SearchAction.cs @@ -72,7 +72,6 @@ private void CheckShouldSprint() private bool SprintEnabled = false; private float RandomSprintTimer = 0f; - private float ResetTimer = 0f; private void Steer(Vector3 pos) { diff --git a/Patches/GlobalSettingsPatches.cs b/Patches/GlobalSettingsPatches.cs index b2270325f..442158d02 100644 --- a/Patches/GlobalSettingsPatches.cs +++ b/Patches/GlobalSettingsPatches.cs @@ -41,7 +41,7 @@ protected override MethodBase GetTargetMethod() [PatchPostfix] public static void PatchPostfix(BotGlobalAimingSettings __instance) { - __instance.SHPERE_FRIENDY_FIRE_SIZE = 0.15f; + //__instance.SHPERE_FRIENDY_FIRE_SIZE = 0.33f; __instance.RECALC_MUST_TIME = 1; __instance.RECALC_MUST_TIME_MIN = 1; __instance.RECALC_MUST_TIME_MAX = 2; diff --git a/Release/SAIN/BepInEx/plugins/SAIN-3.5.7.dll b/Release/SAIN/BepInEx/plugins/SAIN-3.5.7.dll index 5438766bc..f66683041 100644 Binary files a/Release/SAIN/BepInEx/plugins/SAIN-3.5.7.dll and b/Release/SAIN/BepInEx/plugins/SAIN-3.5.7.dll differ diff --git a/bin/x86/3.5.7/SAIN.dll b/bin/x86/3.5.7/SAIN.dll index 5438766bc..f66683041 100644 Binary files a/bin/x86/3.5.7/SAIN.dll and b/bin/x86/3.5.7/SAIN.dll differ diff --git a/bin/x86/3.5.7/SAIN.pdb b/bin/x86/3.5.7/SAIN.pdb index 809f4c4ad..abd0dea6d 100644 Binary files a/bin/x86/3.5.7/SAIN.pdb and b/bin/x86/3.5.7/SAIN.pdb differ diff --git a/obj/x86/3.5.7/SAIN.csproj.AssemblyReference.cache b/obj/x86/3.5.7/SAIN.csproj.AssemblyReference.cache index d417922e7..e426649d8 100644 Binary files a/obj/x86/3.5.7/SAIN.csproj.AssemblyReference.cache and b/obj/x86/3.5.7/SAIN.csproj.AssemblyReference.cache differ diff --git a/obj/x86/3.5.7/SAIN.dll b/obj/x86/3.5.7/SAIN.dll index 5438766bc..f66683041 100644 Binary files a/obj/x86/3.5.7/SAIN.dll and b/obj/x86/3.5.7/SAIN.dll differ diff --git a/obj/x86/3.5.7/SAIN.pdb b/obj/x86/3.5.7/SAIN.pdb index 809f4c4ad..abd0dea6d 100644 Binary files a/obj/x86/3.5.7/SAIN.pdb and b/obj/x86/3.5.7/SAIN.pdb differ