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

Shooting the BTR will make it retaliate. Fixed BTR turret from rotating when non-hostile (!56)

Todo:

* Make the UI notifications for BTR-related stuff appear, e.g. when BTR is about to depart
* Prevent player from entering BTR when it is hostile to the player
* Implement cover fire trader service
* Finish implementation of taxi service
* Finetune the turret machine gun shooting (it might need some pauses as it will shoot nonstop if it is aiming at you)

Co-authored-by: Nympfonic <arys.steam@gmail.com>
Reviewed-on: SPT-AKI/Modules#56
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-12 08:54:12 +00:00 committed by chomp
parent b82ef6dd30
commit 0cabe9dc12
7 changed files with 201 additions and 18 deletions

View File

@ -41,7 +41,7 @@ namespace Aki.Custom.Patches
[PatchPrefix]
private static bool PatchPrefix(ref bool __result, BotsGroup __instance, IPlayer requester)
{
if (__instance.InitialBotType == WildSpawnType.peacefullZryachiyEvent)
if (__instance.InitialBotType == WildSpawnType.peacefullZryachiyEvent || __instance.InitialBotType == WildSpawnType.shooterBTR)
{
return true; // Do original code
}

View File

@ -24,6 +24,9 @@ namespace Aki.Debugging
new BTRExtractPassengersPatch().Enable();
new BTRBotAttachPatch().Enable();
new BTRBotInitPatch().Enable();
new BTRReceiveDamageInfoPatch().Enable();
new BTRTurretCanShootPatch().Enable();
new BTRTurretDefaultAimingPositionPatch().Enable();
new BTRIsDoorsClosedPath().Enable();
new BTRPatch().Enable();
new BTRTransferItemsPatch().Enable();

View File

@ -1,9 +1,11 @@
using Aki.Debugging.BTR.Utils;
using Comfort.Common;
using EFT;
using EFT.InventoryLogic;
using EFT.Vehicle;
using HarmonyLib;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@ -20,12 +22,20 @@ namespace Aki.Debugging.BTR
private BTRControllerClass btrController;
private BTRVehicle btrServerSide;
private BTRView btrClientSide;
private BotOwner btrBotShooter;
private BTRDataPacket btrDataPacket = default;
private bool btrBotShooterInitialized = false;
private EPlayerBtrState previousPlayerBtrState;
private BTRSide lastInteractedBtrSide;
public BTRSide LastInteractedBtrSide => lastInteractedBtrSide;
private BTRTurretServer btrTurretServer;
private Transform btrTurretDefaultTargetTransform;
private Coroutine _shootingTargetCoroutine;
private bool isShooting = false;
private BulletClass btrMachineGunAmmo;
private Item btrMachineGunWeapon;
private MethodInfo _updateTaxiPriceMethod;
@ -91,9 +101,6 @@ namespace Aki.Debugging.BTR
gameWorld.BtrController = btrController = Singleton<BTRControllerClass>.Instance;
}
botsController = Singleton<IBotGame>.Instance.BotsController;
btrBotService = botsController.BotTradersServices.BTRServices;
InitBTR();
}
catch
@ -111,11 +118,17 @@ namespace Aki.Debugging.BTR
// BotShooterBtr doesn't get assigned to BtrController immediately so we nullcheck this in Update
if (btrController.BotShooterBtr != null && !btrBotShooterInitialized)
{
btrBotShooter = btrController.BotShooterBtr;
btrBotService.Reset(); // Player will be added to Neutrals list and removed from Enemies list
btrBotShooterInitialized = true;
}
AimAtEnemy();
if (btrController.BotShooterBtr == null) return;
if (IsAimingAtTarget() && !isShooting)
{
_shootingTargetCoroutine = StaticManager.BeginCoroutine(ShootTarget());
}
}
public void HandleBtrDoorState(EPlayerBtrState playerBtrState)
@ -172,10 +185,14 @@ namespace Aki.Debugging.BTR
private void InitBTR()
{
// Initial setup
botsController = Singleton<IBotGame>.Instance.BotsController;
btrBotService = botsController.BotTradersServices.BTRServices;
var btrControllerType = btrController.GetType();
AccessTools.Method(btrControllerType, "method_3").Invoke(btrController, null); // spawns server-side BTR game object
botsController.BotSpawner.SpawnBotBTR(); // spawns the scav bot which controls the BTR's turret
// Initial BTR configuration
btrServerSide = btrController.BtrVehicle;
btrServerSide.moveSpeed = 20f;
var btrMapConfig = btrController.MapPathsConfiguration;
@ -190,11 +207,18 @@ namespace Aki.Debugging.BTR
btrServerSide.MoveEnable();
btrServerSide.IncomingToDestinationEvent += ToDestinationEvent;
// Sync initial position and rotation
UpdateDataPacket();
btrClientSide = btrController.BtrView;
btrClientSide.transform.position = btrDataPacket.position;
btrClientSide.transform.rotation = btrDataPacket.rotation;
// Initialise turret variables
btrTurretServer = btrServerSide.BTRTurret;
btrTurretDefaultTargetTransform = (Transform)AccessTools.Field(btrTurretServer.GetType(), "defaultTargetTransform").GetValue(btrTurretServer);
btrMachineGunAmmo = (BulletClass)BTRUtil.CreateItem(BTRUtil.BTRMachineGunAmmoTplId);
btrMachineGunWeapon = BTRUtil.CreateItem(BTRUtil.BTRMachineGunWeaponTplId);
// Pull services data for the BTR from the server
BTRUtil.PopulateTraderServicesData(BTRUtil.BTRTraderId);
}
@ -233,10 +257,10 @@ namespace Aki.Debugging.BTR
{
btrDataPacket.position = btrServerSide.transform.position;
btrDataPacket.rotation = btrServerSide.transform.rotation;
if (btrServerSide.BTRTurret?.gunsBlockRoot != null)
if (btrTurretServer?.gunsBlockRoot != null)
{
btrDataPacket.turretRotation = btrServerSide.BTRTurret.transform.rotation;
btrDataPacket.gunsBlockRotation = btrServerSide.BTRTurret.gunsBlockRoot.rotation;
btrDataPacket.turretRotation = btrTurretServer.transform.rotation;
btrDataPacket.gunsBlockRotation = btrTurretServer.gunsBlockRoot.rotation;
}
btrDataPacket.State = (byte)btrServerSide.BtrState;
btrDataPacket.RouteState = (byte)btrServerSide.VehicleRouteState;
@ -267,23 +291,76 @@ namespace Aki.Debugging.BTR
}
}
private void AimAtEnemy()
private bool IsAimingAtTarget()
{
if (btrController.BotShooterBtr == null)
{
return;
}
var turretInDefaultRotation = btrTurretServer.targetTransform == btrTurretDefaultTargetTransform
&& btrTurretServer.targetPosition == btrTurretServer.defaultAimingPosition;
var btrTurret = btrServerSide.BTRTurret;
var enemies = btrController.BotShooterBtr.BotsGroup.Enemies;
var enemies = btrBotShooter.BotsGroup.Enemies;
if (enemies.Any())
{
btrTurret.EnableAimingObject(enemies.First().Key.Transform.Original);
IPlayer currentTarget = enemies.First().Key;
Transform currentTargetTransform = currentTarget.Transform.Original;
Vector3 currentTargetPosition = currentTargetTransform.position;
var currentTargetInfo = btrBotShooter.EnemiesController.EnemyInfos[currentTarget];
if (!currentTarget.HealthController.IsAlive)
{
enemies.Remove(currentTarget);
}
if (currentTargetInfo.IsVisible)
{
if (btrTurretServer.CheckPositionInAimingZone(currentTargetPosition))
{
btrTurretServer.EnableAimingObject(currentTargetTransform);
}
// If turret machine gun aim is close enough to target and has line of sight
if (btrBotShooter.BotBtrData.CanShoot())
{
return true;
}
}
else if (!currentTargetInfo.IsVisible)
{
// Turret will hold the angle where target was last seen for 3 seconds before resetting its rotation
if (btrTurretServer.targetPosition != currentTargetInfo.EnemyLastPosition && btrTurretServer.targetTransform != null)
{
btrTurretServer.EnableAimingPosition(currentTargetInfo.EnemyLastPosition);
}
else if (currentTargetInfo.TimeLastSeen >= 3f && !turretInDefaultRotation)
{
btrTurretServer.DisableAiming();
}
}
}
else
else if (!enemies.Any() && !turretInDefaultRotation)
{
btrTurret.DisableAiming();
btrTurretServer.DisableAiming();
}
return false;
}
/// <summary>
/// Custom method to make the BTR coaxial machine gun shoot.
/// </summary>
private IEnumerator ShootTarget()
{
isShooting = true;
Transform machineGunMuzzle = btrTurretServer.machineGunLaunchPoint;
Player btrBotPlayer = btrBotShooter.GetPlayer;
gameWorld.SharedBallisticsCalculator.Shoot(btrMachineGunAmmo, machineGunMuzzle.position, machineGunMuzzle.forward, btrBotPlayer.ProfileId, btrMachineGunWeapon, 1f, 0);
Player.FirearmController firearmController = btrBotShooter.GetComponent<Player.FirearmController>();
WeaponPrefab weaponPrefab = (WeaponPrefab)AccessTools.Field(firearmController.GetType(), "weaponPrefab_0").GetValue(firearmController);
WeaponSoundPlayer weaponSoundPlayer = weaponPrefab.GetComponent<WeaponSoundPlayer>();
AccessTools.Method(firearmController.GetType(), "method_54").Invoke(firearmController, new object[] { weaponSoundPlayer, btrMachineGunAmmo, machineGunMuzzle.position, machineGunMuzzle.forward, false });
yield return new WaitForSecondsRealtime(0.092308f); // 650 RPM
isShooting = false;
}
private void DestroyGameObjects()
@ -306,6 +383,8 @@ namespace Aki.Debugging.BTR
{
gameWorld.MainPlayer.OnBtrStateChanged -= HandleBtrDoorState;
}
StaticManager.KillCoroutine(ref _shootingTargetCoroutine);
Destroy(this);
}
}

View File

@ -0,0 +1,33 @@
using Aki.Reflection.Patching;
using Comfort.Common;
using EFT;
using EFT.Vehicle;
using HarmonyLib;
using System.Reflection;
namespace Aki.Debugging.BTR.Patches
{
/// <summary>
/// Patches an empty method in <see cref="BTRView"/> to handle updating the BTR bot's Neutrals and Enemies lists in response to taking damage.
/// </summary>
public class BTRReceiveDamageInfoPatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(BTRView), "method_1");
}
[PatchPrefix]
public static void PatchPrefix(DamageInfo damageInfo)
{
var globalEvents = Singleton<GClass595>.Instance;
if (globalEvents == null)
{
return;
}
var shotBy = (Player)damageInfo.Player.iPlayer;
globalEvents.InterruptTraderServiceBtrSupportByBetrayer(shotBy);
}
}
}

View File

@ -0,0 +1,31 @@
using Aki.Reflection.Patching;
using EFT.Vehicle;
using HarmonyLib;
using System.Reflection;
using UnityEngine;
namespace Aki.Debugging.BTR.Patches
{
public class BTRTurretCanShootPatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(BTRTurretServer), "method_1");
}
[PatchPrefix]
public static bool PatchPrefix(BTRTurretServer __instance)
{
Transform defaultTargetTransform = (Transform)AccessTools.Field(__instance.GetType(), "defaultTargetTransform").GetValue(__instance);
bool flag = __instance.targetTransform != null && __instance.targetTransform != defaultTargetTransform;
bool flag2 = (bool)AccessTools.Method(__instance.GetType(), "method_2").Invoke(__instance, null);
bool flag3 = __instance.targetPosition != __instance.defaultAimingPosition;
var isCanShootProperty = AccessTools.DeclaredProperty(__instance.GetType(), nameof(__instance.IsCanShoot));
isCanShootProperty.SetValue(__instance, (flag || flag3) && flag2);
return false;
}
}
}

View File

@ -0,0 +1,24 @@
using Aki.Reflection.Patching;
using EFT.Vehicle;
using HarmonyLib;
using System.Reflection;
using UnityEngine;
namespace Aki.Debugging.BTR.Patches
{
public class BTRTurretDefaultAimingPositionPatch : ModulePatch
{
protected override MethodBase GetTargetMethod()
{
return AccessTools.Method(typeof(BTRTurretServer), "Start");
}
[PatchPrefix]
public static bool PatchPrefix(BTRTurretServer __instance)
{
__instance.defaultAimingPosition = Vector3.zero;
return false;
}
}
}

View File

@ -2,8 +2,10 @@
using Aki.Debugging.BTR.Models;
using Comfort.Common;
using EFT;
using EFT.InventoryLogic;
using HarmonyLib;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
@ -15,6 +17,8 @@ namespace Aki.Debugging.BTR.Utils
public static class BTRUtil
{
public static readonly string BTRTraderId = Profile.TraderInfo.BTR_TRADER_ID;
public static readonly string BTRMachineGunWeaponTplId = "657857faeff4c850222dff1b"; // BTR PKTM machine gun
public static readonly string BTRMachineGunAmmoTplId = "5e023d34e8a400319a28ed44"; // 7.62x54mmR BT
private static FieldInfo _traderAvailableServicesField = AccessTools.Field(typeof(Profile.TraderInfo), "_availableServices");
@ -145,5 +149,14 @@ namespace Aki.Debugging.BTR.Utils
return true;
}
/// <summary>
/// Used to create an instance of the item in-raid.
/// </summary>
public static Item CreateItem(string tplId)
{
var id = Guid.NewGuid().ToString("N").Substring(0, 24);
return Singleton<ItemFactory>.Instance.CreateItem(id, tplId, null);
}
}
}