2023-03-03 18:52:31 +00:00
using Aki.Common.Http ;
using Aki.Reflection.Patching ;
using Comfort.Common ;
using EFT ;
using Newtonsoft.Json ;
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Reflection ;
using Aki.PrePatch ;
using Random = System . Random ;
namespace Aki.Custom.Patches
{
public class CustomAiPatch : ModulePatch
{
private static readonly Random random = new Random ( ) ;
private static Dictionary < WildSpawnType , Dictionary < string , Dictionary < string , int > > > botTypeCache = new Dictionary < WildSpawnType , Dictionary < string , Dictionary < string , int > > > ( ) ;
private static DateTime cacheDate = new DateTime ( ) ;
protected override MethodBase GetTargetMethod ( )
{
return typeof ( BotBrainClass ) . GetMethod ( "Activate" , BindingFlags . Public | BindingFlags . Instance ) ;
}
/// <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
/// </summary>
/// <param name="__state">state to save for postfix to use later</param>
/// <param name="__instance"></param>
/// <param name="___botOwner_0">botOwner_0 property</param>
[PatchPrefix]
private static bool PatchPrefix ( out WildSpawnType __state , object __instance , BotOwner ___botOwner_0 )
{
// Store original type in state param
__state = ___botOwner_0 . Profile . Info . Settings . Role ;
//Console.WriteLine($"Processing bot {___botOwner_0.Profile.Info.Nickname} with role {___botOwner_0.Profile.Info.Settings.Role}");
try
{
if ( BotIsSptPmc ( ___botOwner_0 . Profile . Info . Settings . Role ) )
{
string currentMapName = GetCurrentMap ( ) ;
if ( ! botTypeCache . TryGetValue ( ___botOwner_0 . Profile . Info . Settings . Role , out var botSettings ) | | CacheIsStale ( ) )
{
ResetCacheDate ( ) ;
HydrateCacheWithServerData ( ) ;
if ( ! botTypeCache . TryGetValue ( ___botOwner_0 . Profile . Info . Settings . Role , out botSettings ) )
{
throw new Exception ( $"Bots were refreshed from the server but the cache still doesnt contain an appropriate bot for type {___botOwner_0.Profile.Info.Settings.Role}" ) ;
}
}
var mapSettings = botSettings [ currentMapName . ToLower ( ) ] ;
var randomType = WeightedRandom ( mapSettings . Keys . ToArray ( ) , mapSettings . Values . ToArray ( ) ) ;
if ( Enum . TryParse ( randomType , out WildSpawnType newAiType ) )
{
Console . WriteLine ( $"Updated spt bot {___botOwner_0.Profile.Info.Nickname}: {___botOwner_0.Profile.Info.Settings.Role} to {newAiType}" ) ;
___botOwner_0 . Profile . Info . Settings . Role = newAiType ;
}
else
{
Console . WriteLine ( $"Couldnt not update spt bot {___botOwner_0.Profile.Info.Nickname} to the new type, random type {randomType} does not exist for WildSpawnType" ) ;
}
}
}
catch ( Exception ex )
{
Console . WriteLine ( $"Error processing log: {ex.Message}" ) ;
Console . WriteLine ( ex . StackTrace ) ;
}
return true ; // Do original
}
/// <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]
private static void PatchPostFix ( WildSpawnType __state , BotOwner ___botOwner_0 )
{
if ( BotIsSptPmc ( __state ) )
{
// Set spt bot bot back to original type
___botOwner_0 . Profile . Info . Settings . Role = __state ;
}
}
private static bool BotIsSptPmc ( WildSpawnType role )
{
2023-04-10 06:15:21 +01:00
return ( int ) role = = AkiBotsPrePatcher . sptBearValue | | ( int ) role = = AkiBotsPrePatcher . sptUsecValue ;
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
}
private static bool CacheIsStale ( )
{
TimeSpan cacheAge = DateTime . Now - cacheDate ;
return cacheAge . Minutes > 20 ;
}
private static void ResetCacheDate ( )
{
cacheDate = DateTime . Now ;
}
private static void HydrateCacheWithServerData ( )
{
// Get weightings for PMCs from server and store in dict
var result = RequestHandler . GetJson ( $"/singleplayer/settings/bot/getBotBehaviours/" ) ;
botTypeCache = JsonConvert . DeserializeObject < Dictionary < WildSpawnType , Dictionary < string , Dictionary < string , int > > > > ( result ) ;
Console . WriteLine ( $"cached: {botTypeCache.Count} bots" ) ;
}
private static string WeightedRandom ( string [ ] botTypes , int [ ] weights )
{
var cumulativeWeights = new int [ botTypes . Length ] ;
for ( int i = 0 ; i < weights . Length ; i + + )
{
cumulativeWeights [ i ] = weights [ i ] + ( i = = 0 ? 0 : cumulativeWeights [ i - 1 ] ) ;
}
var maxCumulativeWeight = cumulativeWeights [ cumulativeWeights . Length - 1 ] ;
var randomNumber = maxCumulativeWeight * random . NextDouble ( ) ;
for ( var itemIndex = 0 ; itemIndex < botTypes . Length ; itemIndex + + )
{
if ( cumulativeWeights [ itemIndex ] > = randomNumber )
{
return botTypes [ itemIndex ] ;
}
}
Console . WriteLine ( "failed to get random bot weighting, returned assault" ) ;
return "assault" ;
}
}
}