Decompiled source of DeathHeadHopperVRBridge v0.1.0

DeathHeadHopperVRBridge.dll

Decompiled 21 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DeathHeadHopper.DeathHead;
using DeathHeadHopper.Managers;
using DeathHeadHopper.UI;
using DeathHeadHopperVRBridge.Modules.Config;
using DeathHeadHopperVRBridge.Modules.Logging;
using DeathHeadHopperVRBridge.Modules.Spectate;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using RepoXR;
using RepoXR.Input;
using RepoXR.Managers;
using RepoXR.Player;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 DeathHeadHopperVRBridge
{
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("AdrenSnyder.DeathHeadHopperVRBridge", "Death Head Hopper - VRBridge", "0.1.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private Harmony? _harmony;

		private void Awake()
		{
			//IL_000d: 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_0037: Expected O, but got Unknown
			ConfigManager.Initialize(((BaseUnityPlugin)this).Config);
			if (((Enum)Plugin.Flags).HasFlag((Enum)(object)(Flags)1))
			{
				_harmony = new Harmony("DeathHeadHopperFix-VR.Spectate");
				_harmony.PatchAll();
				VrAbilityBarBridge.EnsureAttached(((Component)this).gameObject);
				VanillaAbilityInputBridge.EnsureAttached(((Component)this).gameObject);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"DeathHeadHopperFix-VR bridge ready (spectate head bridge enabled).");
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"RepoXR did not initialize VR mode, keeping Bridge disabled.");
			}
		}

		private void OnDestroy()
		{
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace DeathHeadHopperVRBridge.Modules.Spectate
{
	internal static class DHHInputBridge
	{
		internal enum MovementDirectionSource
		{
			Controller,
			Head
		}

		internal enum AbilityDirectionSource
		{
			Controller,
			Head
		}

		private static readonly FieldInfo? TMainCameraField = AccessTools.Field(typeof(DHHInputManager), "_tMainCamera");

		internal static readonly ManualLogSource Log = MovementAnalog.Log;

		internal const string ModuleTag = "[DeathHeadHopperFix-VR] [SpectateCam]";

		private static Transform? _abilityAimProxy;

		internal static MovementDirectionSource MovementDirection => ParseMovementDirection();

		internal static AbilityDirectionSource AbilityDirection => ParseAbilityDirection();

		internal static void UpdateCameraReference(DHHInputManager instance)
		{
			if ((Object)(object)instance == (Object)null || !SpectateHeadBridge.IsSpectatingHead())
			{
				return;
			}
			Transform val = SpectateHeadBridge.GetHmdCameraTransform() ?? SpectateHeadBridge.GetAlignedCameraTransform();
			if ((Object)(object)val == (Object)null || TMainCameraField == null)
			{
				return;
			}
			object? value = TMainCameraField.GetValue(instance);
			Transform val2 = (Transform)((value is Transform) ? value : null);
			if (!((Object)(object)val2 == (Object)(object)val))
			{
				TMainCameraField.SetValue(instance, val);
				if (FeatureFlags.DebugHeadAlignment && LogLimiter.Allow("UpdateCameraReference", 1f))
				{
					Log.LogInfo((object)"[DeathHeadHopperFix-VR] [SpectateCam] UpdateCameraReference swapped camera transform");
				}
			}
		}

		internal static bool TryGetAnalogMovement(out Vector2 movement)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			bool flag = MovementAnalog.TryGetAnalog(out movement);
			if (FeatureFlags.DebugHeadAlignment && LogLimiter.Allow("AnalogMovementCheck", 0.5f))
			{
				Log.LogInfo((object)string.Format("{0} TryGetAnalogMovement analogAvailable={1} movement={2:F3}", "[DeathHeadHopperFix-VR] [SpectateCam]", flag, movement));
			}
			return flag;
		}

		internal static Vector2 GetLegacyMovement()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			return new Vector2(SemiFunc.InputMovementX(), SemiFunc.InputMovementY());
		}

		internal static Vector3 CalculateMoveDirection(Vector2 input)
		{
			//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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_001b: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = GetMovementForward(input);
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				val = Vector3.forward;
			}
			Vector3 val2 = Vector3.Cross(Vector3.up, val);
			Vector3 val3 = val * input.y + val2 * input.x;
			if (((Vector3)(ref val3)).sqrMagnitude < 0.0001f)
			{
				return Vector3.zero;
			}
			if (FeatureFlags.DebugMovementDirection && LogLimiter.Allow("MovementDirectionDebug", 0.5f))
			{
				Log.LogInfo((object)string.Format("{0} DebugMovement direction={1:F3} forward={2:F3} input={3:F3} source={4}", "[DeathHeadHopperFix-VR] [SpectateCam]", val3, val, input, MovementDirection));
			}
			if (FeatureFlags.UseAnalogMagnitude)
			{
				return Vector3.ClampMagnitude(val3, 1f);
			}
			return ((Vector3)(ref val3)).normalized;
		}

		internal static void LogMovement(Vector2 selected, Vector2 legacy, bool analogUsed, Vector3 direction, MovementDirectionSource source)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (FeatureFlags.DebugMovementDirection && LogLimiter.Allow("MovementInput", 0.5f))
			{
				Log.LogDebug((object)string.Format("{0} Movement selected={1:F3} legacy={2:F3} analogUsed={3} forward={4:F3} source={5}", "[DeathHeadHopperFix-VR] [SpectateCam]", selected, legacy, analogUsed, direction, source));
			}
		}

		private static void DebugTrace(string key, string message)
		{
			if (FeatureFlags.DebugHeadAlignment && LogLimiter.Allow(key, 0.5f))
			{
				Log.LogInfo((object)("[DeathHeadHopperFix-VR] [SpectateCam] " + message));
			}
		}

		private static Vector3 GetMovementForward(Vector2 input)
		{
			//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_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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_0045: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			MovementDirectionSource movementDirection = MovementDirection;
			if (1 == 0)
			{
			}
			Vector3 val = ((movementDirection != MovementDirectionSource.Head) ? GetControllerForward(input) : SpectateHeadBridge.GetAlignedForward());
			if (1 == 0)
			{
			}
			Vector3 result = val;
			if (((Vector3)(ref result)).sqrMagnitude < 0.0001f)
			{
				return SpectateHeadBridge.GetBaseForward();
			}
			return result;
		}

		internal static Vector3 GetControllerForward(Vector2 movementInput)
		{
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: 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_0031: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string handName;
				Transform val = TryGetPreferredControllerTransform(GetCameraGripSelection(), out handName);
				if ((Object)(object)val != (Object)null)
				{
					Vector3 planarForward = Vector3.ProjectOnPlane(val.forward, Vector3.up);
					LogControllerOrientation(val, handName, movementInput, planarForward);
					if (((Vector3)(ref planarForward)).sqrMagnitude > 1E-06f)
					{
						return ((Vector3)(ref planarForward)).normalized;
					}
				}
				TrackingInput instance = TrackingInput.Instance;
				Transform val2 = ((instance != null) ? instance.HeadTransform : null);
				if ((Object)(object)val2 != (Object)null)
				{
					Vector3 val3 = Vector3.ProjectOnPlane(val2.forward, Vector3.up);
					if (((Vector3)(ref val3)).sqrMagnitude > 0.0001f)
					{
						return ((Vector3)(ref val3)).normalized;
					}
				}
			}
			catch
			{
			}
			return SpectateHeadBridge.GetBaseForward();
		}

		internal static Vector3 GetAbilityForward()
		{
			//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_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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_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_004e: Unknown result type (might be due to invalid IL or missing references)
			AbilityDirectionSource abilityDirection = AbilityDirection;
			if (1 == 0)
			{
			}
			Vector3 val = ((abilityDirection != AbilityDirectionSource.Head) ? GetControllerAimForward() : GetHeadAimForward());
			if (1 == 0)
			{
			}
			Vector3 val2 = val;
			if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f)
			{
				return SpectateHeadBridge.GetAlignedForward();
			}
			return ((Vector3)(ref val2)).normalized;
		}

		private static Vector3 GetHeadAimForward()
		{
			//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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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)
			Transform val = SpectateHeadBridge.GetHmdCameraTransform() ?? SpectateHeadBridge.GetAlignedCameraTransform();
			if ((Object)(object)val != (Object)null)
			{
				return val.forward;
			}
			return SpectateHeadBridge.GetAlignedForward();
		}

		private static Vector3 GetControllerAimForward()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				string handName;
				Transform val = TryGetPreferredControllerTransform(GetAbilityGripSelection(), out handName);
				if ((Object)(object)val != (Object)null)
				{
					return val.forward;
				}
				TrackingInput instance = TrackingInput.Instance;
				Transform val2 = ((instance != null) ? instance.HeadTransform : null);
				if ((Object)(object)val2 != (Object)null)
				{
					return val2.forward;
				}
			}
			catch
			{
			}
			return SpectateHeadBridge.GetAlignedForward();
		}

		private static GripSelection GetCameraGripSelection()
		{
			return GripSelectionHelper.Parse(FeatureFlags.CameraGripPreference);
		}

		private static GripSelection GetAbilityGripSelection()
		{
			return GripSelectionHelper.Parse(FeatureFlags.AbilityGripPreference);
		}

		private static Transform? TryGetPreferredControllerTransform(GripSelection preference, out string handName)
		{
			TrackingInput instance = TrackingInput.Instance;
			bool flag = GripSelectionHelper.ShouldUseLeft(preference);
			handName = (flag ? "Left" : "Right");
			Transform val = ((!flag) ? ((instance != null) ? instance.RightHandTransform : null) : ((instance != null) ? instance.LeftHandTransform : null));
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			VRSession instance2 = VRSession.Instance;
			VRPlayer val2 = ((instance2 != null) ? instance2.Player : null);
			if ((Object)(object)val2 != (Object)null)
			{
				Transform secondaryHand = val2.SecondaryHand;
				if ((Object)(object)secondaryHand != (Object)null)
				{
					handName = (flag ? "Left" : "Right");
					return secondaryHand;
				}
			}
			handName = "unknown";
			return null;
		}

		private static void LogControllerOrientation(Transform transform, string handName, Vector2 movementInput, Vector3 planarForward)
		{
			//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_0041: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (FeatureFlags.LogControllerOrientation && !(((Vector2)(ref movementInput)).sqrMagnitude < 0.0001f) && LogLimiter.Allow("ControllerOrientation", 0.5f))
			{
				Vector3 forward = transform.forward;
				Quaternion rotation = transform.rotation;
				Vector3 eulerAngles = ((Quaternion)(ref rotation)).eulerAngles;
				Log.LogInfo((object)string.Format("{0} Controller orientation {1} forward={2:F3} planar={3:F3} euler={4:F3} input={5:F3}", "[DeathHeadHopperFix-VR] [SpectateCam]", handName, forward, planarForward, eulerAngles, movementInput));
			}
		}

		private static MovementDirectionSource ParseMovementDirection()
		{
			string movementDirectionSource = FeatureFlags.MovementDirectionSource;
			if (Enum.TryParse<MovementDirectionSource>(movementDirectionSource, ignoreCase: true, out var result))
			{
				return result;
			}
			return MovementDirectionSource.Controller;
		}

		private static AbilityDirectionSource ParseAbilityDirection()
		{
			string abilityDirectionSource = FeatureFlags.AbilityDirectionSource;
			if (Enum.TryParse<AbilityDirectionSource>(abilityDirectionSource, ignoreCase: true, out var result))
			{
				return result;
			}
			return AbilityDirectionSource.Head;
		}

		internal static bool TrySwapAimCamera(DHHInputManager instance, out Transform? original)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			original = null;
			if (TMainCameraField == null || (Object)(object)instance == (Object)null)
			{
				return false;
			}
			Vector3 abilityForward = GetAbilityForward();
			if (((Vector3)(ref abilityForward)).sqrMagnitude < 0.0001f)
			{
				return false;
			}
			object? value = TMainCameraField.GetValue(instance);
			Transform val = (Transform)((value is Transform) ? value : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if ((Object)(object)_abilityAimProxy == (Object)null)
			{
				GameObject val2 = new GameObject("DeathHeadHopperVRBridge_AbilityAimProxy");
				Object.DontDestroyOnLoad((Object)(object)val2);
				_abilityAimProxy = val2.transform;
			}
			original = val;
			_abilityAimProxy.position = val.position;
			_abilityAimProxy.rotation = Quaternion.LookRotation(abilityForward, Vector3.up);
			TMainCameraField.SetValue(instance, _abilityAimProxy);
			return true;
		}

		internal static void RestoreAimCamera(DHHInputManager instance, Transform original)
		{
			if (!(TMainCameraField == null) && !((Object)(object)instance == (Object)null) && !((Object)(object)original == (Object)null))
			{
				TMainCameraField.SetValue(instance, original);
			}
		}
	}
	[HarmonyPatch(typeof(DHHInputManager), "Update")]
	internal static class DHHInputManagerUpdatePatch
	{
		private static void Prefix(DHHInputManager __instance)
		{
			if (FeatureFlags.DebugHeadAlignment && LogLimiter.Allow("DHHUpdatePatch", 1f))
			{
				DHHInputBridge.Log.LogInfo((object)"[DeathHeadHopperFix-VR] [SpectateCam] DHHInputManager.Update patch running");
			}
			DHHInputBridge.UpdateCameraReference(__instance);
		}
	}
	[HarmonyPatch(typeof(DHHInputManager), "LateUpdate")]
	internal static class DHHInputManagerLateUpdatePatch
	{
		private static void Prefix(DHHInputManager __instance)
		{
			DHHInputBridge.UpdateCameraReference(__instance);
			bool flag = SpectateHeadBridge.IsSpectatingHead();
			if (FeatureFlags.DebugHeadAlignment && LogLimiter.Allow("LateUpdateSpectate", 2f))
			{
				DHHInputBridge.Log.LogInfo((object)string.Format("{0} LateUpdate running spectatingHead={1}", "[DeathHeadHopperFix-VR] [SpectateCam]", flag));
			}
		}
	}
	[HarmonyPatch(typeof(DHHInputManager), "GetMoveDirection")]
	internal static class DHHInputManagerGetMoveDirectionPatch
	{
		private static bool Prefix(ref Vector3 __result)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			if (!SpectateHeadBridge.IsSpectatingHead())
			{
				if (FeatureFlags.DebugSpectateGuard && LogLimiter.Allow("MovementGuard", 2f))
				{
					DHHInputBridge.Log.LogInfo((object)"[DeathHeadHopperFix-VR] [SpectateCam] Movement guard skipped (not spectating head)");
				}
				return true;
			}
			Vector2 movement;
			bool flag = DHHInputBridge.TryGetAnalogMovement(out movement);
			Vector2 legacyMovement = DHHInputBridge.GetLegacyMovement();
			if (!flag)
			{
				movement = legacyMovement;
			}
			Vector3 val = DHHInputBridge.CalculateMoveDirection(movement);
			if (FeatureFlags.DebugMovementDirection && LogLimiter.Allow("MovementInputs", 1f))
			{
				DHHInputBridge.Log.LogInfo((object)string.Format("{0} Movement patch triggered analog={1} input={2:F3} direction={3:F3} source={4}", "[DeathHeadHopperFix-VR] [SpectateCam]", flag, movement, val, DHHInputBridge.MovementDirection));
			}
			if (!flag && FeatureFlags.DebugMovementDirection && LogLimiter.Allow("MovementFallback", 1f))
			{
				DHHInputBridge.Log.LogInfo((object)string.Format("{0} Falling back to legacy WASD input {1:F3}", "[DeathHeadHopperFix-VR] [SpectateCam]", legacyMovement));
			}
			DHHInputBridge.LogMovement(movement, legacyMovement, flag, val, DHHInputBridge.MovementDirection);
			__result = val;
			return false;
		}
	}
	[HarmonyPatch(typeof(DHHInputManager), "ChargeWindup")]
	internal static class DHHInputManagerChargeWindupPatch
	{
		private static void Prefix(DHHInputManager __instance, ref Transform? __state)
		{
			if (SpectateHeadBridge.IsSpectatingHead() && DHHInputBridge.TrySwapAimCamera(__instance, out Transform original))
			{
				__state = original;
			}
		}

		private static void Postfix(DHHInputManager __instance, Transform? __state)
		{
			if ((Object)(object)__state != (Object)null)
			{
				DHHInputBridge.RestoreAimCamera(__instance, __state);
			}
		}
	}
	[HarmonyPatch(typeof(DHHInputManager), "UpdateChargeWindup")]
	internal static class DHHInputManagerUpdateChargeWindupPatch
	{
		private static void Prefix(DHHInputManager __instance, ref Transform? __state)
		{
			if (SpectateHeadBridge.IsSpectatingHead() && DHHInputBridge.TrySwapAimCamera(__instance, out Transform original))
			{
				__state = original;
			}
		}

		private static void Postfix(DHHInputManager __instance, Transform? __state)
		{
			if ((Object)(object)__state != (Object)null)
			{
				DHHInputBridge.RestoreAimCamera(__instance, __state);
			}
		}
	}
	[HarmonyPatch(typeof(SemiFunc), "InputMovementX")]
	internal static class SemiFuncInputMovementXPatch
	{
		private static bool Prefix(ref float __result)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if (!SpectateHeadBridge.IsSpectatingHead())
			{
				return true;
			}
			if (!MovementAnalog.TryGetAnalog(out var movement))
			{
				return true;
			}
			MovementAnalog.LogAnalog(movement, triggered: true);
			__result = movement.x;
			return false;
		}
	}
	[HarmonyPatch(typeof(SemiFunc), "InputMovementY")]
	internal static class SemiFuncInputMovementYPatch
	{
		private static bool Prefix(ref float __result)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (!SpectateHeadBridge.IsSpectatingHead())
			{
				return true;
			}
			if (!MovementAnalog.TryGetAnalog(out var movement))
			{
				return true;
			}
			__result = movement.y;
			return false;
		}
	}
	internal enum GripSelection
	{
		Auto,
		Left,
		Right
	}
	internal static class GripSelectionHelper
	{
		internal static GripSelection Parse(string raw)
		{
			if (string.IsNullOrWhiteSpace(raw))
			{
				return GripSelection.Auto;
			}
			if (Enum.TryParse<GripSelection>(raw.Trim(), ignoreCase: true, out var result))
			{
				return result;
			}
			return GripSelection.Auto;
		}

		internal static bool ShouldUseLeft(GripSelection selection)
		{
			if (1 == 0)
			{
			}
			bool result = selection switch
			{
				GripSelection.Left => true, 
				GripSelection.Right => false, 
				_ => !VRSession.IsLeftHanded, 
			};
			if (1 == 0)
			{
			}
			return result;
		}
	}
	internal static class Math
	{
		internal static int Max(int x, int y)
		{
			return System.Math.Max(x, y);
		}

		internal static int Clamp(int value, int min, int max)
		{
			if (value < min)
			{
				return min;
			}
			if (value > max)
			{
				return max;
			}
			return value;
		}
	}
	internal static class MovementAnalog
	{
		private static readonly ManualLogSource LogSource = Logger.CreateLogSource("DeathHeadHopperFix-VR.InputBridge");

		internal const string ModuleTag = "[DeathHeadHopperFix-VR] [SpectateCam]";

		internal static ManualLogSource Log => LogSource;

		internal static bool TryGetAnalog(out Vector2 movement)
		{
			//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_0036: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			movement = Vector2.zero;
			InputAction val;
			try
			{
				val = Actions.Instance["Movement"];
			}
			catch
			{
				val = null;
			}
			if (val == null)
			{
				return false;
			}
			movement = val.ReadValue<Vector2>();
			if (((Vector2)(ref movement)).sqrMagnitude > 0.0001f && SpectateHeadBridge.IsSpectatingHead() && FeatureFlags.DebugMovementDirection && LogLimiter.Allow("JoystickSpectateLog", 0.5f))
			{
				LogSource.LogInfo((object)string.Format("{0} Joystick moved while spectating head movement={1:F3}", "[DeathHeadHopperFix-VR] [SpectateCam]", movement));
			}
			return ((Vector2)(ref movement)).sqrMagnitude > 0.0001f;
		}

		internal static void LogAnalog(Vector2 analog, bool triggered)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (FeatureFlags.DebugMovementDirection && LogLimiter.Allow("MovementAnalog", 0.5f))
			{
				LogSource.LogDebug((object)string.Format("{0} Analog movement triggered={1} vector={2:F3}", "[DeathHeadHopperFix-VR] [SpectateCam]", triggered, analog));
			}
		}
	}
	internal static class SpectateHeadBridge
	{
		internal static readonly ManualLogSource Log = Logger.CreateLogSource("DeathHeadHopperFix-VR.HeadBridge");

		internal const string ModuleTag = "[DeathHeadHopperFix-VR] [SpectateCam]";

		private static readonly FieldInfo? SpectateCameraMainCameraField = AccessTools.Field(typeof(SpectateCamera), "MainCamera");

		private static readonly Type? ActionsType = AccessTools.TypeByName("RepoXR.Input.Actions");

		private static readonly PropertyInfo? InstanceProperty = ActionsType?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);

		private static readonly PropertyInfo? IndexerProperty = ActionsType?.GetProperty("Item", new Type[1] { typeof(string) });

		private static readonly FieldInfo? PlayerDeathHeadGrabField = AccessTools.Field(typeof(PlayerDeathHead), "physGrabObject");

		private static Vector3 _baseForward = Vector3.forward;

		private static bool _baseForwardSet;

		private static readonly string[] LeftGripBindings = new string[4] { "GripLeft", "leftGrip", "Grab", "MapGrabLeft" };

		private static readonly string[] RightGripBindings = new string[4] { "GripRight", "rightGrip", "Grab", "MapGrabRight" };

		internal static readonly FieldInfo? DeathHeadSpectatedField = AccessTools.Field(typeof(DeathHeadController), "spectated");

		private static bool _spectatedAnchorActive;

		internal static bool VrModeActive => VRSession.InVR;

		private static object? ActionsInstance => InstanceProperty?.GetValue(null);

		internal static void AlignHeadCamera(bool force)
		{
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			if (!VrModeActive)
			{
				return;
			}
			TraceAlignment($"AlignHeadCamera start force={force}");
			LogImmediate($"AlignHeadCamera immediate start force={force}");
			LogImmediate($"AlignHeadCamera immediate log force={force}");
			SpectateCamera instance = SpectateCamera.instance;
			PlayerAvatar instance2 = PlayerAvatar.instance;
			PlayerDeathHead val = instance2?.playerDeathHead;
			Camera spectateMainCamera = GetSpectateMainCamera(instance);
			if ((Object)(object)instance == (Object)null || (Object)(object)val == (Object)null || (Object)(object)spectateMainCamera == (Object)null)
			{
				TraceAlignment("AlignHeadCamera aborted: missing context or camera");
				return;
			}
			bool flag = IsGripPressed();
			if (FeatureFlags.DebugHeadAlignment && LogLimiter.Allow("AlignHeadCamera", 0.25f))
			{
				Log.LogDebug((object)string.Format("{0} AlignHeadCamera force={1} gripPressed={2}", "[DeathHeadHopperFix-VR] [SpectateCam]", force, flag));
			}
			if (!force && flag)
			{
				if (FeatureFlags.DebugHeadAlignment && LogLimiter.Allow("HeadAlignmentGuard", 0.5f))
				{
					Log.LogInfo((object)"[DeathHeadHopperFix-VR] [SpectateCam] alignment skipped because grip is pressed");
				}
				return;
			}
			object? obj = PlayerDeathHeadGrabField?.GetValue(val);
			Vector3 val2 = ((PhysGrabObject)(((obj is PhysGrabObject) ? obj : null)?)).centerPoint ?? ((Component)val).transform.position;
			Vector3 val3 = Vector3.ProjectOnPlane(((Component)val).transform.forward, Vector3.up);
			if (((Vector3)(ref val3)).sqrMagnitude < 0.0001f && (Object)(object)instance2 != (Object)null)
			{
				val3 = Vector3.ProjectOnPlane(((Component)instance2).transform.forward, Vector3.up);
			}
			if (((Vector3)(ref val3)).sqrMagnitude < 0.0001f)
			{
				val3 = Vector3.forward;
			}
			((Vector3)(ref val3)).Normalize();
			if (force || !_baseForwardSet)
			{
				_baseForward = val3;
				_baseForwardSet = true;
			}
			float headCameraDistance = FeatureFlags.HeadCameraDistance;
			float headCameraHeightOffset = FeatureFlags.HeadCameraHeightOffset;
			Vector3 val4 = val2 + Vector3.up * headCameraHeightOffset - val3 * headCameraDistance;
			Quaternion val5 = Quaternion.LookRotation(val3, Vector3.up);
			float headCameraLerpSpeed = FeatureFlags.HeadCameraLerpSpeed;
			float num = Mathf.Clamp01(headCameraLerpSpeed * Time.deltaTime);
			((Component)instance).transform.position = Vector3.Lerp(((Component)instance).transform.position, val4, num);
			((Component)instance).transform.rotation = Quaternion.Slerp(((Component)instance).transform.rotation, val5, num);
			((Component)spectateMainCamera).transform.position = ((Component)instance).transform.position;
			((Component)spectateMainCamera).transform.rotation = ((Component)instance).transform.rotation;
		}

		internal static bool IsGripPressed()
		{
			return IsGripPressedForCamera();
		}

		internal static bool IsGripPressedForCamera()
		{
			GripSelection selection = GripSelectionHelper.Parse(FeatureFlags.CameraGripPreference);
			return IsGripPressed(selection);
		}

		internal static bool IsGripPressedForAbility()
		{
			GripSelection selection = GripSelectionHelper.Parse(FeatureFlags.AbilityGripPreference);
			return IsGripPressed(selection);
		}

		private static bool IsGripPressed(GripSelection selection)
		{
			bool flag = GripSelectionHelper.ShouldUseLeft(selection);
			string[] array = (flag ? LeftGripBindings : RightGripBindings);
			string[] array2 = array;
			foreach (string text in array2)
			{
				object action = GetAction(text);
				if (IsActionPressed(action))
				{
					if (FeatureFlags.DebugHeadAlignment && LogLimiter.Allow("IsGripPressed", 0.5f))
					{
						Log.LogDebug((object)string.Format("{0} Grip detected on binding {1} (leftGrip={2}) using {3}", "[DeathHeadHopperFix-VR] [SpectateCam]", text, flag, selection));
					}
					return true;
				}
			}
			return false;
		}

		private static Camera? GetSpectateMainCamera(SpectateCamera? spectate)
		{
			if ((Object)(object)spectate == (Object)null || SpectateCameraMainCameraField == null)
			{
				return null;
			}
			object? value = SpectateCameraMainCameraField.GetValue(spectate);
			return (Camera?)((value is Camera) ? value : null);
		}

		internal static Vector3 GetBaseForward()
		{
			//IL_000f: 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_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			return _baseForwardSet ? _baseForward : GetAlignedForward();
		}

		internal static bool IsSpectatingHead()
		{
			if (!VrModeActive)
			{
				return false;
			}
			SpectateCamera instance = SpectateCamera.instance;
			if ((Object)(object)instance != (Object)null && instance.CheckState((State)2))
			{
				TraceAlignment("SpectateCamera reports State.Head");
				return true;
			}
			PlayerDeathHead val = PlayerAvatar.instance?.playerDeathHead;
			if ((Object)(object)val != (Object)null)
			{
				DeathHeadController val2 = null;
				DeathHeadController val3 = default(DeathHeadController);
				if (((Component)val).TryGetComponent<DeathHeadController>(ref val3))
				{
					val2 = val3;
				}
				if ((Object)(object)val2 != (Object)null && DeathHeadSpectatedField != null && DeathHeadSpectatedField.GetValue(val2) is bool flag)
				{
					TraceAlignment($"DeathHeadController.spectated={flag}");
					if (flag)
					{
						return true;
					}
				}
				FieldInfo fieldInfo = AccessTools.Field(typeof(PlayerDeathHead), "overrideSpectated");
				bool flag2 = default(bool);
				int num;
				if (fieldInfo != null)
				{
					object value = fieldInfo.GetValue(val);
					if (value is bool)
					{
						flag2 = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag2 ? 1u : 0u)) != 0)
				{
					TraceAlignment("PlayerDeathHead.overrideSpectated true");
					return true;
				}
			}
			return false;
		}

		internal static Transform? GetAlignedCameraTransform()
		{
			SpectateCamera instance = SpectateCamera.instance;
			Camera spectateMainCamera = GetSpectateMainCamera(instance);
			if ((Object)(object)spectateMainCamera != (Object)null)
			{
				return ((Component)spectateMainCamera).transform;
			}
			return (instance != null) ? ((Component)instance).transform : null;
		}

		internal static Vector3 GetAlignedForward()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			Transform alignedCameraTransform = GetAlignedCameraTransform();
			if ((Object)(object)alignedCameraTransform == (Object)null)
			{
				return Vector3.forward;
			}
			Vector3 val = Vector3.ProjectOnPlane(alignedCameraTransform.forward, Vector3.up);
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				val = alignedCameraTransform.forward;
				val.y = 0f;
			}
			return ((Vector3)(ref val)).normalized;
		}

		private static object? GetAction(string name)
		{
			object actionsInstance = ActionsInstance;
			if (actionsInstance == null || IndexerProperty == null)
			{
				return null;
			}
			try
			{
				return IndexerProperty.GetValue(actionsInstance, new object[1] { name });
			}
			catch
			{
				return null;
			}
		}

		private static bool IsActionPressed(object? action)
		{
			if (action == null)
			{
				return false;
			}
			MethodInfo method = action.GetType().GetMethod("IsPressed", Type.EmptyTypes);
			if (method == null)
			{
				return false;
			}
			try
			{
				object obj = method.Invoke(action, null);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		internal static Transform? GetHmdCameraTransform()
		{
			if (!VrModeActive || (Object)(object)VRSession.Instance == (Object)null)
			{
				return null;
			}
			Camera mainCamera = VRSession.Instance.MainCamera;
			return (mainCamera != null) ? ((Component)mainCamera).transform : null;
		}

		internal static void TraceAlignment(string message)
		{
			if (FeatureFlags.DebugHeadAlignment && LogLimiter.Allow("AlignTrace", 0.5f))
			{
				Log.LogInfo((object)("[DeathHeadHopperFix-VR] [SpectateCam] " + message));
			}
		}

		internal static void LogImmediate(string message)
		{
			if (FeatureFlags.DebugHeadAlignment)
			{
				Log.LogInfo((object)("[DeathHeadHopperFix-VR] [SpectateCam] " + message));
			}
		}

		private static void LogAnchorCameraState(string message, SpectateCamera? spectate)
		{
			//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_002d: 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_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_0084: 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_0053: 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)
			if (FeatureFlags.DebugHeadAlignment)
			{
				Camera spectateMainCamera = GetSpectateMainCamera(spectate);
				Vector3 val = ((spectateMainCamera != null) ? ((Component)spectateMainCamera).transform.forward : ((spectate != null) ? ((Component)spectate).transform.forward : Vector3.zero));
				Vector3 val2 = ((spectateMainCamera != null) ? ((Component)spectateMainCamera).transform.position : ((spectate != null) ? ((Component)spectate).transform.position : Vector3.zero));
				Log.LogInfo((object)string.Format("{0} {1} pos={2:F3} forward={3:F3}", "[DeathHeadHopperFix-VR] [SpectateCam]", message, val2, val));
			}
		}

		internal static void OnSpectatedAnchor(bool value)
		{
			if (value && !_spectatedAnchorActive)
			{
				_spectatedAnchorActive = true;
				TraceAlignment("Spectated anchor activated");
				LogImmediate("Spectated anchor activated");
				AlignHeadCamera(force: true);
				LogAnchorCameraState("Spectated anchor camera", SpectateCamera.instance);
			}
			else if (!value && _spectatedAnchorActive)
			{
				_spectatedAnchorActive = false;
				TraceAlignment("Spectated anchor cleared");
				LogImmediate("Spectated anchor cleared");
			}
		}
	}
	[HarmonyPatch(typeof(SpectateCamera), "StateHead")]
	internal static class SpectateCameraStateHeadPatch
	{
		private static void Postfix()
		{
			SpectateHeadBridge.TraceAlignment("SpectateCamera.StateHead Postfix triggered");
			SpectateHeadBridge.LogImmediate("SpectateCamera.StateHead Postfix triggered");
			if (FeatureFlags.DebugHeadAlignment && LogLimiter.Allow("StateHead.Enter", 0.5f))
			{
				SpectateHeadBridge.Log.LogDebug((object)"[DeathHeadHopperFix-VR] [SpectateCam] SpectateCamera.StateHead entered, queuing head alignment");
			}
			SpectateHeadBridge.AlignHeadCamera(force: false);
		}
	}
	[HarmonyPatch(typeof(SpectateCamera), "UpdateState")]
	internal static class SpectateCameraUpdateStatePatch
	{
		private static State _previousState;

		private static void Postfix(State _state)
		{
			//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_0009: Invalid comparison between Unknown and I4
			_previousState = _state;
			if ((int)_state == 2)
			{
				SpectateHeadBridge.AlignHeadCamera(force: false);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerDeathHead), "OverrideSpectatedReset")]
	internal static class PlayerDeathHeadResetPatch
	{
		private static void Postfix()
		{
			SpectateHeadBridge.TraceAlignment("PlayerDeathHead.OverrideSpectatedReset Postfix triggered");
			SpectateHeadBridge.LogImmediate("PlayerDeathHead.OverrideSpectatedReset Postfix triggered");
		}
	}
	[HarmonyPatch(typeof(DeathHeadController), "SetSpectated")]
	internal static class DeathHeadControllerSetSpectatedPatch
	{
		private static void Postfix(DeathHeadController __instance, bool value)
		{
			SpectateHeadBridge.LogImmediate($"DeathHeadController.SetSpectated value={value}");
			SpectateHeadBridge.OnSpectatedAnchor(value);
		}
	}
	[HarmonyPatch(typeof(PlayerDeathHead), "SpectatedSet")]
	internal static class PlayerDeathHeadSpectatedSetPatch
	{
		private static void Postfix(PlayerDeathHead __instance, bool _active)
		{
			SpectateHeadBridge.LogImmediate($"PlayerDeathHead.SpectatedSet _active={_active}");
			if (_active)
			{
				SpectateHeadBridge.OnSpectatedAnchor(value: true);
			}
		}
	}
	[HarmonyPatch]
	internal static class HeadCameraTurnGripGuard
	{
		private static readonly Type? SpectatePatchesType = AccessTools.TypeByName("RepoXR.Patches.SpectatePatches");

		private static MethodBase? TargetMethod()
		{
			if (SpectatePatchesType == null)
			{
				return null;
			}
			return AccessTools.Method(SpectatePatchesType, "HeadCameraTurnPatch", (Type[])null, (Type[])null);
		}

		private static bool Prefix()
		{
			bool flag = SpectateHeadBridge.IsGripPressedForCamera();
			if (FeatureFlags.DebugHeadAlignment && LogLimiter.Allow("HeadCameraTurnGuard", 0.5f))
			{
				SpectateHeadBridge.Log.LogDebug((object)string.Format("{0} HeadCameraTurnPatch guard gripPressed={1}", "[DeathHeadHopperFix-VR] [SpectateCam]", flag));
			}
			return flag;
		}
	}
	[HarmonyPatch]
	internal static class SpectateMovementGuard
	{
		[HarmonyPatch]
		internal static class SpectateMovementGuardY
		{
			private static MethodBase? TargetMethod()
			{
				Type type = AccessTools.TypeByName("RepoXR.Patches.SpectatePatches");
				return (type == null) ? null : AccessTools.Method(type, "<InputPatch>g__GetRotationY|5_1", (Type[])null, (Type[])null);
			}

			private static bool Prefix(ref float __result)
			{
				return TryGuard(ref __result);
			}
		}

		private static readonly ManualLogSource Log = Logger.CreateLogSource("DeathHeadHopperFix-VR.HeadBridge");

		private static MethodBase? TargetMethod()
		{
			Type type = AccessTools.TypeByName("RepoXR.Patches.SpectatePatches");
			return (type == null) ? null : AccessTools.Method(type, "<InputPatch>g__GetRotationX|5_0", (Type[])null, (Type[])null);
		}

		private static bool Prefix(ref float __result)
		{
			return TryGuard(ref __result);
		}

		private static bool TryGuard(ref float result)
		{
			if (SpectateHeadBridge.IsGripPressedForCamera())
			{
				return true;
			}
			if (FeatureFlags.DebugSpectateGuard && LogLimiter.Allow("MovementGuard.Override", 0.25f))
			{
				Log.LogDebug((object)"[DeathHeadHopperFix-VR] [SpectateCam] Movement guard zeroed rotation because grip is not pressed");
			}
			result = 0f;
			return false;
		}
	}
	internal sealed class VanillaAbilityInputBridge : MonoBehaviour
	{
		[CompilerGenerated]
		private sealed class <ParseActionNames>d__30 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private string <>2__current;

			private int <>l__initialThreadId;

			private string raw;

			public string <>3__raw;

			private string[] <>s__1;

			private int <>s__2;

			private string <part>5__3;

			private string <trimmed>5__4;

			string? IEnumerator<string>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object? IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <ParseActionNames>d__30(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>s__1 = null;
				<part>5__3 = null;
				<trimmed>5__4 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					goto IL_00b6;
				}
				<>1__state = -1;
				if (string.IsNullOrWhiteSpace(raw))
				{
					return false;
				}
				<>s__1 = raw.Split(new char[2] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries);
				<>s__2 = 0;
				goto IL_00d3;
				IL_00b6:
				<trimmed>5__4 = null;
				<part>5__3 = null;
				<>s__2++;
				goto IL_00d3;
				IL_00d3:
				if (<>s__2 < <>s__1.Length)
				{
					<part>5__3 = <>s__1[<>s__2];
					<trimmed>5__4 = <part>5__3.Trim();
					if (!string.IsNullOrEmpty(<trimmed>5__4))
					{
						<>2__current = <trimmed>5__4;
						<>1__state = 1;
						return true;
					}
					goto IL_00b6;
				}
				<>s__1 = null;
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<string> IEnumerable<string>.GetEnumerator()
			{
				<ParseActionNames>d__30 <ParseActionNames>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<ParseActionNames>d__ = this;
				}
				else
				{
					<ParseActionNames>d__ = new <ParseActionNames>d__30(0);
				}
				<ParseActionNames>d__.raw = <>3__raw;
				return <ParseActionNames>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<string>)this).GetEnumerator();
			}
		}

		private const int SlotCount = 3;

		private static readonly Type? RepoXRActionsType = AccessTools.TypeByName("RepoXR.Input.Actions");

		private static readonly PropertyInfo? RepoXRActionsInstance = RepoXRActionsType?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);

		private static readonly PropertyInfo? RepoXRActionsIndexer = RepoXRActionsType?.GetProperty("Item", new Type[1] { typeof(string) });

		private static readonly Type? InputActionType = AccessTools.TypeByName("UnityEngine.InputSystem.InputAction");

		private static readonly MethodInfo? WasPressedMethod = InputActionType?.GetMethod("WasPressedThisFrame", Type.EmptyTypes);

		private static readonly MethodInfo? WasReleasedMethod = InputActionType?.GetMethod("WasReleasedThisFrame", Type.EmptyTypes);

		private static readonly MethodInfo? IsPressedMethod = InputActionType?.GetMethod("IsPressed", Type.EmptyTypes);

		private static readonly PropertyInfo? DhhInstanceProperty = AccessTools.Property(typeof(DHHAbilityManager), "instance");

		private static readonly FieldInfo? AbilitySpotsField = AccessTools.Field(typeof(DHHAbilityManager), "abilitySpots");

		private static readonly MethodInfo? HandleInputDownMethod = AccessTools.Method(typeof(DHHAbilityManager), "HandleInputDown", new Type[1] { typeof(AbilitySpot) }, (Type[])null);

		private static readonly MethodInfo? HandleInputHoldMethod = AccessTools.Method(typeof(DHHAbilityManager), "HandleInputHold", new Type[1] { typeof(AbilitySpot) }, (Type[])null);

		private static readonly MethodInfo? HandleInputUpMethod = AccessTools.Method(typeof(DHHAbilityManager), "HandleInputUp", new Type[1] { typeof(AbilitySpot) }, (Type[])null);

		private static readonly MethodInfo? HasEquippedAbilityMethod = AccessTools.Method(typeof(DHHAbilityManager), "HasEquippedAbility", (Type[])null, (Type[])null);

		private static readonly FieldInfo? BackgroundIconField = AccessTools.Field(typeof(AbilitySpot), "backgroundIcon");

		internal static readonly ManualLogSource Log = Logger.CreateLogSource("DeathHeadHopperFix-VR.VanillaAbility");

		internal const string ModuleTag = "[DeathHeadHopperFix-VR] [VanillaAbility]";

		private static VanillaAbilityInputBridge? _instance;

		private AbilitySpot? _slotDownSpot;

		private object? _activeAction;

		private Quaternion? _savedAvatarRotation;

		private const int PrimarySlotIndex = 0;

		public static void EnsureAttached(GameObject host)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (!((Object)(object)_instance != (Object)null))
			{
				GameObject val = new GameObject("DeathHeadHopperVRBridge_VanillaAbilityBridge");
				Object.DontDestroyOnLoad((Object)(object)val);
				val.transform.SetParent(host.transform, false);
				_instance = val.AddComponent<VanillaAbilityInputBridge>();
			}
		}

		private void Update()
		{
			if (!FeatureFlags.EnableVanillaAbilityBridge)
			{
				ReleaseAbility();
				return;
			}
			if (!SpectateHeadBridge.IsSpectatingHead())
			{
				ReleaseAbility();
				return;
			}
			if (!TryPreparePrimarySpot(out AbilitySpot[] _, out AbilitySpot primarySpot))
			{
				ReleaseAbility();
				return;
			}
			bool flag = SpectateHeadBridge.IsGripPressedForAbility();
			LogDebug("ListeningInputs", flag ? "Listening for the primary ability slot while DeathHeadHopper is active and the configured ability grip is held." : "Waiting for the configured ability grip before reacting to the primary ability slot.");
			HandleActivation(primarySpot, flag);
		}

		private bool TryPreparePrimarySpot(out AbilitySpot[]? spots, out AbilitySpot? primarySpot)
		{
			spots = null;
			primarySpot = null;
			DHHAbilityManager abilityManager = GetAbilityManager();
			if ((Object)(object)abilityManager == (Object)null)
			{
				return false;
			}
			if (!ManagerHasEquipped(abilityManager))
			{
				return false;
			}
			spots = GetAbilitySpots(abilityManager);
			if (spots == null || spots.Length == 0)
			{
				return false;
			}
			AbilitySpot abilitySpot = GetAbilitySpot(spots, 0);
			if ((Object)(object)abilitySpot == (Object)null || (Object)(object)abilitySpot.CurrentAbility == (Object)null)
			{
				LogDebug("PrimarySlotMissing", "Primary ability slot is empty while DeathHeadHopper is active.");
				return false;
			}
			primarySpot = abilitySpot;
			return true;
		}

		private void HandleActivation(AbilitySpot spot, bool allowStart)
		{
			string[] array = ParseActionNames(FeatureFlags.AbilityActivateAction).ToArray();
			object firstAvailableAction = GetFirstAvailableAction(array);
			if (firstAvailableAction == null)
			{
				ReleaseAbility();
				LogDebug("ActivateActionMissing", "Activate action not found (" + string.Join(", ", array) + ")");
				return;
			}
			LogDebug("ActivateActionReady", string.Format("Activate action ready ({0}). Primary slot index={1}", string.Join(", ", array), 0));
			if (allowStart && WasActionPressed(firstAvailableAction))
			{
				LogDebug("ActivateActionTriggered", "Left stick click (L3) triggered activation for the primary slot while DeathHeadHopper is active.");
				ReleaseAbility();
				AlignAvatarToCamera();
				InvokeAbilityMethod(HandleInputDownMethod, spot);
				_slotDownSpot = spot;
				_activeAction = firstAvailableAction;
				LogDebug("AbilityStart", string.Format("Started primary ability slot {0} ({1})", 0, ((object)spot.CurrentAbility)?.GetType().Name ?? "none"));
			}
			if ((Object)(object)_slotDownSpot != (Object)null && _activeAction != null)
			{
				if (IsActionPressed(_activeAction))
				{
					InvokeAbilityMethod(HandleInputHoldMethod, _slotDownSpot);
					LogDebug("AbilityHold", string.Format("Holding primary ability slot {0} ({1})", 0, ((object)_slotDownSpot.CurrentAbility)?.GetType().Name ?? "none"));
				}
				if (WasActionReleased(_activeAction))
				{
					ReleaseAbility();
				}
			}
		}

		private void ReleaseAbility()
		{
			if ((Object)(object)_slotDownSpot == (Object)null)
			{
				_activeAction = null;
				return;
			}
			InvokeAbilityMethod(HandleInputUpMethod, _slotDownSpot);
			LogDebug("AbilityRelease", $"Released primary ability slot {0}");
			_slotDownSpot = null;
			_activeAction = null;
			RestoreAvatarAlignment();
		}

		private static AbilitySpot? GetAbilitySpot(AbilitySpot[] spots, int index)
		{
			if (spots.Length == 0)
			{
				return null;
			}
			int num = index;
			if (num < 0)
			{
				num = 0;
			}
			if (num >= spots.Length)
			{
				num = spots.Length - 1;
			}
			return spots[num];
		}

		private void AlignAvatarToCamera()
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			if (_savedAvatarRotation.HasValue)
			{
				return;
			}
			PlayerAvatar instance = PlayerAvatar.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				_savedAvatarRotation = ((Component)instance).transform.rotation;
				Vector3 val = DHHInputBridge.GetAbilityForward();
				val = Vector3.ProjectOnPlane(val, Vector3.up);
				if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f))
				{
					((Component)instance).transform.rotation = Quaternion.LookRotation(val, Vector3.up);
					LogDebug("AbilityOrient", $"Aligned avatar forward to {val:F3}");
				}
			}
		}

		private void RestoreAvatarAlignment()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			PlayerAvatar instance = PlayerAvatar.instance;
			if (!((Object)(object)instance == (Object)null) && _savedAvatarRotation.HasValue)
			{
				((Component)instance).transform.rotation = _savedAvatarRotation.Value;
				_savedAvatarRotation = null;
				LogDebug("AbilityOrient", "Restored avatar rotation");
			}
		}

		[IteratorStateMachine(typeof(<ParseActionNames>d__30))]
		private static IEnumerable<string> ParseActionNames(string raw)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <ParseActionNames>d__30(-2)
			{
				<>3__raw = raw
			};
		}

		private static object? GetFirstAvailableAction(IEnumerable<string> names)
		{
			foreach (string name in names)
			{
				object action = GetAction(name);
				if (action != null)
				{
					return action;
				}
			}
			return null;
		}

		private static object? GetAction(string name)
		{
			object obj = RepoXRActionsInstance?.GetValue(null);
			if (obj == null || RepoXRActionsIndexer == null)
			{
				return null;
			}
			try
			{
				return RepoXRActionsIndexer.GetValue(obj, new object[1] { name });
			}
			catch
			{
				return null;
			}
		}

		private static bool WasActionPressed(object? action)
		{
			return InvokeBooleanMethod(WasPressedMethod, action);
		}

		private static bool WasActionReleased(object? action)
		{
			return InvokeBooleanMethod(WasReleasedMethod, action);
		}

		private static bool IsActionPressed(object? action)
		{
			return InvokeBooleanMethod(IsPressedMethod, action);
		}

		private static bool InvokeBooleanMethod(MethodInfo? method, object? action)
		{
			if (method == null || action == null)
			{
				return false;
			}
			try
			{
				object obj = method.Invoke(action, Array.Empty<object>());
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		private static DHHAbilityManager? GetAbilityManager()
		{
			object? obj = DhhInstanceProperty?.GetValue(null);
			return (DHHAbilityManager?)((obj is DHHAbilityManager) ? obj : null);
		}

		private static AbilitySpot[]? GetAbilitySpots(DHHAbilityManager? manager)
		{
			if (AbilitySpotsField == null || (Object)(object)manager == (Object)null)
			{
				return null;
			}
			return AbilitySpotsField.GetValue(manager) as AbilitySpot[];
		}

		private static void InvokeAbilityMethod(MethodInfo? method, AbilitySpot spot)
		{
			DHHAbilityManager abilityManager = GetAbilityManager();
			if (method == null || (Object)(object)abilityManager == (Object)null)
			{
				return;
			}
			try
			{
				method.Invoke(abilityManager, new object[1] { spot });
			}
			catch
			{
			}
		}

		private static bool ManagerHasEquipped(DHHAbilityManager manager)
		{
			if (HasEquippedAbilityMethod == null)
			{
				return false;
			}
			try
			{
				object obj = HasEquippedAbilityMethod.Invoke(manager, Array.Empty<object>());
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		private void LogDebug(string key, string message, float interval = 0.5f)
		{
			if (FeatureFlags.DebugAbility && LogLimiter.Allow(key, interval))
			{
				Log.LogInfo((object)("[DeathHeadHopperFix-VR] [VanillaAbility] " + message));
			}
		}

		private void OnDestroy()
		{
			_instance = null;
		}
	}
	internal sealed class VrAbilityBarBridge : MonoBehaviour
	{
		private const int SlotCount = 3;

		private static readonly string[] ConfirmActionNames = new string[3] { "Grab", "Interact", "Push" };

		private static readonly Type? RepoXRActionsType = AccessTools.TypeByName("RepoXR.Input.Actions");

		private static readonly PropertyInfo? RepoXRActionsInstance = RepoXRActionsType?.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public);

		private static readonly PropertyInfo? RepoXRActionsIndexer = RepoXRActionsType?.GetProperty("Item", new Type[1] { typeof(string) });

		private static readonly Type? InputActionType = AccessTools.TypeByName("UnityEngine.InputSystem.InputAction");

		private static readonly MethodInfo? ReadValueGenericDefinition = InputActionType?.GetMethods().FirstOrDefault((MethodInfo m) => m.Name == "ReadValue" && m.IsGenericMethodDefinition && m.GetParameters().Length == 0);

		private static readonly MethodInfo? WasPressedThisFrameMethod = InputActionType?.GetMethod("WasPressedThisFrame", Type.EmptyTypes);

		private static readonly MethodInfo? WasReleasedThisFrameMethod = InputActionType?.GetMethod("WasReleasedThisFrame", Type.EmptyTypes);

		private static readonly MethodInfo? IsPressedMethod = InputActionType?.GetMethod("IsPressed", Type.EmptyTypes);

		private static readonly PropertyInfo? DhhInstanceProperty = AccessTools.Property(typeof(DHHAbilityManager), "instance");

		private static readonly FieldInfo? SpectateCameraStateField = AccessTools.Field(typeof(SpectateCamera), "currentState");

		private static readonly MethodInfo? HasEquippedAbilityMethod = AccessTools.Method(typeof(DHHAbilityManager), "HasEquippedAbility", (Type[])null, (Type[])null);

		private static readonly FieldInfo? AbilitySpotsField = AccessTools.Field(typeof(DHHAbilityManager), "abilitySpots");

		private static readonly MethodInfo? HandleInputDownMethod = AccessTools.Method(typeof(DHHAbilityManager), "HandleInputDown", new Type[1] { typeof(AbilitySpot) }, (Type[])null);

		private static readonly MethodInfo? HandleInputHoldMethod = AccessTools.Method(typeof(DHHAbilityManager), "HandleInputHold", new Type[1] { typeof(AbilitySpot) }, (Type[])null);

		private static readonly MethodInfo? HandleInputUpMethod = AccessTools.Method(typeof(DHHAbilityManager), "HandleInputUp", new Type[1] { typeof(AbilitySpot) }, (Type[])null);

		private static VrAbilityBarBridge? _instance;

		private readonly GameObject[] _markers = (GameObject[])(object)new GameObject[3];

		private readonly Renderer[] _renderers = (Renderer[])(object)new Renderer[3];

		private readonly Material[] _materials = (Material[])(object)new Material[3];

		private int _selectedSlot = 1;

		private int _slotDown = -1;

		private bool _visible;

		public static void EnsureAttached(GameObject host)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (!((Object)(object)_instance != (Object)null))
			{
				GameObject val = new GameObject("DeathHeadHopperVRBridge_VrAbilityBridge");
				Object.DontDestroyOnLoad((Object)(object)val);
				val.transform.SetParent(host.transform, false);
				_instance = val.AddComponent<VrAbilityBarBridge>();
			}
		}

		private void Awake()
		{
			for (int i = 0; i < 3; i++)
			{
				_markers[i] = CreateMarker($"AbilitySlot{i}");
				_renderers[i] = _markers[i].GetComponent<Renderer>();
				_materials[i] = _renderers[i].material;
				_renderers[i].enabled = false;
			}
		}

		private void Update()
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Invalid comparison between Unknown and I4
			SpectateCamera instance = SpectateCamera.instance;
			DHHAbilityManager abilityManager = GetAbilityManager();
			if ((Object)(object)instance == (Object)null || (Object)(object)abilityManager == (Object)null)
			{
				SetMarkersVisible(visible: false);
				ReleaseAbility();
				return;
			}
			if ((int)GetSpectateState().GetValueOrDefault() != 2 || !ManagerHasEquipped(abilityManager))
			{
				SetMarkersVisible(visible: false);
				ReleaseAbility();
				return;
			}
			bool flag = SpectateHeadBridge.IsGripPressedForAbility();
			SetMarkersVisible(flag);
			if (!flag)
			{
				ReleaseAbility();
				return;
			}
			UpdateSelection(ReadTurnValue());
			if (_visible)
			{
				UpdateMarkerVisuals();
				PositionMarkers(((Component)instance).transform);
			}
			object firstAvailableAction = GetFirstAvailableAction(ConfirmActionNames);
			TryStartAbility(firstAvailableAction);
			if (_slotDown != -1)
			{
				if (firstAvailableAction != null && IsActionPressed(firstAvailableAction))
				{
					MaintainAbilityHold();
				}
				if (firstAvailableAction == null || WasActionReleased(firstAvailableAction))
				{
					ReleaseAbility();
				}
			}
		}

		private GameObject CreateMarker(string name)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			GameObject val = GameObject.CreatePrimitive((PrimitiveType)3);
			((Object)val).name = name;
			((Object)val).hideFlags = (HideFlags)61;
			Collider component = val.GetComponent<Collider>();
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
			Renderer component2 = val.GetComponent<Renderer>();
			component2.shadowCastingMode = (ShadowCastingMode)0;
			component2.receiveShadows = false;
			component2.material = new Material(Shader.Find("Unlit/Color") ?? Shader.Find("Standard"))
			{
				color = new Color(0.08f, 0.45f, 0.9f, 0.35f),
				hideFlags = (HideFlags)61
			};
			return val;
		}

		private void PositionMarkers(Transform cameraTransform)
		{
			//IL_0015: 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_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_002f: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: 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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			if (_visible)
			{
				Vector3 val = cameraTransform.position + cameraTransform.forward * FeatureFlags.AbilityCursorDistance + Vector3.up * FeatureFlags.AbilityCursorVerticalOffset;
				Quaternion val2 = Quaternion.LookRotation(cameraTransform.forward, Vector3.up);
				for (int i = 0; i < 3; i++)
				{
					Vector3 val3 = val2 * Vector3.right * ((float)(i - 1) * FeatureFlags.AbilityCursorSpacing);
					GameObject val4 = _markers[i];
					val4.transform.position = val + val3;
					val4.transform.rotation = val2;
					float num = FeatureFlags.AbilityCursorScale * ((i == _selectedSlot) ? 1.45f : 1f);
					val4.transform.localScale = Vector3.one * num;
				}
			}
		}

		private void SetMarkersVisible(bool visible)
		{
			if (_visible != visible)
			{
				_visible = visible;
				for (int i = 0; i < 3; i++)
				{
					_renderers[i].enabled = visible;
				}
			}
		}

		private void UpdateSelection(float turnValue)
		{
			if (turnValue < 0f - FeatureFlags.AbilitySelectionDeadzone)
			{
				_selectedSlot = 0;
			}
			else if (turnValue > FeatureFlags.AbilitySelectionDeadzone)
			{
				_selectedSlot = 2;
			}
			else
			{
				_selectedSlot = 1;
			}
		}

		private void UpdateMarkerVisuals()
		{
			//IL_0060: 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)
			if (_visible)
			{
				Color val = default(Color);
				((Color)(ref val))..ctor(0.08f, 0.45f, 0.9f, 0.35f);
				Color val2 = default(Color);
				((Color)(ref val2))..ctor(1f, 0.85f, 0.25f, 0.95f);
				for (int i = 0; i < 3; i++)
				{
					_materials[i].color = ((i == _selectedSlot) ? val2 : val);
				}
			}
		}

		private void TryStartAbility(object? confirmAction)
		{
			if (confirmAction != null && WasActionPressed(confirmAction))
			{
				ReleaseAbility();
				AbilitySpot abilitySpot = GetAbilitySpot(_selectedSlot);
				if (!((Object)(object)abilitySpot == (Object)null) && !((Object)(object)abilitySpot.CurrentAbility == (Object)null))
				{
					InvokeAbilityMethod(HandleInputDownMethod, abilitySpot);
					_slotDown = _selectedSlot;
				}
			}
		}

		private void MaintainAbilityHold()
		{
			if (_slotDown != -1)
			{
				AbilitySpot abilitySpot = GetAbilitySpot(_slotDown);
				if ((Object)(object)abilitySpot == (Object)null)
				{
					_slotDown = -1;
				}
				else
				{
					InvokeAbilityMethod(HandleInputHoldMethod, abilitySpot);
				}
			}
		}

		private void ReleaseAbility()
		{
			if (_slotDown != -1)
			{
				AbilitySpot abilitySpot = GetAbilitySpot(_slotDown);
				if ((Object)(object)abilitySpot != (Object)null)
				{
					InvokeAbilityMethod(HandleInputUpMethod, abilitySpot);
				}
				_slotDown = -1;
			}
		}

		private AbilitySpot? GetAbilitySpot(int index)
		{
			DHHAbilityManager abilityManager = GetAbilityManager();
			if (AbilitySpotsField == null || (Object)(object)abilityManager == (Object)null)
			{
				return null;
			}
			if (!(AbilitySpotsField.GetValue(abilityManager) is AbilitySpot[] array) || index < 0 || index >= array.Length)
			{
				return null;
			}
			return array[index];
		}

		private object? GetFirstAvailableAction(string[] names)
		{
			foreach (string name in names)
			{
				object action = GetAction(name);
				if (action != null)
				{
					return action;
				}
			}
			return null;
		}

		private static object? GetAction(string name)
		{
			object obj = RepoXRActionsInstance?.GetValue(null);
			if (obj == null || RepoXRActionsIndexer == null)
			{
				return null;
			}
			try
			{
				return RepoXRActionsIndexer.GetValue(obj, new object[1] { name });
			}
			catch
			{
				return null;
			}
		}

		private static bool IsActionPressed(object? action)
		{
			return InvokeBooleanMethod(IsPressedMethod, action);
		}

		private static bool WasActionPressed(object? action)
		{
			return InvokeBooleanMethod(WasPressedThisFrameMethod, action);
		}

		private static bool WasActionReleased(object? action)
		{
			return InvokeBooleanMethod(WasReleasedThisFrameMethod, action);
		}

		private float ReadTurnValue()
		{
			return ReadFloat(GetAction("Turn"));
		}

		private static float ReadFloat(object? action)
		{
			if (action == null || ReadValueGenericDefinition == null)
			{
				return 0f;
			}
			try
			{
				MethodInfo methodInfo = ReadValueGenericDefinition.MakeGenericMethod(typeof(float));
				return (methodInfo.Invoke(action, Array.Empty<object>()) is float num) ? num : 0f;
			}
			catch
			{
				return 0f;
			}
		}

		private static bool InvokeBooleanMethod(MethodInfo? method, object? action)
		{
			if (method == null || action == null)
			{
				return false;
			}
			try
			{
				object obj = method.Invoke(action, Array.Empty<object>());
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		private static void InvokeAbilityMethod(MethodInfo? method, AbilitySpot spot)
		{
			DHHAbilityManager abilityManager = GetAbilityManager();
			if (method == null || (Object)(object)abilityManager == (Object)null)
			{
				return;
			}
			try
			{
				method.Invoke(abilityManager, new object[1] { spot });
			}
			catch
			{
			}
		}

		private static DHHAbilityManager? GetAbilityManager()
		{
			object? obj = DhhInstanceProperty?.GetValue(null);
			return (DHHAbilityManager?)((obj is DHHAbilityManager) ? obj : null);
		}

		private static State? GetSpectateState()
		{
			//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_005a: Unknown result type (might be due to invalid IL or missing references)
			SpectateCamera instance = SpectateCamera.instance;
			if ((Object)(object)instance == (Object)null || SpectateCameraStateField == null)
			{
				return null;
			}
			return (SpectateCameraStateField.GetValue(instance) is State value) ? new State?(value) : null;
		}

		private static bool ManagerHasEquipped(DHHAbilityManager manager)
		{
			if (HasEquippedAbilityMethod == null)
			{
				return false;
			}
			try
			{
				object obj = HasEquippedAbilityMethod.Invoke(manager, Array.Empty<object>());
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		private void OnDestroy()
		{
			GameObject[] markers = _markers;
			foreach (GameObject val in markers)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.Destroy((Object)(object)val);
				}
			}
			Material[] materials = _materials;
			foreach (Material val2 in materials)
			{
				if ((Object)(object)val2 != (Object)null)
				{
					Object.Destroy((Object)(object)val2);
				}
			}
			_instance = null;
		}
	}
}
namespace DeathHeadHopperVRBridge.Modules.Logging
{
	internal static class LogLimiter
	{
		private static readonly Dictionary<string, float> _next = new Dictionary<string, float>();

		internal static bool Allow(string key, float everySeconds)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (_next.TryGetValue(key, out var value) && realtimeSinceStartup < value)
			{
				return false;
			}
			_next[key] = realtimeSinceStartup + everySeconds;
			return true;
		}
	}
}
namespace DeathHeadHopperVRBridge.Modules.Config
{
	[AttributeUsage(AttributeTargets.Field)]
	internal sealed class FeatureConfigEntryAttribute : Attribute
	{
		public string Section { get; }

		public string Description { get; }

		public string Key { get; set; } = string.Empty;


		public float Min { get; set; } = float.NaN;


		public float Max { get; set; } = float.NaN;


		public float Step { get; set; } = float.NaN;


		public string[] AcceptableValues { get; set; } = Array.Empty<string>();


		public bool HasRange => !float.IsNaN(Min) && !float.IsNaN(Max);

		public FeatureConfigEntryAttribute(string section, string description)
		{
			Section = section;
			Description = description;
		}
	}
	internal static class ConfigManager
	{
		private struct RangeF
		{
			public float Min;

			public float Max;

			public float Step;
		}

		private struct RangeI
		{
			public int Min;

			public int Max;

			public int Step;
		}

		private static bool s_initialized;

		private static readonly char[] ColorSeparators = new char[2] { ',', ';' };

		private static readonly Dictionary<string, RangeF> s_floatRanges = new Dictionary<string, RangeF>(StringComparer.Ordinal);

		private static readonly Dictionary<string, RangeI> s_intRanges = new Dictionary<string, RangeI>(StringComparer.Ordinal);

		internal static void Initialize(ConfigFile config)
		{
			if (!s_initialized && config != null)
			{
				s_initialized = true;
				BindConfigEntries(config, typeof(FeatureFlags), "General");
			}
		}

		private static void BindConfigEntries(ConfigFile config, Type targetType, string defaultSection)
		{
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Expected O, but got Unknown
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Expected O, but got Unknown
			//IL_0307: Unknown result type (might be due to invalid IL or missing references)
			//IL_030c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			FieldInfo[] fields = targetType.GetFields(BindingFlags.Static | BindingFlags.Public);
			foreach (FieldInfo field in fields)
			{
				FeatureConfigEntryAttribute customAttribute = field.GetCustomAttribute<FeatureConfigEntryAttribute>();
				if (customAttribute == null)
				{
					continue;
				}
				string text = (string.IsNullOrWhiteSpace(customAttribute.Section) ? defaultSection : customAttribute.Section);
				string text2 = (string.IsNullOrWhiteSpace(customAttribute.Key) ? field.Name : customAttribute.Key);
				string text3 = customAttribute.Description ?? string.Empty;
				if (field.FieldType == typeof(bool))
				{
					bool flag = (bool)field.GetValue(null);
					ConfigEntry<bool> entry = config.Bind<bool>(text, text2, flag, text3);
					ApplyAndWatch(entry, delegate(bool value)
					{
						field.SetValue(null, value);
					});
				}
				else if (field.FieldType == typeof(int))
				{
					int num = (int)field.GetValue(null);
					ConfigEntry<int> entry2;
					if (customAttribute.HasRange)
					{
						int intRangeStart = GetIntRangeStart(customAttribute);
						int intRangeEnd = GetIntRangeEnd(customAttribute);
						entry2 = config.Bind<int>(text, text2, num, new ConfigDescription(text3, (AcceptableValueBase)(object)new AcceptableValueRange<int>(intRangeStart, intRangeEnd), Array.Empty<object>()));
						RegisterIntRange(text2, intRangeStart, intRangeEnd, DetermineIntStep(customAttribute));
					}
					else
					{
						entry2 = config.Bind<int>(text, text2, num, text3);
					}
					ApplyAndWatch(entry2, delegate(int value)
					{
						field.SetValue(null, value);
					});
				}
				else if (field.FieldType == typeof(float))
				{
					float num2 = (float)field.GetValue(null);
					ConfigEntry<float> entry3;
					if (customAttribute.HasRange)
					{
						float num3 = System.Math.Min(customAttribute.Min, customAttribute.Max);
						float num4 = System.Math.Max(customAttribute.Min, customAttribute.Max);
						entry3 = config.Bind<float>(text, text2, num2, new ConfigDescription(text3, (AcceptableValueBase)(object)new AcceptableValueRange<float>(num3, num4), Array.Empty<object>()));
						RegisterFloatRange(text2, num3, num4, DetermineFloatStep(customAttribute, num3));
					}
					else
					{
						entry3 = config.Bind<float>(text, text2, num2, text3);
					}
					ApplyAndWatch(entry3, delegate(float value)
					{
						field.SetValue(null, value);
					});
				}
				else if (field.FieldType == typeof(string))
				{
					string text4 = (field.GetValue(null) as string) ?? string.Empty;
					ConfigEntry<string> entry4 = config.Bind<string>(text, text2, text4, BuildStringDescription(text3, customAttribute.AcceptableValues));
					ApplyAndWatch(entry4, delegate(string value)
					{
						field.SetValue(null, value);
					});
				}
				else if (field.FieldType == typeof(Color))
				{
					Color input = (Color)field.GetValue(null);
					ConfigEntry<string> entry5 = config.Bind<string>(text, text2, ColorToString(input), text3);
					ApplyAndWatch(entry5, ColorFromString, delegate(Color value)
					{
						//IL_0007: Unknown result type (might be due to invalid IL or missing references)
						field.SetValue(null, value);
					});
				}
			}
		}

		private static int GetIntRangeStart(FeatureConfigEntryAttribute attribute)
		{
			ValidateIntegerRange(attribute);
			return (int)System.Math.Min(attribute.Min, attribute.Max);
		}

		private static int GetIntRangeEnd(FeatureConfigEntryAttribute attribute)
		{
			ValidateIntegerRange(attribute);
			return (int)System.Math.Max(attribute.Min, attribute.Max);
		}

		private static void ValidateIntegerRange(FeatureConfigEntryAttribute attribute)
		{
			if (!IsWholeNumber(attribute.Min) || !IsWholeNumber(attribute.Max))
			{
				throw new InvalidOperationException("FeatureConfigEntryAttribute integer range values must be whole numbers.");
			}
		}

		private static bool IsWholeNumber(float value)
		{
			double num = System.Math.Truncate(value);
			return System.Math.Abs((double)value - num) < 1.401298464324817E-45;
		}

		private static void ApplyAndWatch<T>(ConfigEntry<T> entry, Action<T> setter)
		{
			Action<T> setter2 = setter;
			ConfigEntry<T> entry2 = entry;
			if (entry2 != null && setter2 != null)
			{
				Update();
				entry2.SettingChanged += delegate
				{
					Update();
				};
			}
			void Update()
			{
				setter2(SanitizeValue(entry2.Value, ((ConfigEntryBase)entry2).Definition.Key));
			}
		}

		private static void ApplyAndWatch(ConfigEntry<string> entry, Func<string, Color> parser, Action<Color> setter)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			Action<Color> setter2 = setter;
			Func<string, Color> parser2 = parser;
			ConfigEntry<string> entry2 = entry;
			if (entry2 != null && parser2 != null && setter2 != null)
			{
				setter2(parser2(entry2.Value));
				entry2.SettingChanged += delegate
				{
					//IL_0017: Unknown result type (might be due to invalid IL or missing references)
					setter2(parser2(entry2.Value));
				};
			}
		}

		private static string ColorToString(Color input)
		{
			return string.Join(",", input.r.ToString(CultureInfo.InvariantCulture), input.g.ToString(CultureInfo.InvariantCulture), input.b.ToString(CultureInfo.InvariantCulture), input.a.ToString(CultureInfo.InvariantCulture));
		}

		private static Color ColorFromString(string input)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(input))
			{
				return Color.black;
			}
			string[] array = input.Split(ColorSeparators, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0)
			{
				return Color.black;
			}
			float slot = 0f;
			float slot2 = 0f;
			float slot3 = 0f;
			float slot4 = 1f;
			TryParseComponent(array, 0, ref slot);
			TryParseComponent(array, 1, ref slot2);
			TryParseComponent(array, 2, ref slot3);
			TryParseComponent(array, 3, ref slot4);
			return new Color(slot, slot2, slot3, slot4);
		}

		private static void TryParseComponent(string[] segments, int index, ref float slot)
		{
			if (index < segments.Length)
			{
				string s = segments[index].Trim();
				if (float.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					slot = result;
				}
			}
		}

		private static T SanitizeValue<T>(T value, string key)
		{
			if (value is float)
			{
				object obj = value;
				float val = (float)((obj is float) ? obj : null);
				if (s_floatRanges.TryGetValue(key, out var value2))
				{
					float num = System.Math.Min(value2.Max, System.Math.Max(value2.Min, val));
					if (value2.Step > 0f)
					{
						num = SnapFloatToStep(num, value2.Min, value2.Step);
						num = System.Math.Min(value2.Max, System.Math.Max(value2.Min, num));
					}
					return (T)(object)num;
				}
			}
			if (value is int)
			{
				object obj2 = value;
				int val2 = (int)((obj2 is int) ? obj2 : null);
				if (s_intRanges.TryGetValue(key, out var value3))
				{
					int num2 = System.Math.Min(value3.Max, System.Math.Max(value3.Min, val2));
					if (value3.Step > 0)
					{
						num2 = SnapIntToStep(num2, value3.Min, value3.Step);
						num2 = System.Math.Min(value3.Max, System.Math.Max(value3.Min, num2));
					}
					return (T)(object)num2;
				}
			}
			return value;
		}

		private static ConfigDescription BuildStringDescription(string description, string[] acceptableValues)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (acceptableValues == null || acceptableValues.Length == 0)
			{
				return new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>());
			}
			return new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueList<string>(acceptableValues), Array.Empty<object>());
		}

		private static int DetermineIntStep(FeatureConfigEntryAttribute attribute)
		{
			if (!float.IsNaN(attribute.Step) && attribute.Step >= 1f)
			{
				return System.Math.Max(1, (int)System.Math.Round(attribute.Step));
			}
			return 1;
		}

		private static float DetermineFloatStep(FeatureConfigEntryAttribute attribute, float minValue)
		{
			if (!float.IsNaN(attribute.Step) && attribute.Step > 0f)
			{
				return attribute.Step;
			}
			return DetermineDefaultFloatStep(minValue);
		}

		private static float DetermineDefaultFloatStep(float minValue)
		{
			float num = System.Math.Abs(minValue);
			if (num <= 0f)
			{
				return 0.1f;
			}
			double y = System.Math.Floor(System.Math.Log10(num));
			return (float)System.Math.Pow(10.0, y);
		}

		private static void RegisterFloatRange(string key, float min, float max, float step)
		{
			s_floatRanges[key] = new RangeF
			{
				Min = min,
				Max = max,
				Step = step
			};
		}

		private static void RegisterIntRange(string key, int min, int max, int step)
		{
			s_intRanges[key] = new RangeI
			{
				Min = min,
				Max = max,
				Step = step
			};
		}

		private static float SnapFloatToStep(float value, float min, float step)
		{
			if (step <= 0f)
			{
				return value;
			}
			float num = (value - min) / step;
			double num2 = System.Math.Round(num, MidpointRounding.AwayFromZero);
			return min + (float)num2 * step;
		}

		private static int SnapIntToStep(int value, int min, int step)
		{
			if (step <= 0)
			{
				return value;
			}
			double value2 = (double)(value - min) / (double)step;
			int num = (int)System.Math.Round(value2, MidpointRounding.AwayFromZero);
			return min + num * step;
		}
	}
	internal static class FeatureFlags
	{
		[FeatureConfigEntry("Spectate Movement", "Defines the forward vector used to rotate jump direction; Controller uses the tracked left-hand controller, Head uses the HMD.", AcceptableValues = new string[] { "Controller", "Head" })]
		public static string MovementDirectionSource = "Controller";

		[FeatureConfigEntry("Spectate Camera", "Distance that the spectator camera stays behind the death head.", Min = 0.05f, Max = 1.5f)]
		public static float HeadCameraDistance = 0.45f;

		[FeatureConfigEntry("Spectate Camera", "Vertical offset applied to the death head when aligning the camera.", Min = -0.2f, Max = 1.5f)]
		public static float HeadCameraHeightOffset = 0.5f;

		[FeatureConfigEntry("Spectate Camera", "How fast the spectator camera lerps back to the default position.", Min = 1f, Max = 12f)]
		public static float HeadCameraLerpSpeed = 8f;

		[FeatureConfigEntry("Spectate VR Ability", "Defines which forward vector is used to aim DeathHeadHopper abilities; Controller uses the tracked hand, Head uses the HMD.", AcceptableValues = new string[] { "Controller", "Head" })]
		public static string AbilityDirectionSource = "Controller";

		[FeatureConfigEntry("Spectate VR Ability", "Hand used to hold the grip that activates the spectate ability cursor. Valid values: Auto (opposite the selected main hand in RepoXR), Left, Right.", AcceptableValues = new string[] { "Auto", "Left", "Right" })]
		public static string AbilityGripPreference = "Auto";

		[FeatureConfigEntry("Spectate VR Ability", "Action name (or semicolon-separated fallback names) used to activate the selected slot.")]
		public static string AbilityActivateAction = "VR Actions/ResetHeight";

		[FeatureConfigEntry("Spectate VR Ability", "Distance in meters at which the VR ability cursor floats in front of the view.", Min = 0.2f, Max = 2f)]
		public static float AbilityCursorDistance = 0.6f;

		[FeatureConfigEntry("Spectate VR Ability", "Vertical offset applied to the ability cursor relative to the view origin.", Min = -1f, Max = 1f)]
		public static float AbilityCursorVerticalOffset = -0.25f;

		[FeatureConfigEntry("Spectate VR Ability", "Horizontal spacing between the ability markers.", Min = 0.1f, Max = 1f)]
		public static float AbilityCursorSpacing = 0.35f;

		[FeatureConfigEntry("Spectate VR Ability", "Base scale applied to the ability markers.", Min = 0.04f, Max = 0.5f)]
		public static float AbilityCursorScale = 0.15f;

		[FeatureConfigEntry("Spectate VR Ability", "Deadzone applied to the right stick before changing the selected slot.", Min = 0f, Max = 0.7f)]
		public static float AbilitySelectionDeadzone = 0.25f;

		[FeatureConfigEntry("Spectate VR Ability", "Enable verbose logging for the vanilla ability bridge (rate-limited).")]
		public static bool DebugAbility = false;

		public static bool EnableVanillaAbilityBridge = true;

		public static bool DebugSpectateGuard = false;

		public static bool DebugMovementDirection = false;

		public static bool LogControllerOrientation = false;

		public static bool DebugHeadAlignment = false;

		[FeatureConfigEntry("Spectate Movement", "Hand used to hold the grip that controls camera look/movement while spectating. Valid values: Auto (opposite the selected main hand in RepoXR), Left, Right.", AcceptableValues = new string[] { "Auto", "Left", "Right" })]
		public static string CameraGripPreference = "Auto";

		[FeatureConfigEntry("Spectate Movement", "When true, scale the jump direction by the analog stick magnitude instead of always normalizing to 1.")]
		public static bool UseAnalogMagnitude = true;
	}
}