mirror of
https://github.com/sp-tarkov/modules.git
synced 2025-02-13 01:10: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.Custom.Airdrops.Models;
|
||||
using SPT.Custom.CustomAI;
|
||||
using SPT.Reflection.Patching;
|
||||
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 SPT.Common;
|
||||
using SPT.Custom.Airdrops.Patches;
|
||||
using SPT.Custom.BTR.Patches;
|
||||
using SPT.Custom.Patches;
|
||||
using SPT.Custom.Utils;
|
||||
using SPT.Reflection.Utils;
|
||||
@ -23,31 +21,19 @@ namespace SPT.Custom
|
||||
// Bundle patches should always load first
|
||||
new EasyAssetsPatch().Enable();
|
||||
new EasyBundlePatch().Enable();
|
||||
|
||||
new BossSpawnChancePatch().Enable();
|
||||
new BotDifficultyPatch().Enable();
|
||||
new CoreDifficultyPatch().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 IsEnemyPatch().Enable();
|
||||
new BotCalledDataTryCallPatch().Enable();
|
||||
new BotCallForHelpCallBotPatch().Enable();
|
||||
new BotOwnerDisposePatch().Enable();
|
||||
new LocationLootCacheBustingPatch().Enable();
|
||||
//new AddSelfAsEnemyPatch().Enable();
|
||||
new CheckAndAddEnemyPatch().Enable();
|
||||
new BotSelfEnemyPatch().Enable(); // needed
|
||||
new AddEnemyToAllGroupsInBotZonePatch().Enable();
|
||||
//new AirdropPatch().Enable();
|
||||
//new AirdropFlarePatch().Enable();
|
||||
//new AddSptBotSettingsPatch().Enable();
|
||||
new CustomAiPatch().Enable();
|
||||
new AddTraitorScavsPatch().Enable();
|
||||
new ExitWhileLootingPatch().Enable();
|
||||
@ -55,26 +41,9 @@ namespace SPT.Custom
|
||||
new PmcFirstAidPatch().Enable();
|
||||
new SettingsLocationPatch().Enable();
|
||||
new SetLocationIdOnRaidStartPatch().Enable();
|
||||
//new RankPanelPatch().Enable();
|
||||
new RagfairFeePatch().Enable();
|
||||
new ScavQuestPatch().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 ResetTraderServicesPatch().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 ExfilDumper().Enable();
|
||||
|
||||
// BTR debug command patches, can be disabled later
|
||||
//new BTRDebugCommandPatch().Enable();
|
||||
//new BTRDebugDataPatch().Enable();
|
||||
|
||||
//new PMCBotSpawnLocationPatch().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]
|
||||
private static bool PatchPrefix(ProfileEndpointFactoryAbstractClass __instance, ref Task<IResult> __result, string playerId, string petId)
|
||||
{
|
||||
|
||||
// Build request with additional information
|
||||
OwnerInfo fromOwner = new OwnerInfo
|
||||
{
|
||||
|
@ -20,17 +20,16 @@ namespace SPT.SinglePlayer
|
||||
|
||||
try
|
||||
{
|
||||
//new OfflineSaveProfilePatch().Enable();
|
||||
//new OfflineSpawnPointPatch().Enable(); // Spawns are properly randomised and patch is likely no longer needed
|
||||
//new ExperienceGainPatch().Enable();
|
||||
// TODO: check if these patches are needed
|
||||
new MidRaidQuestChangePatch().Enable();
|
||||
new MidRaidAchievementChangePatch().Enable();
|
||||
new ScavSellAllPriceStorePatch().Enable();
|
||||
new ScavSellAllRequestPatch().Enable();
|
||||
|
||||
new ScavExperienceGainPatch().Enable();
|
||||
new MainMenuControllerPatch().Enable();
|
||||
new PlayerPatch().Enable();
|
||||
new DisableReadyLocationReadyPatch().Enable();
|
||||
|
||||
// No longer required with PVE offline mode
|
||||
// new InsuranceScreenPatch().Enable();
|
||||
|
||||
new BotTemplateLimitPatch().Enable();
|
||||
new GetNewBotTemplatesPatch().Enable();
|
||||
new RemoveUsedBotProfilePatch().Enable();
|
||||
@ -44,11 +43,8 @@ namespace SPT.SinglePlayer
|
||||
new MaxBotPatch().Enable();
|
||||
new SpawnPmcPatch().Enable();
|
||||
new PostRaidHealingPricePatch().Enable();
|
||||
//new EndByTimerPatch().Enable();
|
||||
new InRaidQuestAvailablePatch().Enable();
|
||||
// new PostRaidHealScreenPatch().Enable(); // TODO: Temp disabled, this might not be needed
|
||||
new VoIPTogglerPatch().Enable();
|
||||
new MidRaidQuestChangePatch().Enable();
|
||||
new HealthControllerPatch().Enable();
|
||||
new LighthouseBridgePatch().Enable();
|
||||
new LighthouseTransmitterPatch().Enable();
|
||||
@ -61,11 +57,8 @@ namespace SPT.SinglePlayer
|
||||
new MapReadyButtonPatch().Enable();
|
||||
new LabsKeycardRemovalPatch().Enable();
|
||||
new ScavLateStartPatch().Enable();
|
||||
new MidRaidAchievementChangePatch().Enable();
|
||||
new GetTraderServicesPatch().Enable();
|
||||
new PurchaseTraderServicePatch().Enable();
|
||||
new ScavSellAllPriceStorePatch().Enable();
|
||||
new ScavSellAllRequestPatch().Enable();
|
||||
new HideoutQuestIgnorePatch().Enable();
|
||||
new LightKeeperServicesPatch().Enable();
|
||||
new ScavEncyclopediaPatch().Enable();
|
||||
|
Loading…
x
Reference in New Issue
Block a user