diff --git a/Source/AkiSupport/Custom/IsEnemyPatch.cs b/Source/AkiSupport/Custom/IsEnemyPatch.cs new file mode 100644 index 00000000..b04e85e2 --- /dev/null +++ b/Source/AkiSupport/Custom/IsEnemyPatch.cs @@ -0,0 +1,131 @@ +using EFT; +using System.Linq; +using System.Reflection; +using HarmonyLib; +using StayInTarkov; + +namespace Aki.Custom.Patches +{ + public class IsEnemyPatch : ModulePatch + { + protected override MethodBase GetTargetMethod() + { + return AccessTools.Method(typeof(BotsGroup), nameof(BotsGroup.IsEnemy)); + } + + /// + /// IsEnemy() + /// Goal: Make bots take Side into account when deciding if another player/bot is an enemy + /// Check enemy cache list first, if not found, check side, if they differ, add to enemy list and return true + /// Needed to ensure bot checks the enemy side, not just its botType + /// + [PatchPrefix] + private static bool PatchPrefix(ref bool __result, BotsGroup __instance, IPlayer requester) + { + if (__instance.InitialBotType == WildSpawnType.peacefullZryachiyEvent + || __instance.InitialBotType == WildSpawnType.shooterBTR + || __instance.InitialBotType == WildSpawnType.gifter + || __instance.InitialBotType == WildSpawnType.sectantWarrior + || __instance.InitialBotType == WildSpawnType.sectantPriest) + { + return true; // Do original code + } + + var isEnemy = false; // default not an enemy + if (requester == null) + { + __result = isEnemy; + + 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 + } + 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); + } + } + } + + __result = isEnemy; + + return false; // Skip original + } + + /// + /// 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; + + if (requesterMind == null) + { + return false; + } + + return requester.IsAI && requesterMind.DEFAULT_USEC_BEHAVIOUR == EWarnBehaviour.Attack && requester.Side == EPlayerSide.Usec; + } + + /// + /// Return True when bear default behavior is attack + bot is bear + /// + /// + /// + private static bool ShouldAttackBear(IPlayer requester) + { + var requesterMind = requester.AIData?.BotOwner?.Settings?.FileSettings?.Mind; + + if (requesterMind == null) + { + return false; + } + + return requester.IsAI && requesterMind.DEFAULT_BEAR_BEHAVIOUR == EWarnBehaviour.Attack && requester.Side == EPlayerSide.Bear; + } + } +} diff --git a/Source/Coop/Components/CoopGameComponents/CoopGameComponent.cs b/Source/Coop/Components/CoopGameComponents/CoopGameComponent.cs index 68a55189..b776f979 100644 --- a/Source/Coop/Components/CoopGameComponents/CoopGameComponent.cs +++ b/Source/Coop/Components/CoopGameComponents/CoopGameComponent.cs @@ -317,6 +317,7 @@ private IEnumerator SendPlayerStatePacket() private void GameWorld_AfterGameStarted() { + GameWorldGameStarted = true; Logger.LogDebug(nameof(GameWorld_AfterGameStarted)); if (Singleton.Instance.RegisteredPlayers.Any()) { @@ -397,6 +398,14 @@ private IEnumerator EverySecondCoroutine() if (!Singleton.Instantiated) continue; + if (!Singleton.Instantiated) + continue; + + if (!GameWorldGameStarted) + continue; + + //Logger.LogDebug($"DEBUG: {nameof(EverySecondCoroutine)}"); + var coopGame = Singleton.Instance; var playersToExtract = new HashSet(); @@ -459,6 +468,20 @@ private IEnumerator EverySecondCoroutine() } } } + + + // Add players who have joined to the AI Enemy Lists + var botController = (BotsController)ReflectionHelpers.GetFieldFromTypeByFieldType(typeof(BaseLocalGame), typeof(BotsController)).GetValue(Singleton.Instance); + if (botController != null) + { + while (PlayersForAIToTarget.TryDequeue(out var otherPlayer)) + { + Logger.LogDebug($"Adding {otherPlayer.Profile.Nickname} to Enemy list"); + botController.AddActivePLayer(otherPlayer); + botController.AddEnemyToAllGroups(otherPlayer, otherPlayer, otherPlayer); + } + } + } } @@ -1141,17 +1164,19 @@ private LocalPlayer CreateLocalPlayer(Profile profile, Vector3 position, int pla if (!SITMatchmaking.IsClient) { - if (otherPlayer.ProfileId.StartsWith("pmc")) + if (ProfileIdsUser.Contains(otherPlayer.ProfileId)) { - if (LocalGameInstance != null) - { - var botController = (BotsController)ReflectionHelpers.GetFieldFromTypeByFieldType(typeof(BaseLocalGame), typeof(BotsController)).GetValue(LocalGameInstance); - if (botController != null) - { - Logger.LogDebug("Adding Client Player to Enemy list"); - botController.AddActivePLayer(otherPlayer); - } - } + PlayersForAIToTarget.Enqueue(otherPlayer); + //if (Singleton.Instantiated) + //{ + // var botController = (BotsController)ReflectionHelpers.GetFieldFromTypeByFieldType(typeof(BaseLocalGame), typeof(BotsController)).GetValue(Singleton.Instance); + // if (botController != null) + // { + // Logger.LogDebug($"Adding {otherPlayer.Profile.Nickname} to Enemy list"); + // botController.AddActivePLayer(otherPlayer); + // botController.AddEnemyToAllGroups(otherPlayer, otherPlayer, otherPlayer); + // } + //} } } @@ -1339,6 +1364,8 @@ private void CreatePlayerStatePacketFromPRC(ref List playerSt //public bool HighPingMode { get; set; } = false; public bool ServerHasStopped { get; set; } private bool ServerHasStoppedActioned { get; set; } + public ConcurrentQueue PlayersForAIToTarget { get; } = new(); + public bool GameWorldGameStarted { get; private set; } GUIStyle middleLabelStyle; GUIStyle middleLargeLabelStyle; diff --git a/Source/StayInTarkovPlugin.cs b/Source/StayInTarkovPlugin.cs index 37b0a42e..426c03fb 100644 --- a/Source/StayInTarkovPlugin.cs +++ b/Source/StayInTarkovPlugin.cs @@ -407,6 +407,7 @@ private static void EnableSPPatches_Bots(BepInEx.Configuration.ConfigFile config new SpawnProcessNegativeValuePatch().Enable(); new LocationLootCacheBustingPatch().Enable(); new FixBrokenSpawnOnSandboxPatch().Enable(); + new IsEnemyPatch().Enable(); } private void EnableCoopPatches()