Decompiled source of CalmDownQuickbolt v1.1.1

Niko666.CalmDownQuickbolt.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FistVR;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("Niko666")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Stops the rifle from following your hand's leftover motion right after a bolt cycle, so your aim stays where it was while your hand settles back into the gun. Also supports straight-pull rifles!")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1")]
[assembly: AssemblyProduct("Niko666.CalmDownQuickbolt")]
[assembly: AssemblyTitle("CalmDownQuickbolt")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string id = null, string name = null, string version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string id = null, string name = null, string version = null)
		{
		}
	}
}
namespace Niko666
{
	[BepInProcess("h3vr.exe")]
	[BepInPlugin("Niko666.CalmDownQuickbolt", "CalmDownQuickbolt", "1.1.1")]
	public class CalmDownQuickbolt : BaseUnityPlugin
	{
		public ConfigEntry<bool> configEnabled;

		public ConfigEntry<float> configWindowSeconds;

		public ConfigEntry<float> configRotationStrength;

		public ConfigEntry<float> configPositionStrength;

		public ConfigEntry<float> configRampOutSeconds;

		public ConfigEntry<float> configMaxAngularSpeed;

		public ConfigEntry<float> configMaxLinearSpeed;

		public ConfigEntry<float> configTailCapSeconds;

		public ConfigEntry<float> configTailCapExitAngle;

		public ConfigEntry<bool> configTriggerOnHandBack;

		public ConfigEntry<bool> configTriggerOnBoltClose;

		public ConfigEntry<bool> configGateUseTriggerHold;

		public ConfigEntry<bool> configReleaseOnFire;

		public ConfigEntry<bool> configTriggerOnRegrab;

		public ConfigEntry<bool> configTakeOverScripts;

		public ConfigEntry<bool> configDebugLog;

		public ConfigEntry<string> configToggleKey;

		private long m_cfgFileStamp = -1L;

		private float m_pollTimer;

		private KeyCode m_toggleKey = (KeyCode)291;

		private bool m_applyPending;

		private const int PATCH_FAILED = 0;

		private const int PATCH_OK = 1;

		private const int PATCH_UNVERIFIED = 2;

		public const string Id = "Niko666.CalmDownQuickbolt";

		public static CalmDownQuickbolt Instance { get; private set; }

		internal static ManualLogSource Logger { get; private set; }

		public static string Name => "CalmDownQuickbolt";

		public static string Version => "1.1.1";

		private void Awake()
		{
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			configEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. Off = the mod does nothing at all and any running buffer ends at once. F10 toggles this live too.");
			configWindowSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "WindowSeconds", 1.3f, "How long the rifle holds its aim after a bolt cycle, in seconds.");
			configRotationStrength = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RotationStrength", 0.95f, "0..1. 1 = during the window the rifle's rotation is held at the aim it had and does not follow your hand rotating at all, 0.5 = half held, 0 = vanilla (slower follow only). This is the main knob for whether the aim stays put.");
			configPositionStrength = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PositionStrength", 0.9f, "0..1. Same as RotationStrength, but for the rifle following your hand moving through space.");
			configRampOutSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RampOutSeconds", 0.7f, "The last N seconds of the window fade the hold out smoothly instead of letting go in one frame.");
			configMaxAngularSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxAngularSpeed", 300f, "Degrees per second the rifle is still allowed to turn while the buffer is active. Stops handing control back from ever snapping. 0 = no limit.");
			configMaxLinearSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxLinearSpeed", 1.2f, "Metres per second the rifle is still allowed to move while the buffer is active. 0 = no limit.");
			configTailCapSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "TailCapSeconds", 0.5f, "After the window the speed limit above stays on for up to this long, until the rifle has caught up with your hand. 0 = off.");
			configTailCapExitAngle = ((BaseUnityPlugin)this).Config.Bind<float>("General", "TailCapExitAngle", 2f, "The tail speed limit lets go early once the rifle is this close (degrees) to where your hand wants it.");
			configTriggerOnHandBack = ((BaseUnityPlugin)this).Config.Bind<bool>("Quickbolt", "TriggerOnHandBack", true, "Start the buffer when the game hands your hand from the bolt handle back to the rifle (the normal quickbolt hand-over).");
			configTriggerOnBoltClose = ((BaseUnityPlugin)this).Config.Bind<bool>("Quickbolt", "TriggerOnBoltClose", false, "Also start it whenever the bolt closes, whichever path did it (touchpad quickbolting, or your hand staying on the bolt handle).");
			configGateUseTriggerHold = ((BaseUnityPlugin)this).Config.Bind<bool>("Gate", "UseTriggerHold", true, "Only smooth while your support/foregrip hand is holding its trigger - the vanilla way of steadying a rifle (the game's own LongGunSnipingAssist reads the same trigger). Release the trigger and the hold ends at once. This is also why there are no escape heuristics any more: nobody keeps the foregrip trigger pressed while swinging the rifle onto another target.");
			configReleaseOnFire = ((BaseUnityPlugin)this).Config.Bind<bool>("Gate", "ReleaseOnFire", true, "Firing ends the buffer at once, so the recoil is never held back.");
			configTriggerOnRegrab = ((BaseUnityPlugin)this).Config.Bind<bool>("StraightPull", "Regrab", true, "Start the buffer when you take hold of a rifle that is already in your hands - the straight-pull case. A closed-bolt rifle with no quickbolt script is cycled by letting go of the grip to work the bolt; when you take the grip again the rifle re-anchors to that hand and the aim jumps by however differently you grabbed it. On by default (tested on the Winchester Lee Navy); it works on any gun, so turn it off if you would rather have the plain grab back.");
			configTakeOverScripts = ((BaseUnityPlugin)this).Config.Bind<bool>("StraightPull", "TakeOverScripts", true, "Some gun mods ship their own quickbolt stabiliser that writes the rifle's transform every LateUpdate (e.g. Volks' ClosedBoltQBFixed on the Swiss K31), which overwrites this mod's hold. With this on, such a component's own stabilisation is switched off for as long as our buffer runs and put back afterwards, so the trigger gate does the steadying instead - measured better than the K31's own stabiliser. Only components whose type name says quickbolt and that carry a bool field about stabilising are ever touched.");
			configDebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "DebugLog", false, "Log one line per buffer with the aim drift, what your hand was doing, and why the buffer ended. Leave it on while tuning.");
			configToggleKey = ((BaseUnityPlugin)this).Config.Bind<string>("Debug", "ToggleKey", "F10", "Key that toggles the mod (Unity KeyCode name). Empty = no hotkey.");
			ApplyToggleKey();
			HookSettingChanged();
			ApplyPatches();
			RefreshSettings();
			Logger.LogMessage((object)("CalmDownQuickbolt ready. Sent from Niko666.CalmDownQuickbolt " + Version));
		}

		private void HookSettingChanged()
		{
			configEnabled.SettingChanged += OnSettingChanged;
			configWindowSeconds.SettingChanged += OnSettingChanged;
			configRotationStrength.SettingChanged += OnSettingChanged;
			configPositionStrength.SettingChanged += OnSettingChanged;
			configRampOutSeconds.SettingChanged += OnSettingChanged;
			configMaxAngularSpeed.SettingChanged += OnSettingChanged;
			configMaxLinearSpeed.SettingChanged += OnSettingChanged;
			configTailCapSeconds.SettingChanged += OnSettingChanged;
			configTailCapExitAngle.SettingChanged += OnSettingChanged;
			configTriggerOnHandBack.SettingChanged += OnSettingChanged;
			configTriggerOnBoltClose.SettingChanged += OnSettingChanged;
			configGateUseTriggerHold.SettingChanged += OnSettingChanged;
			configReleaseOnFire.SettingChanged += OnSettingChanged;
			configTriggerOnRegrab.SettingChanged += OnSettingChanged;
			configTakeOverScripts.SettingChanged += OnSettingChanged;
			configDebugLog.SettingChanged += OnSettingChanged;
			configToggleKey.SettingChanged += OnSettingChanged;
		}

		private void OnSettingChanged(object sender, EventArgs e)
		{
			m_applyPending = true;
		}

		private void ApplyPatches()
		{
			if (PatchGroup(typeof(QuickboltBufferPatch_Fu), AccessTools.Method(typeof(FVRPhysicalObject), "FU", (Type[])null, (Type[])null)) == 0)
			{
				Logger.LogWarning((object)"[CDQB] FVRPhysicalObject.FU could not be patched, falling back to FVRPhysicalObject.FVRFixedUpdate");
				PatchGroup(typeof(QuickboltBufferPatch_FixedUpdate), AccessTools.Method(typeof(FVRPhysicalObject), "FVRFixedUpdate", (Type[])null, (Type[])null));
			}
			PatchGroup(typeof(QuickboltBufferPatch_HandBack), AccessTools.Method(typeof(FVRViveHand), "ForceSetInteractable", new Type[1] { typeof(FVRInteractiveObject) }, (Type[])null));
			PatchGroup(typeof(QuickboltBufferPatch_BoltClose), AccessTools.Method(typeof(BoltActionRifle_Handle), "UpdateInteraction", new Type[1] { typeof(FVRViveHand) }, (Type[])null), AccessTools.Method(typeof(BoltActionRifle_Handle), "DriveBolt", new Type[1] { typeof(float) }, (Type[])null));
			PatchGroup(typeof(QuickboltBufferPatch_Fire), AccessTools.Method(typeof(FVRFireArm), "Fire", new Type[5]
			{
				typeof(FVRFireArmChamber),
				typeof(Transform),
				typeof(bool),
				typeof(float),
				typeof(float)
			}, (Type[])null));
			PatchGroup(typeof(QuickboltBufferPatch_Regrab), AccessTools.Method(typeof(FVRFireArm), "BeginInteraction", new Type[1] { typeof(FVRViveHand) }, (Type[])null));
		}

		private int PatchGroup(Type patchClass, params MethodInfo[] targets)
		{
			try
			{
				Harmony.CreateAndPatchAll(patchClass, "Niko666.CalmDownQuickbolt");
				Logger.LogInfo((object)("[CDQB] patch group applied: " + patchClass.Name));
				bool flag = true;
				for (int i = 0; i < targets.Length; i++)
				{
					if (!VerifyPatch(targets[i]))
					{
						flag = false;
					}
				}
				return flag ? 1 : 2;
			}
			catch (Exception ex)
			{
				Logger.LogError((object)("[CDQB] patch group FAILED: " + patchClass.Name + " -> " + ex.Message));
				return 0;
			}
		}

		private bool VerifyPatch(MethodInfo target)
		{
			if ((object)target == null)
			{
				Logger.LogError((object)"[CDQB] patch verify FAILED: target method not found in this game build");
				return false;
			}
			string text = target.DeclaringType.Name + "." + target.Name;
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)target);
			bool flag = false;
			if (patchInfo != null)
			{
				if (patchInfo.Prefixes != null)
				{
					for (int i = 0; i < patchInfo.Prefixes.Count; i++)
					{
						if (patchInfo.Prefixes[i].owner == "Niko666.CalmDownQuickbolt")
						{
							flag = true;
						}
					}
				}
				if (!flag && patchInfo.Postfixes != null)
				{
					for (int j = 0; j < patchInfo.Postfixes.Count; j++)
					{
						if (patchInfo.Postfixes[j].owner == "Niko666.CalmDownQuickbolt")
						{
							flag = true;
						}
					}
				}
			}
			if (flag)
			{
				Logger.LogInfo((object)("[CDQB] patch verified in Harmony: " + text));
			}
			else
			{
				Logger.LogError((object)("[CDQB] patch NOT verifiable in Harmony: " + text + " (this group is inert)"));
			}
			return flag;
		}

		private void RefreshSettings()
		{
			PostCycleBuffer.SetSettings(BufferSettings.FromConfig(this));
			if (configDebugLog.Value)
			{
				Logger.LogInfo((object)($"[CDQB] settings: enabled={configEnabled.Value} window={configWindowSeconds.Value:F2}s " + $"hold rot/pos={configRotationStrength.Value:F2}/{configPositionStrength.Value:F2} ramp={configRampOutSeconds.Value:F2}s " + $"caps={configMaxAngularSpeed.Value:F0}deg/s {configMaxLinearSpeed.Value:F2}m/s tail={configTailCapSeconds.Value:F2}s " + $"handback={configTriggerOnHandBack.Value} boltclose={configTriggerOnBoltClose.Value} " + $"gate={configGateUseTriggerHold.Value} releaseOnFire={configReleaseOnFire.Value} " + $"regrab={configTriggerOnRegrab.Value} takeOver={configTakeOverScripts.Value}"));
			}
		}

		private void ApplyToggleKey()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			string value = configToggleKey.Value;
			m_toggleKey = (KeyCode)0;
			if (string.IsNullOrEmpty(value))
			{
				return;
			}
			try
			{
				m_toggleKey = (KeyCode)Enum.Parse(typeof(KeyCode), value.Trim(), ignoreCase: true);
			}
			catch (Exception)
			{
				Logger.LogWarning((object)("[CDQB] ToggleKey '" + value + "' 不是合法的 Unity KeyCode 名字,热键已关闭。"));
				m_toggleKey = (KeyCode)0;
			}
		}

		private long ConfigFileStamp()
		{
			try
			{
				string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
				if (string.IsNullOrEmpty(configFilePath) || !File.Exists(configFilePath))
				{
					return 0L;
				}
				return File.GetLastWriteTimeUtc(configFilePath).Ticks;
			}
			catch (Exception)
			{
				return 0L;
			}
		}

		private void Update()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (m_applyPending)
			{
				m_applyPending = false;
				ApplyToggleKey();
				RefreshSettings();
			}
			if ((int)m_toggleKey != 0 && Input.GetKeyDown(m_toggleKey))
			{
				configEnabled.Value = !configEnabled.Value;
				m_cfgFileStamp = ConfigFileStamp();
				Logger.LogMessage((object)$"[CDQB] hotkey {m_toggleKey}: Enabled = {configEnabled.Value}");
			}
			m_pollTimer += Time.unscaledDeltaTime;
			if (m_pollTimer < 1f)
			{
				return;
			}
			m_pollTimer = 0f;
			PostCycleBuffer.PurgeStale(Time.realtimeSinceStartup);
			long num = ConfigFileStamp();
			if (num != 0L && BufferMath.StampIsNew(m_cfgFileStamp, num))
			{
				m_cfgFileStamp = num;
				try
				{
					((BaseUnityPlugin)this).Config.Reload();
					ApplyToggleKey();
					RefreshSettings();
					Logger.LogInfo((object)"[CDQB] config file changed on disk, reloaded.");
					return;
				}
				catch (Exception ex)
				{
					Logger.LogWarning((object)("[CDQB] config hot reload failed: " + ex.Message));
					return;
				}
			}
			if (num != 0L)
			{
				m_cfgFileStamp = num;
			}
		}
	}
	internal static class BufferMath
	{
		public static float Clamp01(float v)
		{
			if (v < 0f)
			{
				return 0f;
			}
			if (v > 1f)
			{
				return 1f;
			}
			return v;
		}

		public static float SmoothStep01(float x)
		{
			x = Clamp01(x);
			return x * x * (3f - 2f * x);
		}

		public static float Multiplier(float elapsed, float duration, float rampOut, float strength)
		{
			if (duration <= 0f)
			{
				return 1f;
			}
			strength = Clamp01(strength);
			if (strength <= 0f)
			{
				return 1f;
			}
			if (elapsed < 0f)
			{
				elapsed = 0f;
			}
			if (elapsed >= duration)
			{
				return 1f;
			}
			float num = rampOut;
			if (num < 0f)
			{
				num = 0f;
			}
			if (num > duration)
			{
				num = duration;
			}
			float num2 = duration - num;
			float num3 = 0f;
			if (num > 0f && elapsed > num2)
			{
				num3 = SmoothStep01((elapsed - num2) / num);
			}
			return 1f - strength * (1f - num3);
		}

		public static float HoldWeight(float elapsed, float duration, float rampOut, float strength)
		{
			return 1f - Multiplier(elapsed, duration, rampOut, strength);
		}

		public static bool HoldActive(bool gateUsed, bool gateHeld, float elapsed, float duration)
		{
			if (duration <= 0f)
			{
				return false;
			}
			if (elapsed >= duration)
			{
				return false;
			}
			if (gateUsed && !gateHeld)
			{
				return false;
			}
			return true;
		}

		public static float ClampMagnitude(float value, float max)
		{
			if (max <= 0f)
			{
				return value;
			}
			if (value > max)
			{
				return max;
			}
			if (value < 0f - max)
			{
				return 0f - max;
			}
			return value;
		}

		public static Vector3 ClampMagnitude(Vector3 v, float max)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (max <= 0f)
			{
				return v;
			}
			float magnitude = ((Vector3)(ref v)).magnitude;
			if (magnitude <= max || magnitude <= 1E-06f)
			{
				return v;
			}
			return v * (max / magnitude);
		}

		public static bool StampIsNew(long previous, long current)
		{
			if (previous < 0)
			{
				return false;
			}
			return current != previous;
		}

		public static bool IsBoltLike(string typeName)
		{
			if (string.IsNullOrEmpty(typeName))
			{
				return false;
			}
			if (typeName.EndsWith("Weapon") || typeName.EndsWith("Rifle") || typeName.EndsWith("Receiver"))
			{
				return false;
			}
			if (typeName.IndexOf("Bolt", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return true;
			}
			return typeName.IndexOf("RailTater", StringComparison.OrdinalIgnoreCase) >= 0;
		}
	}
	internal struct BufferSettings
	{
		public bool Enabled;

		public float Window;

		public float RampOut;

		public float RotStrength;

		public float PosStrength;

		public bool OnHandBack;

		public bool OnBoltClose;

		public bool OnRegrab;

		public bool TakeOverScripts;

		public bool UseTriggerHold;

		public bool ReleaseOnFire;

		public float MaxAngularSpeed;

		public float MaxLinearSpeed;

		public float TailCapSeconds;

		public float TailCapExitAngle;

		public bool Debug;

		public static BufferSettings FromConfig(CalmDownQuickbolt p)
		{
			BufferSettings result = default(BufferSettings);
			if ((Object)(object)p == (Object)null)
			{
				return result;
			}
			try
			{
				result.Enabled = p.configEnabled.Value;
				result.Window = p.configWindowSeconds.Value;
				result.RampOut = p.configRampOutSeconds.Value;
				result.RotStrength = p.configRotationStrength.Value;
				result.PosStrength = p.configPositionStrength.Value;
				result.OnHandBack = p.configTriggerOnHandBack.Value;
				result.OnBoltClose = p.configTriggerOnBoltClose.Value;
				result.OnRegrab = p.configTriggerOnRegrab.Value;
				result.TakeOverScripts = p.configTakeOverScripts.Value;
				result.UseTriggerHold = p.configGateUseTriggerHold.Value;
				result.ReleaseOnFire = p.configReleaseOnFire.Value;
				result.MaxAngularSpeed = p.configMaxAngularSpeed.Value;
				result.MaxLinearSpeed = p.configMaxLinearSpeed.Value;
				result.TailCapSeconds = p.configTailCapSeconds.Value;
				result.TailCapExitAngle = p.configTailCapExitAngle.Value;
				result.Debug = p.configDebugLog.Value;
			}
			catch (Exception ex)
			{
				CalmDownQuickbolt.Logger.LogWarning((object)("[CDQB] settings snapshot failed, using defaults: " + ex.Message));
			}
			return result;
		}
	}
	internal sealed class HandTrack
	{
		public FVRViveHand Hand;

		public Vector3 RefPosLocal;

		public Quaternion RefRotLocal;

		public Vector3 LastPosLocal;

		public Quaternion LastRotLocal;

		public float MovedDist;

		public float MovedAngle;

		public float Speed;

		public float Angular;

		public float PeakSpeed;

		public float PeakAngular;
	}
	internal sealed class CycleWindow
	{
		public float Elapsed;

		public float HeldTime;

		public float Duration;

		public float RampOut;

		public float RotStrength;

		public float PosStrength;

		public float LastTouched;

		public bool InTail;

		public Vector3 LatchPosLocal;

		public Quaternion LatchRotLocal;

		public Quaternion HeadRotAtStart;

		public HandTrack Weapon;

		public readonly List<HandTrack> Supports = new List<HandTrack>();

		public float FlatDrift;

		public float PeakDrift;

		public float FinalDrift;

		public float HeadMovedAngle;

		public float GunPeakAngular;

		public bool HasLatch;

		public bool Ready;

		public string Reason;

		public string EndReason;
	}
	internal static class PostCycleBuffer
	{
		private sealed class StabSnapshot
		{
			public MonoBehaviour Comp;

			public FieldInfo Field;

			public bool Was;
		}

		private const float HoldSpringAngular = 14f;

		private const float HoldSpringLinear = 12f;

		private static readonly Dictionary<int, CycleWindow> Windows = new Dictionary<int, CycleWindow>();

		private static BufferSettings _s;

		private static readonly Dictionary<int, List<StabSnapshot>> Suppressed = new Dictionary<int, List<StabSnapshot>>();

		public static int ActiveWindows => Windows.Count;

		public static bool TriggerOnHandBack
		{
			get
			{
				if (_s.Enabled)
				{
					return _s.OnHandBack;
				}
				return false;
			}
		}

		public static bool TriggerOnBoltClose
		{
			get
			{
				if (_s.Enabled)
				{
					return _s.OnBoltClose;
				}
				return false;
			}
		}

		public static bool TriggerOnRegrab
		{
			get
			{
				if (_s.Enabled)
				{
					return _s.OnRegrab;
				}
				return false;
			}
		}

		public static bool ReleaseOnFire
		{
			get
			{
				if (_s.Enabled)
				{
					return _s.ReleaseOnFire;
				}
				return false;
			}
		}

		public static void SetSettings(BufferSettings s)
		{
			_s = s;
			if (!s.Enabled)
			{
				Windows.Clear();
				RestoreAllStabilizers();
			}
		}

		public static bool HeldByOtherHand(FVRPhysicalObject obj, FVRViveHand hand)
		{
			if ((Object)(object)obj == (Object)null || (Object)(object)hand == (Object)null)
			{
				return false;
			}
			if ((Object)(object)((FVRInteractiveObject)obj).m_hand != (Object)null && (Object)(object)((FVRInteractiveObject)obj).m_hand != (Object)(object)hand)
			{
				return true;
			}
			if ((Object)(object)obj.AltGrip != (Object)null && (Object)(object)((FVRInteractiveObject)obj.AltGrip).m_hand != (Object)null && (Object)(object)((FVRInteractiveObject)obj.AltGrip).m_hand != (Object)(object)hand)
			{
				return true;
			}
			return false;
		}

		private static void TakeOverStabilizers(FVRPhysicalObject obj, int id)
		{
			if (!_s.TakeOverScripts)
			{
				return;
			}
			RestoreStabilizers(id);
			List<StabSnapshot> list = null;
			try
			{
				MonoBehaviour[] componentsInChildren = ((Component)obj).GetComponentsInChildren<MonoBehaviour>(true);
				foreach (MonoBehaviour val in componentsInChildren)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					string name = ((object)val).GetType().Name;
					if (name.IndexOf("quickbolt", StringComparison.OrdinalIgnoreCase) < 0 && (name.IndexOf("qb", StringComparison.OrdinalIgnoreCase) < 0 || name.IndexOf("bolt", StringComparison.OrdinalIgnoreCase) < 0))
					{
						continue;
					}
					FieldInfo[] fields = ((object)val).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (FieldInfo fieldInfo in fields)
					{
						if ((object)fieldInfo.FieldType != typeof(bool) || fieldInfo.Name.IndexOf("stabiliz", StringComparison.OrdinalIgnoreCase) < 0)
						{
							continue;
						}
						bool flag = (bool)fieldInfo.GetValue(val);
						if (flag)
						{
							fieldInfo.SetValue(val, false);
							if (list == null)
							{
								list = new List<StabSnapshot>();
							}
							StabSnapshot stabSnapshot = new StabSnapshot();
							stabSnapshot.Comp = val;
							stabSnapshot.Field = fieldInfo;
							stabSnapshot.Was = flag;
							list.Add(stabSnapshot);
							if (_s.Debug)
							{
								CalmDownQuickbolt.Logger.LogInfo((object)("[CDQB] took over stabilization from " + name + "." + fieldInfo.Name + " on " + ((Object)obj).name));
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				if (_s.Debug)
				{
					CalmDownQuickbolt.Logger.LogWarning((object)("[CDQB] stabilizer take-over skipped: " + ex.Message));
				}
			}
			if (list != null)
			{
				Suppressed[id] = list;
			}
		}

		private static void RestoreStabilizers(int id)
		{
			if (!Suppressed.TryGetValue(id, out var value))
			{
				return;
			}
			Suppressed.Remove(id);
			for (int i = 0; i < value.Count; i++)
			{
				try
				{
					if ((Object)(object)value[i].Comp != (Object)null)
					{
						value[i].Field.SetValue(value[i].Comp, value[i].Was);
					}
				}
				catch (Exception)
				{
				}
			}
		}

		private static void RestoreAllStabilizers()
		{
			if (Suppressed.Count != 0)
			{
				List<int> list = new List<int>(Suppressed.Keys);
				for (int i = 0; i < list.Count; i++)
				{
					RestoreStabilizers(list[i]);
				}
			}
		}

		private static void Close(int id)
		{
			Windows.Remove(id);
			RestoreStabilizers(id);
		}

		public static void Engage(FVRPhysicalObject obj, FVRViveHand hand, string reason)
		{
			if ((Object)(object)obj == (Object)null || !_s.Enabled || _s.Window <= 0f || (_s.RotStrength <= 0f && _s.PosStrength <= 0f) || !((FVRInteractiveObject)obj).IsHeld)
			{
				return;
			}
			Rigidbody rootRigidbody = obj.RootRigidbody;
			if ((Object)(object)rootRigidbody == (Object)null || rootRigidbody.isKinematic)
			{
				return;
			}
			int instanceID = ((Object)obj).GetInstanceID();
			if (!Windows.TryGetValue(instanceID, out var value))
			{
				value = new CycleWindow();
				Windows[instanceID] = value;
			}
			value.Elapsed = 0f;
			value.HeldTime = 0f;
			value.Duration = _s.Window;
			value.RampOut = _s.RampOut;
			value.RotStrength = BufferMath.Clamp01(_s.RotStrength);
			value.PosStrength = BufferMath.Clamp01(_s.PosStrength);
			value.InTail = false;
			value.LastTouched = Time.realtimeSinceStartup;
			value.FlatDrift = 0f;
			value.PeakDrift = 0f;
			value.FinalDrift = 0f;
			value.HeadMovedAngle = 0f;
			value.GunPeakAngular = 0f;
			value.Ready = false;
			value.Reason = reason;
			value.EndReason = "window over";
			FVRViveHand hand2 = (((Object)(object)hand != (Object)null) ? hand : ((FVRInteractiveObject)obj).m_hand);
			TakeOverStabilizers(obj, instanceID);
			CaptureRefs(value, obj, hand2);
			if (_s.Debug)
			{
				int num = 0;
				if (Suppressed.TryGetValue(instanceID, out var value2))
				{
					num = value2.Count;
				}
				CalmDownQuickbolt.Logger.LogInfo((object)(string.Format("[CDQB] buffer start on {0} ({1}): {2:F2}s hold rot={3:F2} pos={4:F2} gate={5}", ((Object)obj).name, reason, value.Duration, value.RotStrength, value.PosStrength, _s.UseTriggerHold ? "support trigger" : "off") + ((num > 0) ? $" tookOver={num}" : string.Empty)));
			}
		}

		private static void CaptureRefs(CycleWindow w, FVRPhysicalObject obj, FVRViveHand hand)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			w.HasLatch = false;
			w.Weapon = null;
			w.Supports.Clear();
			if (!((Object)(object)GM.CurrentPlayerBody == (Object)null))
			{
				Transform transform = ((Component)GM.CurrentPlayerBody).transform;
				w.LatchPosLocal = transform.InverseTransformPoint(((Component)obj).transform.position);
				w.LatchRotLocal = Quaternion.Inverse(transform.rotation) * ((Component)obj).transform.rotation;
				w.HasLatch = true;
				w.HeadRotAtStart = (((Object)(object)GM.CurrentPlayerBody.Head != (Object)null) ? GM.CurrentPlayerBody.Head.rotation : transform.rotation);
				if (!((Object)(object)hand == (Object)null) && !((Object)(object)hand.PoseOverride == (Object)null))
				{
					w.Weapon = MakeTrack(hand, transform);
					AddSupport(w, ResolveSupportHand(obj, hand), hand, transform);
					w.Ready = true;
				}
			}
		}

		private static FVRViveHand ResolveSupportHand(FVRPhysicalObject obj, FVRViveHand weaponHand)
		{
			if ((Object)(object)obj.AltGrip != (Object)null && (Object)(object)((FVRInteractiveObject)obj.AltGrip).m_hand != (Object)null && (Object)(object)((FVRInteractiveObject)obj.AltGrip).m_hand != (Object)(object)weaponHand)
			{
				return ((FVRInteractiveObject)obj.AltGrip).m_hand;
			}
			if ((Object)(object)weaponHand != (Object)null && (Object)(object)weaponHand.OtherHand != (Object)null && (Object)(object)weaponHand.OtherHand != (Object)(object)weaponHand)
			{
				return weaponHand.OtherHand;
			}
			return null;
		}

		private static void AddSupport(CycleWindow w, FVRViveHand candidate, FVRViveHand weaponHand, Transform body)
		{
			if ((Object)(object)candidate == (Object)null || (Object)(object)candidate == (Object)(object)weaponHand || (Object)(object)candidate.PoseOverride == (Object)null)
			{
				return;
			}
			for (int i = 0; i < w.Supports.Count; i++)
			{
				if ((Object)(object)w.Supports[i].Hand == (Object)(object)candidate)
				{
					return;
				}
			}
			w.Supports.Add(MakeTrack(candidate, body));
		}

		private static HandTrack MakeTrack(FVRViveHand hand, Transform body)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			HandTrack obj = new HandTrack
			{
				Hand = hand,
				RefPosLocal = body.InverseTransformPoint(hand.PoseOverride.position),
				RefRotLocal = Quaternion.Inverse(body.rotation) * hand.PoseOverride.rotation
			};
			obj.LastPosLocal = obj.RefPosLocal;
			obj.LastRotLocal = obj.RefRotLocal;
			return obj;
		}

		public static void ReleaseNow(FVRPhysicalObject obj, string reason)
		{
			if ((Object)(object)obj == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)obj).GetInstanceID();
			if (Windows.TryGetValue(instanceID, out var value))
			{
				Close(instanceID);
				if (_s.Debug)
				{
					LogSummary(obj, value, reason);
				}
			}
		}

		public static void PurgeStale(float now)
		{
			if (Windows.Count == 0)
			{
				return;
			}
			List<int> list = null;
			foreach (KeyValuePair<int, CycleWindow> window in Windows)
			{
				if (now - window.Value.LastTouched > 6f)
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(window.Key);
				}
			}
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					Close(list[i]);
				}
			}
		}

		public static void Damp(FVRPhysicalObject obj)
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_034a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0359: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02df: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			int instanceID = ((Object)obj).GetInstanceID();
			if (!Windows.TryGetValue(instanceID, out var value))
			{
				return;
			}
			if (!_s.Enabled || _s.Window <= 0f)
			{
				Close(instanceID);
				return;
			}
			Rigidbody rootRigidbody = obj.RootRigidbody;
			FVRViveHand hand = ((FVRInteractiveObject)obj).m_hand;
			if (!((FVRInteractiveObject)obj).IsHeld || (Object)(object)hand == (Object)null || (Object)(object)rootRigidbody == (Object)null || rootRigidbody.isKinematic)
			{
				Close(instanceID);
				if (_s.Debug)
				{
					LogSummary(obj, value, "no longer held");
				}
				return;
			}
			if ((Object)(object)GM.CurrentPlayerBody == (Object)null || !value.HasLatch || !value.Ready)
			{
				Close(instanceID);
				return;
			}
			value.LastTouched = Time.realtimeSinceStartup;
			float fixedDeltaTime = Time.fixedDeltaTime;
			value.Elapsed += fixedDeltaTime;
			Transform transform = ((Component)GM.CurrentPlayerBody).transform;
			Vector3 target = transform.TransformPoint(value.LatchPosLocal);
			Quaternion val = transform.rotation * value.LatchRotLocal;
			FVRViveHand val2 = ResolveSupportHand(obj, hand);
			MeasureHands(value, hand, transform, fixedDeltaTime);
			value.FinalDrift = Quaternion.Angle(((Component)obj).transform.rotation, val);
			if (value.FinalDrift > value.PeakDrift)
			{
				value.PeakDrift = value.FinalDrift;
			}
			float num = value.Duration - Mathf.Max(value.RampOut, 0f);
			if (value.Elapsed <= num)
			{
				value.FlatDrift = value.FinalDrift;
			}
			bool gateUsed = _s.UseTriggerHold && (Object)(object)val2 != (Object)null;
			bool flag = (Object)(object)val2 == (Object)null || val2.Input.TriggerPressed;
			if (_s.UseTriggerHold && (Object)(object)val2 != (Object)null && !flag && value.Elapsed < value.Duration)
			{
				value.EndReason = $"support trigger released after {value.HeldTime:F2}s";
				value.Elapsed = value.Duration;
			}
			float num2 = BufferMath.Multiplier(value.Elapsed, value.Duration, value.RampOut, value.RotStrength);
			float num3 = BufferMath.Multiplier(value.Elapsed, value.Duration, value.RampOut, value.PosStrength);
			if (BufferMath.HoldActive(gateUsed, flag, value.Elapsed, value.Duration))
			{
				value.HeldTime += fixedDeltaTime;
				Vector3 val3 = AngularSpring(((Component)obj).transform.rotation, val, 14f, _s.MaxAngularSpeed * ((float)Math.PI / 180f));
				Vector3 val4 = LinearSpring(((Component)obj).transform.position, target, 12f, _s.MaxLinearSpeed);
				Vector3 v = rootRigidbody.angularVelocity * num2 + val3 * (1f - num2);
				Vector3 v2 = rootRigidbody.velocity * num3 + val4 * (1f - num3);
				rootRigidbody.angularVelocity = BufferMath.ClampMagnitude(v, _s.MaxAngularSpeed * ((float)Math.PI / 180f));
				rootRigidbody.velocity = BufferMath.ClampMagnitude(v2, _s.MaxLinearSpeed);
				return;
			}
			value.InTail = true;
			rootRigidbody.angularVelocity = BufferMath.ClampMagnitude(rootRigidbody.angularVelocity, _s.MaxAngularSpeed * ((float)Math.PI / 180f));
			rootRigidbody.velocity = BufferMath.ClampMagnitude(rootRigidbody.velocity, _s.MaxLinearSpeed);
			bool flag2 = _s.TailCapExitAngle <= 0f || value.FinalDrift <= _s.TailCapExitAngle;
			bool flag3 = value.Elapsed >= value.Duration + Mathf.Max(_s.TailCapSeconds, 0f);
			if (flag2 || flag3)
			{
				Close(instanceID);
				if (_s.Debug)
				{
					LogSummary(obj, value, flag2 ? "caught up" : "tail cap over");
				}
			}
		}

		private static void MeasureHands(CycleWindow w, FVRViveHand weaponHand, Transform body, float dt)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			if (w.Weapon != null && (Object)(object)w.Weapon.Hand == (Object)(object)weaponHand)
			{
				MeasureTrack(w.Weapon, body, dt);
			}
			for (int num = w.Supports.Count - 1; num >= 0; num--)
			{
				HandTrack handTrack = w.Supports[num];
				FVRViveHand hand = handTrack.Hand;
				if ((Object)(object)hand == (Object)null || (Object)(object)hand.PoseOverride == (Object)null)
				{
					w.Supports.RemoveAt(num);
				}
				else
				{
					MeasureTrack(handTrack, body, dt);
				}
			}
			if ((Object)(object)GM.CurrentPlayerBody.Head != (Object)null)
			{
				float num2 = Quaternion.Angle(GM.CurrentPlayerBody.Head.rotation, w.HeadRotAtStart);
				if (num2 > w.HeadMovedAngle)
				{
					w.HeadMovedAngle = num2;
				}
			}
		}

		private static void MeasureTrack(HandTrack t, Transform body, float dt)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = body.InverseTransformPoint(t.Hand.PoseOverride.position);
			Quaternion val2 = Quaternion.Inverse(body.rotation) * t.Hand.PoseOverride.rotation;
			t.MovedDist = Vector3.Distance(val, t.RefPosLocal);
			t.MovedAngle = Quaternion.Angle(val2, t.RefRotLocal);
			if (dt > 0f)
			{
				t.Speed = Vector3.Distance(val, t.LastPosLocal) / dt;
				t.Angular = Quaternion.Angle(val2, t.LastRotLocal) / dt;
			}
			t.LastPosLocal = val;
			t.LastRotLocal = val2;
			if (t.Speed > t.PeakSpeed)
			{
				t.PeakSpeed = t.Speed;
			}
			if (t.Angular > t.PeakAngular)
			{
				t.PeakAngular = t.Angular;
			}
		}

		private static void LogSummary(FVRPhysicalObject obj, CycleWindow w, string endReason)
		{
			CalmDownQuickbolt.Logger.LogInfo((object)($"[CDQB] buffer {w.Duration:F2}s on {((Object)obj).name} ({w.Reason}) -> {endReason} | " + $"drift flat/peak/final {w.FlatDrift:F1}/{w.PeakDrift:F1}/{w.FinalDrift:F1}deg | " + string.Format("rifle max {0:F0}deg/s | {1} | {2} | ", w.GunPeakAngular, Describe(w.Weapon, "weapon hand"), DescribeSupport(w)) + $"head {w.HeadMovedAngle:F0}deg"));
		}

		private static string Describe(HandTrack t, string label)
		{
			if (t == null)
			{
				return label + " -";
			}
			return $"{label} {t.MovedDist:F2}m/{t.MovedAngle:F0}deg peak {t.PeakSpeed:F1}m/s {t.PeakAngular:F0}deg/s";
		}

		private static string DescribeSupport(CycleWindow w)
		{
			if (w.Supports.Count == 0)
			{
				return "support hand -";
			}
			HandTrack handTrack = w.Supports[0];
			return $"support hand {handTrack.MovedDist:F2}m/{handTrack.MovedAngle:F0}deg peak {handTrack.PeakSpeed:F1}m/s {handTrack.PeakAngular:F0}deg/s";
		}

		private static Vector3 AngularSpring(Quaternion current, Quaternion target, float kp, float maxSpeed)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			Quaternion val = target * Quaternion.Inverse(current);
			float num = default(float);
			Vector3 val2 = default(Vector3);
			((Quaternion)(ref val)).ToAngleAxis(ref num, ref val2);
			if (num > 180f)
			{
				num -= 360f;
			}
			if (num == 0f)
			{
				return Vector3.zero;
			}
			return BufferMath.ClampMagnitude(val2 * (num * ((float)Math.PI / 180f)) * kp, maxSpeed);
		}

		private static Vector3 LinearSpring(Vector3 current, Vector3 target, float kp, float maxSpeed)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			return BufferMath.ClampMagnitude((target - current) * kp, maxSpeed);
		}
	}
	internal static class QuickboltBufferPatch_Fu
	{
		[HarmonyPatch(typeof(FVRPhysicalObject), "FU")]
		[HarmonyPostfix]
		private static void Postfix(FVRPhysicalObject __instance)
		{
			if (PostCycleBuffer.ActiveWindows != 0)
			{
				PostCycleBuffer.Damp(__instance);
			}
		}
	}
	internal static class QuickboltBufferPatch_FixedUpdate
	{
		[HarmonyPatch(typeof(FVRPhysicalObject), "FVRFixedUpdate")]
		[HarmonyPostfix]
		private static void Postfix(FVRPhysicalObject __instance)
		{
			if (PostCycleBuffer.ActiveWindows != 0)
			{
				PostCycleBuffer.Damp(__instance);
			}
		}
	}
	internal static class QuickboltBufferPatch_HandBack
	{
		[HarmonyPatch(typeof(FVRViveHand), "ForceSetInteractable", new Type[] { typeof(FVRInteractiveObject) })]
		[HarmonyPrefix]
		private static void Prefix(FVRViveHand __instance, FVRInteractiveObject inter)
		{
			if (PostCycleBuffer.TriggerOnHandBack && !((Object)(object)inter == (Object)null) && inter is FVRFireArm)
			{
				FVRInteractiveObject currentInteractable = __instance.CurrentInteractable;
				if (!((Object)(object)currentInteractable == (Object)null) && !((Object)(object)currentInteractable == (Object)(object)inter) && BufferMath.IsBoltLike(((object)currentInteractable).GetType().Name))
				{
					PostCycleBuffer.Engage((FVRPhysicalObject)(object)((inter is FVRPhysicalObject) ? inter : null), __instance, "hand back from " + ((object)currentInteractable).GetType().Name);
				}
			}
		}
	}
	internal static class QuickboltBufferPatch_Regrab
	{
		[HarmonyPatch(typeof(FVRFireArm), "BeginInteraction", new Type[] { typeof(FVRViveHand) })]
		[HarmonyPrefix]
		private static void Prefix(FVRFireArm __instance, FVRViveHand hand)
		{
			if (PostCycleBuffer.TriggerOnRegrab && !((Object)(object)__instance == (Object)null) && !((Object)(object)hand == (Object)null) && PostCycleBuffer.HeldByOtherHand((FVRPhysicalObject)(object)__instance, hand))
			{
				PostCycleBuffer.Engage((FVRPhysicalObject)(object)__instance, hand, "re-grab while held");
			}
		}
	}
	internal static class QuickboltBufferPatch_BoltClose
	{
		private static readonly Dictionary<int, bool> WasClosed = new Dictionary<int, bool>();

		[HarmonyPatch(typeof(BoltActionRifle_Handle), "UpdateInteraction", new Type[] { typeof(FVRViveHand) })]
		[HarmonyPostfix]
		private static void InteractionPostfix(BoltActionRifle_Handle __instance, FVRViveHand hand)
		{
			CheckClosed(__instance, hand);
		}

		[HarmonyPatch(typeof(BoltActionRifle_Handle), "DriveBolt", new Type[] { typeof(float) })]
		[HarmonyPostfix]
		private static void DriveBoltPostfix(BoltActionRifle_Handle __instance)
		{
			CheckClosed(__instance, null);
		}

		private static void CheckClosed(BoltActionRifle_Handle h, FVRViveHand hand)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			if (!((Object)(object)h == (Object)null) && PostCycleBuffer.TriggerOnBoltClose)
			{
				bool flag = (int)h.HandleState == 0 && (int)h.HandleRot == 2;
				int instanceID = ((Object)h).GetInstanceID();
				bool value;
				bool num = WasClosed.TryGetValue(instanceID, out value);
				WasClosed[instanceID] = flag;
				if (!(!num || value) && flag && !((Object)(object)h.Rifle == (Object)null))
				{
					PostCycleBuffer.Engage((FVRPhysicalObject)(object)h.Rifle, ((Object)(object)hand != (Object)null) ? hand : ((FVRInteractiveObject)h).m_hand, "bolt closed");
				}
			}
		}
	}
	internal static class QuickboltBufferPatch_Fire
	{
		[HarmonyPatch(typeof(FVRFireArm), "Fire", new Type[]
		{
			typeof(FVRFireArmChamber),
			typeof(Transform),
			typeof(bool),
			typeof(float),
			typeof(float)
		})]
		[HarmonyPostfix]
		private static void Postfix(FVRFireArm __instance)
		{
			if (PostCycleBuffer.ReleaseOnFire)
			{
				PostCycleBuffer.ReleaseNow((FVRPhysicalObject)(object)__instance, "fired");
			}
		}
	}
}