0
0
mirror of https://github.com/sp-tarkov/modules.git synced 2025-02-13 09:50:43 -05:00
modules/project/Aki.Custom/Patches/CustomAiPatch.cs
Terkoiz 337a0733ae Publicized assembly refactor (!58)
Depends on SPT-AKI/SPT-AssemblyTool#3

* Refactored Modules for better consistency and general readability, along with preparing the code for a publicized assembly
* Added `PublicDeclaredFlags` to `PatchConstants` to cover a set of commonly used flags to get methods post-publicizing
* Added a replacement to LINQ's `.Single()` - `.SingleCustom()` which has improved logging to help with debugging Module code. Replaced all `.Single()` usages where applicable
* Replaced most method info fetching with `AccessTools` for consistency and better readability, especially in places where methods were being retrieved by their name anyways

**NOTE:**
As a side effect of publicizing all properties, some property access code such as `Player.Position` will now show "ambiguous reference" errors during compile, due to there being multiple interfaces with the Property name being defined on the class. The way to get around this is to use a cast to an explicit interface
Example:
```cs
Singleton<GameWorld>.Instance.MainPlayer.Position
```
will now need to be
```cs
((IPlayer)Singleton<GameWorld>.Instance.MainPlayer).Position
```

Co-authored-by: Terkoiz <terkoiz@spt.dev>
Reviewed-on: SPT-AKI/Modules#58
Co-authored-by: Terkoiz <terkoiz@noreply.dev.sp-tarkov.com>
Co-committed-by: Terkoiz <terkoiz@noreply.dev.sp-tarkov.com>
2024-01-13 22:08:29 +00:00

119 lines
5.0 KiB
C#

using Aki.Reflection.Patching;
using EFT;
using System;
using Comfort.Common;
using System.Reflection;
using Aki.PrePatch;
using Aki.Custom.CustomAI;
using HarmonyLib;
namespace Aki.Custom.Patches
{
public class CustomAiPatch : ModulePatch
{
private static readonly PmcFoundInRaidEquipment pmcFoundInRaidEquipment = new PmcFoundInRaidEquipment(Logger);
private static readonly AIBrainSpawnWeightAdjustment aIBrainSpawnWeightAdjustment = new AIBrainSpawnWeightAdjustment(Logger);
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(StandartBotBrain), nameof(StandartBotBrain.Activate));
}
/// <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
/// Postfix will adjust it back to original type
/// </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, StandartBotBrain __instance, BotOwner ___botOwner_0)
{
___botOwner_0.Profile.Info.Settings.Role = FixAssaultGroupPmcsRole(___botOwner_0);
__state = ___botOwner_0.Profile.Info.Settings.Role; // Store original type in state param to allow access in PatchPostFix()
try
{
string currentMapName = GetCurrentMap();
if (AiHelpers.BotIsPlayerScav(__state, ___botOwner_0))
{
___botOwner_0.Profile.Info.Settings.Role = aIBrainSpawnWeightAdjustment.GetRandomisedPlayerScavType(___botOwner_0, currentMapName);
return true; // Do original
}
if (AiHelpers.BotIsNormalAssaultScav(__state, ___botOwner_0))
{
___botOwner_0.Profile.Info.Settings.Role = aIBrainSpawnWeightAdjustment.GetAssaultScavWildSpawnType(___botOwner_0, currentMapName);
return true; // Do original
}
if (AiHelpers.BotIsSptPmc(__state, ___botOwner_0))
{
// Bot has inventory equipment
if (___botOwner_0.Profile?.Inventory?.Equipment != null)
{
pmcFoundInRaidEquipment.ConfigurePMCFindInRaidStatus(___botOwner_0);
}
___botOwner_0.Profile.Info.Settings.Role = aIBrainSpawnWeightAdjustment.GetPmcWildSpawnType(___botOwner_0, ___botOwner_0.Profile.Info.Settings.Role, currentMapName);
}
}
catch (Exception ex)
{
Logger.LogError($"Error running CustomAiPatch PatchPrefix(): {ex.Message}");
Logger.LogError(ex.StackTrace);
}
return true; // Do original
}
/// <summary>
/// the client sometimes replaces PMC roles with 'assaultGroup', give PMCs their original role back (sptBear/sptUsec)
/// </summary>
/// <returns>WildSpawnType</returns>
private static WildSpawnType FixAssaultGroupPmcsRole(BotOwner botOwner)
{
if (botOwner.Profile.Info.IsStreamerModeAvailable && botOwner.Profile.Info.Settings.Role == WildSpawnType.assaultGroup)
{
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
? (WildSpawnType)AkiBotsPrePatcher.sptBearValue
: (WildSpawnType)AkiBotsPrePatcher.sptUsecValue;
}
// Not broken pmc, return original role
return botOwner.Profile.Info.Settings.Role;
}
/// <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 (AiHelpers.BotIsSptPmc(__state, ___botOwner_0))
{
// Set spt bot bot back to original type
___botOwner_0.Profile.Info.Settings.Role = __state;
}
else if (AiHelpers.BotIsPlayerScav(__state, ___botOwner_0))
{
// Set pscav back to original type
___botOwner_0.Profile.Info.Settings.Role = __state;
}
}
private static string GetCurrentMap()
{
var gameWorld = Singleton<GameWorld>.Instance;
return gameWorld.MainPlayer.Location;
}
}
}