using SPT.Reflection.Patching; using EFT; using System.Linq; using System.Reflection; using HarmonyLib; namespace SPT.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 (requester == null) { __result = false; return false; // Skip original } if (__instance.InitialBotType == WildSpawnType.peacefullZryachiyEvent || __instance.InitialBotType == WildSpawnType.shooterBTR || __instance.InitialBotType == WildSpawnType.gifter || __instance.InitialBotType == WildSpawnType.sectantWarrior || __instance.InitialBotType == WildSpawnType.sectantPriest || __instance.InitialBotType == WildSpawnType.sectactPriestEvent || __instance.InitialBotType == WildSpawnType.ravangeZryachiyEvent) { 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; } } }