0
0
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:
CWX 2024-07-06 09:32:55 +01:00
parent 0c850bc8d2
commit 0bc05f80d4
49 changed files with 6 additions and 3096 deletions

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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; }
}
}

View File

@ -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; }
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View File

@ -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>();
}
}
}
}

View File

@ -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
};
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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)
{
}
}
}

View File

@ -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}");
}
}
}
}
}

View File

@ -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; }
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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));
}
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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.");
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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()));
// }
// }
//}

View File

@ -1,5 +1,4 @@
using SPT.Common.Http;
using SPT.Custom.Airdrops.Models;
using SPT.Custom.CustomAI;
using SPT.Reflection.Patching;
using Comfort.Common;

View File

@ -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");
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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();

View File

@ -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]);
}
}
}

View File

@ -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();
}

View File

@ -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
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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()));
}
}
}

View File

@ -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();
}
}
}

View File

@ -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
}
}
}

View File

@ -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
{

View File

@ -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();