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()