diff --git a/JBTrackIR/JBTrackIR.csproj b/JBTrackIR/JBTrackIR.csproj
new file mode 100644
index 0000000..5996305
--- /dev/null
+++ b/JBTrackIR/JBTrackIR.csproj
@@ -0,0 +1,45 @@
+
+
+
+ netstandard2.0
+ JBTrackIR
+ My first plugin
+ 1.0.0
+ true
+ latest
+
+ https://api.nuget.org/v3/index.json;
+ https://nuget.bepinex.dev/v3/index.json;
+ https://nuget.samboy.dev/v3/index.json
+
+ JBTrackIR
+
+
+
+
+
+
+
+
+ ..\References\0Harmony.dll
+
+
+ ..\References\Aki.Reflection.dll
+
+
+ ..\References\Assembly-CSharp.dll
+
+
+ ..\References\BepInEx.dll
+
+
+ ..\References\TrackIRUnity.dll
+
+
+ ..\References\UnityEngine.dll
+
+
+ ..\References\UnityEngine.CoreModule.dll
+
+
+
diff --git a/JBTrackIR/JBTrackIR.sln b/JBTrackIR/JBTrackIR.sln
new file mode 100644
index 0000000..99621be
--- /dev/null
+++ b/JBTrackIR/JBTrackIR.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.5.33516.290
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JBTrackIR", "JBTrackIR.csproj", "{A4AC854D-33F9-464C-A05D-E1030F892CB1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A4AC854D-33F9-464C-A05D-E1030F892CB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A4AC854D-33F9-464C-A05D-E1030F892CB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A4AC854D-33F9-464C-A05D-E1030F892CB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A4AC854D-33F9-464C-A05D-E1030F892CB1}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C22410CD-BDAB-4AE8-87C7-E5CF5D1A0FFA}
+ EndGlobalSection
+EndGlobal
diff --git a/JBTrackIR/Plugin.cs b/JBTrackIR/Plugin.cs
new file mode 100644
index 0000000..81ca20a
--- /dev/null
+++ b/JBTrackIR/Plugin.cs
@@ -0,0 +1,174 @@
+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.0")]
+public class Plugin : BaseUnityPlugin
+{
+ 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)
+ {
+ tirClient.TrackIR_Enhanced_Init();
+ tirRunning = true;
+ Logger.LogInfo($"com.jonbons.trackir: trackir is running");
+ }
+
+ BindSettings();
+
+ new Transpiler().Enable();
+ }
+
+ private void OnDestroy()
+ {
+ if (tirClient != null && tirRunning)
+ {
+ tirClient.TrackIR_Shutdown();
+ tirRunning = false;
+ }
+ }
+
+ private void BindSettings()
+ {
+ 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 = 0;
+ upper = 360;
+ }
+ 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.ProceduralWeaponAnimation.SetHeadRotation(rot);
+ //Logger.LogInfo(string.Format("TIR DATA Final pos = {0}; Final rot = {1}", pos, rot));
+ }
+ }
+}