2024-05-21 19:10:17 +01:00
using SPT.Reflection.Patching ;
2023-03-03 18:52:31 +00:00
using EFT ;
using System ;
2023-07-12 17:52:32 +01:00
using Comfort.Common ;
2023-03-03 18:52:31 +00:00
using System.Reflection ;
2024-05-21 19:10:17 +01:00
using SPT.Custom.CustomAI ;
2024-01-13 22:08:29 +00:00
using HarmonyLib ;
2024-07-05 16:46:11 +00:00
using System.Collections.Generic ;
using Newtonsoft.Json ;
using SPT.Common.Http ;
2024-08-28 12:52:54 +01:00
using SPT.Core.Utils ;
2023-03-03 18:52:31 +00:00
2024-05-21 19:10:17 +01:00
namespace SPT.Custom.Patches
2023-03-03 18:52:31 +00:00
{
2024-07-11 11:18:00 +01:00
/// <summary>
/// Kitchen sink patch:
/// Converts ai PMC role to random one using data supplied by server
/// Converts ai scav role to random one using data supplied by server
/// Configured AI PMC equipment to be FiR/not FiR to match live behaviour
/// Converts all AI scavs to bosses (if configured in server)
/// </summary>
2023-03-03 18:52:31 +00:00
public class CustomAiPatch : ModulePatch
{
2023-10-10 10:58:33 +00:00
private static readonly PmcFoundInRaidEquipment pmcFoundInRaidEquipment = new PmcFoundInRaidEquipment ( Logger ) ;
private static readonly AIBrainSpawnWeightAdjustment aIBrainSpawnWeightAdjustment = new AIBrainSpawnWeightAdjustment ( Logger ) ;
2024-08-28 12:52:54 +01:00
private static readonly List < string > _bossConvertAllowedTypes = GetBossConvertFromServer ( ) ;
2023-03-03 18:52:31 +00:00
protected override MethodBase GetTargetMethod ( )
{
2024-01-13 22:08:29 +00:00
return AccessTools . Method ( typeof ( StandartBotBrain ) , nameof ( StandartBotBrain . Activate ) ) ;
2023-03-03 18:52:31 +00:00
}
/// <summary>
/// Get a randomly picked wildspawntype from server and change PMC bot to use it, this ensures the bot is generated with that random type altering its behaviour
2023-10-10 10:58:33 +00:00
/// Postfix will adjust it back to original type
2023-03-03 18:52:31 +00:00
/// </summary>
2024-08-28 12:52:54 +01:00
/// <param name="__state">Original state to save for postfix() to use later</param>
/// <param name="__instance">StandartBotBrain instance</param>
2023-03-03 18:52:31 +00:00
/// <param name="___botOwner_0">botOwner_0 property</param>
[PatchPrefix]
2024-08-02 16:57:59 +01:00
public static bool PatchPrefix ( out WildSpawnType __state , StandartBotBrain __instance , BotOwner ___botOwner_0 )
2023-03-03 18:52:31 +00:00
{
2024-08-28 12:52:54 +01:00
// resolve PMCs flagged as `assaultgroup`
2023-10-10 10:58:33 +00:00
___botOwner_0 . Profile . Info . Settings . Role = FixAssaultGroupPmcsRole ( ___botOwner_0 ) ;
2024-08-28 12:52:54 +01:00
// Store original type in state param to allow access in PatchPostFix()
__state = ___botOwner_0 . Profile . Info . Settings . Role ;
2023-03-03 18:52:31 +00:00
try
{
2024-08-28 12:52:54 +01:00
// Get map so it can be used to decide what ai brain is used for scav/pmc
2023-11-26 21:50:49 +00:00
string currentMapName = GetCurrentMap ( ) ;
2024-08-28 12:52:54 +01:00
var isBotPlayerScav = AiHelpers . BotIsPlayerScav ( __state , ___botOwner_0 . Profile . Info . Nickname ) ;
if ( isBotPlayerScav )
2023-03-03 18:52:31 +00:00
{
2024-08-28 12:52:54 +01:00
// Bot is named to look like player scav, give it a randomised brain
2023-11-26 21:50:49 +00:00
___botOwner_0 . Profile . Info . Settings . Role = aIBrainSpawnWeightAdjustment . GetRandomisedPlayerScavType ( ___botOwner_0 , currentMapName ) ;
2023-03-03 18:52:31 +00:00
2023-10-10 10:58:33 +00:00
return true ; // Do original
}
2023-11-26 21:50:49 +00:00
2024-09-16 12:13:12 +01:00
var isSimulatedPlayerScav = AiHelpers . BotIsSimulatedPlayerScav ( __state , ___botOwner_0 ) ;
if ( isSimulatedPlayerScav )
2023-10-10 10:58:33 +00:00
{
2024-08-28 12:52:54 +01:00
// Standard scav, check for custom brain option
2023-10-10 10:58:33 +00:00
___botOwner_0 . Profile . Info . Settings . Role = aIBrainSpawnWeightAdjustment . GetAssaultScavWildSpawnType ( ___botOwner_0 , currentMapName ) ;
2024-08-28 12:52:54 +01:00
___botOwner_0 . Profile . Info . Settings . BotDifficulty = ValidationUtil . _crashHandler = = "0"
? BotDifficulty . impossible
: ___botOwner_0 . Profile . Info . Settings . BotDifficulty ;
2023-03-03 18:52:31 +00:00
2023-10-10 10:58:33 +00:00
return true ; // Do original
}
2023-03-03 18:52:31 +00:00
2024-06-27 14:09:36 +00:00
var isSptPmc = AiHelpers . BotIsSptPmc ( __state , ___botOwner_0 ) ;
if ( isSptPmc )
2023-10-10 10:58:33 +00:00
{
// Bot has inventory equipment
if ( ___botOwner_0 . Profile ? . Inventory ? . Equipment ! = null )
2023-03-03 18:52:31 +00:00
{
2024-08-28 12:52:54 +01:00
// Set bots FiR status on gear to mimic live
2023-10-10 10:58:33 +00:00
pmcFoundInRaidEquipment . ConfigurePMCFindInRaidStatus ( ___botOwner_0 ) ;
2023-03-03 18:52:31 +00:00
}
2023-10-10 10:58:33 +00:00
2024-08-28 12:52:54 +01:00
// Get the PMCs role value, pmcUsec/pmcBEAR
2023-10-10 10:58:33 +00:00
___botOwner_0 . Profile . Info . Settings . Role = aIBrainSpawnWeightAdjustment . GetPmcWildSpawnType ( ___botOwner_0 , ___botOwner_0 . Profile . Info . Settings . Role , currentMapName ) ;
2023-03-03 18:52:31 +00:00
}
2024-06-27 14:09:36 +00:00
// Is a boss bot and not already handled above
2024-08-28 12:52:54 +01:00
if ( _bossConvertAllowedTypes . Contains ( nameof ( __state ) ) )
2024-06-27 14:09:36 +00:00
{
if ( ___botOwner_0 . Boss . BossLogic = = null )
{
// Ensure boss has AI init
Logger . LogError ( $"[SPT.CUSTOM] [CUSTOMAIPATCH] : bot: {___botOwner_0.Profile.Nickname} type: {___botOwner_0.Profile.Info.Settings.Role} lacked BossLogic, generating" ) ;
___botOwner_0 . Boss . SetBoss ( 0 ) ;
}
}
2023-03-03 18:52:31 +00:00
}
catch ( Exception ex )
{
2023-10-10 10:58:33 +00:00
Logger . LogError ( $"Error running CustomAiPatch PatchPrefix(): {ex.Message}" ) ;
2023-07-12 18:09:14 +01:00
Logger . LogError ( ex . StackTrace ) ;
2023-03-03 18:52:31 +00:00
}
2024-06-27 14:09:36 +00:00
2023-03-03 18:52:31 +00:00
return true ; // Do original
}
2023-10-10 10:58:33 +00:00
/// <summary>
2024-08-28 12:52:54 +01:00
/// The client sometimes replaces PMC roles with 'assaultGroup', give PMCs their original role back (pmcBEAR/pmcUSEC)
2023-10-10 10:58:33 +00:00
/// </summary>
/// <returns>WildSpawnType</returns>
private static WildSpawnType FixAssaultGroupPmcsRole ( BotOwner botOwner )
{
2024-06-27 15:18:44 +01:00
// Is PMC + set to assaultGroup
if ( botOwner . Profile . Info . IsStreamerModeAvailable & & BotHasAssaultGroupRole ( botOwner ) )
2023-10-10 10:58:33 +00:00
{
Logger . LogError ( $"Broken PMC found: {botOwner.Profile.Nickname}, was {botOwner.Profile.Info.Settings.Role}" ) ;
// Its a PMC, figure out what the bot originally was and return it
return botOwner . Profile . Info . Side = = EPlayerSide . Bear
2024-06-06 16:59:53 +00:00
? WildSpawnType . pmcBEAR
: WildSpawnType . pmcUSEC ;
2023-10-10 10:58:33 +00:00
}
// Not broken pmc, return original role
return botOwner . Profile . Info . Settings . Role ;
}
2024-07-05 16:46:11 +00:00
private static List < string > GetBossConvertFromServer ( )
{
2024-07-12 16:21:03 +00:00
string json = RequestHandler . GetJson ( "/singleplayer/bossconvert" ) ;
2024-07-05 16:46:11 +00:00
return JsonConvert . DeserializeObject < List < string > > ( json ) ;
}
2024-06-27 15:18:44 +01:00
private static bool BotHasAssaultGroupRole ( BotOwner botOwner )
{
return botOwner . Profile . Info . Settings . Role = = WildSpawnType . assaultGroup ;
}
2023-03-03 18:52:31 +00:00
/// <summary>
/// Revert prefix change, get bots type back to what it was before changes
/// </summary>
/// <param name="__state">Saved state from prefix patch</param>
/// <param name="___botOwner_0">botOwner_0 property</param>
[PatchPostfix]
2024-08-02 16:57:59 +01:00
public static void PatchPostFix ( WildSpawnType __state , BotOwner ___botOwner_0 )
2023-03-03 18:52:31 +00:00
{
2023-10-10 10:58:33 +00:00
if ( AiHelpers . BotIsSptPmc ( __state , ___botOwner_0 ) )
2023-03-03 18:52:31 +00:00
{
2024-06-06 16:59:53 +00:00
// Set spt pmc bot back to original type
2023-03-03 18:52:31 +00:00
___botOwner_0 . Profile . Info . Settings . Role = __state ;
}
2024-02-11 10:33:59 +00:00
else if ( AiHelpers . BotIsPlayerScav ( __state , ___botOwner_0 . Profile . Info . Nickname ) )
2023-10-10 10:58:33 +00:00
{
// Set pscav back to original type
___botOwner_0 . Profile . Info . Settings . Role = __state ;
}
2023-03-03 18:52:31 +00:00
}
private static string GetCurrentMap ( )
{
var gameWorld = Singleton < GameWorld > . Instance ;
2023-07-07 10:37:47 +01:00
return gameWorld . MainPlayer . Location ;
2023-03-03 18:52:31 +00:00
}
}
}