0
0
mirror of https://github.com/sp-tarkov/modules.git synced 2025-02-13 09:50:43 -05:00

Player will receive notifications when purchasing BTR service or when they are blacklisted from the BTR (!63)

Todo:

* Taxi service
* Improve turret aim/firing
  * Switch targets when current target out of line of sight
* Allow certain aspects to be configured from server, for example: price multipliers, wait times at each location, etc.
* Perhaps, a persisting blacklist from the BTR for several raids afterwards
* Further code refactoring

Co-authored-by: Nympfonic <arys.steam@gmail.com>
Reviewed-on: SPT-AKI/Modules#63
Co-authored-by: Arys <arys@noreply.dev.sp-tarkov.com>
Co-committed-by: Arys <arys@noreply.dev.sp-tarkov.com>
This commit is contained in:
Arys 2024-01-20 09:20:32 +00:00 committed by chomp
parent e771501cd5
commit 879d90b71f
15 changed files with 187 additions and 161 deletions

View File

@ -24,6 +24,7 @@
<Reference Include="UnityEngine.UI" HintPath="..\Shared\Managed\UnityEngine.UI.dll" Private="False" /> <Reference Include="UnityEngine.UI" HintPath="..\Shared\Managed\UnityEngine.UI.dll" Private="False" />
<Reference Include="UnityEngine.UIModule" HintPath="..\Shared\Managed\UnityEngine.UIModule.dll" Private="False" /> <Reference Include="UnityEngine.UIModule" HintPath="..\Shared\Managed\UnityEngine.UIModule.dll" Private="False" />
<Reference Include="Comfort.Common" HintPath="..\Shared\Managed\Comfort.dll" Private="False" /> <Reference Include="Comfort.Common" HintPath="..\Shared\Managed\Comfort.dll" Private="False" />
<Reference Include="DissonanceVoip" HintPath="..\Shared\Managed\DissonanceVoip.dll" Private="False" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -92,24 +92,28 @@ namespace Aki.Debugging.BTR
{ {
btrController.SyncBTRVehicleFromServer(UpdateDataPacket()); btrController.SyncBTRVehicleFromServer(UpdateDataPacket());
// BotShooterBtr doesn't get assigned to BtrController immediately so we nullcheck this in Update if (btrController.BotShooterBtr == null) return;
if (btrController.BotShooterBtr != null && !btrBotShooterInitialized)
// BotShooterBtr doesn't get assigned to BtrController immediately so we check this in Update
if (!btrBotShooterInitialized)
{ {
btrBotShooter = btrController.BotShooterBtr; btrBotShooter = btrController.BotShooterBtr;
btrBotService.Reset(); // Player will be added to Neutrals list and removed from Enemies list btrBotService.Reset(); // Player will be added to Neutrals list and removed from Enemies list
TraderServicesManager.Instance.OnTraderServicePurchased += TraderServicePurchased; TraderServicesManager.Instance.OnTraderServicePurchased += BTRTraderServicePurchased;
btrBotShooterInitialized = true; btrBotShooterInitialized = true;
} }
if (btrController.BotShooterBtr == null) return;
if (HasTarget() && IsAimingAtTarget() && !isShooting) if (HasTarget() && IsAimingAtTarget() && !isShooting)
{ {
_shootingTargetCoroutine = StaticManager.BeginCoroutine(ShootTarget()); _shootingTargetCoroutine = StaticManager.BeginCoroutine(ShootTarget());
} }
if (_coverFireTimerCoroutine != null && ShouldCancelCoverFireSupport())
{
CancelCoverFireSupport();
}
} }
// Please tell me there's a better way than this xd
public void OnPlayerInteractDoor(PlayerInteractPacket interactPacket) public void OnPlayerInteractDoor(PlayerInteractPacket interactPacket)
{ {
btrServerSide.LeftSlot0State = 0; btrServerSide.LeftSlot0State = 0;
@ -193,15 +197,37 @@ namespace Aki.Debugging.BTR
_updateTaxiPriceMethod.Invoke(btrController, new object[] { destinationPoint, isFinal }); _updateTaxiPriceMethod.Invoke(btrController, new object[] { destinationPoint, isFinal });
} }
private void TraderServicePurchased(ETraderServiceType serviceType) private bool IsBtrService(ETraderServiceType serviceType)
{ {
if (serviceType == ETraderServiceType.BtrItemsDelivery
|| serviceType == ETraderServiceType.PlayerTaxi
|| serviceType == ETraderServiceType.BtrBotCover)
{
return true;
}
return false;
}
private void BTRTraderServicePurchased(ETraderServiceType serviceType)
{
if (!IsBtrService(serviceType))
{
return;
}
List<Player> passengers = gameWorld.AllAlivePlayersList.Where(x => x.BtrState == EPlayerBtrState.Inside).ToList();
List<int> playersToNotify = passengers.Select(x => x.Id).ToList();
btrController.method_6(playersToNotify, serviceType); // notify BTR passengers that a service has been purchased
switch (serviceType) switch (serviceType)
{ {
case ETraderServiceType.BtrBotCover: case ETraderServiceType.BtrBotCover:
List<Player> passengers = gameWorld.AllAlivePlayersList.Where(x => x.BtrState == EPlayerBtrState.Inside).ToList();
botEventHandler.ApplyTraderServiceBtrSupport(passengers); botEventHandler.ApplyTraderServiceBtrSupport(passengers);
StartCoverFireTimer(90f); StartCoverFireTimer(90f);
break; break;
case ETraderServiceType.PlayerTaxi:
break;
} }
} }
@ -210,6 +236,23 @@ namespace Aki.Debugging.BTR
_coverFireTimerCoroutine = StaticManager.BeginCoroutine(CoverFireTimer(time)); _coverFireTimerCoroutine = StaticManager.BeginCoroutine(CoverFireTimer(time));
} }
private bool ShouldCancelCoverFireSupport()
{
var friendlyPlayersByBtrSupport = (List<Player>)AccessTools.Field(btrBotService.GetType(), "_friendlyPlayersByBtrSupport").GetValue(btrBotService);
if (!friendlyPlayersByBtrSupport.Any())
{
return true;
}
return false;
}
private void CancelCoverFireSupport()
{
StaticManager.KillCoroutine(ref _coverFireTimerCoroutine);
botEventHandler.StopTraderServiceBtrSupport();
}
private IEnumerator CoverFireTimer(float time) private IEnumerator CoverFireTimer(float time)
{ {
yield return new WaitForSecondsRealtime(time); yield return new WaitForSecondsRealtime(time);
@ -331,7 +374,6 @@ namespace Aki.Debugging.BTR
Vector3 currentTargetPosition = currentTargetTransform.position; Vector3 currentTargetPosition = currentTargetTransform.position;
if (btrTurretServer.CheckPositionInAimingZone(currentTargetPosition)) if (btrTurretServer.CheckPositionInAimingZone(currentTargetPosition))
{ {
// If turret machine gun aim is close enough to target and has line of sight
if (btrTurretServer.targetTransform == currentTargetTransform && btrBotShooter.BotBtrData.CanShoot()) if (btrTurretServer.targetTransform == currentTargetTransform && btrBotShooter.BotBtrData.CanShoot())
{ {
return true; return true;
@ -389,6 +431,11 @@ namespace Aki.Debugging.BTR
isShooting = false; isShooting = false;
} }
private void OnDestroy()
{
DestroyGameObjects();
}
private void DestroyGameObjects() private void DestroyGameObjects()
{ {
if (btrController != null) if (btrController != null)
@ -412,7 +459,7 @@ namespace Aki.Debugging.BTR
if (TraderServicesManager.Instance != null) if (TraderServicesManager.Instance != null)
{ {
TraderServicesManager.Instance.OnTraderServicePurchased -= TraderServicePurchased; TraderServicesManager.Instance.OnTraderServicePurchased -= BTRTraderServicePurchased;
} }
StaticManager.KillCoroutine(ref _shootingTargetCoroutine); StaticManager.KillCoroutine(ref _shootingTargetCoroutine);

View File

@ -1,19 +1,16 @@
using Aki.Debugging.BTR.Utils; using Aki.Reflection.Patching;
using Aki.Reflection.Patching;
using Aki.Reflection.Utils;
using Comfort.Common; using Comfort.Common;
using EFT; using EFT;
using EFT.UI.Screens; using EFT.UI.Screens;
using EFT.Vehicle; using EFT.Vehicle;
using HarmonyLib; using HarmonyLib;
using System; using System;
using System.Linq;
using System.Reflection; using System.Reflection;
using static EFT.UI.TraderDialogScreen; using static EFT.UI.TraderDialogScreen;
namespace Aki.Debugging.BTR.Patches namespace Aki.Debugging.BTR.Patches
{ {
public class BTRActivateTraderDialogPatch : ModulePatch internal class BTRActivateTraderDialogPatch : ModulePatch
{ {
private static FieldInfo _playerInventoryControllerField; private static FieldInfo _playerInventoryControllerField;
private static FieldInfo _playerQuestControllerField; private static FieldInfo _playerQuestControllerField;
@ -40,7 +37,7 @@ namespace Aki.Debugging.BTR.Patches
} }
[PatchPrefix] [PatchPrefix]
public static bool PatchPrefix() private static bool PatchPrefix()
{ {
var gameWorld = Singleton<GameWorld>.Instance; var gameWorld = Singleton<GameWorld>.Instance;
var player = gameWorld.MainPlayer; var player = gameWorld.MainPlayer;

View File

@ -15,9 +15,8 @@ namespace Aki.Debugging.BTR.Patches
// Context: // Context:
// ClientGameWorld in LiveEFT will register the server-side BTR Bot as type ObservedPlayerView and is stored in GameWorld's allObservedPlayersByID dictionary. // ClientGameWorld in LiveEFT will register the server-side BTR Bot as type ObservedPlayerView and is stored in GameWorld's allObservedPlayersByID dictionary.
// In SPT, GameWorld.allObservedPlayersByID is empty which results in the game never finishing the initialization of the BTR Bot which includes disabling its gun, voice and mesh renderers. // In SPT, GameWorld.allObservedPlayersByID is empty which results in the game never finishing the initialization of the BTR Bot which includes disabling its gun, voice and mesh renderers.
// Perhaps some research should be done into getting the dictionary populated as ObservedPlayerView seems to be utilised by several aspects of the BTR's functionality.
// For now, we do dirty patches to work around the lack of ObservedPlayerView, using Player instead. // For now, we do dirty patches to work around the lack of ObservedPlayerView, using Player instead.
public class BTRBotAttachPatch : ModulePatch internal class BTRBotAttachPatch : ModulePatch
{ {
protected override MethodBase GetTargetMethod() protected override MethodBase GetTargetMethod()
{ {
@ -25,20 +24,19 @@ namespace Aki.Debugging.BTR.Patches
} }
[PatchPrefix] [PatchPrefix]
public static bool PatchPrefix(object __instance, int btrBotId) private static bool PatchPrefix(BTRTurretView __instance, int btrBotId)
{ {
var gameWorld = Singleton<GameWorld>.Instance; var gameWorld = Singleton<GameWorld>.Instance;
var btrTurretView = (BTRTurretView)__instance;
var btrTurretViewTupleField = (ValueTuple<ObservedPlayerView, bool>)AccessTools.Field(btrTurretView.GetType(), "valueTuple_0") var __instanceTupleField = (ValueTuple<ObservedPlayerView, bool>)AccessTools.Field(__instance.GetType(), "valueTuple_0")
.GetValue(btrTurretView); .GetValue(__instance);
if (!btrTurretViewTupleField.Item2) if (!__instanceTupleField.Item2)
{ {
var btrTurretViewMethod = AccessTools.Method(btrTurretView.GetType(), "method_1"); var __instanceMethod = AccessTools.Method(__instance.GetType(), "method_1");
btrTurretViewMethod.Invoke(btrTurretView, new object[] { btrBotId }); __instanceMethod.Invoke(__instance, new object[] { btrBotId });
} }
if (!btrTurretViewTupleField.Item2) if (!__instanceTupleField.Item2)
{ {
return false; return false;
} }
@ -50,12 +48,12 @@ namespace Aki.Debugging.BTR.Patches
} }
try try
{ {
var botRootTransform = btrTurretView.BotRoot; var botRootTransform = __instance.BotRoot;
btrBot.Transform.position = botRootTransform.position; btrBot.Transform.position = botRootTransform.position;
var aiFirearmController = btrBot.gameObject.GetComponent<Player.FirearmController>(); var firearmController = btrBot.gameObject.GetComponent<Player.FirearmController>();
var currentWeaponPrefab = (WeaponPrefab)AccessTools.Field(aiFirearmController.GetType(), "weaponPrefab_0").GetValue(aiFirearmController); var currentWeaponPrefab = (WeaponPrefab)AccessTools.Field(firearmController.GetType(), "weaponPrefab_0").GetValue(firearmController);
currentWeaponPrefab.transform.position = botRootTransform.position; currentWeaponPrefab.transform.position = botRootTransform.position;
btrBot.PlayerBones.Weapon_Root_Anim.SetPositionAndRotation(botRootTransform.position, botRootTransform.rotation); btrBot.PlayerBones.Weapon_Root_Anim.SetPositionAndRotation(botRootTransform.position, botRootTransform.rotation);

View File

@ -1,13 +1,14 @@
using Aki.Reflection.Patching; using Aki.Reflection.Patching;
using Comfort.Common;
using EFT;
using EFT.NextObservedPlayer;
using EFT.UI; using EFT.UI;
using EFT.Vehicle; using EFT.Vehicle;
using EFT;
using HarmonyLib; using HarmonyLib;
using System;
using System.Linq;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using Comfort.Common;
using System;
using EFT.NextObservedPlayer;
namespace Aki.Debugging.BTR.Patches namespace Aki.Debugging.BTR.Patches
{ {
@ -17,7 +18,7 @@ namespace Aki.Debugging.BTR.Patches
// ClientGameWorld in LiveEFT will register the server-side BTR Bot as type ObservedPlayerView and is stored in GameWorld's allObservedPlayersByID dictionary. // ClientGameWorld in LiveEFT will register the server-side BTR Bot as type ObservedPlayerView and is stored in GameWorld's allObservedPlayersByID dictionary.
// In SPT, allObservedPlayersByID is empty which results in the game never finishing the initialization of the BTR Bot which includes disabling its gun, voice and mesh renderers. // In SPT, allObservedPlayersByID is empty which results in the game never finishing the initialization of the BTR Bot which includes disabling its gun, voice and mesh renderers.
// For now, we do dirty patches to work around the lack of ObservedPlayerView, using Player instead. // For now, we do dirty patches to work around the lack of ObservedPlayerView, using Player instead.
public class BTRBotInitPatch : ModulePatch internal class BTRBotInitPatch : ModulePatch
{ {
protected override MethodBase GetTargetMethod() protected override MethodBase GetTargetMethod()
{ {
@ -25,56 +26,60 @@ namespace Aki.Debugging.BTR.Patches
} }
[PatchPostfix] [PatchPostfix]
public static void PatchPostfix(BTRTurretView __instance, int btrBotId, ref bool __result) private static void PatchPostfix(BTRTurretView __instance, int btrBotId, ref bool __result)
{ {
var gameWorld = Singleton<GameWorld>.Instance; var gameWorld = Singleton<GameWorld>.Instance;
if (gameWorld == null)
foreach (var playerKeyValue in gameWorld.allAlivePlayersByID)
{ {
if (playerKeyValue.Value.Id == btrBotId) return;
}
var alivePlayersList = gameWorld.AllAlivePlayersList;
bool doesBtrBotExist = alivePlayersList.Exists(x => x.Id == btrBotId);
if (doesBtrBotExist)
{
try
{ {
try Player player = alivePlayersList.First(x => x.Id == btrBotId);
Renderer[] array = player.GetComponentsInChildren<Renderer>();
for (int i = 0; i < array.Length; i++)
{ {
Renderer[] array = playerKeyValue.Value.GetComponentsInChildren<Renderer>(); array[i].enabled = false;
for (int i = 0; i < array.Length; i++)
{
array[i].enabled = false;
}
var aiFirearmController = playerKeyValue.Value.gameObject.GetComponent<Player.FirearmController>();
var currentWeaponPrefab = (WeaponPrefab)AccessTools.Field(aiFirearmController.GetType(), "weaponPrefab_0").GetValue(aiFirearmController);
if (currentWeaponPrefab.RemoveChildrenOf != null)
{
foreach (var text in currentWeaponPrefab.RemoveChildrenOf)
{
var transform = currentWeaponPrefab.transform.FindTransform(text);
transform?.gameObject.SetActive(false);
}
}
foreach (var renderer in currentWeaponPrefab.GetComponentsInChildren<Renderer>())
{
if (renderer.name == "MuzzleJetCombinedMesh")
{
renderer.transform.localPosition = new Vector3(0.18f, 0f, -0.095f);
}
else
{
renderer.enabled = false;
}
}
var tuple = new ValueTuple<ObservedPlayerView, bool>(new ObservedPlayerView(), true);
var btrTurretViewTupleField = AccessTools.Field(__instance.GetType(), "valueTuple_0");
btrTurretViewTupleField.SetValue(__instance, tuple);
__result = true;
return;
} }
catch
var aiFirearmController = player.gameObject.GetComponent<Player.FirearmController>();
var currentWeaponPrefab = (WeaponPrefab)AccessTools.Field(aiFirearmController.GetType(), "weaponPrefab_0").GetValue(aiFirearmController);
if (currentWeaponPrefab.RemoveChildrenOf != null)
{ {
ConsoleScreen.LogError("[AKI-BTR] BtrBot initialization failed, BtrBot will be visible ingame. Check logs."); foreach (var text in currentWeaponPrefab.RemoveChildrenOf)
throw; {
var transform = currentWeaponPrefab.transform.FindTransform(text);
transform?.gameObject.SetActive(false);
}
} }
foreach (var renderer in currentWeaponPrefab.GetComponentsInChildren<Renderer>())
{
if (renderer.name == "MuzzleJetCombinedMesh")
{
renderer.transform.localPosition = new Vector3(0.18f, 0f, -0.095f);
}
else
{
renderer.enabled = false;
}
}
var tuple = new ValueTuple<ObservedPlayerView, bool>(new ObservedPlayerView(), true);
var btrTurretViewTupleField = AccessTools.Field(__instance.GetType(), "valueTuple_0");
btrTurretViewTupleField.SetValue(__instance, tuple);
__result = true;
}
catch
{
ConsoleScreen.LogError("[AKI-BTR] BtrBot initialization failed, BtrBot will be visible ingame. Check logs.");
throw;
} }
} }
} }

View File

@ -3,12 +3,11 @@ using Comfort.Common;
using EFT; using EFT;
using EFT.Vehicle; using EFT.Vehicle;
using HarmonyLib; using HarmonyLib;
using System;
using System.Reflection; using System.Reflection;
namespace Aki.Debugging.BTR.Patches namespace Aki.Debugging.BTR.Patches
{ {
public class BTRExtractPassengersPatch : ModulePatch internal class BTRExtractPassengersPatch : ModulePatch
{ {
protected override MethodBase GetTargetMethod() protected override MethodBase GetTargetMethod()
{ {
@ -16,39 +15,32 @@ namespace Aki.Debugging.BTR.Patches
} }
[PatchPrefix] [PatchPrefix]
public static void PatchPrefix() private static void PatchPrefix()
{ {
var gameWorld = Singleton<GameWorld>.Instance; var gameWorld = Singleton<GameWorld>.Instance;
var player = gameWorld.MainPlayer; var player = gameWorld.MainPlayer;
var btrManager = gameWorld.GetComponent<BTRManager>(); var btrManager = gameWorld.GetComponent<BTRManager>();
try var btrSide = btrManager.LastInteractedBtrSide;
if (btrSide == null)
{ {
var btrSide = btrManager.LastInteractedBtrSide; return;
if (btrSide == null)
{
return;
}
if (btrSide.TryGetCachedPlace(out byte b))
{
var interactionBtrPacket = btrSide.GetInteractWithBtrPacket(b, EInteractionType.GoOut);
if (interactionBtrPacket.HasInteraction)
{
BTRView btrView = gameWorld.BtrController.BtrView;
if (btrView == null)
{
throw new NullReferenceException("BtrView not found");
}
btrManager.OnPlayerInteractDoor(interactionBtrPacket);
btrView.Interaction(player, interactionBtrPacket);
}
}
} }
catch (Exception ex19)
if (btrSide.TryGetCachedPlace(out byte b))
{ {
UnityEngine.Debug.LogException(ex19); var interactionBtrPacket = btrSide.GetInteractWithBtrPacket(b, EInteractionType.GoOut);
if (interactionBtrPacket.HasInteraction)
{
BTRView btrView = gameWorld.BtrController.BtrView;
if (btrView == null)
{
return;
}
btrManager.OnPlayerInteractDoor(interactionBtrPacket);
btrView.Interaction(player, interactionBtrPacket);
}
} }
} }
} }

View File

@ -1,14 +1,15 @@
using Aki.Reflection.Patching; using Aki.Reflection.Patching;
using Comfort.Common; using Comfort.Common;
using EFT; using EFT;
using EFT.GlobalEvents;
using EFT.Vehicle; using EFT.Vehicle;
using HarmonyLib; using HarmonyLib;
using System;
using System.Reflection; using System.Reflection;
using GlobalEventHandler = GClass2909;
namespace Aki.Debugging.BTR.Patches namespace Aki.Debugging.BTR.Patches
{ {
public class BTRInteractionPatch : ModulePatch internal class BTRInteractionPatch : ModulePatch
{ {
protected override MethodBase GetTargetMethod() protected override MethodBase GetTargetMethod()
{ {
@ -24,32 +25,33 @@ namespace Aki.Debugging.BTR.Patches
} }
[PatchPostfix] [PatchPostfix]
public static void PatchPostfix(object __instance, BTRSide btr, byte placeId, EInteractionType interaction) private static void PatchPostfix(Player __instance, BTRSide btr, byte placeId, EInteractionType interaction)
{ {
var gameWorld = Singleton<GameWorld>.Instance; var gameWorld = Singleton<GameWorld>.Instance;
var player = (Player)__instance;
var btrManager = gameWorld.GetComponent<BTRManager>(); var btrManager = gameWorld.GetComponent<BTRManager>();
try var interactionBtrPacket = btr.GetInteractWithBtrPacket(placeId, interaction);
{ __instance.UpdateInteractionCast();
var interactionBtrPacket = btr.GetInteractWithBtrPacket(placeId, interaction);
player.UpdateInteractionCast();
if (interactionBtrPacket.HasInteraction) // Prevent player from entering BTR when blacklisted
{ var btrBot = gameWorld.BtrController.BotShooterBtr;
BTRView btrView = gameWorld.BtrController.BtrView; if (btrBot.BotsGroup.Enemies.ContainsKey(__instance))
if (btrView == null)
{
throw new NullReferenceException("BtrView not found");
}
btrManager.OnPlayerInteractDoor(interactionBtrPacket);
btrView.Interaction(player, interactionBtrPacket);
}
}
catch (Exception ex19)
{ {
UnityEngine.Debug.LogException(ex19); // Notify player they are blacklisted from entering BTR
GlobalEventHandler.CreateEvent<BtrNotificationInteractionMessageEvent>().Invoke(__instance.Id, EBtrInteractionStatus.Blacklisted);
return;
}
if (interactionBtrPacket.HasInteraction)
{
BTRView btrView = gameWorld.BtrController.BtrView;
if (btrView == null)
{
return;
}
btrManager.OnPlayerInteractDoor(interactionBtrPacket);
btrView.Interaction(__instance, interactionBtrPacket);
} }
} }
} }

View File

@ -6,7 +6,7 @@ using System.Reflection;
namespace Aki.Debugging.BTR.Patches namespace Aki.Debugging.BTR.Patches
{ {
public class BTRIsDoorsClosedPath : ModulePatch internal class BTRIsDoorsClosedPath : ModulePatch
{ {
protected override MethodBase GetTargetMethod() protected override MethodBase GetTargetMethod()
{ {
@ -14,7 +14,7 @@ namespace Aki.Debugging.BTR.Patches
} }
[PatchPrefix] [PatchPrefix]
public static bool PatchPrefix(ref bool __result) private static bool PatchPrefix(ref bool __result)
{ {
var serverSideBTR = Singleton<GameWorld>.Instance?.BtrController.BtrVehicle; var serverSideBTR = Singleton<GameWorld>.Instance?.BtrController.BtrVehicle;
if (serverSideBTR == null) if (serverSideBTR == null)

View File

@ -10,7 +10,7 @@ namespace Aki.Debugging.BTR.Patches
/// <summary> /// <summary>
/// Adds a BTRManager component to the GameWorld game object when raid starts. /// Adds a BTRManager component to the GameWorld game object when raid starts.
/// </summary> /// </summary>
public class BTRPatch : ModulePatch internal class BTRPatch : ModulePatch
{ {
protected override MethodBase GetTargetMethod() protected override MethodBase GetTargetMethod()
{ {
@ -18,7 +18,7 @@ namespace Aki.Debugging.BTR.Patches
} }
[PatchPostfix] [PatchPostfix]
public static void PatchPostfix() private static void PatchPostfix()
{ {
try try
{ {

View File

@ -8,7 +8,7 @@ namespace Aki.Debugging.BTR.Patches
{ {
// The BTRManager MapPathsConfiguration loading depends on the game state being set to Starting // The BTRManager MapPathsConfiguration loading depends on the game state being set to Starting
// so set it to Starting while the method is running, then reset it afterwards // so set it to Starting while the method is running, then reset it afterwards
public class BTRPathLoadPatch : ModulePatch internal class BTRPathLoadPatch : ModulePatch
{ {
private static PropertyInfo _statusProperty; private static PropertyInfo _statusProperty;
private static GameStatus originalStatus; private static GameStatus originalStatus;
@ -20,14 +20,14 @@ namespace Aki.Debugging.BTR.Patches
} }
[PatchPrefix] [PatchPrefix]
public static void PatchPrefix() private static void PatchPrefix()
{ {
originalStatus = Singleton<AbstractGame>.Instance.Status; originalStatus = Singleton<AbstractGame>.Instance.Status;
_statusProperty.SetValue(Singleton<AbstractGame>.Instance, GameStatus.Starting); _statusProperty.SetValue(Singleton<AbstractGame>.Instance, GameStatus.Starting);
} }
[PatchPostfix] [PatchPostfix]
public static void PatchPostfix() private static void PatchPostfix()
{ {
_statusProperty.SetValue(Singleton<AbstractGame>.Instance, originalStatus); _statusProperty.SetValue(Singleton<AbstractGame>.Instance, originalStatus);
} }

View File

@ -1,15 +1,13 @@
using Aki.Debugging.BTR.Utils; using Aki.Debugging.BTR.Utils;
using Aki.Reflection.Patching; using Aki.Reflection.Patching;
using Aki.SinglePlayer.Utils.TraderServices; using Aki.SinglePlayer.Utils.TraderServices;
using Comfort.Common;
using EFT;
using EFT.UI; using EFT.UI;
using HarmonyLib; using HarmonyLib;
using System.Reflection; using System.Reflection;
namespace Aki.Debugging.BTR.Patches namespace Aki.Debugging.BTR.Patches
{ {
public class BTRTransferItemsPatch : ModulePatch internal class BTRTransferItemsPatch : ModulePatch
{ {
protected override MethodBase GetTargetMethod() protected override MethodBase GetTargetMethod()
{ {
@ -18,7 +16,7 @@ namespace Aki.Debugging.BTR.Patches
} }
[PatchPostfix] [PatchPostfix]
public static void PatchPostfix(bool ___bool_1) private static void PatchPostfix(bool ___bool_1)
{ {
// Didn't extract items // Didn't extract items
if (!___bool_1) if (!___bool_1)

View File

@ -6,20 +6,20 @@ using UnityEngine;
namespace Aki.Debugging.BTR.Patches namespace Aki.Debugging.BTR.Patches
{ {
public class BTRTurretCanShootPatch : ModulePatch internal class BTRTurretCanShootPatch : ModulePatch
{ {
protected override MethodBase GetTargetMethod() protected override MethodBase GetTargetMethod()
{ {
return AccessTools.Method(typeof(BTRTurretServer), "method_1"); return AccessTools.Method(typeof(BTRTurretServer), nameof(BTRTurretServer.method_1));
} }
[PatchPrefix] [PatchPrefix]
public static bool PatchPrefix(BTRTurretServer __instance) private static bool PatchPrefix(BTRTurretServer __instance)
{ {
Transform defaultTargetTransform = (Transform)AccessTools.Field(__instance.GetType(), "defaultTargetTransform").GetValue(__instance); Transform defaultTargetTransform = (Transform)AccessTools.Field(__instance.GetType(), "defaultTargetTransform").GetValue(__instance);
bool flag = __instance.targetTransform != null && __instance.targetTransform != defaultTargetTransform; bool flag = __instance.targetTransform != null && __instance.targetTransform != defaultTargetTransform;
bool flag2 = (bool)AccessTools.Method(__instance.GetType(), "method_2").Invoke(__instance, null); bool flag2 = __instance.method_2();
bool flag3 = __instance.targetPosition != __instance.defaultAimingPosition; bool flag3 = __instance.targetPosition != __instance.defaultAimingPosition;
var isCanShootProperty = AccessTools.DeclaredProperty(__instance.GetType(), nameof(__instance.IsCanShoot)); var isCanShootProperty = AccessTools.DeclaredProperty(__instance.GetType(), nameof(__instance.IsCanShoot));

View File

@ -6,7 +6,7 @@ using UnityEngine;
namespace Aki.Debugging.BTR.Patches namespace Aki.Debugging.BTR.Patches
{ {
public class BTRTurretDefaultAimingPositionPatch : ModulePatch internal class BTRTurretDefaultAimingPositionPatch : ModulePatch
{ {
protected override MethodBase GetTargetMethod() protected override MethodBase GetTargetMethod()
{ {
@ -14,7 +14,7 @@ namespace Aki.Debugging.BTR.Patches
} }
[PatchPrefix] [PatchPrefix]
public static bool PatchPrefix(BTRTurretServer __instance) private static bool PatchPrefix(BTRTurretServer __instance)
{ {
__instance.defaultAimingPosition = Vector3.zero; __instance.defaultAimingPosition = Vector3.zero;

View File

@ -1,15 +1,7 @@
using Aki.Common.Http; using Comfort.Common;
using Comfort.Common;
using EFT; using EFT;
using EFT.InventoryLogic; using EFT.InventoryLogic;
using HarmonyLib;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using static BackendConfigSettingsClass;
using TraderServiceClass = GClass1789;
namespace Aki.Debugging.BTR.Utils namespace Aki.Debugging.BTR.Utils
{ {
@ -19,13 +11,6 @@ namespace Aki.Debugging.BTR.Utils
public static readonly string BTRMachineGunWeaponTplId = "657857faeff4c850222dff1b"; // BTR PKTM machine gun public static readonly string BTRMachineGunWeaponTplId = "657857faeff4c850222dff1b"; // BTR PKTM machine gun
public static readonly string BTRMachineGunAmmoTplId = "5e023d34e8a400319a28ed44"; // 7.62x54mmR BT public static readonly string BTRMachineGunAmmoTplId = "5e023d34e8a400319a28ed44"; // 7.62x54mmR BT
static BTRUtil()
{
// Sanity checks for compile time failure in the event the GClass changes
_ = nameof(TraderServiceClass.CanAfford);
_ = nameof(TraderServiceClass.WasPurchasedInThisRaid);
}
/// <summary> /// <summary>
/// Used to create an instance of the item in-raid. /// Used to create an instance of the item in-raid.
/// </summary> /// </summary>

View File

@ -1,11 +1,9 @@
using Aki.Common.Http; using Aki.Common.Http;
using Comfort.Common; using Comfort.Common;
using EFT; using EFT;
using HarmonyLib;
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection;
using UnityEngine; using UnityEngine;
using static BackendConfigSettingsClass; using static BackendConfigSettingsClass;
using TraderServiceClass = GClass1789; using TraderServiceClass = GClass1789;
@ -14,7 +12,10 @@ namespace Aki.SinglePlayer.Utils.TraderServices
{ {
public class TraderServicesManager public class TraderServicesManager
{ {
public event Action<ETraderServiceType> OnTraderServicePurchased; // Subscribe to this event to trigger trader service logic /// <summary>
/// Subscribe to this event to trigger trader service logic.
/// </summary>
public event Action<ETraderServiceType> OnTraderServicePurchased;
private static TraderServicesManager _instance; private static TraderServicesManager _instance;