mirror of
https://github.com/sp-tarkov/modules.git
synced 2025-02-12 22:30:44 -05:00
Player can now enter and exit the BTR (!51)
Todo: * BTR Driver services * Send to items to player stash (2x4) * Provide covering fire * Taxi to a specific location on Streets * BTR turns hostile if you shoot it * BTR automatically follows a set path from raid start, stopping at locations for 90 seconds each stop Co-authored-by: Nympfonic <arys.steam@gmail.com> Reviewed-on: SPT-AKI/Modules#51 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:
parent
e4f91a58e4
commit
33e6c151af
@ -18,8 +18,11 @@ namespace Aki.Debugging
|
||||
new EndRaidDebug().Enable();
|
||||
// new CoordinatesPatch().Enable();
|
||||
// new StaticLootDumper().Enable();
|
||||
new BTRPathLoadPatch().Enable();
|
||||
new BTRInteractionPatch().Enable();
|
||||
new BTRBotAttachPatch().Enable();
|
||||
new BTRBotInitPatch().Enable();
|
||||
new BTRIsDoorsClosedPath().Enable();
|
||||
new BTRPatch().Enable();
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -5,22 +5,26 @@ using HarmonyLib;
|
||||
using UnityEngine;
|
||||
using BTRController = GClass2911;
|
||||
using BTRDataPacket = GStruct378;
|
||||
using PlayerInteractPacket = GStruct167;
|
||||
|
||||
namespace Aki.Debugging.BTR
|
||||
{
|
||||
public class BTRManager : MonoBehaviour
|
||||
{
|
||||
private GameWorld gameWorld;
|
||||
private BTRController btrController;
|
||||
private BTRVehicle serverSideBtr;
|
||||
private BTRView clientSideBtr;
|
||||
private BTRDataPacket btrDataPacket = default;
|
||||
private EPlayerBtrState previousPlayerBtrState;
|
||||
private BTRSide previousInteractedBtrSide;
|
||||
|
||||
|
||||
private void Start()
|
||||
{
|
||||
try
|
||||
{
|
||||
var gameWorld = Singleton<GameWorld>.Instance;
|
||||
gameWorld = Singleton<GameWorld>.Instance;
|
||||
|
||||
if (gameWorld == null)
|
||||
{
|
||||
@ -55,6 +59,9 @@ namespace Aki.Debugging.BTR
|
||||
clientSideBtr.transform.rotation = btrDataPacket.rotation;
|
||||
|
||||
DisableServerSideRenderers();
|
||||
|
||||
previousPlayerBtrState = gameWorld.MainPlayer.BtrState;
|
||||
gameWorld.MainPlayer.OnBtrStateChanged += HandleBtrDoorState;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -69,6 +76,80 @@ namespace Aki.Debugging.BTR
|
||||
btrController.SyncBTRVehicleFromServer(UpdateDataPacket());
|
||||
}
|
||||
|
||||
public void HandleBtrDoorState(EPlayerBtrState playerBtrState)
|
||||
{
|
||||
if (previousPlayerBtrState == EPlayerBtrState.Approach && playerBtrState == EPlayerBtrState.GoIn
|
||||
|| previousPlayerBtrState == EPlayerBtrState.Inside && playerBtrState == EPlayerBtrState.GoOut)
|
||||
{
|
||||
// Open Door
|
||||
UpdateBTRSideDoorState(1);
|
||||
}
|
||||
else if (previousPlayerBtrState == EPlayerBtrState.GoIn && playerBtrState == EPlayerBtrState.Inside
|
||||
|| previousPlayerBtrState == EPlayerBtrState.GoOut && playerBtrState == EPlayerBtrState.Outside)
|
||||
{
|
||||
// Close Door
|
||||
UpdateBTRSideDoorState(0);
|
||||
}
|
||||
|
||||
previousPlayerBtrState = playerBtrState;
|
||||
}
|
||||
|
||||
// Please tell me there's a better way than this xd
|
||||
public void OnPlayerInteractDoor(PlayerInteractPacket interactPacket)
|
||||
{
|
||||
var playerGoIn = interactPacket.InteractionType == EInteractionType.GoIn;
|
||||
var playerGoOut = interactPacket.InteractionType == EInteractionType.GoOut;
|
||||
|
||||
if (interactPacket.SideId == 0)
|
||||
{
|
||||
if (interactPacket.SlotId == 0)
|
||||
{
|
||||
if (playerGoIn) serverSideBtr.LeftSlot0State = 1;
|
||||
else if (playerGoOut) serverSideBtr.LeftSlot0State = 0;
|
||||
}
|
||||
else if (interactPacket.SlotId == 1)
|
||||
{
|
||||
if (playerGoIn) serverSideBtr.LeftSlot1State = 1;
|
||||
else if (playerGoOut) serverSideBtr.LeftSlot1State = 0;
|
||||
}
|
||||
}
|
||||
else if (interactPacket.SideId == 1)
|
||||
{
|
||||
if (interactPacket.SlotId == 0)
|
||||
{
|
||||
if (playerGoIn) serverSideBtr.RightSlot0State = 1;
|
||||
else if (playerGoOut) serverSideBtr.RightSlot0State = 0;
|
||||
}
|
||||
else if (interactPacket.SlotId == 1)
|
||||
{
|
||||
if (playerGoIn) serverSideBtr.RightSlot1State = 1;
|
||||
else if (playerGoOut) serverSideBtr.RightSlot1State = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateBTRSideDoorState(byte state)
|
||||
{
|
||||
var player = gameWorld.MainPlayer;
|
||||
var btrSides = (BTRSide[])AccessTools.Field(typeof(BTRView), "_btrSides").GetValue(btrController.BtrView);
|
||||
|
||||
for (int i = 0; i < btrSides.Length; i++)
|
||||
{
|
||||
if (player.BtrInteractionSide != null && btrSides[i] == player.BtrInteractionSide
|
||||
|| previousInteractedBtrSide != null && btrSides[i] == previousInteractedBtrSide)
|
||||
{
|
||||
if (i == 0) serverSideBtr.LeftSideState = state;
|
||||
else if (i == 1) serverSideBtr.RightSideState = state;
|
||||
|
||||
if ((previousInteractedBtrSide != player.BtrInteractionSide && player.BtrInteractionSide != null)
|
||||
|| previousInteractedBtrSide == null)
|
||||
{
|
||||
previousInteractedBtrSide = player.BtrInteractionSide;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BTRDataPacket UpdateDataPacket()
|
||||
{
|
||||
btrDataPacket.position = serverSideBtr.transform.position;
|
||||
@ -122,6 +203,11 @@ namespace Aki.Debugging.BTR
|
||||
|
||||
btrController.Dispose();
|
||||
}
|
||||
|
||||
if (gameWorld != null)
|
||||
{
|
||||
gameWorld.MainPlayer.OnBtrStateChanged -= HandleBtrDoorState;
|
||||
}
|
||||
Destroy(this);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ namespace Aki.Debugging.BTR.Patches
|
||||
if (!btrTurretViewTupleField.Item2)
|
||||
{
|
||||
var btrTurretViewMethod = AccessTools.Method(btrTurretView.GetType(), "method_1");
|
||||
btrTurretViewMethod.Invoke(btrTurretView, new object[]{btrBotId});
|
||||
btrTurretViewMethod.Invoke(btrTurretView, new object[] { btrBotId });
|
||||
}
|
||||
if (!btrTurretViewTupleField.Item2)
|
||||
{
|
||||
@ -59,11 +59,12 @@ namespace Aki.Debugging.BTR.Patches
|
||||
currentWeaponPrefab.transform.position = botRootTransform.position;
|
||||
|
||||
btrBot.PlayerBones.Weapon_Root_Anim.SetPositionAndRotation(botRootTransform.position, botRootTransform.rotation);
|
||||
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
ConsoleScreen.LogError("[AKI-BTR] Could not finish BtrBot initialization. Check logs.");
|
||||
ConsoleScreen.LogError("[AKI-BTR]: Could not finish BtrBot initialization. Check logs.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ namespace Aki.Debugging.BTR.Patches
|
||||
// Context:
|
||||
// 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.
|
||||
// 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.
|
||||
public class BTRBotInitPatch : ModulePatch
|
||||
{
|
||||
|
49
project/Aki.Debugging/BTR/Patches/BTRInteractionPatch.cs
Normal file
49
project/Aki.Debugging/BTR/Patches/BTRInteractionPatch.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using Aki.Reflection.Patching;
|
||||
using Comfort.Common;
|
||||
using EFT;
|
||||
using EFT.Vehicle;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Aki.Debugging.BTR.Patches
|
||||
{
|
||||
public class BTRInteractionPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
var bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance;
|
||||
return typeof(Player).GetMethod("BtrInteraction", bindingFlags);
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void PatchPostfix(object __instance, BTRSide btr, byte placeId, EInteractionType interaction)
|
||||
{
|
||||
var gameWorld = Singleton<GameWorld>.Instance;
|
||||
var player = (Player)__instance;
|
||||
|
||||
try
|
||||
{
|
||||
var interactionBtrPacket = btr.GetInteractWithBtrPacket(placeId, interaction);
|
||||
player.UpdateInteractionCast();
|
||||
|
||||
if (interactionBtrPacket.HasInteraction)
|
||||
{
|
||||
BTRView btrView = gameWorld.BtrController.BtrView;
|
||||
if (btrView == null)
|
||||
{
|
||||
throw new NullReferenceException("BtrView not found");
|
||||
}
|
||||
|
||||
var btrManager = gameWorld.GetComponent<BTRManager>();
|
||||
btrManager.OnPlayerInteractDoor(interactionBtrPacket);
|
||||
|
||||
btrView.Interaction(player, interactionBtrPacket);
|
||||
}
|
||||
}
|
||||
catch (Exception ex19)
|
||||
{
|
||||
UnityEngine.Debug.LogException(ex19);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
44
project/Aki.Debugging/BTR/Patches/BTRIsDoorsClosedPatch.cs
Normal file
44
project/Aki.Debugging/BTR/Patches/BTRIsDoorsClosedPatch.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using Aki.Reflection.Patching;
|
||||
using Comfort.Common;
|
||||
using EFT;
|
||||
using EFT.Vehicle;
|
||||
using HarmonyLib;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Aki.Debugging.BTR.Patches
|
||||
{
|
||||
public class BTRIsDoorsClosedPath : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
return AccessTools.Method(typeof(VehicleBase), "IsDoorsClosed");
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static bool PatchPrefix(ref bool __result)
|
||||
{
|
||||
var btrView = Singleton<GameWorld>.Instance?.BtrController?.BtrView;
|
||||
if (btrView == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var btrSides = (BTRSide[])AccessTools.Field(typeof(BTRView), "_btrSides").GetValue(btrView);
|
||||
int doorsClosed = 0;
|
||||
foreach (var side in btrSides)
|
||||
{
|
||||
if (side.State == BTRSide.EState.Free)
|
||||
{
|
||||
doorsClosed++;
|
||||
}
|
||||
}
|
||||
if (doorsClosed == btrSides.Length)
|
||||
{
|
||||
__result = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -6,7 +6,9 @@ using EFT.UI;
|
||||
|
||||
namespace Aki.Debugging.BTR.Patches
|
||||
{
|
||||
// Adds a BTRManager component to the GameWorld game object when raid starts.
|
||||
/// <summary>
|
||||
/// Adds a BTRManager component to the GameWorld game object when raid starts.
|
||||
/// </summary>
|
||||
public class BTRPatch : ModulePatch
|
||||
{
|
||||
protected override MethodBase GetTargetMethod()
|
||||
@ -36,7 +38,7 @@ namespace Aki.Debugging.BTR.Patches
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
ConsoleScreen.LogError("[AKI-BTR] Exception thrown, check logs.");
|
||||
ConsoleScreen.LogError("[AKI-BTR]: Exception thrown, check logs.");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
35
project/Aki.Debugging/BTR/Patches/BTRPathLoadPatch.cs
Normal file
35
project/Aki.Debugging/BTR/Patches/BTRPathLoadPatch.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using Aki.Reflection.Patching;
|
||||
using Comfort.Common;
|
||||
using EFT;
|
||||
using HarmonyLib;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Aki.Debugging.BTR.Patches
|
||||
{
|
||||
// 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
|
||||
public class BTRPathLoadPatch : ModulePatch
|
||||
{
|
||||
private static PropertyInfo _statusProperty;
|
||||
private static GameStatus originalStatus;
|
||||
protected override MethodBase GetTargetMethod()
|
||||
{
|
||||
_statusProperty = AccessTools.Property(typeof(AbstractGame), "Status");
|
||||
|
||||
return AccessTools.Method(typeof(GClass2911), "method_1");
|
||||
}
|
||||
|
||||
[PatchPrefix]
|
||||
public static void PatchPrefix()
|
||||
{
|
||||
originalStatus = Singleton<AbstractGame>.Instance.Status;
|
||||
_statusProperty.SetValue(Singleton<AbstractGame>.Instance, GameStatus.Starting);
|
||||
}
|
||||
|
||||
[PatchPostfix]
|
||||
public static void PatchPostfix()
|
||||
{
|
||||
_statusProperty.SetValue(Singleton<AbstractGame>.Instance, originalStatus);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user