mirror of
https://github.com/sp-tarkov/modules.git
synced 2025-02-13 01:30:45 -05:00
Remove unused files, some patches to test also
This commit is contained in:
parent
0c850bc8d2
commit
0bc05f80d4
@ -1,259 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT.Airdrop;
|
|
||||||
using EFT.Interactive;
|
|
||||||
using EFT.SynchronizableObjects;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.AI;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Airdrops
|
|
||||||
{
|
|
||||||
public class AirdropBox : MonoBehaviour
|
|
||||||
{
|
|
||||||
private const string CRATE_PATH = "assets/content/location_objects/lootable/prefab/scontainer_crate.bundle";
|
|
||||||
private const string AIRDROP_SOUNDS_PATH = "assets/content/audio/prefabs/airdrop/airdropsounds.bundle";
|
|
||||||
private readonly int CROSSFADE = Shader.PropertyToID("_Crossfade");
|
|
||||||
private readonly int COLLISION = Animator.StringToHash("collision");
|
|
||||||
|
|
||||||
public LootableContainer container;
|
|
||||||
private float fallSpeed;
|
|
||||||
private AirdropSynchronizableObject boxSync;
|
|
||||||
private AirdropLogicClass boxLogic;
|
|
||||||
private Material paraMaterial;
|
|
||||||
private Animator paraAnimator;
|
|
||||||
private AirdropSurfaceSet surfaceSet;
|
|
||||||
private Dictionary<BaseBallistic.ESurfaceSound, AirdropSurfaceSet> soundsDictionary;
|
|
||||||
private BetterSource audioSource;
|
|
||||||
|
|
||||||
private BetterSource AudioSource
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (audioSource != null) return audioSource;
|
|
||||||
|
|
||||||
audioSource = Singleton<BetterAudio>.Instance.GetSource(BetterAudio.AudioSourceGroupType.Environment, false);
|
|
||||||
audioSource.transform.parent = transform;
|
|
||||||
audioSource.transform.localPosition = Vector3.up;
|
|
||||||
|
|
||||||
return audioSource;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<AirdropBox> Init(float crateFallSpeed)
|
|
||||||
{
|
|
||||||
var instance = (await LoadCrate()).AddComponent<AirdropBox>();
|
|
||||||
instance.soundsDictionary = await LoadSounds();
|
|
||||||
|
|
||||||
instance.container = instance.GetComponentInChildren<LootableContainer>();
|
|
||||||
|
|
||||||
instance.boxSync = instance.GetComponent<AirdropSynchronizableObject>();
|
|
||||||
instance.boxLogic = new AirdropLogicClass();
|
|
||||||
instance.boxSync.SetLogic(instance.boxLogic);
|
|
||||||
|
|
||||||
instance.paraAnimator = instance.boxSync.Parachute.GetComponent<Animator>();
|
|
||||||
instance.paraMaterial = instance.boxSync.Parachute.GetComponentInChildren<Renderer>().material;
|
|
||||||
instance.fallSpeed = crateFallSpeed;
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<GameObject> LoadCrate()
|
|
||||||
{
|
|
||||||
var easyAssets = Singleton<PoolManager>.Instance.EasyAssets;
|
|
||||||
await easyAssets.Retain(CRATE_PATH, null, null).LoadingJob;
|
|
||||||
|
|
||||||
var crate = Instantiate(easyAssets.GetAsset<GameObject>(CRATE_PATH));
|
|
||||||
crate.SetActive(false);
|
|
||||||
|
|
||||||
return crate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<Dictionary<BaseBallistic.ESurfaceSound, AirdropSurfaceSet>> LoadSounds()
|
|
||||||
{
|
|
||||||
var easyAssets = Singleton<PoolManager>.Instance.EasyAssets;
|
|
||||||
await easyAssets.Retain(AIRDROP_SOUNDS_PATH, null, null).LoadingJob;
|
|
||||||
|
|
||||||
var soundsDictionary = new Dictionary<BaseBallistic.ESurfaceSound, AirdropSurfaceSet>();
|
|
||||||
var sets = easyAssets.GetAsset<AirdropSounds>(AIRDROP_SOUNDS_PATH).Sets;
|
|
||||||
foreach (var set in sets)
|
|
||||||
{
|
|
||||||
if (!soundsDictionary.ContainsKey(set.Surface))
|
|
||||||
{
|
|
||||||
soundsDictionary.Add(set.Surface, set);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.LogError(set.Surface + " surface sounds are duplicated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return soundsDictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator DropCrate(Vector3 position)
|
|
||||||
{
|
|
||||||
RaycastBoxDistance(LayerMaskClass.TerrainLowPoly, out var hitInfo, position);
|
|
||||||
SetLandingSound();
|
|
||||||
boxSync.Init(1, position, Vector3.zero);
|
|
||||||
PlayAudioClip(boxSync.SqueakClip, true);
|
|
||||||
|
|
||||||
if(hitInfo.distance < 155f)
|
|
||||||
{
|
|
||||||
for (float i = 0; i < 1; i += Time.deltaTime / 6f)
|
|
||||||
{
|
|
||||||
transform.position = Vector3.Lerp(position, hitInfo.point, i*i);
|
|
||||||
yield return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
transform.position = hitInfo.point;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var parachuteOpenPos = position + new Vector3(0f, -148.2f, 0f); // (5.5s * -9.8m/s^2) / 2
|
|
||||||
for (float i = 0; i < 1; i += Time.deltaTime / 5.5f)
|
|
||||||
{
|
|
||||||
transform.position = Vector3.Lerp(position, parachuteOpenPos, i * i);
|
|
||||||
yield return null;
|
|
||||||
}
|
|
||||||
OpenParachute();
|
|
||||||
while (RaycastBoxDistance(LayerMaskClass.TerrainLowPoly, out _))
|
|
||||||
{
|
|
||||||
transform.Translate(Vector3.down * (Time.deltaTime * fallSpeed));
|
|
||||||
transform.Rotate(Vector3.up, Time.deltaTime * 6f);
|
|
||||||
yield return null;
|
|
||||||
}
|
|
||||||
transform.position = hitInfo.point;
|
|
||||||
CloseParachute();
|
|
||||||
}
|
|
||||||
|
|
||||||
OnBoxLand(out var clipLength);
|
|
||||||
yield return new WaitForSecondsRealtime(clipLength + 0.5f);
|
|
||||||
ReleaseAudioSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBoxLand(out float clipLength)
|
|
||||||
{
|
|
||||||
var landingClip = surfaceSet.LandingSoundBank.PickSingleClip(surfaceSet.LandingSoundBank.GetRandomClipIndex(2));
|
|
||||||
clipLength = landingClip.length;
|
|
||||||
boxSync.AirdropDust.SetActive(true);
|
|
||||||
boxSync.AirdropDust.GetComponent<ParticleSystem>().Play();
|
|
||||||
AudioSource.source1.Stop();
|
|
||||||
PlayAudioClip(new TaggedClip
|
|
||||||
{
|
|
||||||
Clip = landingClip,
|
|
||||||
Falloff = (int)surfaceSet.LandingSoundBank.Rolloff,
|
|
||||||
Volume = surfaceSet.LandingSoundBank.BaseVolume
|
|
||||||
});
|
|
||||||
|
|
||||||
AddNavMeshObstacle();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddNavMeshObstacle()
|
|
||||||
{
|
|
||||||
var navMeshObstacle = this.GetOrAddComponent<NavMeshObstacle>();
|
|
||||||
navMeshObstacle.size = boxSync.CollisionCollider.bounds.size;
|
|
||||||
navMeshObstacle.carving = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool RaycastBoxDistance(LayerMask layerMask, out RaycastHit hitInfo)
|
|
||||||
{
|
|
||||||
return RaycastBoxDistance(layerMask, out hitInfo, transform.position);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool RaycastBoxDistance(LayerMask layerMask, out RaycastHit hitInfo, Vector3 origin)
|
|
||||||
{
|
|
||||||
var ray = new Ray(origin, Vector3.down);
|
|
||||||
|
|
||||||
var raycast = Physics.Raycast(ray, out hitInfo, Mathf.Infinity, layerMask);
|
|
||||||
if (!raycast) return false;
|
|
||||||
|
|
||||||
return hitInfo.distance > 0.05f;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetLandingSound()
|
|
||||||
{
|
|
||||||
if (!RaycastBoxDistance(LayerMaskClass.AudioControllerStepLayerMask, out var raycast))
|
|
||||||
{
|
|
||||||
Debug.LogError("Raycast to ground returns no hit. Choose Concrete sound landing set");
|
|
||||||
surfaceSet = soundsDictionary[BaseBallistic.ESurfaceSound.Concrete];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (raycast.collider.TryGetComponent(out BaseBallistic component))
|
|
||||||
{
|
|
||||||
var surfaceSound = component.GetSurfaceSound(raycast.point);
|
|
||||||
if (soundsDictionary.ContainsKey(surfaceSound))
|
|
||||||
{
|
|
||||||
surfaceSet = soundsDictionary[surfaceSound];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
surfaceSet = soundsDictionary[BaseBallistic.ESurfaceSound.Concrete];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PlayAudioClip(TaggedClip clip, bool looped = false)
|
|
||||||
{
|
|
||||||
var volume = clip.Volume;
|
|
||||||
var occlusionGroupSimple = Singleton<BetterAudio>.Instance.GetOcclusionGroupSimple(transform.position, ref volume);
|
|
||||||
AudioSource.gameObject.SetActive(true);
|
|
||||||
AudioSource.source1.outputAudioMixerGroup = occlusionGroupSimple;
|
|
||||||
AudioSource.source1.spatialBlend = 1f;
|
|
||||||
AudioSource.SetRolloff(clip.Falloff);
|
|
||||||
AudioSource.source1.volume = volume;
|
|
||||||
|
|
||||||
if (AudioSource.source1.isPlaying)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioSource.source1.clip = clip.Clip;
|
|
||||||
AudioSource.source1.loop = looped;
|
|
||||||
AudioSource.source1.Play();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OpenParachute()
|
|
||||||
{
|
|
||||||
boxSync.Parachute.SetActive(true);
|
|
||||||
paraAnimator.SetBool(COLLISION, false);
|
|
||||||
StartCoroutine(CrossFadeAnimation(1f));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CloseParachute()
|
|
||||||
{
|
|
||||||
paraAnimator.SetBool(COLLISION, true);
|
|
||||||
StartCoroutine(CrossFadeAnimation(0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator CrossFadeAnimation(float targetFadeValue)
|
|
||||||
{
|
|
||||||
var curFadeValue = paraMaterial.GetFloat(CROSSFADE);
|
|
||||||
for (float i = 0; i < 1; i += Time.deltaTime / 2f)
|
|
||||||
{
|
|
||||||
paraMaterial.SetFloat(CROSSFADE, Mathf.Lerp(curFadeValue, targetFadeValue, i*i));
|
|
||||||
yield return null;
|
|
||||||
}
|
|
||||||
paraMaterial.SetFloat(CROSSFADE, targetFadeValue);
|
|
||||||
|
|
||||||
if (targetFadeValue == 0f)
|
|
||||||
{
|
|
||||||
boxSync.Parachute.SetActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReleaseAudioSource()
|
|
||||||
{
|
|
||||||
if (audioSource == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
audioSource.transform.parent = null;
|
|
||||||
audioSource.Release();
|
|
||||||
audioSource = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
using System.Collections;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.SynchronizableObjects;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Airdrops
|
|
||||||
{
|
|
||||||
public class AirdropPlane : MonoBehaviour
|
|
||||||
{
|
|
||||||
private const string PLANE_PATH = "assets/content/location_objects/lootable/prefab/il76md-90.prefab";
|
|
||||||
private const float RADIUS_TO_PICK_RANDOM_POINT = 3000f;
|
|
||||||
|
|
||||||
private AirplaneSynchronizableObject airplaneSync;
|
|
||||||
private float speed;
|
|
||||||
private float distanceToDrop;
|
|
||||||
private float flaresCooldown;
|
|
||||||
private bool flaresDeployed;
|
|
||||||
private bool headingChanged;
|
|
||||||
|
|
||||||
public static async Task<AirdropPlane> Init(Vector3 airdropPoint, int dropHeight, float planeVolume, float speed)
|
|
||||||
{
|
|
||||||
var instance = (await LoadPlane()).AddComponent<AirdropPlane>();
|
|
||||||
|
|
||||||
instance.airplaneSync = instance.GetComponent<AirplaneSynchronizableObject>();
|
|
||||||
// now has new parameter for AirplaneLogicClass, might be wrong
|
|
||||||
instance.airplaneSync.SetLogic(new AirplaneLogicClass(true));
|
|
||||||
|
|
||||||
instance.SetPosition(dropHeight, airdropPoint);
|
|
||||||
instance.SetAudio(planeVolume);
|
|
||||||
instance.speed = speed;
|
|
||||||
instance.gameObject.SetActive(false);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<GameObject> LoadPlane()
|
|
||||||
{
|
|
||||||
var easyAssets = Singleton<PoolManager>.Instance.EasyAssets;
|
|
||||||
await easyAssets.Retain(PLANE_PATH, null, null).LoadingJob;
|
|
||||||
var plane = Instantiate(easyAssets.GetAsset<GameObject>(PLANE_PATH));
|
|
||||||
return plane;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetAudio(float planeVolume)
|
|
||||||
{
|
|
||||||
var airplaneAudio = gameObject.AddComponent<AudioSource>();
|
|
||||||
airplaneAudio.clip = airplaneSync.soundClip.Clip;
|
|
||||||
|
|
||||||
airplaneAudio.dopplerLevel = 1f;
|
|
||||||
airplaneAudio.outputAudioMixerGroup = Singleton<BetterAudio>.Instance.VeryStandartMixerGroup;
|
|
||||||
airplaneAudio.loop = true;
|
|
||||||
airplaneAudio.maxDistance = 2000;
|
|
||||||
airplaneAudio.minDistance = 1;
|
|
||||||
airplaneAudio.pitch = 0.5f;
|
|
||||||
airplaneAudio.priority = 128;
|
|
||||||
airplaneAudio.reverbZoneMix = 1;
|
|
||||||
airplaneAudio.rolloffMode = AudioRolloffMode.Custom;
|
|
||||||
airplaneAudio.spatialBlend = 1;
|
|
||||||
airplaneAudio.spread = 60;
|
|
||||||
airplaneAudio.volume = planeVolume;
|
|
||||||
|
|
||||||
airplaneAudio.Play();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetPosition(int dropHeight, Vector3 airdropPoint)
|
|
||||||
{
|
|
||||||
var pointOnCircle = Random.insideUnitCircle.normalized * RADIUS_TO_PICK_RANDOM_POINT;
|
|
||||||
|
|
||||||
transform.position = new Vector3(pointOnCircle.x, dropHeight, pointOnCircle.y);
|
|
||||||
transform.LookAt(new Vector3(airdropPoint.x, dropHeight, airdropPoint.z));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ManualUpdate(float distance)
|
|
||||||
{
|
|
||||||
transform.Translate(Vector3.forward * (Time.deltaTime * speed));
|
|
||||||
distanceToDrop = distance;
|
|
||||||
UpdateFlaresLogic();
|
|
||||||
|
|
||||||
if (distance - 200f > 0f || headingChanged) return;
|
|
||||||
|
|
||||||
StartCoroutine(ChangeHeading());
|
|
||||||
headingChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateFlaresLogic()
|
|
||||||
{
|
|
||||||
if (flaresDeployed) return;
|
|
||||||
|
|
||||||
if (distanceToDrop > 0f && flaresCooldown <= Time.unscaledTime)
|
|
||||||
{
|
|
||||||
flaresCooldown = Time.unscaledTime + 4f;
|
|
||||||
StartCoroutine(DeployFlares(Random.Range(0.2f, 0.4f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (distanceToDrop > 0f) return;
|
|
||||||
|
|
||||||
flaresDeployed = true;
|
|
||||||
StartCoroutine(DeployFlares(5f));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator DeployFlares(float emissionTime)
|
|
||||||
{
|
|
||||||
var projectile = Instantiate(airplaneSync.infraredCountermeasureParticles, transform);
|
|
||||||
projectile.transform.localPosition = new Vector3(0f, -5f, 0f);
|
|
||||||
var flares = projectile.GetComponentsInChildren<ParticleSystem>();
|
|
||||||
var endTime = Time.unscaledTime + emissionTime;
|
|
||||||
Singleton<GameWorld>.Instance.SynchronizableObjectLogicProcessor.AirdropManager.AddProjectile(projectile,
|
|
||||||
endTime + flares[0].main.duration + flares[0].main.startLifetime.Evaluate(1f));
|
|
||||||
|
|
||||||
while (endTime > Time.unscaledTime)
|
|
||||||
yield return null;
|
|
||||||
|
|
||||||
projectile.transform.parent = null;
|
|
||||||
foreach (var particleSystem in flares)
|
|
||||||
particleSystem.Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator ChangeHeading()
|
|
||||||
{
|
|
||||||
var startingRotation = transform.eulerAngles;
|
|
||||||
var middleRotation = startingRotation + new Vector3(0f, 40f, -200f);
|
|
||||||
var endRotation = middleRotation + new Vector3(0f, 40f, 200f);
|
|
||||||
|
|
||||||
for (float i = 0; i < 1; i += Time.deltaTime / 25f)
|
|
||||||
{
|
|
||||||
var finalRotation = Vector3.Lerp(middleRotation, endRotation, EasingSmoothSquared(i));
|
|
||||||
transform.eulerAngles = Vector3.Lerp(startingRotation, finalRotation, EasingSmoothSquared(i));
|
|
||||||
yield return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float EasingSmoothSquared(float x)
|
|
||||||
{
|
|
||||||
return x < 0.5 ? x * x * 2 : (1 - (1 - x) * (1 - x) * 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
using SPT.Custom.Airdrops.Models;
|
|
||||||
using SPT.Custom.Airdrops.Utils;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Airdrops
|
|
||||||
{
|
|
||||||
public class AirdropsManager : MonoBehaviour
|
|
||||||
{
|
|
||||||
private AirdropPlane airdropPlane;
|
|
||||||
private AirdropBox airdropBox;
|
|
||||||
private ItemFactoryUtil factory;
|
|
||||||
public bool isFlareDrop;
|
|
||||||
private AirdropParametersModel airdropParameters;
|
|
||||||
|
|
||||||
public async void Start()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
|
|
||||||
if (gameWorld == null)
|
|
||||||
{
|
|
||||||
Destroy(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
airdropParameters = AirdropUtil.InitAirdropParams(gameWorld, isFlareDrop);
|
|
||||||
|
|
||||||
if (!airdropParameters.AirdropAvailable)
|
|
||||||
{
|
|
||||||
Destroy(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Debug.LogError("[SPT-AIRDROPS]: Unable to get config from server, airdrop won't occur");
|
|
||||||
Destroy(this);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
airdropPlane = await AirdropPlane.Init(
|
|
||||||
airdropParameters.RandomAirdropPoint,
|
|
||||||
airdropParameters.DropHeight,
|
|
||||||
airdropParameters.Config.PlaneVolume,
|
|
||||||
airdropParameters.Config.PlaneSpeed);
|
|
||||||
airdropBox = await AirdropBox.Init(airdropParameters.Config.CrateFallSpeed);
|
|
||||||
factory = new ItemFactoryUtil();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Debug.LogError("[SPT-AIRDROPS]: Unable to create plane or crate, airdrop won't occur");
|
|
||||||
Destroy(this);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetDistanceToDrop();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FixedUpdate()
|
|
||||||
{
|
|
||||||
if (airdropParameters == null || airdropPlane == null || airdropBox == null) return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
airdropParameters.Timer += 0.02f;
|
|
||||||
|
|
||||||
if (airdropParameters.Timer >= airdropParameters.TimeToStart && !airdropParameters.PlaneSpawned)
|
|
||||||
{
|
|
||||||
StartPlane();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!airdropParameters.PlaneSpawned)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (airdropParameters.DistanceTraveled >= airdropParameters.DistanceToDrop && !airdropParameters.BoxSpawned)
|
|
||||||
{
|
|
||||||
StartBox();
|
|
||||||
BuildLootContainer(airdropParameters.Config);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (airdropParameters.DistanceTraveled < airdropParameters.DistanceToTravel)
|
|
||||||
{
|
|
||||||
airdropParameters.DistanceTraveled += Time.deltaTime * airdropParameters.Config.PlaneSpeed;
|
|
||||||
var distanceToDrop = airdropParameters.DistanceToDrop - airdropParameters.DistanceTraveled;
|
|
||||||
airdropPlane.ManualUpdate(distanceToDrop);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Destroy(airdropPlane.gameObject);
|
|
||||||
Destroy(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Debug.LogError("[SPT-AIRDROPS]: An error occurred during the airdrop FixedUpdate process");
|
|
||||||
Destroy(airdropBox.gameObject);
|
|
||||||
Destroy(airdropPlane.gameObject);
|
|
||||||
Destroy(this);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartPlane()
|
|
||||||
{
|
|
||||||
airdropPlane.gameObject.SetActive(true);
|
|
||||||
airdropParameters.PlaneSpawned = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartBox()
|
|
||||||
{
|
|
||||||
airdropParameters.BoxSpawned = true;
|
|
||||||
var pointPos = airdropParameters.RandomAirdropPoint;
|
|
||||||
var dropPos = new Vector3(pointPos.x, airdropParameters.DropHeight, pointPos.z);
|
|
||||||
airdropBox.gameObject.SetActive(true);
|
|
||||||
airdropBox.StartCoroutine(airdropBox.DropCrate(dropPos));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void BuildLootContainer(AirdropConfigModel config)
|
|
||||||
{
|
|
||||||
var lootData = factory.GetLoot();
|
|
||||||
factory.BuildContainer(airdropBox.container, config, lootData.DropType);
|
|
||||||
factory.AddLoot(airdropBox.container, lootData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetDistanceToDrop()
|
|
||||||
{
|
|
||||||
airdropParameters.DistanceToDrop = Vector3.Distance(
|
|
||||||
new Vector3(airdropParameters.RandomAirdropPoint.x, airdropParameters.DropHeight, airdropParameters.RandomAirdropPoint.z),
|
|
||||||
airdropPlane.transform.position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Airdrops.Models
|
|
||||||
{
|
|
||||||
public class AirdropConfigModel
|
|
||||||
{
|
|
||||||
[JsonProperty("airdropChancePercent")]
|
|
||||||
public AirdropChancePercent AirdropChancePercent { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("airdropMinStartTimeSeconds")]
|
|
||||||
public int AirdropMinStartTimeSeconds { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("airdropMaxStartTimeSeconds")]
|
|
||||||
public int AirdropMaxStartTimeSeconds { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("planeMinFlyHeight")]
|
|
||||||
public int PlaneMinFlyHeight { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("planeMaxFlyHeight")]
|
|
||||||
public int PlaneMaxFlyHeight { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("planeVolume")]
|
|
||||||
public float PlaneVolume { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("planeSpeed")]
|
|
||||||
public float PlaneSpeed { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("crateFallSpeed")]
|
|
||||||
public float CrateFallSpeed { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("containerIds")]
|
|
||||||
public Dictionary<string, string> ContainerIds { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AirdropChancePercent
|
|
||||||
{
|
|
||||||
[JsonProperty("bigmap")]
|
|
||||||
public int Bigmap { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("woods")]
|
|
||||||
public int Woods { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("lighthouse")]
|
|
||||||
public int Lighthouse { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("shoreline")]
|
|
||||||
public int Shoreline { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("interchange")]
|
|
||||||
public int Interchange { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("reserve")]
|
|
||||||
public int Reserve { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("tarkovStreets")]
|
|
||||||
public int TarkovStreets { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Airdrops.Models
|
|
||||||
{
|
|
||||||
public class AirdropLootResultModel
|
|
||||||
{
|
|
||||||
[JsonProperty("dropType")]
|
|
||||||
public string DropType { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("loot")]
|
|
||||||
public IEnumerable<AirdropLootModel> Loot { get;set;}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class AirdropLootModel
|
|
||||||
{
|
|
||||||
[JsonProperty("tpl")]
|
|
||||||
public string Tpl { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("isPreset")]
|
|
||||||
public bool IsPreset { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("stackCount")]
|
|
||||||
public int StackCount { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("id")]
|
|
||||||
public string ID { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
namespace SPT.Custom.Airdrops.Models
|
|
||||||
{
|
|
||||||
public class AirdropParametersModel
|
|
||||||
{
|
|
||||||
public AirdropConfigModel Config;
|
|
||||||
|
|
||||||
public bool AirdropAvailable;
|
|
||||||
public float DistanceTraveled;
|
|
||||||
public float DistanceToTravel;
|
|
||||||
public float DistanceToDrop;
|
|
||||||
public float Timer;
|
|
||||||
public bool PlaneSpawned;
|
|
||||||
public bool BoxSpawned;
|
|
||||||
|
|
||||||
public int DropHeight;
|
|
||||||
public int TimeToStart;
|
|
||||||
|
|
||||||
public UnityEngine.Vector3 RandomAirdropPoint;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.Airdrop;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Airdrops.Patches
|
|
||||||
{
|
|
||||||
public class AirdropFlarePatch : ModulePatch
|
|
||||||
{
|
|
||||||
private static readonly string[] _usableFlares = { "624c09cfbc2e27219346d955", "62389ba9a63f32501b1b4451" };
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return typeof(FlareCartridge).GetMethod(nameof(FlareCartridge.Init),
|
|
||||||
BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
private static void PatchPostfix(BulletClass flareCartridge)
|
|
||||||
{
|
|
||||||
var gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
var points = LocationScene.GetAll<AirdropPoint>().Any();
|
|
||||||
|
|
||||||
if (gameWorld != null && points && _usableFlares.Any(x => x == flareCartridge.Template._id))
|
|
||||||
{
|
|
||||||
gameWorld.gameObject.AddComponent<AirdropsManager>().isFlareDrop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.Airdrop;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Airdrops.Patches
|
|
||||||
{
|
|
||||||
public class AirdropPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return typeof(GameWorld).GetMethod(nameof(GameWorld.OnGameStarted));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
public static void PatchPostFix()
|
|
||||||
{
|
|
||||||
var gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
var points = LocationScene.GetAll<AirdropPoint>().Any();
|
|
||||||
|
|
||||||
if (gameWorld != null && points)
|
|
||||||
{
|
|
||||||
gameWorld.gameObject.AddComponent<AirdropsManager>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,133 +0,0 @@
|
|||||||
using SPT.Common.Http;
|
|
||||||
using SPT.Custom.Airdrops.Models;
|
|
||||||
using EFT;
|
|
||||||
using EFT.Airdrop;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
using Random = UnityEngine.Random;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Airdrops.Utils
|
|
||||||
{
|
|
||||||
public static class AirdropUtil
|
|
||||||
{
|
|
||||||
public static AirdropConfigModel GetConfigFromServer()
|
|
||||||
{
|
|
||||||
string json = RequestHandler.GetJson("/singleplayer/airdrop/config");
|
|
||||||
return JsonConvert.DeserializeObject<AirdropConfigModel>(json);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int ChanceToSpawn(GameWorld gameWorld, AirdropConfigModel config, bool isFlare)
|
|
||||||
{
|
|
||||||
// Flare summoned airdrops are guaranteed
|
|
||||||
if (isFlare)
|
|
||||||
{
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get players current location
|
|
||||||
string playerLocation = gameWorld.MainPlayer.Location;
|
|
||||||
|
|
||||||
int result = 0;
|
|
||||||
switch (playerLocation.ToLower())
|
|
||||||
{
|
|
||||||
case "bigmap":
|
|
||||||
{
|
|
||||||
result = config.AirdropChancePercent.Bigmap;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "interchange":
|
|
||||||
{
|
|
||||||
result = config.AirdropChancePercent.Interchange;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "rezervbase":
|
|
||||||
{
|
|
||||||
result = config.AirdropChancePercent.Reserve;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "shoreline":
|
|
||||||
{
|
|
||||||
result = config.AirdropChancePercent.Shoreline;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "woods":
|
|
||||||
{
|
|
||||||
result = config.AirdropChancePercent.Woods;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "lighthouse":
|
|
||||||
{
|
|
||||||
result = config.AirdropChancePercent.Lighthouse;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "tarkovstreets":
|
|
||||||
{
|
|
||||||
result = config.AirdropChancePercent.TarkovStreets;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
Debug.LogError($"[SPT-AIRDROPS]: Map with name {playerLocation} not handled, defaulting spawn chance to 25%");
|
|
||||||
result = 25;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool ShouldAirdropOccur(int dropChance, List<AirdropPoint> airdropPoints)
|
|
||||||
{
|
|
||||||
return airdropPoints.Count > 0 && Random.Range(0, 100) <= dropChance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AirdropParametersModel InitAirdropParams(GameWorld gameWorld, bool isFlare)
|
|
||||||
{
|
|
||||||
var serverConfig = GetConfigFromServer();
|
|
||||||
var allAirdropPoints = LocationScene.GetAll<AirdropPoint>().ToList();
|
|
||||||
var playerPosition = ((IPlayer)gameWorld.MainPlayer).Position;
|
|
||||||
var flareAirdropPoints = new List<AirdropPoint>();
|
|
||||||
var dropChance = ChanceToSpawn(gameWorld, serverConfig, isFlare);
|
|
||||||
var flareSpawnRadiusDistance = 100f;
|
|
||||||
|
|
||||||
if (isFlare && allAirdropPoints.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (AirdropPoint point in allAirdropPoints)
|
|
||||||
{
|
|
||||||
if (Vector3.Distance(playerPosition, point.transform.position) <= flareSpawnRadiusDistance)
|
|
||||||
{
|
|
||||||
flareAirdropPoints.Add(point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flareAirdropPoints.Count == 0 && isFlare)
|
|
||||||
{
|
|
||||||
Debug.LogError($"[SPT-AIRDROPS]: Airdrop called in by flare, Unable to find an airdropPoint within {flareSpawnRadiusDistance}m, defaulting to normal drop");
|
|
||||||
flareAirdropPoints.Add(allAirdropPoints.OrderBy(_ => Guid.NewGuid()).FirstOrDefault());
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AirdropParametersModel()
|
|
||||||
{
|
|
||||||
Config = serverConfig,
|
|
||||||
AirdropAvailable = ShouldAirdropOccur(dropChance, allAirdropPoints),
|
|
||||||
|
|
||||||
DistanceTraveled = 0f,
|
|
||||||
DistanceToTravel = 8000f,
|
|
||||||
Timer = 0,
|
|
||||||
PlaneSpawned = false,
|
|
||||||
BoxSpawned = false,
|
|
||||||
|
|
||||||
DropHeight = Random.Range(serverConfig.PlaneMinFlyHeight, serverConfig.PlaneMaxFlyHeight),
|
|
||||||
TimeToStart = isFlare
|
|
||||||
? 5
|
|
||||||
: Random.Range(serverConfig.AirdropMinStartTimeSeconds, serverConfig.AirdropMaxStartTimeSeconds),
|
|
||||||
|
|
||||||
RandomAirdropPoint = isFlare && allAirdropPoints.Count > 0
|
|
||||||
? flareAirdropPoints.OrderBy(_ => Guid.NewGuid()).First().transform.position
|
|
||||||
: allAirdropPoints.OrderBy(_ => Guid.NewGuid()).First().transform.position
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using UnityEngine;
|
|
||||||
using EFT.Interactive;
|
|
||||||
using EFT.InventoryLogic;
|
|
||||||
using SPT.Common.Http;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using SPT.Custom.Airdrops.Models;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Airdrops.Utils
|
|
||||||
{
|
|
||||||
public class ItemFactoryUtil
|
|
||||||
{
|
|
||||||
private readonly ItemFactory itemFactory;
|
|
||||||
|
|
||||||
public ItemFactoryUtil()
|
|
||||||
{
|
|
||||||
itemFactory = Singleton<ItemFactory>.Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void BuildContainer(LootableContainer container, AirdropConfigModel config, string dropType)
|
|
||||||
{
|
|
||||||
var containerId = config.ContainerIds[dropType];
|
|
||||||
if (itemFactory.ItemTemplates.TryGetValue(containerId, out var template))
|
|
||||||
{
|
|
||||||
Item item = itemFactory.CreateItem(containerId, template._id, null);
|
|
||||||
LootItem.CreateLootContainer(container, item, "CRATE", Singleton<GameWorld>.Instance);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.LogError($"[SPT-AIRDROPS]: unable to find template: {containerId}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AddLoot(LootableContainer container, AirdropLootResultModel lootToAdd)
|
|
||||||
{
|
|
||||||
Item actualItem;
|
|
||||||
foreach (var item in lootToAdd.Loot)
|
|
||||||
{
|
|
||||||
ResourceKey[] resources;
|
|
||||||
if (item.IsPreset)
|
|
||||||
{
|
|
||||||
actualItem = itemFactory.GetPresetItem(item.Tpl);
|
|
||||||
actualItem.SpawnedInSession = true;
|
|
||||||
actualItem.GetAllItems().ExecuteForEach(x => x.SpawnedInSession = true);
|
|
||||||
resources = actualItem.GetAllItems().Select(x => x.Template).SelectMany(x => x.AllResources).ToArray();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
actualItem = itemFactory.CreateItem(item.ID, item.Tpl, null);
|
|
||||||
actualItem.StackObjectsCount = item.StackCount;
|
|
||||||
actualItem.SpawnedInSession = true;
|
|
||||||
|
|
||||||
resources = actualItem.Template.AllResources.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
container.ItemOwner.MainStorage[0].Add(actualItem);
|
|
||||||
await Singleton<PoolManager>.Instance.LoadBundlesAndCreatePools(PoolManager.PoolsCategory.Raid, PoolManager.AssemblyType.Local, resources, JobPriority.Immediate, null, PoolManager.DefaultCancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public AirdropLootResultModel GetLoot()
|
|
||||||
{
|
|
||||||
var json = RequestHandler.GetJson("/client/location/getAirdropLoot");
|
|
||||||
var result = JsonConvert.DeserializeObject<AirdropLootResultModel> (json);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,579 +0,0 @@
|
|||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.InventoryLogic;
|
|
||||||
using EFT.UI;
|
|
||||||
using EFT.Vehicle;
|
|
||||||
using GPUInstancer;
|
|
||||||
using HarmonyLib;
|
|
||||||
using SPT.Custom.BTR.Utils;
|
|
||||||
using SPT.SinglePlayer.Utils.TraderServices;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using UnityEngine;
|
|
||||||
using Random = UnityEngine.Random;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR
|
|
||||||
{
|
|
||||||
public class BTRManager : MonoBehaviour
|
|
||||||
{
|
|
||||||
private GameWorld gameWorld;
|
|
||||||
private BotEventHandler botEventHandler;
|
|
||||||
|
|
||||||
private BotBTRService btrBotService;
|
|
||||||
private BTRControllerClass btrController;
|
|
||||||
private BTRVehicle btrServerSide;
|
|
||||||
private BTRView btrClientSide;
|
|
||||||
private BotOwner btrBotShooter;
|
|
||||||
private BTRDataPacket btrDataPacket = default;
|
|
||||||
private bool btrInitialized = false;
|
|
||||||
private bool btrBotShooterInitialized = false;
|
|
||||||
|
|
||||||
private float coverFireTime = 90f;
|
|
||||||
private Coroutine _coverFireTimerCoroutine;
|
|
||||||
|
|
||||||
private BTRSide lastInteractedBtrSide;
|
|
||||||
public BTRSide LastInteractedBtrSide => lastInteractedBtrSide;
|
|
||||||
|
|
||||||
private Coroutine _shootingTargetCoroutine;
|
|
||||||
private BTRTurretServer btrTurretServer;
|
|
||||||
private bool isTurretInDefaultRotation;
|
|
||||||
private EnemyInfo currentTarget = null;
|
|
||||||
private bool isShooting = false;
|
|
||||||
private float machineGunAimDelay = 0.4f;
|
|
||||||
private Vector2 machineGunBurstCount;
|
|
||||||
private Vector2 machineGunRecoveryTime;
|
|
||||||
private BulletClass btrMachineGunAmmo;
|
|
||||||
private Item btrMachineGunWeapon;
|
|
||||||
private Player.FirearmController firearmController;
|
|
||||||
private WeaponSoundPlayer weaponSoundPlayer;
|
|
||||||
|
|
||||||
private float originalDamageCoeff;
|
|
||||||
|
|
||||||
private async void Awake()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
if (gameWorld == null)
|
|
||||||
{
|
|
||||||
Destroy(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gameWorld.BtrController == null)
|
|
||||||
{
|
|
||||||
gameWorld.BtrController = new BTRControllerClass();
|
|
||||||
}
|
|
||||||
|
|
||||||
btrController = gameWorld.BtrController;
|
|
||||||
|
|
||||||
await InitBtr();
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
ConsoleScreen.LogError("[SPT-BTR] Unable to spawn BTR. Check logs.");
|
|
||||||
Destroy(this);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnPlayerInteractDoor(PlayerInteractPacket interactPacket)
|
|
||||||
{
|
|
||||||
btrServerSide.LeftSlot0State = 0;
|
|
||||||
btrServerSide.LeftSlot1State = 0;
|
|
||||||
btrServerSide.RightSlot0State = 0;
|
|
||||||
btrServerSide.RightSlot1State = 0;
|
|
||||||
|
|
||||||
bool playerGoIn = interactPacket.InteractionType == EInteractionType.GoIn;
|
|
||||||
|
|
||||||
if (interactPacket.SideId == 0 && playerGoIn)
|
|
||||||
{
|
|
||||||
if (interactPacket.SlotId == 0)
|
|
||||||
{
|
|
||||||
btrServerSide.LeftSlot0State = 1;
|
|
||||||
}
|
|
||||||
else if (interactPacket.SlotId == 1)
|
|
||||||
{
|
|
||||||
btrServerSide.LeftSlot1State = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (interactPacket.SideId == 1 && playerGoIn)
|
|
||||||
{
|
|
||||||
if (interactPacket.SlotId == 0)
|
|
||||||
{
|
|
||||||
btrServerSide.RightSlot0State = 1;
|
|
||||||
}
|
|
||||||
else if (interactPacket.SlotId == 1)
|
|
||||||
{
|
|
||||||
btrServerSide.RightSlot1State = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the player is going into the BTR, store their damage coefficient
|
|
||||||
// and set it to 0, so they don't die while inside the BTR
|
|
||||||
if (interactPacket.InteractionType == EInteractionType.GoIn)
|
|
||||||
{
|
|
||||||
originalDamageCoeff = gameWorld.MainPlayer.ActiveHealthController.DamageCoeff;
|
|
||||||
gameWorld.MainPlayer.ActiveHealthController.SetDamageCoeff(0f);
|
|
||||||
|
|
||||||
}
|
|
||||||
// Otherwise restore the damage coefficient
|
|
||||||
else if (interactPacket.InteractionType == EInteractionType.GoOut)
|
|
||||||
{
|
|
||||||
gameWorld.MainPlayer.ActiveHealthController.SetDamageCoeff(originalDamageCoeff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
if (!btrInitialized) return;
|
|
||||||
|
|
||||||
btrController.SyncBTRVehicleFromServer(UpdateDataPacket());
|
|
||||||
|
|
||||||
if (btrController.BotShooterBtr == null) return;
|
|
||||||
|
|
||||||
// BotShooterBtr doesn't get assigned to BtrController immediately so we check this in Update
|
|
||||||
if (!btrBotShooterInitialized)
|
|
||||||
{
|
|
||||||
InitBtrBotService();
|
|
||||||
btrBotShooterInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateTarget();
|
|
||||||
|
|
||||||
if (HasTarget())
|
|
||||||
{
|
|
||||||
SetAim();
|
|
||||||
|
|
||||||
if (!isShooting && CanShoot())
|
|
||||||
{
|
|
||||||
StartShooting();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!isTurretInDefaultRotation)
|
|
||||||
{
|
|
||||||
btrTurretServer.DisableAiming();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task InitBtr()
|
|
||||||
{
|
|
||||||
// Initial setup
|
|
||||||
await btrController.InitBtrController();
|
|
||||||
|
|
||||||
botEventHandler = Singleton<BotEventHandler>.Instance;
|
|
||||||
var botsController = Singleton<IBotGame>.Instance.BotsController;
|
|
||||||
btrBotService = botsController.BotTradersServices.BTRServices;
|
|
||||||
btrController.method_3(); // 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;
|
|
||||||
btrClientSide = btrController.BtrView;
|
|
||||||
btrServerSide.transform.Find("KillBox").gameObject.AddComponent<BTRRoadKillTrigger>();
|
|
||||||
|
|
||||||
// Get config from server and initialise respective settings
|
|
||||||
ConfigureSettingsFromServer();
|
|
||||||
|
|
||||||
var btrMapConfig = btrController.MapPathsConfiguration;
|
|
||||||
if (btrMapConfig == null)
|
|
||||||
{
|
|
||||||
ConsoleScreen.LogError($"{nameof(btrController.MapPathsConfiguration)}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
btrServerSide.CurrentPathConfig = btrMapConfig.PathsConfiguration.pathsConfigurations.RandomElement();
|
|
||||||
btrServerSide.Initialization(btrMapConfig);
|
|
||||||
btrController.method_19(); // creates and assigns the BTR a fake stash
|
|
||||||
|
|
||||||
DisableServerSideObjects();
|
|
||||||
|
|
||||||
gameWorld.MainPlayer.OnBtrStateChanged += HandleBtrDoorState;
|
|
||||||
|
|
||||||
btrServerSide.MoveEnable();
|
|
||||||
btrServerSide.IncomingToDestinationEvent += ToDestinationEvent;
|
|
||||||
|
|
||||||
// Sync initial position and rotation
|
|
||||||
UpdateDataPacket();
|
|
||||||
btrClientSide.transform.position = btrDataPacket.position;
|
|
||||||
btrClientSide.transform.rotation = btrDataPacket.rotation;
|
|
||||||
|
|
||||||
// Initialise turret variables
|
|
||||||
btrTurretServer = btrServerSide.BTRTurret;
|
|
||||||
var btrTurretDefaultTargetTransform = (Transform)AccessTools.Field(btrTurretServer.GetType(), "defaultTargetTransform").GetValue(btrTurretServer);
|
|
||||||
isTurretInDefaultRotation = btrTurretServer.targetTransform == btrTurretDefaultTargetTransform
|
|
||||||
&& btrTurretServer.targetPosition == btrTurretServer.defaultAimingPosition;
|
|
||||||
btrMachineGunAmmo = (BulletClass)BTRUtil.CreateItem(BTRUtil.BTRMachineGunAmmoTplId);
|
|
||||||
btrMachineGunWeapon = BTRUtil.CreateItem(BTRUtil.BTRMachineGunWeaponTplId);
|
|
||||||
|
|
||||||
// Pull services data for the BTR from the server
|
|
||||||
TraderServicesManager.Instance.GetTraderServicesDataFromServer(BTRUtil.BTRTraderId);
|
|
||||||
|
|
||||||
btrInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConfigureSettingsFromServer()
|
|
||||||
{
|
|
||||||
var serverConfig = BTRUtil.GetConfigFromServer();
|
|
||||||
|
|
||||||
btrServerSide.moveSpeed = serverConfig.MoveSpeed;
|
|
||||||
btrServerSide.pauseDurationRange.x = serverConfig.PointWaitTime.Min;
|
|
||||||
btrServerSide.pauseDurationRange.y = serverConfig.PointWaitTime.Max;
|
|
||||||
btrServerSide.readyToDeparture = serverConfig.TaxiWaitTime;
|
|
||||||
coverFireTime = serverConfig.CoverFireTime;
|
|
||||||
machineGunAimDelay = serverConfig.MachineGunAimDelay;
|
|
||||||
machineGunBurstCount = new Vector2(serverConfig.MachineGunBurstCount.Min, serverConfig.MachineGunBurstCount.Max);
|
|
||||||
machineGunRecoveryTime = new Vector2(serverConfig.MachineGunRecoveryTime.Min, serverConfig.MachineGunRecoveryTime.Max);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InitBtrBotService()
|
|
||||||
{
|
|
||||||
btrBotShooter = btrController.BotShooterBtr;
|
|
||||||
btrBotShooter.GetPlayer.GetComponent<Rigidbody>().detectCollisions = false; // disable rigidbody collisions with BTR bot
|
|
||||||
firearmController = btrBotShooter.GetComponent<Player.FirearmController>();
|
|
||||||
var weaponPrefab = (WeaponPrefab)AccessTools.Field(firearmController.GetType(), "weaponPrefab_0").GetValue(firearmController);
|
|
||||||
weaponSoundPlayer = weaponPrefab.GetComponent<WeaponSoundPlayer>();
|
|
||||||
|
|
||||||
btrBotService.Reset(); // Player will be added to Neutrals list and removed from Enemies list
|
|
||||||
TraderServicesManager.Instance.OnTraderServicePurchased += BtrTraderServicePurchased;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BTR has arrived at a destination, re-calculate taxi prices and remove purchased taxi service
|
|
||||||
*/
|
|
||||||
private void ToDestinationEvent(PathDestination destinationPoint, bool isFirst, bool isFinal, bool isLastRoutePoint)
|
|
||||||
{
|
|
||||||
// Remove purchased taxi service
|
|
||||||
TraderServicesManager.Instance.RemovePurchasedService(ETraderServiceType.PlayerTaxi, BTRUtil.BTRTraderId);
|
|
||||||
|
|
||||||
// Update the prices for the taxi service
|
|
||||||
btrController.UpdateTaxiPrice(destinationPoint, isFinal);
|
|
||||||
|
|
||||||
// Update the UI
|
|
||||||
TraderServicesManager.Instance.GetTraderServicesDataFromServer(BTRUtil.BTRTraderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsBtrService(ETraderServiceType serviceType)
|
|
||||||
{
|
|
||||||
return serviceType == ETraderServiceType.BtrItemsDelivery
|
|
||||||
|| serviceType == ETraderServiceType.PlayerTaxi
|
|
||||||
|| serviceType == ETraderServiceType.BtrBotCover;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void BtrTraderServicePurchased(ETraderServiceType serviceType, string subserviceId)
|
|
||||||
{
|
|
||||||
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_11(playersToNotify, serviceType); // notify BTR passengers that a service has been purchased
|
|
||||||
|
|
||||||
switch (serviceType)
|
|
||||||
{
|
|
||||||
case ETraderServiceType.BtrBotCover:
|
|
||||||
botEventHandler.ApplyTraderServiceBtrSupport(passengers);
|
|
||||||
StartCoverFireTimer(coverFireTime);
|
|
||||||
break;
|
|
||||||
case ETraderServiceType.PlayerTaxi:
|
|
||||||
btrController.BtrVehicle.IsPaid = true;
|
|
||||||
btrController.BtrVehicle.MoveToDestination(subserviceId.Split('/')[1]); // TODO: Look into fixing the main cause of this issue.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartCoverFireTimer(float time)
|
|
||||||
{
|
|
||||||
_coverFireTimerCoroutine = StaticManager.BeginCoroutine(CoverFireTimer(time));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerator CoverFireTimer(float time)
|
|
||||||
{
|
|
||||||
yield return new WaitForSeconds(time);
|
|
||||||
botEventHandler.StopTraderServiceBtrSupport();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleBtrDoorState(EPlayerBtrState playerBtrState)
|
|
||||||
{
|
|
||||||
if (playerBtrState == EPlayerBtrState.GoIn || playerBtrState == EPlayerBtrState.GoOut)
|
|
||||||
{
|
|
||||||
// Open Door
|
|
||||||
UpdateBTRSideDoorState(1);
|
|
||||||
}
|
|
||||||
else if (playerBtrState == EPlayerBtrState.Inside || playerBtrState == EPlayerBtrState.Outside)
|
|
||||||
{
|
|
||||||
// Close Door
|
|
||||||
UpdateBTRSideDoorState(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateBTRSideDoorState(byte state)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var player = gameWorld.MainPlayer;
|
|
||||||
|
|
||||||
BTRSide btrSide = player.BtrInteractionSide != null
|
|
||||||
? player.BtrInteractionSide
|
|
||||||
: lastInteractedBtrSide;
|
|
||||||
byte sideId = btrClientSide.GetSideId(btrSide);
|
|
||||||
switch (sideId)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
btrServerSide.LeftSideState = state;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
btrServerSide.RightSideState = state;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastInteractedBtrSide = player.BtrInteractionSide;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
ConsoleScreen.LogError($"[SPT-BTR] {nameof(lastInteractedBtrSide)} is null when it shouldn't be. Check logs.");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private BTRDataPacket UpdateDataPacket()
|
|
||||||
{
|
|
||||||
btrDataPacket.position = btrServerSide.transform.position;
|
|
||||||
btrDataPacket.rotation = btrServerSide.transform.rotation;
|
|
||||||
if (btrTurretServer != null && btrTurretServer.gunsBlockRoot != null)
|
|
||||||
{
|
|
||||||
btrDataPacket.turretRotation = btrTurretServer.transform.localEulerAngles.y;
|
|
||||||
btrDataPacket.gunsBlockRotation = btrTurretServer.gunsBlockRoot.localEulerAngles.x;
|
|
||||||
}
|
|
||||||
btrDataPacket.State = (byte)btrServerSide.BtrState;
|
|
||||||
btrDataPacket.RouteState = (byte)btrServerSide.VehicleRouteState;
|
|
||||||
btrDataPacket.LeftSideState = btrServerSide.LeftSideState;
|
|
||||||
btrDataPacket.LeftSlot0State = btrServerSide.LeftSlot0State;
|
|
||||||
btrDataPacket.LeftSlot1State = btrServerSide.LeftSlot1State;
|
|
||||||
btrDataPacket.RightSideState = btrServerSide.RightSideState;
|
|
||||||
btrDataPacket.RightSlot0State = btrServerSide.RightSlot0State;
|
|
||||||
btrDataPacket.RightSlot1State = btrServerSide.RightSlot1State;
|
|
||||||
btrDataPacket.currentSpeed = btrServerSide.currentSpeed;
|
|
||||||
btrDataPacket.timeToEndPause = btrServerSide.timeToEndPause;
|
|
||||||
btrDataPacket.moveDirection = (byte)btrServerSide.VehicleMoveDirection;
|
|
||||||
btrDataPacket.MoveSpeed = btrServerSide.moveSpeed;
|
|
||||||
if (btrController != null && btrController.BotShooterBtr != null)
|
|
||||||
{
|
|
||||||
btrDataPacket.BtrBotId = btrController.BotShooterBtr.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return btrDataPacket;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DisableServerSideObjects()
|
|
||||||
{
|
|
||||||
var meshRenderers = btrServerSide.transform.GetComponentsInChildren<MeshRenderer>();
|
|
||||||
foreach (var renderer in meshRenderers)
|
|
||||||
{
|
|
||||||
renderer.enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
btrServerSide.turnCheckerObject.GetComponent<Renderer>().enabled = false; // Disables the red debug sphere
|
|
||||||
|
|
||||||
// For some reason the client BTR collider is disabled but the server collider is enabled.
|
|
||||||
// Initially we assumed there was a reason for this so it was left as is.
|
|
||||||
// Turns out disabling the server collider in favour of the client collider fixes the "BTR doing a wheelie" bug,
|
|
||||||
// while preventing the player from walking through the BTR.
|
|
||||||
// We also need to change the client collider's layer to HighPolyCollider due to unknown collisions that occur
|
|
||||||
// when going down a steep slope.
|
|
||||||
|
|
||||||
// Add collision debugger component to log collisions in the EFT Console
|
|
||||||
var clientColliders = btrClientSide.GetComponentsInChildren<Collider>(true);
|
|
||||||
//foreach (var collider in clientColliders)
|
|
||||||
//{
|
|
||||||
// collider.gameObject.AddComponent<CollisionDebugger>();
|
|
||||||
//}
|
|
||||||
|
|
||||||
var serverColliders = btrServerSide.GetComponentsInChildren<Collider>(true);
|
|
||||||
//foreach (var collider in serverColliders)
|
|
||||||
//{
|
|
||||||
// collider.gameObject.AddComponent<CollisionDebugger>();
|
|
||||||
//}
|
|
||||||
|
|
||||||
var clientRootCollider = clientColliders.First(x => x.gameObject.name == "Root");
|
|
||||||
|
|
||||||
// Retrieve all TerrainColliders
|
|
||||||
var terrainColliders = new List<TerrainCollider>();
|
|
||||||
|
|
||||||
foreach (GPUInstancerManager manager in GPUInstancerManager.activeManagerList)
|
|
||||||
{
|
|
||||||
if (manager.GetType() != typeof(GPUInstancerDetailManager))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var detailManager = (GPUInstancerDetailManager)manager;
|
|
||||||
if (detailManager.terrain == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
terrainColliders.Add(detailManager.terrain.GetComponent<TerrainCollider>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the Root collider ignore collisions with TerrainColliders
|
|
||||||
foreach (var collider in terrainColliders)
|
|
||||||
{
|
|
||||||
Physics.IgnoreCollision(clientRootCollider, collider);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve all wheel colliders on the serverside BTR
|
|
||||||
const string wheelColliderParentName = "BTR_82_wheel";
|
|
||||||
const string wheelColliderName = "Cylinder";
|
|
||||||
|
|
||||||
var serverWheelColliders = serverColliders
|
|
||||||
.Where(x => x.transform.parent.name.StartsWith(wheelColliderParentName) && x.gameObject.name.StartsWith(wheelColliderName))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
// Make the Root collider ignore collisions with the serverside BTR wheels
|
|
||||||
foreach (var collider in serverWheelColliders)
|
|
||||||
{
|
|
||||||
Physics.IgnoreCollision(clientRootCollider, collider);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable clientside BTR collider and disable serverside BTR collider
|
|
||||||
const string exteriorColliderName = "BTR_82_exterior_COLLIDER";
|
|
||||||
|
|
||||||
var serverExteriorCollider = serverColliders
|
|
||||||
.First(x => x.gameObject.name == exteriorColliderName);
|
|
||||||
var clientExteriorCollider = clientColliders
|
|
||||||
.First(x => x.gameObject.name == exteriorColliderName);
|
|
||||||
|
|
||||||
serverExteriorCollider.gameObject.SetActive(false);
|
|
||||||
clientExteriorCollider.gameObject.SetActive(true);
|
|
||||||
clientExteriorCollider.gameObject.layer = LayerMask.NameToLayer("HighPolyCollider");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateTarget()
|
|
||||||
{
|
|
||||||
currentTarget = btrBotShooter.Memory.GoalEnemy;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool HasTarget()
|
|
||||||
{
|
|
||||||
return currentTarget != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetAim()
|
|
||||||
{
|
|
||||||
if (currentTarget.IsVisible)
|
|
||||||
{
|
|
||||||
Vector3 targetPos = currentTarget.CurrPosition;
|
|
||||||
Transform targetTransform = currentTarget.Person.Transform.Original;
|
|
||||||
if (btrTurretServer.CheckPositionInAimingZone(targetPos) && btrTurretServer.targetTransform != targetTransform)
|
|
||||||
{
|
|
||||||
btrTurretServer.EnableAimingObject(targetTransform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Vector3 targetLastPos = currentTarget.EnemyLastPositionReal;
|
|
||||||
if (btrTurretServer.CheckPositionInAimingZone(targetLastPos)
|
|
||||||
&& Time.time - currentTarget.PersonalLastSeenTime < 3f
|
|
||||||
&& btrTurretServer.targetPosition != targetLastPos)
|
|
||||||
{
|
|
||||||
btrTurretServer.EnableAimingPosition(targetLastPos);
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (Time.time - currentTarget.PersonalLastSeenTime >= 3f && !isTurretInDefaultRotation)
|
|
||||||
{
|
|
||||||
btrTurretServer.DisableAiming();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CanShoot()
|
|
||||||
{
|
|
||||||
return currentTarget.IsVisible && btrBotShooter.BotBtrData.CanShoot();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartShooting()
|
|
||||||
{
|
|
||||||
_shootingTargetCoroutine = StaticManager.BeginCoroutine(ShootMachineGun());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Custom method to make the BTR coaxial machine gun shoot.
|
|
||||||
/// </summary>
|
|
||||||
private IEnumerator ShootMachineGun()
|
|
||||||
{
|
|
||||||
isShooting = true;
|
|
||||||
|
|
||||||
yield return new WaitForSeconds(machineGunAimDelay);
|
|
||||||
if (currentTarget?.Person == null || currentTarget?.IsVisible == false || !btrBotShooter.BotBtrData.CanShoot())
|
|
||||||
{
|
|
||||||
isShooting = false;
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Transform machineGunMuzzle = btrTurretServer.machineGunLaunchPoint;
|
|
||||||
var ballisticCalculator = gameWorld.SharedBallisticsCalculator;
|
|
||||||
|
|
||||||
int burstMin = Mathf.FloorToInt(machineGunBurstCount.x);
|
|
||||||
int burstMax = Mathf.FloorToInt(machineGunBurstCount.y);
|
|
||||||
int burstCount = Random.Range(burstMin, burstMax + 1);
|
|
||||||
Vector3 targetHeadPos = currentTarget.Person.PlayerBones.Head.position;
|
|
||||||
while (burstCount > 0)
|
|
||||||
{
|
|
||||||
// Only update shooting position if the target isn't null
|
|
||||||
if (currentTarget?.Person != null)
|
|
||||||
{
|
|
||||||
targetHeadPos = currentTarget.Person.PlayerBones.Head.position;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector3 aimDirection = Vector3.Normalize(targetHeadPos - machineGunMuzzle.position);
|
|
||||||
ballisticCalculator.Shoot(btrMachineGunAmmo, machineGunMuzzle.position, aimDirection, btrBotShooter.ProfileId, btrMachineGunWeapon, 1f, 0);
|
|
||||||
firearmController.PlayWeaponSound(weaponSoundPlayer, btrMachineGunAmmo, machineGunMuzzle.position, aimDirection, false);
|
|
||||||
|
|
||||||
burstCount--;
|
|
||||||
yield return new WaitForSeconds(0.092308f); // 650 RPM
|
|
||||||
}
|
|
||||||
|
|
||||||
float waitTime = Random.Range(machineGunRecoveryTime.x, machineGunRecoveryTime.y);
|
|
||||||
yield return new WaitForSeconds(waitTime);
|
|
||||||
|
|
||||||
isShooting = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
|
||||||
{
|
|
||||||
if (gameWorld == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StaticManager.KillCoroutine(ref _shootingTargetCoroutine);
|
|
||||||
StaticManager.KillCoroutine(ref _coverFireTimerCoroutine);
|
|
||||||
|
|
||||||
if (TraderServicesManager.Instance != null)
|
|
||||||
{
|
|
||||||
TraderServicesManager.Instance.OnTraderServicePurchased -= BtrTraderServicePurchased;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gameWorld.MainPlayer != null)
|
|
||||||
{
|
|
||||||
gameWorld.MainPlayer.OnBtrStateChanged -= HandleBtrDoorState;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (btrClientSide != null)
|
|
||||||
{
|
|
||||||
Debug.LogWarning($"[SPT-BTR] {nameof(BTRManager)} - Destroying btrClientSide");
|
|
||||||
Destroy(btrClientSide.gameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (btrServerSide != null)
|
|
||||||
{
|
|
||||||
Debug.LogWarning($"[SPT-BTR] {nameof(BTRManager)} - Destroying btrServerSide");
|
|
||||||
Destroy(btrServerSide.gameObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
using EFT;
|
|
||||||
using EFT.Interactive;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR
|
|
||||||
{
|
|
||||||
public class BTRRoadKillTrigger : DamageTrigger
|
|
||||||
{
|
|
||||||
public override bool IsStatic => false;
|
|
||||||
|
|
||||||
public override void AddPenalty(IPlayerOwner player)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void PlaySound()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ProceedDamage(IPlayerOwner player, BodyPartCollider bodyPart)
|
|
||||||
{
|
|
||||||
bodyPart.ApplyInstantKill(new DamageInfo()
|
|
||||||
{
|
|
||||||
Damage = 9999f,
|
|
||||||
Direction = Vector3.zero,
|
|
||||||
HitCollider = bodyPart.Collider,
|
|
||||||
HitNormal = Vector3.zero,
|
|
||||||
HitPoint = Vector3.zero,
|
|
||||||
DamageType = EDamageType.Btr,
|
|
||||||
HittedBallisticCollider = bodyPart,
|
|
||||||
Player = null
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void RemovePenalty(IPlayerOwner player)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
using EFT.UI;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR
|
|
||||||
{
|
|
||||||
public class CollisionDebugger : MonoBehaviour
|
|
||||||
{
|
|
||||||
private int _resetFrame = 10;
|
|
||||||
private int _frame = 0;
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
_frame = (_frame + 1) % _resetFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCollisionEnter(Collision collision)
|
|
||||||
{
|
|
||||||
foreach (var contact in collision.contacts)
|
|
||||||
{
|
|
||||||
ConsoleScreen.LogWarning($"Collision between {gameObject.name} and {contact.otherCollider.gameObject.name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCollisionStay(Collision collision)
|
|
||||||
{
|
|
||||||
if (_frame == 0)
|
|
||||||
{
|
|
||||||
foreach (var contact in collision.contacts)
|
|
||||||
{
|
|
||||||
ConsoleScreen.LogWarning($"Collision between {gameObject.name} and {contact.otherCollider.gameObject.name}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Models
|
|
||||||
{
|
|
||||||
public class BTRConfigModel
|
|
||||||
{
|
|
||||||
[JsonProperty("moveSpeed")]
|
|
||||||
public float MoveSpeed { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("coverFireTime")]
|
|
||||||
public float CoverFireTime { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("pointWaitTime")]
|
|
||||||
public BtrMinMaxValue PointWaitTime { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("taxiWaitTime")]
|
|
||||||
public float TaxiWaitTime { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("machineGunAimDelay")]
|
|
||||||
public float MachineGunAimDelay { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("machineGunBurstCount")]
|
|
||||||
public BtrMinMaxValue MachineGunBurstCount { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("machineGunRecoveryTime")]
|
|
||||||
public BtrMinMaxValue MachineGunRecoveryTime { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BtrMinMaxValue
|
|
||||||
{
|
|
||||||
[JsonProperty("min")]
|
|
||||||
public float Min { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("max")]
|
|
||||||
public float Max { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.UI.Screens;
|
|
||||||
using EFT.Vehicle;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using BTRDialog = EFT.UI.TraderDialogScreen.BTRDialogClass;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
public class BTRActivateTraderDialogPatch : ModulePatch
|
|
||||||
{
|
|
||||||
private static FieldInfo _playerInventoryControllerField;
|
|
||||||
private static FieldInfo _playerQuestControllerField;
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
_playerInventoryControllerField = AccessTools.Field(typeof(Player), "_inventoryController");
|
|
||||||
_playerQuestControllerField = AccessTools.Field(typeof(Player), "_questController");
|
|
||||||
|
|
||||||
var targetType = AccessTools.FirstInner(typeof(GetActionsClass), IsTargetType);
|
|
||||||
return AccessTools.Method(targetType, "method_2");
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsTargetType(Type type)
|
|
||||||
{
|
|
||||||
FieldInfo btrField = type.GetField("btr");
|
|
||||||
|
|
||||||
return btrField != null && btrField.FieldType == typeof(BTRSide);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static bool PatchPrefix()
|
|
||||||
{
|
|
||||||
var gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
var player = gameWorld.MainPlayer;
|
|
||||||
|
|
||||||
InventoryControllerClass inventoryController = _playerInventoryControllerField.GetValue(player) as InventoryControllerClass;
|
|
||||||
AbstractQuestControllerClass questController = _playerQuestControllerField.GetValue(player) as AbstractQuestControllerClass;
|
|
||||||
|
|
||||||
BTRDialog btrDialog = new BTRDialog(player.Profile, Profile.TraderInfo.TraderServiceToId[Profile.ETraderServiceSource.Btr], questController, inventoryController, null);
|
|
||||||
btrDialog.OnClose += player.UpdateInteractionCast;
|
|
||||||
btrDialog.ShowScreen(EScreenState.Queued);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.AssetsManager;
|
|
||||||
using EFT.NextObservedPlayer;
|
|
||||||
using EFT.Vehicle;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
// Fixes the BTR Bot initialization in AttachBot() of BTRTurretView
|
|
||||||
//
|
|
||||||
// Context:
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// This is essentially a full reimplementation of the BTRTurretView class, but using Player instead of ObservedPlayerView.
|
|
||||||
//
|
|
||||||
public class BTRBotAttachPatch : ModulePatch
|
|
||||||
{
|
|
||||||
private static FieldInfo _valueTuple0Field;
|
|
||||||
private static FieldInfo _gunModsToDisableField;
|
|
||||||
private static FieldInfo _weaponPrefab0Field;
|
|
||||||
private static readonly List<Renderer> rendererList = new List<Renderer>(256);
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
var targetType = typeof(BTRTurretView);
|
|
||||||
|
|
||||||
_valueTuple0Field = AccessTools.Field(targetType, "valueTuple_0");
|
|
||||||
_gunModsToDisableField = AccessTools.Field(targetType, "_gunModsToDisable");
|
|
||||||
_weaponPrefab0Field = AccessTools.Field(typeof(Player.FirearmController), "weaponPrefab_0");
|
|
||||||
|
|
||||||
return AccessTools.Method(targetType, nameof(BTRTurretView.AttachBot));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static bool PatchPrefix(BTRTurretView __instance, int btrBotId)
|
|
||||||
{
|
|
||||||
var gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
if (gameWorld == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the BTR turret
|
|
||||||
var alivePlayersList = gameWorld.AllAlivePlayersList;
|
|
||||||
Player turretPlayer = alivePlayersList.FirstOrDefault(x => x.Id == btrBotId);
|
|
||||||
if (turretPlayer == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init the turret view
|
|
||||||
var valueTuple = (ValueTuple<ObservedPlayerView, bool>)_valueTuple0Field.GetValue(__instance);
|
|
||||||
if (!valueTuple.Item2 && !InitTurretView(__instance, turretPlayer))
|
|
||||||
{
|
|
||||||
Logger.LogError("[SPT-BTR] BTRBotAttachPatch - BtrBot initialization failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
WeaponPrefab weaponPrefab;
|
|
||||||
Transform transform;
|
|
||||||
if (FindTurretObjects(turretPlayer, out weaponPrefab, out transform))
|
|
||||||
{
|
|
||||||
weaponPrefab.transform.SetPositionAndRotation(__instance.GunRoot.position, __instance.GunRoot.rotation);
|
|
||||||
transform.SetPositionAndRotation(__instance.GunRoot.position, __instance.GunRoot.rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool InitTurretView(BTRTurretView btrTurretView, Player turretPlayer)
|
|
||||||
{
|
|
||||||
EnableTurretObjects(btrTurretView, turretPlayer, false);
|
|
||||||
|
|
||||||
// We only use this for tracking whether the turret is initialized, so we don't need to set the ObservedPlayerView
|
|
||||||
_valueTuple0Field.SetValue(btrTurretView, new ValueTuple<ObservedPlayerView, bool>(null, true));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void EnableTurretObjects(BTRTurretView btrTurretView, Player player, bool enable)
|
|
||||||
{
|
|
||||||
// Find the turret weapon transform
|
|
||||||
WeaponPrefab weaponPrefab;
|
|
||||||
Transform weaponTransform;
|
|
||||||
if (!FindTurretObjects(player, out weaponPrefab, out weaponTransform))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide the turret bot
|
|
||||||
SetVisible(player, weaponPrefab, false);
|
|
||||||
|
|
||||||
// Disable the components we need to disaable
|
|
||||||
var _gunModsToDisable = (string[])_gunModsToDisableField.GetValue(btrTurretView);
|
|
||||||
foreach (Transform child in weaponTransform)
|
|
||||||
{
|
|
||||||
if (_gunModsToDisable.Contains(child.name))
|
|
||||||
{
|
|
||||||
child.gameObject.SetActive(enable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool FindTurretObjects(Player player, out WeaponPrefab weaponPrefab, out Transform weapon)
|
|
||||||
{
|
|
||||||
// Find the WeaponPrefab and Transform of the turret weapon
|
|
||||||
var aiFirearmController = player.gameObject.GetComponent<Player.FirearmController>();
|
|
||||||
weaponPrefab = (WeaponPrefab)_weaponPrefab0Field.GetValue(aiFirearmController);
|
|
||||||
|
|
||||||
if (weaponPrefab == null)
|
|
||||||
{
|
|
||||||
weapon = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
weapon = weaponPrefab.Hierarchy.GetTransform(ECharacterWeaponBones.weapon);
|
|
||||||
return weapon != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A re-implementation of the ObservedPlayerController.Culling.Mode setter that works for a Player object
|
|
||||||
*/
|
|
||||||
private static void SetVisible(Player player, WeaponPrefab weaponPrefab, bool isVisible)
|
|
||||||
{
|
|
||||||
// Toggle any animators and colliders
|
|
||||||
if (player.HealthController.IsAlive)
|
|
||||||
{
|
|
||||||
IAnimator bodyAnimatorCommon = player.GetBodyAnimatorCommon();
|
|
||||||
if (bodyAnimatorCommon.enabled != isVisible)
|
|
||||||
{
|
|
||||||
bool flag = !bodyAnimatorCommon.enabled;
|
|
||||||
bodyAnimatorCommon.enabled = isVisible;
|
|
||||||
FirearmsAnimator firearmsAnimator = player.HandsController.FirearmsAnimator;
|
|
||||||
if (firearmsAnimator != null && firearmsAnimator.Animator.enabled != isVisible)
|
|
||||||
{
|
|
||||||
firearmsAnimator.Animator.enabled = isVisible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerPoolObject component = player.gameObject.GetComponent<PlayerPoolObject>();
|
|
||||||
foreach (Collider collider in component.Colliders)
|
|
||||||
{
|
|
||||||
if (collider.enabled != isVisible)
|
|
||||||
{
|
|
||||||
collider.enabled = isVisible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build a list of renderers for this player object and set their rendering state
|
|
||||||
rendererList.Clear();
|
|
||||||
player.PlayerBody.GetRenderersNonAlloc(rendererList);
|
|
||||||
if (weaponPrefab != null)
|
|
||||||
{
|
|
||||||
rendererList.AddRange(weaponPrefab.Renderers);
|
|
||||||
}
|
|
||||||
rendererList.ForEach(renderer => renderer.forceRenderingOff = !isVisible);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
public class BTRControllerInitPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.FirstMethod(typeof(BTRControllerClass), IsTargetMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsTargetMethod(MethodInfo method)
|
|
||||||
{
|
|
||||||
ParameterInfo[] parameters = method.GetParameters();
|
|
||||||
|
|
||||||
return method.ReturnType == typeof(Task)
|
|
||||||
&& parameters.Length == 1
|
|
||||||
&& parameters[0].ParameterType == typeof(CancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static bool PatchPrefix(ref Task __result)
|
|
||||||
{
|
|
||||||
// The BTRControllerClass constructor expects the original method to return a Task,
|
|
||||||
// as it calls another method on said Task.
|
|
||||||
__result = Task.CompletedTask;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
using Object = UnityEngine.Object;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
public class BTRDestroyAtRaidEndPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(BaseLocalGame<EftGamePlayerOwner>), nameof(BaseLocalGame<EftGamePlayerOwner>.Stop));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static void PatchPrefix()
|
|
||||||
{
|
|
||||||
var gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
if (gameWorld == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var btrManager = gameWorld.GetComponent<BTRManager>();
|
|
||||||
if (btrManager != null)
|
|
||||||
{
|
|
||||||
Logger.LogWarning("[SPT-BTR] BTRDestroyAtRaidEndPatch - Raid Ended: Destroying BTRManager");
|
|
||||||
Object.Destroy(btrManager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
using SPT.Common.Http;
|
|
||||||
using SPT.Custom.BTR.Utils;
|
|
||||||
using SPT.Reflection.Patching;
|
|
||||||
using SPT.Reflection.Utils;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using HarmonyLib;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
public class BTREndRaidItemDeliveryPatch : ModulePatch
|
|
||||||
{
|
|
||||||
private static JsonConverter[] _defaultJsonConverters;
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
var converterClass = typeof(AbstractGame).Assembly.GetTypes()
|
|
||||||
.First(t => t.GetField("Converters", BindingFlags.Static | BindingFlags.Public) != null);
|
|
||||||
_defaultJsonConverters = Traverse.Create(converterClass).Field<JsonConverter[]>("Converters").Value;
|
|
||||||
|
|
||||||
Type baseLocalGameType = PatchConstants.LocalGameType.BaseType;
|
|
||||||
return AccessTools.Method(baseLocalGameType, nameof(LocalGame.Stop));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
public static void PatchPrefix()
|
|
||||||
{
|
|
||||||
GameWorld gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
if (gameWorld == null)
|
|
||||||
{
|
|
||||||
Logger.LogError("[SPT-BTR] BTREndRaidItemDeliveryPatch - GameWorld is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var player = gameWorld.MainPlayer;
|
|
||||||
if (player == null)
|
|
||||||
{
|
|
||||||
Logger.LogError("[SPT-BTR] BTREndRaidItemDeliveryPatch - Player is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match doesn't have a BTR
|
|
||||||
if (gameWorld.BtrController == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gameWorld.BtrController.HasNonEmptyTransferContainer(player.Profile.Id))
|
|
||||||
{
|
|
||||||
Logger.LogDebug("[SPT-BTR] BTREndRaidItemDeliveryPatch - No items in transfer container");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var btrStash = gameWorld.BtrController.GetOrAddTransferContainer(player.Profile.Id);
|
|
||||||
var flatItems = Singleton<ItemFactory>.Instance.TreeToFlatItems(btrStash.Grid.Items);
|
|
||||||
|
|
||||||
RequestHandler.PutJson("/singleplayer/traderServices/itemDelivery", new
|
|
||||||
{
|
|
||||||
items = flatItems,
|
|
||||||
traderId = BTRUtil.BTRTraderId
|
|
||||||
}.ToJson(_defaultJsonConverters));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.Vehicle;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
public class BTRExtractPassengersPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(VehicleBase), nameof(VehicleBase.ExtractPassengers));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static void PatchPrefix()
|
|
||||||
{
|
|
||||||
var gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
var player = gameWorld.MainPlayer;
|
|
||||||
var btrManager = gameWorld.GetComponent<BTRManager>();
|
|
||||||
|
|
||||||
var btrSide = btrManager.LastInteractedBtrSide;
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
Logger.LogError($"[SPT-BTR] BTRExtractPassengersPatch - btrView is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
btrView.Interaction(player, interactionBtrPacket);
|
|
||||||
btrManager.OnPlayerInteractDoor(interactionBtrPacket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.GlobalEvents;
|
|
||||||
using EFT.Vehicle;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
public class BTRInteractionPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.FirstMethod(typeof(Player), IsTargetMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the "BtrInteraction" method that takes parameters
|
|
||||||
*/
|
|
||||||
private bool IsTargetMethod(MethodBase method)
|
|
||||||
{
|
|
||||||
return method.Name == nameof(Player.BtrInteraction) && method.GetParameters().Length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
private static void PatchPostfix(Player __instance, BTRSide btr, byte placeId, EInteractionType interaction)
|
|
||||||
{
|
|
||||||
var gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
var btrManager = gameWorld.GetComponent<BTRManager>();
|
|
||||||
|
|
||||||
var interactionBtrPacket = btr.GetInteractWithBtrPacket(placeId, interaction);
|
|
||||||
__instance.UpdateInteractionCast();
|
|
||||||
|
|
||||||
// Prevent player from entering BTR when blacklisted
|
|
||||||
var btrBot = gameWorld.BtrController.BotShooterBtr;
|
|
||||||
if (btrBot.BotsGroup.Enemies.ContainsKey(__instance))
|
|
||||||
{
|
|
||||||
// Notify player they are blacklisted from entering BTR
|
|
||||||
GlobalEventHandlerClass.CreateEvent<BtrNotificationInteractionMessageEvent>().Invoke(__instance.Id, EBtrInteractionStatus.Blacklisted);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interactionBtrPacket.HasInteraction)
|
|
||||||
{
|
|
||||||
BTRView btrView = gameWorld.BtrController.BtrView;
|
|
||||||
if (btrView == null)
|
|
||||||
{
|
|
||||||
Logger.LogError("[SPT-BTR] BTRInteractionPatch - btrView is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
btrView.Interaction(__instance, interactionBtrPacket);
|
|
||||||
btrManager.OnPlayerInteractDoor(interactionBtrPacket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
public class BTRIsDoorsClosedPath : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(VehicleBase), nameof(VehicleBase.IsDoorsClosed));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static bool PatchPrefix(ref bool __result)
|
|
||||||
{
|
|
||||||
var gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
if (gameWorld == null)
|
|
||||||
{
|
|
||||||
Logger.LogError("[SPT-BTR] BTRIsDoorsClosedPatch - GameWorld is null");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var serverSideBTR = gameWorld.BtrController.BtrVehicle;
|
|
||||||
if (serverSideBTR == null)
|
|
||||||
{
|
|
||||||
Logger.LogError("[SPT-BTR] BTRIsDoorsClosedPatch - serverSideBTR is null");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serverSideBTR.LeftSideState == 0 && serverSideBTR.RightSideState == 0)
|
|
||||||
{
|
|
||||||
__result = true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.UI;
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a BTRManager component to the GameWorld game object when raid starts.
|
|
||||||
/// </summary>
|
|
||||||
public class BTRPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
// Note: This may seem like a weird place to hook, but `SetTime` requires that the BtrController
|
|
||||||
// exist and be setup, so we'll use this as the entry point
|
|
||||||
return AccessTools.Method(typeof(ExtractionTimersPanel), nameof(ExtractionTimersPanel.SetTime));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static void PatchPrefix()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var btrSettings = Singleton<BackendConfigSettingsClass>.Instance.BTRSettings;
|
|
||||||
var gameWorld = Singleton<GameWorld>.Instance;
|
|
||||||
|
|
||||||
// Only run on maps that have the BTR enabled
|
|
||||||
string location = gameWorld.MainPlayer.Location;
|
|
||||||
if (!btrSettings.LocationsWithBTR.Contains(location))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gameWorld.gameObject.AddComponent<BTRManager>();
|
|
||||||
}
|
|
||||||
catch (System.Exception)
|
|
||||||
{
|
|
||||||
ConsoleScreen.LogError("[SPT-BTR] Exception thrown, check logs.");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.UI;
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Fixes an issue where in a pathConfig.once type, finding destination path points was impossible because destinationID would be prefixed with "Map/", which the pathPoints do not contain.
|
|
||||||
/// </summary>
|
|
||||||
public class BTRPathConfigMapPrefixPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.FirstMethod(typeof(BTRControllerClass), IsTargetMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsTargetMethod(MethodInfo method)
|
|
||||||
{
|
|
||||||
ParameterInfo[] parameters = method.GetParameters();
|
|
||||||
|
|
||||||
// BTRControllerClass.method_8
|
|
||||||
return method.ReturnType == typeof(int)
|
|
||||||
&& parameters.Length == 2
|
|
||||||
&& parameters[0].ParameterType == typeof(string)
|
|
||||||
&& parameters[0].Name == "destinationID"
|
|
||||||
&& parameters[1].ParameterType == typeof(int)
|
|
||||||
&& parameters[1].Name == "currentDestinationIndex";
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static void PatchPrefix(ref string destinationID)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var locationIdSlash = Singleton<GameWorld>.Instance.LocationId + "/";
|
|
||||||
|
|
||||||
if (destinationID.Contains(locationIdSlash))
|
|
||||||
{
|
|
||||||
// destinationID is in the form of "Map/pX", strip the "Map/" part.
|
|
||||||
destinationID = destinationID.Replace(locationIdSlash, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
ConsoleScreen.LogError($"[SPT-BTR] Exception in {nameof(BTRPathConfigMapPrefixPatch)}, check logs.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.Custom.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), nameof(AbstractGame.Status));
|
|
||||||
|
|
||||||
return AccessTools.Method(typeof(BTRControllerClass), nameof(BTRControllerClass.method_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static void PatchPrefix()
|
|
||||||
{
|
|
||||||
originalStatus = Singleton<AbstractGame>.Instance.Status;
|
|
||||||
_statusProperty.SetValue(Singleton<AbstractGame>.Instance, GameStatus.Starting);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
private static void PatchPostfix()
|
|
||||||
{
|
|
||||||
_statusProperty.SetValue(Singleton<AbstractGame>.Instance, originalStatus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.Vehicle;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.Custom.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.FirstMethod(typeof(BTRView), IsTargetMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsTargetMethod(MethodBase method)
|
|
||||||
{
|
|
||||||
var parameters = method.GetParameters();
|
|
||||||
|
|
||||||
return parameters.Length == 1
|
|
||||||
&& parameters[0].ParameterType == typeof(DamageInfo)
|
|
||||||
&& parameters[0].Name == "damageInfo";
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static void PatchPrefix(DamageInfo damageInfo)
|
|
||||||
{
|
|
||||||
var botEventHandler = Singleton<BotEventHandler>.Instance;
|
|
||||||
if (botEventHandler == null)
|
|
||||||
{
|
|
||||||
Logger.LogError($"[SPT-BTR] {nameof(BTRReceiveDamageInfoPatch)} - BotEventHandler is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var shotBy = (Player)damageInfo.Player.iPlayer;
|
|
||||||
if (shotBy != null)
|
|
||||||
{
|
|
||||||
botEventHandler.InterruptTraderServiceBtrSupportByBetrayer(shotBy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
using SPT.Custom.BTR.Utils;
|
|
||||||
using SPT.Reflection.Patching;
|
|
||||||
using SPT.SinglePlayer.Utils.TraderServices;
|
|
||||||
using EFT.UI;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
public class BTRTransferItemsPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
|
|
||||||
return AccessTools.Method(typeof(TransferItemsInRaidScreen), nameof(TransferItemsInRaidScreen.Close));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
private static void PatchPostfix(bool ___bool_1)
|
|
||||||
{
|
|
||||||
// Didn't extract items
|
|
||||||
if (!___bool_1)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the trader services information now that we've used this service
|
|
||||||
TraderServicesManager.Instance.GetTraderServicesDataFromServer(BTRUtil.BTRTraderId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using EFT.Vehicle;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
public class BTRTurretCanShootPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(BTRTurretServer), nameof(BTRTurretServer.method_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static bool PatchPrefix(BTRTurretServer __instance, Transform ___defaultTargetTransform)
|
|
||||||
{
|
|
||||||
bool flag = __instance.targetTransform != null && __instance.targetTransform != ___defaultTargetTransform;
|
|
||||||
bool flag2 = __instance.method_2();
|
|
||||||
bool flag3 = __instance.targetPosition != __instance.defaultAimingPosition;
|
|
||||||
|
|
||||||
var isCanShootProperty = AccessTools.DeclaredProperty(__instance.GetType(), nameof(__instance.IsCanShoot));
|
|
||||||
isCanShootProperty.SetValue(__instance, (flag || flag3) && flag2);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using EFT.Vehicle;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
public class BTRTurretDefaultAimingPositionPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(BTRTurretServer), nameof(BTRTurretServer.Start));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static bool PatchPrefix(BTRTurretServer __instance)
|
|
||||||
{
|
|
||||||
__instance.defaultAimingPosition = Vector3.zero;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using EFT.Vehicle;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Patches
|
|
||||||
{
|
|
||||||
public class BTRVehicleMovementSpeedPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(BTRVehicle), nameof(BTRVehicle.Update));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static void PatchPrefix(ref float ___float_10, float ___moveSpeed)
|
|
||||||
{
|
|
||||||
___float_10 = ___moveSpeed * Time.deltaTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
using EFT;
|
|
||||||
using EFT.Vehicle;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SPT.Custom.BTR.Utils
|
|
||||||
{
|
|
||||||
public static class BTRReflectionHelper
|
|
||||||
{
|
|
||||||
private static Type _btrControllerType = typeof(BTRControllerClass);
|
|
||||||
private static Type _firearmControllerType = typeof(Player.FirearmController);
|
|
||||||
|
|
||||||
private static MethodInfo _initBtrControllerMethod = AccessTools.GetDeclaredMethods(_btrControllerType).Single(IsInitBtrControllerMethod);
|
|
||||||
private static MethodInfo _updateTaxiPriceMethod = AccessTools.GetDeclaredMethods(_btrControllerType).Single(IsUpdateTaxiPriceMethod);
|
|
||||||
|
|
||||||
private static MethodInfo _playWeaponSoundMethod = AccessTools.GetDeclaredMethods(_firearmControllerType).Single(IsPlayWeaponSoundMethod);
|
|
||||||
|
|
||||||
public static Task InitBtrController(this BTRControllerClass controller)
|
|
||||||
{
|
|
||||||
return (Task)_initBtrControllerMethod.Invoke(controller, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void UpdateTaxiPrice(this BTRControllerClass controller, PathDestination destinationPoint, bool isFinal)
|
|
||||||
{
|
|
||||||
_updateTaxiPriceMethod.Invoke(controller, new object[] { destinationPoint, isFinal });
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void PlayWeaponSound(this Player.FirearmController controller, WeaponSoundPlayer weaponSoundPlayer, BulletClass ammo, Vector3 shotPosition, Vector3 shotDirection, bool multiShot)
|
|
||||||
{
|
|
||||||
_playWeaponSoundMethod.Invoke(controller, new object[] { weaponSoundPlayer, ammo, shotPosition, shotDirection, multiShot });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find `BTRControllerClass.method_1()`
|
|
||||||
private static bool IsInitBtrControllerMethod(MethodInfo method)
|
|
||||||
{
|
|
||||||
return method.ReturnType == typeof(Task)
|
|
||||||
&& method.GetParameters().Length == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find `BTRControllerClass.method_9(PathDestination currentDestinationPoint, bool lastRoutePoint)`
|
|
||||||
private static bool IsUpdateTaxiPriceMethod(MethodInfo method)
|
|
||||||
{
|
|
||||||
ParameterInfo[] parameters = method.GetParameters();
|
|
||||||
|
|
||||||
return parameters.Length == 2
|
|
||||||
&& parameters[0].ParameterType == typeof(PathDestination);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find `Player.FirearmController.method_54(WeaponSoundPlayer weaponSoundPlayer, BulletClass ammo, Vector3 shotPosition, Vector3 shotDirection, bool multiShot)`
|
|
||||||
private static bool IsPlayWeaponSoundMethod(MethodInfo method)
|
|
||||||
{
|
|
||||||
ParameterInfo[] parameters = method.GetParameters();
|
|
||||||
|
|
||||||
return parameters.Length == 5
|
|
||||||
&& parameters[0].ParameterType == typeof(WeaponSoundPlayer)
|
|
||||||
&& parameters[1].ParameterType == typeof(BulletClass)
|
|
||||||
&& parameters[2].ParameterType == typeof(Vector3)
|
|
||||||
&& parameters[3].ParameterType == typeof(Vector3)
|
|
||||||
&& parameters[4].ParameterType == typeof(bool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
using SPT.Common.Http;
|
|
||||||
using SPT.Custom.BTR.Models;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.InventoryLogic;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace SPT.Custom.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
|
|
||||||
|
|
||||||
/// <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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BTRConfigModel GetConfigFromServer()
|
|
||||||
{
|
|
||||||
string json = RequestHandler.GetJson("/singleplayer/btr/config");
|
|
||||||
return JsonConvert.DeserializeObject<BTRConfigModel>(json);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
//using System.Collections.Generic;
|
|
||||||
//using System.Reflection;
|
|
||||||
//using SPT.PrePatch;
|
|
||||||
//using SPT.Reflection.Patching;
|
|
||||||
//using EFT;
|
|
||||||
//using HarmonyLib;
|
|
||||||
|
|
||||||
//namespace SPT.Custom.Patches
|
|
||||||
//{
|
|
||||||
// public class AddSptBotSettingsPatch : ModulePatch
|
|
||||||
// {
|
|
||||||
// protected override MethodBase GetTargetMethod()
|
|
||||||
// {
|
|
||||||
// return AccessTools.Method(typeof(BotSettingsRepoClass), nameof(BotSettingsRepoClass.Init));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// [PatchPrefix]
|
|
||||||
// private static void PatchPrefix(ref Dictionary<WildSpawnType, BotSettingsValuesClass> ___dictionary_0)
|
|
||||||
// {
|
|
||||||
// if (___dictionary_0.ContainsKey((WildSpawnType)SPTPrePatcher.sptUsecValue))
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ___dictionary_0.Add((WildSpawnType)SPTPrePatcher.sptUsecValue, new BotSettingsValuesClass(false, false, false, EPlayerSide.Savage.ToStringNoBox()));
|
|
||||||
// ___dictionary_0.Add((WildSpawnType)SPTPrePatcher.sptBearValue, new BotSettingsValuesClass(false, false, false, EPlayerSide.Savage.ToStringNoBox()));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
@ -1,5 +1,4 @@
|
|||||||
using SPT.Common.Http;
|
using SPT.Common.Http;
|
||||||
using SPT.Custom.Airdrops.Models;
|
|
||||||
using SPT.Custom.CustomAI;
|
using SPT.Custom.CustomAI;
|
||||||
using SPT.Reflection.Patching;
|
using SPT.Reflection.Patching;
|
||||||
using Comfort.Common;
|
using Comfort.Common;
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using SPT.Reflection.Patching;
|
|
||||||
using EFT.UI;
|
|
||||||
using EFT.UI.Matchmaker;
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Patches
|
|
||||||
{
|
|
||||||
public class OfflineRaidSettingsMenuPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(RaidSettingsWindow), nameof(RaidSettingsWindow.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
private static void PatchPostfix(UiElementBlocker ____coopModeBlocker)
|
|
||||||
{
|
|
||||||
// Always disable the Coop Mode checkbox
|
|
||||||
____coopModeBlocker.SetBlock(true, "SPT will never support Co-op");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
using SPT.Common.Http;
|
|
||||||
using SPT.Common.Utils;
|
|
||||||
using SPT.Reflection.Patching;
|
|
||||||
using SPT.Custom.Models;
|
|
||||||
using EFT.UI;
|
|
||||||
using EFT.UI.Matchmaker;
|
|
||||||
using System.Reflection;
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Patches
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The purpose of this patch is to make RaidSettingsWindow not reset the values to BSG default
|
|
||||||
/// Keeping our own from InRaid.json therefore not defaulting to bosses being disabled.
|
|
||||||
/// </summary>
|
|
||||||
public class RaidSettingsWindowPatch : ModulePatch
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Target method should have ~20 .UpdateValue() calls in it
|
|
||||||
/// </summary>
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(RaidSettingsWindow), nameof(RaidSettingsWindow.method_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static bool PatchPreFix(
|
|
||||||
UpdatableToggle ____enableBosses,
|
|
||||||
UpdatableToggle ____scavWars,
|
|
||||||
UpdatableToggle ____taggedAndCursed,
|
|
||||||
DropDownBox ____aiDifficultyDropdown,
|
|
||||||
DropDownBox ____aiAmountDropdown,
|
|
||||||
UpdatableToggle ____randomWeatherToggle,
|
|
||||||
UpdatableToggle ____randomTimeToggle)
|
|
||||||
{
|
|
||||||
var json = RequestHandler.GetJson("/singleplayer/settings/raid/menu");
|
|
||||||
var settings = Json.Deserialize<DefaultRaidSettings>(json);
|
|
||||||
|
|
||||||
____enableBosses.UpdateValue(settings.BossEnabled);
|
|
||||||
____scavWars.UpdateValue(false);
|
|
||||||
____taggedAndCursed.UpdateValue(settings.TaggedAndCursed);
|
|
||||||
____aiDifficultyDropdown.UpdateValue((int)settings.AiDifficulty);
|
|
||||||
____aiAmountDropdown.UpdateValue((int)settings.AiAmount);
|
|
||||||
|
|
||||||
____randomWeatherToggle.UpdateValue(settings.RandomWeather);
|
|
||||||
____randomTimeToggle.UpdateValue(settings.RandomTime);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using EFT.UI;
|
|
||||||
using System.Reflection;
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Patches
|
|
||||||
{
|
|
||||||
public class RankPanelPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(RankPanel), nameof(RankPanel.Show));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static bool PatchPreFix(ref int rankLevel, ref int maxRank)
|
|
||||||
{
|
|
||||||
if (Singleton<GameWorld>.Instance != null)
|
|
||||||
{
|
|
||||||
Logger.LogWarning("Rank Level: " + rankLevel.ToString() + " Max Rank Level: " + maxRank.ToString());
|
|
||||||
ConsoleScreen.LogError("Rank Level: " + rankLevel.ToString() + " Max Rank Level: " + maxRank.ToString());
|
|
||||||
ConsoleScreen.LogError("Game Broke!");
|
|
||||||
Logger.LogWarning("This Shouldn't happen!! Please report this in discord");
|
|
||||||
ConsoleScreen.LogError("This Shouldn't happen!! Please report this in discord");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using EFT.UI;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using EFT;
|
|
||||||
using HarmonyLib;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace SPT.Custom.Patches
|
|
||||||
{
|
|
||||||
public class SessionIdPatch : ModulePatch
|
|
||||||
{
|
|
||||||
private static PreloaderUI _preloader;
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(BaseLocalGame<EftGamePlayerOwner>), nameof(BaseLocalGame<EftGamePlayerOwner>.method_5));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
private static void PatchPostfix()
|
|
||||||
{
|
|
||||||
if (_preloader == null)
|
|
||||||
{
|
|
||||||
_preloader = Object.FindObjectOfType<PreloaderUI>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_preloader != null)
|
|
||||||
{
|
|
||||||
var raidID = Path.GetRandomFileName().Replace(".", string.Empty).Substring(0, 6).ToUpperInvariant();
|
|
||||||
_preloader.SetSessionId(raidID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using SPT.Common;
|
using SPT.Common;
|
||||||
using SPT.Custom.Airdrops.Patches;
|
|
||||||
using SPT.Custom.BTR.Patches;
|
|
||||||
using SPT.Custom.Patches;
|
using SPT.Custom.Patches;
|
||||||
using SPT.Custom.Utils;
|
using SPT.Custom.Utils;
|
||||||
using SPT.Reflection.Utils;
|
using SPT.Reflection.Utils;
|
||||||
@ -23,31 +21,19 @@ namespace SPT.Custom
|
|||||||
// Bundle patches should always load first
|
// Bundle patches should always load first
|
||||||
new EasyAssetsPatch().Enable();
|
new EasyAssetsPatch().Enable();
|
||||||
new EasyBundlePatch().Enable();
|
new EasyBundlePatch().Enable();
|
||||||
|
|
||||||
new BossSpawnChancePatch().Enable();
|
new BossSpawnChancePatch().Enable();
|
||||||
new BotDifficultyPatch().Enable();
|
new BotDifficultyPatch().Enable();
|
||||||
new CoreDifficultyPatch().Enable();
|
new CoreDifficultyPatch().Enable();
|
||||||
new OfflineRaidMenuPatch().Enable();
|
new OfflineRaidMenuPatch().Enable();
|
||||||
|
|
||||||
// Fixed in live, no need for patch
|
|
||||||
// new RaidSettingsWindowPatch().Enable();
|
|
||||||
// new SessionIdPatch().Enable();
|
|
||||||
|
|
||||||
// Unused in PvE mode
|
|
||||||
// new OfflineRaidSettingsMenuPatch().Enable();
|
|
||||||
new VersionLabelPatch().Enable();
|
new VersionLabelPatch().Enable();
|
||||||
new IsEnemyPatch().Enable();
|
new IsEnemyPatch().Enable();
|
||||||
new BotCalledDataTryCallPatch().Enable();
|
new BotCalledDataTryCallPatch().Enable();
|
||||||
new BotCallForHelpCallBotPatch().Enable();
|
new BotCallForHelpCallBotPatch().Enable();
|
||||||
new BotOwnerDisposePatch().Enable();
|
new BotOwnerDisposePatch().Enable();
|
||||||
new LocationLootCacheBustingPatch().Enable();
|
new LocationLootCacheBustingPatch().Enable();
|
||||||
//new AddSelfAsEnemyPatch().Enable();
|
|
||||||
new CheckAndAddEnemyPatch().Enable();
|
new CheckAndAddEnemyPatch().Enable();
|
||||||
new BotSelfEnemyPatch().Enable(); // needed
|
new BotSelfEnemyPatch().Enable(); // needed
|
||||||
new AddEnemyToAllGroupsInBotZonePatch().Enable();
|
new AddEnemyToAllGroupsInBotZonePatch().Enable();
|
||||||
//new AirdropPatch().Enable();
|
|
||||||
//new AirdropFlarePatch().Enable();
|
|
||||||
//new AddSptBotSettingsPatch().Enable();
|
|
||||||
new CustomAiPatch().Enable();
|
new CustomAiPatch().Enable();
|
||||||
new AddTraitorScavsPatch().Enable();
|
new AddTraitorScavsPatch().Enable();
|
||||||
new ExitWhileLootingPatch().Enable();
|
new ExitWhileLootingPatch().Enable();
|
||||||
@ -55,26 +41,9 @@ namespace SPT.Custom
|
|||||||
new PmcFirstAidPatch().Enable();
|
new PmcFirstAidPatch().Enable();
|
||||||
new SettingsLocationPatch().Enable();
|
new SettingsLocationPatch().Enable();
|
||||||
new SetLocationIdOnRaidStartPatch().Enable();
|
new SetLocationIdOnRaidStartPatch().Enable();
|
||||||
//new RankPanelPatch().Enable();
|
|
||||||
new RagfairFeePatch().Enable();
|
new RagfairFeePatch().Enable();
|
||||||
new ScavQuestPatch().Enable();
|
new ScavQuestPatch().Enable();
|
||||||
new FixBrokenSpawnOnSandboxPatch().Enable();
|
new FixBrokenSpawnOnSandboxPatch().Enable();
|
||||||
//new BTRControllerInitPatch().Enable();
|
|
||||||
//new BTRPathLoadPatch().Enable();
|
|
||||||
//new BTRActivateTraderDialogPatch().Enable();
|
|
||||||
//new BTRInteractionPatch().Enable();
|
|
||||||
//new BTRExtractPassengersPatch().Enable();
|
|
||||||
//new BTRBotAttachPatch().Enable();
|
|
||||||
//new BTRReceiveDamageInfoPatch().Enable();
|
|
||||||
//new BTRTurretCanShootPatch().Enable();
|
|
||||||
//new BTRTurretDefaultAimingPositionPatch().Enable();
|
|
||||||
//new BTRIsDoorsClosedPath().Enable();
|
|
||||||
//new BTRPatch().Enable();
|
|
||||||
//new BTRTransferItemsPatch().Enable();
|
|
||||||
//new BTREndRaidItemDeliveryPatch().Enable();
|
|
||||||
//new BTRDestroyAtRaidEndPatch().Enable();
|
|
||||||
//new BTRVehicleMovementSpeedPatch().Enable();
|
|
||||||
//new BTRPathConfigMapPrefixPatch().Enable();
|
|
||||||
new ScavItemCheckmarkPatch().Enable();
|
new ScavItemCheckmarkPatch().Enable();
|
||||||
new ResetTraderServicesPatch().Enable();
|
new ResetTraderServicesPatch().Enable();
|
||||||
new CultistAmuletRemovalPatch().Enable();
|
new CultistAmuletRemovalPatch().Enable();
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
using SPT.Custom.BTR.Patches;
|
|
||||||
using SPT.Reflection.Patching;
|
|
||||||
using SPT.SinglePlayer.Utils.TraderServices;
|
|
||||||
using EFT;
|
|
||||||
using EFT.UI;
|
|
||||||
using HarmonyLib;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.Debugging.Patches
|
|
||||||
{
|
|
||||||
// Enable the `debug_show_dialog_screen` command, and custom `btr_deliver_items` command
|
|
||||||
internal class BTRDebugCommandPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(ConsoleScreen), nameof(ConsoleScreen.InitConsole));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
internal static void PatchPostfix()
|
|
||||||
{
|
|
||||||
ConsoleScreen.Processor.RegisterCommandGroup<TraderDialogInteractionScreenClass>();
|
|
||||||
ConsoleScreen.Processor.RegisterCommand("btr_deliver_items", new System.Action(BtrDeliverItemsCommand));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom command to force item extraction sending
|
|
||||||
public static void BtrDeliverItemsCommand()
|
|
||||||
{
|
|
||||||
BTREndRaidItemDeliveryPatch.PatchPrefix();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When running the `debug_show_dialog_screen` command, fetch the service data first, and force debug off
|
|
||||||
public class BTRDebugDataPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(TraderDialogInteractionScreenClass), nameof(TraderDialogInteractionScreenClass.ShowDialogScreen));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
internal static void PatchPrefix(Profile.ETraderServiceSource traderServiceSourceType, ref bool useDebugData)
|
|
||||||
{
|
|
||||||
useDebugData = false;
|
|
||||||
TraderServicesManager.Instance.GetTraderServicesDataFromServer(Profile.TraderInfo.TraderServiceToId[traderServiceSourceType]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -24,10 +24,6 @@ namespace SPT.Debugging
|
|||||||
// new StaticLootDumper().Enable();
|
// new StaticLootDumper().Enable();
|
||||||
// new ExfilDumper().Enable();
|
// new ExfilDumper().Enable();
|
||||||
|
|
||||||
// BTR debug command patches, can be disabled later
|
|
||||||
//new BTRDebugCommandPatch().Enable();
|
|
||||||
//new BTRDebugDataPatch().Enable();
|
|
||||||
|
|
||||||
//new PMCBotSpawnLocationPatch().Enable();
|
//new PMCBotSpawnLocationPatch().Enable();
|
||||||
new ReloadClientPatch().Enable();
|
new ReloadClientPatch().Enable();
|
||||||
}
|
}
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using SPT.Reflection.Utils;
|
|
||||||
using EFT;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.SinglePlayer.Patches.Healing
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// We need to alter Class1049.smethod_0().
|
|
||||||
/// Set the passed in ERaidMode to online, this ensures the heal screen shows.
|
|
||||||
/// It cannot be changed in the calling method as doing so causes the post-raid exp display to remain at 0
|
|
||||||
/// </summary>
|
|
||||||
public class PostRaidHealScreenPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
// Class1049.smethod_0 as of 18969
|
|
||||||
// internal static Class1049 smethod_0(GInterface29 backend, string profileId, Profile savageProfile, LocationSettingsClass.GClass1097 location, ExitStatus exitStatus, TimeSpan exitTime, RaidSettings raidSettings)
|
|
||||||
var desiredMethod = typeof(PostRaidHealthScreenClass).GetMethods(BindingFlags.Static | BindingFlags.Public).SingleCustom(IsTargetMethod);
|
|
||||||
|
|
||||||
Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}");
|
|
||||||
|
|
||||||
return desiredMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsTargetMethod(MethodInfo mi)
|
|
||||||
{
|
|
||||||
var parameters = mi.GetParameters();
|
|
||||||
return parameters.Length == 7
|
|
||||||
&& parameters[0].Name == "session"
|
|
||||||
&& parameters[1].Name == "profileId"
|
|
||||||
&& parameters[2].Name == "savageProfile"
|
|
||||||
&& parameters[3].Name == "location"
|
|
||||||
&& parameters[4].Name == "exitStatus"
|
|
||||||
&& parameters[5].Name == "exitTime"
|
|
||||||
&& parameters[6].Name == "raidSettings";
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static bool PatchPrefix(ref ERaidMode raidMode)
|
|
||||||
{
|
|
||||||
raidMode = ERaidMode.Online;
|
|
||||||
return true; // Perform original method
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using EFT;
|
|
||||||
using System.Reflection;
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
namespace SPT.SinglePlayer.Patches.MainMenu
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Force ERaidMode to online to make interface show insurance page
|
|
||||||
/// </summary>
|
|
||||||
public class InsuranceScreenPatch : ModulePatch
|
|
||||||
{
|
|
||||||
static InsuranceScreenPatch()
|
|
||||||
{
|
|
||||||
_ = nameof(MainMenuController.InventoryController);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
//[CompilerGenerated]
|
|
||||||
//private void method_XX()
|
|
||||||
//{
|
|
||||||
// if (this.raidSettings_0.SelectedLocation.Id == "laboratory")
|
|
||||||
// {
|
|
||||||
// this.raidSettings_0.WavesSettings.IsBosses = true;
|
|
||||||
// }
|
|
||||||
// if (this.raidSettings_0.RaidMode == ERaidMode.Online)
|
|
||||||
// {
|
|
||||||
// this.method_40();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// this.method_41();
|
|
||||||
//}
|
|
||||||
|
|
||||||
return AccessTools.Method(typeof(MainMenuController), nameof(MainMenuController.method_76));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static void PrefixPatch(RaidSettings ___raidSettings_0)
|
|
||||||
{
|
|
||||||
___raidSettings_0.RaidMode = ERaidMode.Online;
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
private static void PostfixPatch(RaidSettings ___raidSettings_0)
|
|
||||||
{
|
|
||||||
___raidSettings_0.RaidMode = ERaidMode.Local;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using EFT.UI.SessionEnd;
|
|
||||||
using System.Reflection;
|
|
||||||
using EFT;
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
namespace SPT.SinglePlayer.Patches.Progression
|
|
||||||
{
|
|
||||||
public class ExperienceGainPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(SessionResultExperienceCount), nameof(SessionResultExperienceCount.Show), new []{ typeof(Profile), typeof(bool), typeof(ExitStatus) });
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static void PatchPrefix(ref bool isOnline)
|
|
||||||
{
|
|
||||||
isOnline = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPostfix]
|
|
||||||
private static void PatchPostfix(ref bool isOnline)
|
|
||||||
{
|
|
||||||
isOnline = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
using SPT.Common.Http;
|
|
||||||
using SPT.Reflection.Patching;
|
|
||||||
using SPT.Reflection.Utils;
|
|
||||||
using SPT.SinglePlayer.Models.Progression;
|
|
||||||
using SPT.SinglePlayer.Utils.Progression;
|
|
||||||
using Comfort.Common;
|
|
||||||
using EFT;
|
|
||||||
using HarmonyLib;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.SinglePlayer.Patches.Progression
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// After a raid, the client doesn't update the server on what occurred during the raid. To persist loot/quest changes etc we
|
|
||||||
/// make the client send the active profile to a spt-specific endpoint `/raid/profile/save` where we can update the players profile json
|
|
||||||
/// </summary>
|
|
||||||
public class OfflineSaveProfilePatch : ModulePatch
|
|
||||||
{
|
|
||||||
private static readonly JsonConverter[] _defaultJsonConverters;
|
|
||||||
|
|
||||||
static OfflineSaveProfilePatch()
|
|
||||||
{
|
|
||||||
_ = nameof(ClientMetrics.Metrics);
|
|
||||||
|
|
||||||
var converterClass = typeof(AbstractGame).Assembly.GetTypes()
|
|
||||||
.First(t => t.GetField("Converters", BindingFlags.Static | BindingFlags.Public) != null);
|
|
||||||
|
|
||||||
_defaultJsonConverters = Traverse.Create(converterClass).Field<JsonConverter[]>("Converters").Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
// method_45 - as of 16432
|
|
||||||
// method_43 - as of 18876
|
|
||||||
var desiredType = typeof(TarkovApplication);
|
|
||||||
var desiredMethod = Array.Find(desiredType.GetMethods(PatchConstants.PublicDeclaredFlags), IsTargetMethod);
|
|
||||||
|
|
||||||
Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}");
|
|
||||||
Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}");
|
|
||||||
|
|
||||||
return desiredMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsTargetMethod(MethodInfo arg)
|
|
||||||
{
|
|
||||||
var parameters = arg.GetParameters();
|
|
||||||
return parameters.Length > 4
|
|
||||||
&& parameters[0]?.Name == "profileId"
|
|
||||||
&& parameters[1]?.Name == "savageProfile"
|
|
||||||
&& parameters[2]?.Name == "location"
|
|
||||||
&& arg.ReturnType == typeof(void);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static void PatchPrefix(string profileId, RaidSettings ____raidSettings, TarkovApplication __instance, Result<ExitStatus, TimeSpan, ClientMetrics> result)
|
|
||||||
{
|
|
||||||
// Get scav or pmc profile based on IsScav value
|
|
||||||
var profile = (____raidSettings.IsScav)
|
|
||||||
? PatchConstants.BackEndSession.ProfileOfPet
|
|
||||||
: PatchConstants.BackEndSession.Profile;
|
|
||||||
|
|
||||||
SaveProfileRequest request = new SaveProfileRequest
|
|
||||||
{
|
|
||||||
Exit = result.Value0.ToString().ToLowerInvariant(), // Exit player used to leave raid
|
|
||||||
Profile = profile, // players scav or pmc profile, depending on type of raid they did
|
|
||||||
Health = Utils.Healing.HealthListener.Instance.CurrentHealth, // Specific limb/effect damage data the player has at end of raid
|
|
||||||
Insurance = Utils.Insurance.InsuredItemManager.Instance.GetTrackedItems(), // A copy of items insured by player with durability values as of raid end (if item is returned, send it back with correct durability values)
|
|
||||||
IsPlayerScav = ____raidSettings.IsScav
|
|
||||||
};
|
|
||||||
|
|
||||||
RequestHandler.PutJson("/raid/profile/save", request.ToJson(_defaultJsonConverters.AddItem(new NotesJsonConverter()).ToArray()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
using SPT.Reflection.Patching;
|
|
||||||
using SPT.Reflection.Utils;
|
|
||||||
using EFT;
|
|
||||||
using EFT.Game.Spawning;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace SPT.SinglePlayer.Patches.Progression
|
|
||||||
{
|
|
||||||
public class OfflineSpawnPointPatch : ModulePatch
|
|
||||||
{
|
|
||||||
static OfflineSpawnPointPatch()
|
|
||||||
{
|
|
||||||
_ = nameof(ISpawnPoints.CreateSpawnPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
var desiredType = typeof(SpawnSystemClass);
|
|
||||||
var desiredMethod = desiredType
|
|
||||||
.GetMethods(PatchConstants.PublicDeclaredFlags)
|
|
||||||
.First(m => m.Name.Contains("SelectSpawnPoint"));
|
|
||||||
|
|
||||||
Logger.LogDebug($"{this.GetType().Name} Type: {desiredType?.Name}");
|
|
||||||
Logger.LogDebug($"{this.GetType().Name} Method: {desiredMethod?.Name}");
|
|
||||||
|
|
||||||
return desiredMethod;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool IsTargetType(Type type)
|
|
||||||
{
|
|
||||||
// GClass1812 as of 17349
|
|
||||||
// GClass1886 as of 18876
|
|
||||||
// Remapped to SpawnSystemClass
|
|
||||||
return (type.GetMethods(PatchConstants.PublicDeclaredFlags).Any(x => x.Name.IndexOf("CheckFarthestFromOtherPlayers", StringComparison.OrdinalIgnoreCase) != -1)
|
|
||||||
&& type.IsClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static bool PatchPrefix(
|
|
||||||
ref ISpawnPoint __result,
|
|
||||||
object __instance,
|
|
||||||
ESpawnCategory category,
|
|
||||||
EPlayerSide side,
|
|
||||||
string groupId,
|
|
||||||
IPlayer person,
|
|
||||||
string infiltration)
|
|
||||||
{
|
|
||||||
var spawnPointsField = (ISpawnPoints)__instance.GetType().GetFields(PatchConstants.PublicDeclaredFlags).SingleOrDefault(f => f.FieldType == typeof(ISpawnPoints))?.GetValue(__instance);
|
|
||||||
if (spawnPointsField == null)
|
|
||||||
{
|
|
||||||
throw new Exception($"OfflineSpawnPointPatch: Failed to locate field: {nameof(ISpawnPoints)} on class instance: {__instance.GetType().Name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
var mapSpawnPoints = spawnPointsField.ToList();
|
|
||||||
var unfilteredFilteredSpawnPoints = mapSpawnPoints.ToList();
|
|
||||||
|
|
||||||
// filter by e.g. 'Boiler Tanks' (always seems to be map name?)
|
|
||||||
if (!string.IsNullOrEmpty(infiltration))
|
|
||||||
{
|
|
||||||
mapSpawnPoints = mapSpawnPoints.Where(sp => sp?.Infiltration != null && (string.IsNullOrEmpty(infiltration) || sp.Infiltration.Equals(infiltration))).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
mapSpawnPoints = FilterByPlayerSide(mapSpawnPoints, category, side);
|
|
||||||
|
|
||||||
__result = mapSpawnPoints.Count == 0
|
|
||||||
? GetFallBackSpawnPoint(unfilteredFilteredSpawnPoints, category, side, infiltration)
|
|
||||||
: mapSpawnPoints.RandomElement();
|
|
||||||
|
|
||||||
Logger.LogInfo($"Desired spawnpoint: [category:{category}] [side:{side}] [infil:{infiltration}] [{mapSpawnPoints.Count} total spawn points]");
|
|
||||||
Logger.LogInfo($"Selected SpawnPoint: [id:{__result.Id}] [name:{__result.Name}] [category:{__result.Categories}] [side:{__result.Sides}] [infil:{__result.Infiltration}]");
|
|
||||||
|
|
||||||
return false; // skip original method
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<ISpawnPoint> FilterByPlayerSide(List<ISpawnPoint> mapSpawnPoints, ESpawnCategory category, EPlayerSide side)
|
|
||||||
{
|
|
||||||
// Filter by category 'player' and by side ('usec', 'bear')
|
|
||||||
return mapSpawnPoints.Where(sp => sp.Categories.Contain(category) && sp.Sides.Contain(side)).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ISpawnPoint GetFallBackSpawnPoint(List<ISpawnPoint> spawnPoints, ESpawnCategory category, EPlayerSide side, string infiltration)
|
|
||||||
{
|
|
||||||
Logger.LogWarning($"PatchPrefix SelectSpawnPoint: Couldn't find any spawn points for: {category} | {side} | {infiltration} using random filtered spawn instead");
|
|
||||||
return spawnPoints.Where(sp => sp.Categories.Contain(ESpawnCategory.Player)).RandomElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
using SPT.Common.Http;
|
|
||||||
using SPT.Reflection.Patching;
|
|
||||||
using EFT;
|
|
||||||
using System.Reflection;
|
|
||||||
using HarmonyLib;
|
|
||||||
|
|
||||||
namespace SPT.SinglePlayer.Patches.Quests
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Having the raid timer reach zero results in a successful extract,
|
|
||||||
/// this patch makes it so letting the time reach zero results in a MIA result
|
|
||||||
/// </summary>
|
|
||||||
public class EndByTimerPatch : ModulePatch
|
|
||||||
{
|
|
||||||
protected override MethodBase GetTargetMethod()
|
|
||||||
{
|
|
||||||
return AccessTools.Method(typeof(BaseLocalGame<EftGamePlayerOwner>), nameof(BaseLocalGame<EftGamePlayerOwner>.Stop));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unused, but left here in case patch breaks and finding the intended method is difficult
|
|
||||||
private static bool IsStopRaidMethod(MethodInfo mi)
|
|
||||||
{
|
|
||||||
var parameters = mi.GetParameters();
|
|
||||||
return (parameters.Length == 4
|
|
||||||
&& parameters[0].Name == "profileId"
|
|
||||||
&& parameters[1].Name == "exitStatus"
|
|
||||||
&& parameters[2].Name == "exitName"
|
|
||||||
&& parameters[3].Name == "delay"
|
|
||||||
&& parameters[0].ParameterType == typeof(string)
|
|
||||||
&& parameters[1].ParameterType == typeof(ExitStatus)
|
|
||||||
&& parameters[2].ParameterType == typeof(string)
|
|
||||||
&& parameters[3].ParameterType == typeof(float));
|
|
||||||
}
|
|
||||||
|
|
||||||
[PatchPrefix]
|
|
||||||
private static bool PrefixPatch(ref ExitStatus exitStatus, ref string exitName)
|
|
||||||
{
|
|
||||||
var isParsed = bool.TryParse(RequestHandler.GetJson("/singleplayer/settings/raid/endstate"), out bool MIAOnRaidEnd);
|
|
||||||
if (isParsed)
|
|
||||||
{
|
|
||||||
// No extract name and successful, its a MIA
|
|
||||||
if (MIAOnRaidEnd && string.IsNullOrEmpty(exitName?.Trim()) && exitStatus == ExitStatus.Survived)
|
|
||||||
{
|
|
||||||
exitStatus = ExitStatus.MissingInAction;
|
|
||||||
exitName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true; // Do original
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -31,7 +31,6 @@ namespace SPT.SinglePlayer.Patches.ScavMode
|
|||||||
[PatchPrefix]
|
[PatchPrefix]
|
||||||
private static bool PatchPrefix(ProfileEndpointFactoryAbstractClass __instance, ref Task<IResult> __result, string playerId, string petId)
|
private static bool PatchPrefix(ProfileEndpointFactoryAbstractClass __instance, ref Task<IResult> __result, string playerId, string petId)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Build request with additional information
|
// Build request with additional information
|
||||||
OwnerInfo fromOwner = new OwnerInfo
|
OwnerInfo fromOwner = new OwnerInfo
|
||||||
{
|
{
|
||||||
|
@ -20,17 +20,16 @@ namespace SPT.SinglePlayer
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//new OfflineSaveProfilePatch().Enable();
|
// TODO: check if these patches are needed
|
||||||
//new OfflineSpawnPointPatch().Enable(); // Spawns are properly randomised and patch is likely no longer needed
|
new MidRaidQuestChangePatch().Enable();
|
||||||
//new ExperienceGainPatch().Enable();
|
new MidRaidAchievementChangePatch().Enable();
|
||||||
|
new ScavSellAllPriceStorePatch().Enable();
|
||||||
|
new ScavSellAllRequestPatch().Enable();
|
||||||
|
|
||||||
new ScavExperienceGainPatch().Enable();
|
new ScavExperienceGainPatch().Enable();
|
||||||
new MainMenuControllerPatch().Enable();
|
new MainMenuControllerPatch().Enable();
|
||||||
new PlayerPatch().Enable();
|
new PlayerPatch().Enable();
|
||||||
new DisableReadyLocationReadyPatch().Enable();
|
new DisableReadyLocationReadyPatch().Enable();
|
||||||
|
|
||||||
// No longer required with PVE offline mode
|
|
||||||
// new InsuranceScreenPatch().Enable();
|
|
||||||
|
|
||||||
new BotTemplateLimitPatch().Enable();
|
new BotTemplateLimitPatch().Enable();
|
||||||
new GetNewBotTemplatesPatch().Enable();
|
new GetNewBotTemplatesPatch().Enable();
|
||||||
new RemoveUsedBotProfilePatch().Enable();
|
new RemoveUsedBotProfilePatch().Enable();
|
||||||
@ -44,11 +43,8 @@ namespace SPT.SinglePlayer
|
|||||||
new MaxBotPatch().Enable();
|
new MaxBotPatch().Enable();
|
||||||
new SpawnPmcPatch().Enable();
|
new SpawnPmcPatch().Enable();
|
||||||
new PostRaidHealingPricePatch().Enable();
|
new PostRaidHealingPricePatch().Enable();
|
||||||
//new EndByTimerPatch().Enable();
|
|
||||||
new InRaidQuestAvailablePatch().Enable();
|
new InRaidQuestAvailablePatch().Enable();
|
||||||
// new PostRaidHealScreenPatch().Enable(); // TODO: Temp disabled, this might not be needed
|
|
||||||
new VoIPTogglerPatch().Enable();
|
new VoIPTogglerPatch().Enable();
|
||||||
new MidRaidQuestChangePatch().Enable();
|
|
||||||
new HealthControllerPatch().Enable();
|
new HealthControllerPatch().Enable();
|
||||||
new LighthouseBridgePatch().Enable();
|
new LighthouseBridgePatch().Enable();
|
||||||
new LighthouseTransmitterPatch().Enable();
|
new LighthouseTransmitterPatch().Enable();
|
||||||
@ -61,11 +57,8 @@ namespace SPT.SinglePlayer
|
|||||||
new MapReadyButtonPatch().Enable();
|
new MapReadyButtonPatch().Enable();
|
||||||
new LabsKeycardRemovalPatch().Enable();
|
new LabsKeycardRemovalPatch().Enable();
|
||||||
new ScavLateStartPatch().Enable();
|
new ScavLateStartPatch().Enable();
|
||||||
new MidRaidAchievementChangePatch().Enable();
|
|
||||||
new GetTraderServicesPatch().Enable();
|
new GetTraderServicesPatch().Enable();
|
||||||
new PurchaseTraderServicePatch().Enable();
|
new PurchaseTraderServicePatch().Enable();
|
||||||
new ScavSellAllPriceStorePatch().Enable();
|
|
||||||
new ScavSellAllRequestPatch().Enable();
|
|
||||||
new HideoutQuestIgnorePatch().Enable();
|
new HideoutQuestIgnorePatch().Enable();
|
||||||
new LightKeeperServicesPatch().Enable();
|
new LightKeeperServicesPatch().Enable();
|
||||||
new ScavEncyclopediaPatch().Enable();
|
new ScavEncyclopediaPatch().Enable();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user