diff --git a/project/SPT.Custom/CustomAI/AiHelpers.cs b/project/SPT.Custom/CustomAI/AiHelpers.cs index bbc1979..60153c8 100644 --- a/project/SPT.Custom/CustomAI/AiHelpers.cs +++ b/project/SPT.Custom/CustomAI/AiHelpers.cs @@ -1,4 +1,5 @@ using EFT; +using System.Collections.Generic; namespace SPT.Custom.CustomAI { @@ -42,5 +43,20 @@ namespace SPT.Custom.CustomAI return false; } + + public static List GetAllMembers(this BotsGroup group) + { + List members = new List(); + + if (group != null) + { + for (int m = 0; m < group.MembersCount; m++) + { + members.Add(group.Member(m)); + } + } + + return members; + } } } diff --git a/project/SPT.Custom/Patches/IsEnemyPatch.cs b/project/SPT.Custom/Patches/IsEnemyPatch.cs index 30375fe..c0dae1e 100644 --- a/project/SPT.Custom/Patches/IsEnemyPatch.cs +++ b/project/SPT.Custom/Patches/IsEnemyPatch.cs @@ -1,8 +1,10 @@ -using SPT.Reflection.Patching; -using EFT; +using EFT; +using HarmonyLib; +using SPT.Custom.CustomAI; +using SPT.Reflection.Patching; +using System.Collections.Generic; using System.Linq; using System.Reflection; -using HarmonyLib; namespace SPT.Custom.Patches { @@ -25,7 +27,28 @@ namespace SPT.Custom.Patches if (requester == null) { __result = false; + return false; // Skip original + } + // Check existing enemies list + // Could also check x.Value.Player?.Id - BSG do it this way + if (!__instance.Enemies.IsNullOrEmpty() && __instance.Enemies.Any(x => x.Key?.Id == requester.Id)) + { + __result = true; + return false; // Skip original + } + + // Do not force bots to be enemies if they are allies + if (!__instance.Allies.IsNullOrEmpty() && __instance.Allies.Any(x => x?.Id == requester.Id)) + { + __result = false; + return false; // Skip original + } + + // Bots should not become hostile with their group members here. This is needed in case mods add mixed groups (i.e. BEAR's and USEC's). + if (__instance.GetAllMembers().Any(i => i?.Id == requester.Id)) + { + __result = false; return false; // Skip original } @@ -35,73 +58,59 @@ namespace SPT.Custom.Patches || __instance.InitialBotType == WildSpawnType.sectantWarrior || __instance.InitialBotType == WildSpawnType.sectantPriest || __instance.InitialBotType == WildSpawnType.sectactPriestEvent - || __instance.InitialBotType == WildSpawnType.ravangeZryachiyEvent) + || __instance.InitialBotType == WildSpawnType.ravangeZryachiyEvent + || __instance.InitialBotType == WildSpawnType.bossZryachiy + || __instance.InitialBotType == WildSpawnType.followerZryachiy) { return true; // Do original code } - var isEnemy = false; // default not an enemy - - // Check existing enemies list - // Could also check x.Value.Player?.Id - BSG do it this way - if (!__instance.Enemies.IsNullOrEmpty() && __instance.Enemies.Any(x => x.Key.Id == requester.Id)) + // Let EFT manage Rogue behavior toward PMC's + if (__instance.InitialBotType == WildSpawnType.exUsec + && __instance.Side == EPlayerSide.Savage + && requester.Side != EPlayerSide.Savage) { - __result = true; - return false; // Skip original - } - else - { - // Weird edge case - without this you get spammed with key already in enemy list error when you move around on lighthouse - // Make zryachiy use existing isEnemy() code - if (__instance.InitialBotType == WildSpawnType.bossZryachiy) - { - return false; // Skip original - } - - if (__instance.Side == EPlayerSide.Usec) - { - if (requester.Side == EPlayerSide.Bear || requester.Side == EPlayerSide.Savage || - ShouldAttackUsec(requester)) - { - isEnemy = true; - __instance.AddEnemy(requester, EBotEnemyCause.checkAddTODO); - } - } - else if (__instance.Side == EPlayerSide.Bear) - { - if (requester.Side == EPlayerSide.Usec || requester.Side == EPlayerSide.Savage || - ShouldAttackBear(requester)) - { - isEnemy = true; - __instance.AddEnemy(requester, EBotEnemyCause.checkAddTODO); - } - } - else if (__instance.Side == EPlayerSide.Savage) - { - if (requester.Side != EPlayerSide.Savage) - { - //Lets exUsec warn Usecs and fire at will at Bears - if (__instance.InitialBotType == WildSpawnType.exUsec) - { - return true; // Let BSG handle things - } - // everyone else is an enemy to savage (scavs) - isEnemy = true; - __instance.AddEnemy(requester, EBotEnemyCause.checkAddTODO); - } - } + return true; // Do original code } - __result = isEnemy; + // In all other cases, requester needs to be added to the enemies collection of the bot group if it should be treated as hostile + // NOTE: Manually adding enemies is needed as a result of EFT's implementation of PMC's because they are not hostile toward + // Scavs (any probably other bot types too) + __result = CheckIfPlayerShouldBeEnemy(__instance, requester); + if (__result) + { + __instance.AddEnemy(requester, EBotEnemyCause.checkAddTODO); + } return false; // Skip original } + /// + /// Returns true if requester should be an enemy of the bot group + /// + /// + /// + /// + private static bool CheckIfPlayerShouldBeEnemy(BotsGroup __instance, IPlayer requester) + { + switch (__instance.Side) + { + case EPlayerSide.Usec: + return requester.Side != EPlayerSide.Usec || ShouldAttackUsec(requester); + case EPlayerSide.Bear: + return requester.Side != EPlayerSide.Bear || ShouldAttackBear(requester); + case EPlayerSide.Savage: + return requester.Side != EPlayerSide.Savage; + } + + return false; + } + /// /// Return True when usec default behavior is attack + bot is usec /// /// - /// bool + /// private static bool ShouldAttackUsec(IPlayer requester) { var requesterMind = requester?.AIData?.BotOwner?.Settings?.FileSettings?.Mind;