using Aki.Reflection.Patching; using BepInEx; using BepInEx.Configuration; using EFT; using EFT.Animations; using EFT.InventoryLogic; using HarmonyLib; using RootMotion; using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using System.Security.Cryptography; using TrackIRUnity; using UnityEngine; namespace JBTrackIR; [BepInPlugin("com.jonbons.trackir", "JonBons.TrackIR", "1.0.2")] public class Plugin : BaseUnityPlugin { static ConfigEntry tirRefresh; static ConfigEntry tirEnabled; static ConfigEntry tirSensitivityCoef; static ConfigEntry tirLimitPitchLower; static ConfigEntry tirLimitPitchUpper; static ConfigEntry tirLimitYawLower; static ConfigEntry tirLimitYawUpper; static TrackIRClient tirClient; static bool tirRunning = false; private void Awake() { // Plugin startup logic Logger.LogInfo($"Plugin com.jonbons.trackir is loaded!"); tirClient = new TrackIRClient(); if (tirClient != null && !tirRunning) { var result = tirClient.TrackIR_Enhanced_Init(); Logger.LogInfo($"com.jonbons.trackir: detecting... {result}"); if (!string.IsNullOrEmpty(result)) { tirRunning = true; Logger.LogInfo($"com.jonbons.trackir: detected"); } else { Logger.LogInfo($"com.jonbons.trackir: not detected"); } } BindSettings(); new Transpiler().Enable(); } private void Update() { if (tirRefresh.Value) { tirRefresh.BoxedValue = false; tirRefresh.Value = false; if (tirRunning) tirClient.TrackIR_Shutdown(); var result = tirClient.TrackIR_Enhanced_Init(); if (!string.IsNullOrEmpty(result)) { tirRunning = true; Logger.LogInfo($"com.jonbons.trackir: detected"); } else { Logger.LogInfo($"com.jonbons.trackir: not detected"); } } } private void OnDestroy() { if (tirClient != null && tirRunning) { tirClient.TrackIR_Shutdown(); tirRunning = false; } } private void BindSettings() { tirRefresh = Config.Bind( "Main Settings", "Refresh TrackIR", true, new ConfigDescription("Refresh TrackIR detection") ); tirEnabled = Config.Bind( "Main Settings", "TrackIR Enabled", true, new ConfigDescription("Enable TrackIR support") ); tirSensitivityCoef = Config.Bind( "Main Settings", "TrackIR Sensitivity coef", 0.5f, new ConfigDescription("Senstivity coefficient to apply to all TrackIR inputs", new AcceptableValueRange(0, 1)) ); tirLimitPitchLower = Config.Bind( "Main Settings", "TrackIR Pitch lower limit", -85, new ConfigDescription("Lower limit of TrackIR pitch angles", new AcceptableValueRange(-180, 180)) ); tirLimitPitchUpper = Config.Bind( "Main Settings", "TrackIR Pitch upper limit", 85, new ConfigDescription("Upper limit of TrackIR pitch angles", new AcceptableValueRange(-180, 180)) ); tirLimitYawLower = Config.Bind( "Main Settings", "TrackIR Yaw lower limit", -150, new ConfigDescription("Lower limit of TrackIR yaw angles", new AcceptableValueRange(-180, 180)) ); tirLimitYawUpper = Config.Bind( "Main Settings", "TrackIR Yaw upper limit", 150, new ConfigDescription("Upper limit of TrackIR yaw angles", new AcceptableValueRange(-180, 180)) ); } [Serializable] public class Limit { public Limit() { lower = -180; upper = 180; } public Limit(float low, float up) { lower = low; upper = up; } public float lower, upper; } public class Transpiler : ModulePatch { protected override MethodBase GetTargetMethod() { return typeof(Player).GetMethod("Look", BindingFlags.Instance | BindingFlags.Public); } [PatchPostfix] private static void PatchPostfix(ref Player __instance) { if (!tirRunning) return; if (!tirEnabled.Value) return; float positionReductionFactor = 0.045f * tirSensitivityCoef.Value; float rotationReductionFactor = 0.045f * tirSensitivityCoef.Value; Limit positionXLimits = new Limit(); Limit positionYLimits = new Limit(); Limit positionZLimits = new Limit(); Limit pitchLimits = new Limit(tirLimitPitchLower.Value, tirLimitPitchUpper.Value); Limit yawLimits = new Limit(tirLimitYawLower.Value, tirLimitYawUpper.Value); Limit rollLimits = new Limit(-100, 100); bool useLimits = true; //Logger.LogInfo(string.Format("TIR DATA Start")); TrackIRClient tirClient = Plugin.tirClient; TrackIRClient.LPTRACKIRDATA tid = tirClient.client_HandleTrackIRData(); // Data for head tracking //Logger.LogInfo(string.Format("TIR DATA Pitch = {0}; Yaw = {1}, Roll = {2}", tid.fNPPitch, tid.fNPYaw, tid.fNPRoll)); Vector3 rot = __instance.ProceduralWeaponAnimation.HandsContainer.CameraTransform.localRotation.eulerAngles; rot.z = 0; // we don't need to use the existing Z value if (!useLimits) { rot.y = tid.fNPYaw * rotationReductionFactor; rot.x = tid.fNPPitch * rotationReductionFactor; //rot.z = -tid.fNPRoll * rotationReductionFactor; } else { rot.y = Mathf.Clamp(tid.fNPYaw * rotationReductionFactor, yawLimits.lower, yawLimits.upper); rot.x = Mathf.Clamp(tid.fNPPitch * rotationReductionFactor, pitchLimits.lower, pitchLimits.upper); //rot.z = Mathf.Clamp(-tid.fNPRoll * rotationReductionFactor, rollLimits.lower, rollLimits.upper); } __instance.HeadRotation = rot; // sync head angles __instance.ProceduralWeaponAnimation.SetHeadRotation(rot); //Logger.LogInfo(string.Format("TIR DATA Final pos = {0}; Final rot = {1}", pos, rot)); } } }