Decompiled source of WallGrabJump v1.0.2

Mods/WallGrabJump.dll

Decompiled 8 hours ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BoneLib;
using BoneLib.BoneMenu;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Bonelab;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Interaction;
using MelonLoader;
using MelonLoader.Preferences;
using UnityEngine;
using WallGrabJump;
using WallGrabJump.Compatibility;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(WallGrabJumpMod), "WallGrabJump", "1.0.2", "Cesar", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: MelonAdditionalDependencies(new string[] { "BoneLib" })]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("WallGrabJump")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2")]
[assembly: AssemblyProduct("WallGrabJump")]
[assembly: AssemblyTitle("WallGrabJump")]
[assembly: AssemblyVersion("1.0.2.0")]
namespace WallGrabJump
{
	internal enum ClimbSource
	{
		None,
		Vanilla,
		ClimbAnything
	}
	internal struct ClimbHold
	{
		internal ClimbSource Source;

		internal Handedness Side;

		internal Hand Hand;

		internal Grip Grip;

		internal object State;

		internal Vector3 Anchor;

		internal Collider Surface;

		internal bool Valid => Source != ClimbSource.None;
	}
	internal static class ClimbDetector
	{
		internal static int Snapshot(out ClimbHold left, out ClimbHold right)
		{
			left = Evaluate(Player.LeftHand, (Handedness)1);
			right = Evaluate(Player.RightHand, (Handedness)2);
			return (left.Valid ? 1 : 0) + (right.Valid ? 1 : 0);
		}

		private static ClimbHold Evaluate(Hand hand, Handedness side)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_007b: 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)
			ClimbHold result = new ClimbHold
			{
				Side = side,
				Hand = hand
			};
			if ((Object)(object)hand == (Object)null || !PlayerState.IsLocalHand(hand))
			{
				return default(ClimbHold);
			}
			try
			{
				HandReciever attachedReceiver = hand.AttachedReceiver;
				Grip val = (((Object)(object)attachedReceiver != (Object)null) ? ((Il2CppObjectBase)attachedReceiver).TryCast<Grip>() : null);
				if ((Object)(object)val != (Object)null && IsClimbingGrip(val))
				{
					result.Source = ClimbSource.Vanilla;
					result.Grip = val;
					result.Anchor = ((Component)hand).transform.position;
					result.Surface = SurfaceOf(val);
					return result;
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Vanilla climb check failed (" + ex.GetType().Name + ")");
			}
			ClimbHold result2 = ClimbAnythingCompatibility.Evaluate(hand, side);
			if (!result2.Valid)
			{
				return default(ClimbHold);
			}
			return result2;
		}

		internal static bool IsClimbingGrip(Grip grip)
		{
			try
			{
				if ((Object)(object)grip == (Object)null)
				{
					return false;
				}
				if ((Object)(object)((Il2CppObjectBase)grip).TryCast<UIInteractionVolume>() != (Object)null)
				{
					return false;
				}
				if ((Object)(object)((Il2CppObjectBase)grip).TryCast<WorldGrip>() != (Object)null)
				{
					return true;
				}
				if (!((HandReciever)grip).HasHost)
				{
					return false;
				}
				return ((HandReciever)grip).IsStatic;
			}
			catch
			{
				return false;
			}
		}

		private static Collider SurfaceOf(Grip grip)
		{
			try
			{
				if ((Object)(object)((Il2CppObjectBase)grip).TryCast<WorldGrip>() != (Object)null)
				{
					return null;
				}
				return ((Component)grip).GetComponentInParent<Collider>();
			}
			catch
			{
				return null;
			}
		}
	}
	internal sealed class InputController
	{
		private enum Phase
		{
			Unknown,
			Released,
			Held
		}

		private Phase _phase;

		private int _sampledFrame = -1;

		private bool _pressedThisFrame;

		internal void Reset()
		{
			_phase = Phase.Unknown;
			_sampledFrame = -1;
			_pressedThisFrame = false;
		}

		internal bool PressedThisFrame()
		{
			int frameCount = Time.frameCount;
			if (_sampledFrame == frameCount)
			{
				return _pressedThisFrame;
			}
			_sampledFrame = frameCount;
			_pressedThisFrame = false;
			bool readable;
			bool flag = ReadA(out readable);
			if (!readable)
			{
				_phase = Phase.Unknown;
				return false;
			}
			if (!flag)
			{
				_phase = Phase.Released;
				return false;
			}
			_pressedThisFrame = _phase == Phase.Released;
			_phase = Phase.Held;
			return _pressedThisFrame;
		}

		private static bool ReadA(out bool readable)
		{
			readable = false;
			try
			{
				if (!Player.ControllersExist)
				{
					return false;
				}
				BaseController rightController = Player.RightController;
				if ((Object)(object)rightController == (Object)null)
				{
					return false;
				}
				readable = true;
				return rightController.GetAButton();
			}
			catch
			{
				readable = false;
				return false;
			}
		}
	}
	internal static class PlayerState
	{
		private static PhysicsRig _bodiesOwner;

		private static Rigidbody[] _bodies;

		internal static bool IsValid
		{
			get
			{
				if (HelperMethods.IsLoading())
				{
					return false;
				}
				if ((Object)(object)Player.RigManager != (Object)null)
				{
					return (Object)(object)Player.PhysicsRig != (Object)null;
				}
				return false;
			}
		}

		internal static Transform Head => Player.Head;

		internal static Rigidbody Pelvis
		{
			get
			{
				PhysicsRig physicsRig = Player.PhysicsRig;
				PhysTorso val = (((Object)(object)physicsRig != (Object)null) ? physicsRig.torso : null);
				if (!((Object)(object)val != (Object)null))
				{
					return null;
				}
				return val.rbPelvis;
			}
		}

		internal static Rigidbody Chest
		{
			get
			{
				PhysicsRig physicsRig = Player.PhysicsRig;
				PhysTorso val = (((Object)(object)physicsRig != (Object)null) ? physicsRig.torso : null);
				if (!((Object)(object)val != (Object)null))
				{
					return null;
				}
				return val.rbChest;
			}
		}

		internal static Vector3 BodyCentre
		{
			get
			{
				//IL_0010: 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_0042: 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)
				Rigidbody chest = Chest;
				if ((Object)(object)chest != (Object)null)
				{
					return chest.position;
				}
				Rigidbody pelvis = Pelvis;
				if ((Object)(object)pelvis != (Object)null)
				{
					return pelvis.position;
				}
				Transform head = Head;
				if (!((Object)(object)head != (Object)null))
				{
					return Vector3.zero;
				}
				return head.position;
			}
		}

		internal static Vector3 Velocity
		{
			get
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_000f: Unknown result type (might be due to invalid IL or missing references)
				Rigidbody pelvis = Pelvis;
				if (!((Object)(object)pelvis != (Object)null))
				{
					return Vector3.zero;
				}
				return pelvis.velocity;
			}
		}

		internal static bool IsLocalHand(Hand hand)
		{
			if ((Object)(object)hand == (Object)null)
			{
				return false;
			}
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager != (Object)null)
			{
				return (Object)(object)hand.manager == (Object)(object)rigManager;
			}
			return false;
		}

		internal static bool TryGetMarrowClimbNormal(out Vector3 normal)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			normal = Vector3.zero;
			PhysicsRig physicsRig = Player.PhysicsRig;
			if ((Object)(object)physicsRig == (Object)null)
			{
				return false;
			}
			StepState stepState = physicsRig._stepState;
			if ((int)stepState != 4 && (int)stepState != 3 && (int)stepState != 5)
			{
				return false;
			}
			Vector3 climbingNormal = physicsRig.climbingNormal;
			if (((Vector3)(ref climbingNormal)).sqrMagnitude < 0.25f)
			{
				return false;
			}
			normal = ((Vector3)(ref climbingNormal)).normalized;
			return true;
		}

		internal static Rigidbody[] RigBodies()
		{
			PhysicsRig physicsRig = Player.PhysicsRig;
			if ((Object)(object)physicsRig == (Object)null)
			{
				_bodiesOwner = null;
				_bodies = null;
				return null;
			}
			if (_bodies != null && (Object)(object)_bodiesOwner == (Object)(object)physicsRig)
			{
				return _bodies;
			}
			try
			{
				Il2CppArrayBase<Rigidbody> componentsInChildren = ((Component)physicsRig).GetComponentsInChildren<Rigidbody>(true);
				if (componentsInChildren == null)
				{
					return null;
				}
				int num = 0;
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					if ((Object)(object)componentsInChildren[i] != (Object)null)
					{
						num++;
					}
				}
				Rigidbody[] array = (Rigidbody[])(object)new Rigidbody[num];
				int num2 = 0;
				for (int j = 0; j < componentsInChildren.Length; j++)
				{
					if (num2 >= num)
					{
						break;
					}
					Rigidbody val = componentsInChildren[j];
					if ((Object)(object)val != (Object)null)
					{
						array[num2++] = val;
					}
				}
				_bodies = array;
				_bodiesOwner = physicsRig;
				return _bodies;
			}
			catch (Exception ex)
			{
				Log.Debug("Could not gather rig rigidbodies (" + ex.GetType().Name + ")");
				return null;
			}
		}

		internal static void DropCaches()
		{
			_bodies = null;
			_bodiesOwner = null;
		}
	}
	internal static class Prefs
	{
		internal static MelonPreferences_Category Category;

		internal static MelonPreferences_Entry<bool> Enabled;

		internal static MelonPreferences_Entry<float> JumpStrength;

		internal static MelonPreferences_Entry<float> LookInfluence;

		internal static MelonPreferences_Entry<float> OutwardPush;

		internal static MelonPreferences_Entry<float> UpwardBoost;

		internal static MelonPreferences_Entry<float> OutwardFloor;

		internal static MelonPreferences_Entry<float> AimStrictness;

		internal static MelonPreferences_Entry<float> StaminaCost;

		internal static MelonPreferences_Entry<bool> DebugLogging;

		internal const float DefaultJumpStrength = 1.8f;

		internal const float DefaultLookInfluence = 2f;

		internal const float DefaultOutwardPush = 0.9f;

		internal const float DefaultUpwardBoost = 1.65f;

		internal const float DefaultOutwardFloor = 0.35f;

		internal const float DefaultAimStrictness = 180f;

		internal const float DefaultStaminaCost = 10f;

		internal static bool EnabledValue
		{
			get
			{
				if (Enabled != null)
				{
					return Enabled.Value;
				}
				return true;
			}
		}

		internal static float JumpStrengthValue => Sane(JumpStrength, 1.8f);

		internal static float LookInfluenceValue => Sane(LookInfluence, 2f);

		internal static float OutwardPushValue => Sane(OutwardPush, 0.9f);

		internal static float UpwardBoostValue => Sane(UpwardBoost, 1.65f);

		internal static float OutwardFloorValue => Sane(OutwardFloor, 0.35f);

		internal static float AimStrictnessValue => Sane(AimStrictness, 180f);

		internal static float StaminaCostValue => Sane(StaminaCost, 10f);

		internal static bool DebugLoggingValue
		{
			get
			{
				if (DebugLogging != null)
				{
					return DebugLogging.Value;
				}
				return false;
			}
		}

		private static float Sane(MelonPreferences_Entry<float> entry, float fallback)
		{
			if (entry == null)
			{
				return fallback;
			}
			float value = entry.Value;
			if (!float.IsNaN(value) && !float.IsInfinity(value))
			{
				return value;
			}
			return fallback;
		}

		internal static void Setup()
		{
			Category = MelonPreferences.CreateCategory("WallGrabJump", "Wall Grab Jump");
			Enabled = Category.CreateEntry<bool>("Enabled", true, "Enabled", "Master switch for the wall jump.", false, false, (ValueValidator)null, (string)null);
			JumpStrength = Category.CreateEntry<float>("JumpStrength", 1.8f, "Jump Strength", "Multiplier on the push. 1.0 is a controlled shove off the wall.", false, false, (ValueValidator)null, (string)null);
			LookInfluence = Category.CreateEntry<float>("LookInfluence", 2f, "Look Influence", "How strongly your head direction steers the jump.", false, false, (ValueValidator)null, (string)null);
			OutwardPush = Category.CreateEntry<float>("OutwardPush", 0.9f, "Outward Push", "How hard the jump shoves away from the wall face. Lower this if jumps feel like being kicked off the wall.", false, false, (ValueValidator)null, (string)null);
			UpwardBoost = Category.CreateEntry<float>("UpwardBoost", 1.65f, "Upward Boost", "How much lift is mixed into every jump.", false, false, (ValueValidator)null, (string)null);
			OutwardFloor = Category.CreateEntry<float>("OutwardFloor", 0.35f, "Outward Floor", "Guaranteed separation from the wall, whatever you are looking at. 0 removes the floor entirely.", false, false, (ValueValidator)null, (string)null);
			AimStrictness = Category.CreateEntry<float>("AimStrictness", 180f, "Aim Strictness", "Max degrees your look may differ from straight off the wall before the jump is refused. 180 = never refuse.", false, false, (ValueValidator)null, (string)null);
			StaminaCost = Category.CreateEntry<float>("StaminaCost", 10f, "Stamina Cost %", "Percent of max climbing stamina a wall jump costs. Only used when ClimbStamina is installed.", false, false, (ValueValidator)null, (string)null);
			DebugLogging = Category.CreateEntry<bool>("DebugLogging", false, "Debug Logging", "Verbose per-jump logging for troubleshooting.", false, false, (ValueValidator)null, (string)null);
		}
	}
	internal static class RegrabGuard
	{
		private static float _blockUntil = float.NegativeInfinity;

		private static bool _installed;

		internal static void Suppress(float seconds)
		{
			float num = Time.time + seconds;
			if (num > _blockUntil)
			{
				_blockUntil = num;
			}
		}

		internal static void Clear()
		{
			_blockUntil = float.NegativeInfinity;
		}

		internal static void ApplyPatches(Harmony harmony)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			if (!_installed)
			{
				MethodInfo method = typeof(Grip).GetMethod("OnGrabConfirm", AccessTools.all);
				MethodInfo method2 = typeof(RegrabGuard).GetMethod("OnGrabConfirmPrefix", AccessTools.all);
				if (method == null || method2 == null)
				{
					Log.Warning("Grip.OnGrabConfirm not found; the brief anti-regrab window after a vanilla wall jump is off.");
					return;
				}
				harmony.Patch((MethodBase)method, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				_installed = true;
			}
		}

		private static bool OnGrabConfirmPrefix(Grip __instance, Hand hand)
		{
			try
			{
				if (Time.time >= _blockUntil)
				{
					return true;
				}
				if ((Object)(object)hand == (Object)null || !PlayerState.IsLocalHand(hand))
				{
					return true;
				}
				if ((Object)(object)__instance == (Object)null || !ClimbDetector.IsClimbingGrip(__instance))
				{
					return true;
				}
				return false;
			}
			catch
			{
				return true;
			}
		}
	}
	public class WallGrabJumpMod : MelonMod
	{
		private static Instance _logger;

		private WallJumpController _controller;

		private bool _wasEnabled = true;

		private Action<LevelInfo> _onLevelLoaded;

		private Action _onLevelUnloaded;

		private Action<RigManager> _onPlayerDeath;

		public override void OnInitializeMelon()
		{
			_logger = ((MelonBase)this).LoggerInstance;
			try
			{
				Prefs.Setup();
			}
			catch (Exception ex)
			{
				Log.Warning("Could not create preferences (" + ex.GetType().Name + "); running on defaults.");
			}
			_controller = new WallJumpController();
			RegrabGuard.ApplyPatches(((MelonBase)this).HarmonyInstance);
			ClimbAnythingCompatibility.Initialize(((MelonBase)this).HarmonyInstance);
			ClimbStaminaCompatibility.Initialize();
			_onLevelLoaded = delegate
			{
				ResetAll();
			};
			_onLevelUnloaded = ResetAll;
			_onPlayerDeath = delegate(RigManager rig)
			{
				RigManager rigManager = Player.RigManager;
				if ((Object)(object)rig != (Object)null && (Object)(object)rigManager != (Object)null && (Object)(object)rig == (Object)(object)rigManager)
				{
					ResetAll();
				}
			};
			Hooking.OnLevelLoaded += _onLevelLoaded;
			Hooking.OnLevelUnloaded += _onLevelUnloaded;
			Hooking.OnPlayerDeath += _onPlayerDeath;
			SetupMenu();
			Log.Msg("Initialized.");
		}

		public override void OnLateInitializeMelon()
		{
			ClimbAnythingCompatibility.Retry(((MelonBase)this).HarmonyInstance);
			ClimbStaminaCompatibility.Retry();
		}

		public override void OnDeinitializeMelon()
		{
			Hooking.OnLevelLoaded -= _onLevelLoaded;
			Hooking.OnLevelUnloaded -= _onLevelUnloaded;
			Hooking.OnPlayerDeath -= _onPlayerDeath;
		}

		public override void OnUpdate()
		{
			if (_controller == null)
			{
				return;
			}
			bool enabledValue = Prefs.EnabledValue;
			if (enabledValue != _wasEnabled)
			{
				_wasEnabled = enabledValue;
				if (!enabledValue)
				{
					ResetAll();
				}
			}
			if (enabledValue)
			{
				_controller.Tick();
			}
		}

		public override void OnFixedUpdate()
		{
			if (_controller != null && _wasEnabled)
			{
				_controller.FixedTick(Time.fixedDeltaTime);
			}
		}

		private void ResetAll()
		{
			_controller?.Reset();
			ClimbAnythingCompatibility.ClearSuppression();
			RegrabGuard.Clear();
			PlayerState.DropCaches();
		}

		private static void SetupMenu()
		{
			//IL_0020: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Prefs.Category == null)
				{
					Log.Warning("Preferences are unavailable, so there is no menu to build; the mod runs on defaults.");
					return;
				}
				Page obj = Page.Root.CreatePage("Wall Grab Jump", Color.cyan, 0, true);
				obj.CreateBool("Enabled", Color.cyan, Prefs.EnabledValue, (Action<bool>)delegate(bool value)
				{
					if (Prefs.Enabled != null)
					{
						Prefs.Enabled.Value = value;
					}
				});
				obj.CreateFloat("Jump Strength", Color.cyan, Prefs.JumpStrengthValue, 0.1f, 0.5f, 3f, (Action<float>)delegate(float value)
				{
					if (Prefs.JumpStrength != null)
					{
						Prefs.JumpStrength.Value = value;
					}
				});
				obj.CreateFloat("Upward Boost", Color.cyan, Prefs.UpwardBoostValue, 0.05f, 0f, 3f, (Action<float>)delegate(float value)
				{
					if (Prefs.UpwardBoost != null)
					{
						Prefs.UpwardBoost.Value = value;
					}
				});
				obj.CreateFloat("Stamina Cost %", Color.cyan, Prefs.StaminaCostValue, 5f, 0f, 50f, (Action<float>)delegate(float value)
				{
					if (Prefs.StaminaCost != null)
					{
						Prefs.StaminaCost.Value = value;
					}
				});
			}
			catch (Exception ex)
			{
				Log.Warning("Could not build the BoneMenu page (" + ex.GetType().Name + "); preferences still work from the config file.");
			}
		}

		internal static void LogMsg(string message)
		{
			Instance logger = _logger;
			if (logger != null)
			{
				logger.Msg(message);
			}
		}

		internal static void LogWarn(string message)
		{
			Instance logger = _logger;
			if (logger != null)
			{
				logger.Warning(message);
			}
		}
	}
	internal static class Log
	{
		internal static void Msg(string message)
		{
			WallGrabJumpMod.LogMsg(message);
		}

		internal static void Warning(string message)
		{
			WallGrabJumpMod.LogWarn(message);
		}

		internal static void Debug(string message)
		{
			if (Prefs.DebugLoggingValue)
			{
				WallGrabJumpMod.LogMsg("[debug] " + message);
			}
		}
	}
	internal sealed class WallJumpController
	{
		private const float BaseJumpSpeed = 3.5f;

		private const float AbsoluteMaxDeltaV = 16f;

		private const float OutwardInfluence = 0.9f;

		private const float UpInfluence = 0.55f;

		private const float MinOutwardDot = 0.35f;

		private const float MinVerticalComponent = -0.3f;

		private const float MinOutwardVertical = -0.25f;

		private const float MinOutwardHorizontal = 0.2f;

		private const float MaxIntoWallCancel = 1.5f;

		private const float PushDuration = 0.09f;

		private const float CooldownSeconds = 0.25f;

		private const float RegrabBlanketSeconds = 0.3f;

		private const float SuccessAmplitude = 0.55f;

		private const float SuccessSeconds = 0.09f;

		private const float SuccessFrequency = 150f;

		private const float DeniedAmplitude = 0.22f;

		private const float DeniedSeconds = 0.035f;

		private const float DeniedFrequency = 110f;

		private readonly InputController _input = new InputController();

		private float _readyAt = float.NegativeInfinity;

		private bool _pushing;

		private Vector3 _pushDirection;

		private float _pushBudget;

		private float _pushApplied;

		private float _pushElapsed;

		internal void Reset()
		{
			_input.Reset();
			_readyAt = float.NegativeInfinity;
			CancelPush();
		}

		private void CancelPush()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			_pushing = false;
			_pushDirection = Vector3.zero;
			_pushBudget = 0f;
			_pushApplied = 0f;
			_pushElapsed = 0f;
		}

		internal void Tick()
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			ClimbAnythingCompatibility.Tick();
			if (!_input.PressedThisFrame() || !PlayerState.IsValid || Time.time < _readyAt || ClimbDetector.Snapshot(out var left, out var right) == 0)
			{
				return;
			}
			if (ClimbStaminaCompatibility.ClimbingBlocked)
			{
				Deny(left, right);
				Log.Debug("Wall jump refused: ClimbStamina has climbing blocked");
				return;
			}
			if (!ClimbStaminaCompatibility.CanAfford())
			{
				Deny(left, right);
				Log.Debug("Wall jump refused: not enough stamina");
				return;
			}
			float num = Mathf.Clamp(Prefs.AimStrictnessValue, 0f, 180f);
			if (num < 179.5f)
			{
				Vector3 val = FindOutward(left, right);
				Vector3 val2 = LookDirection();
				if (val != Vector3.zero && val2 != Vector3.zero && Vector3.Angle(val2, val) > num)
				{
					Deny(left, right);
					Log.Debug($"Wall jump refused: looking {Vector3.Angle(val2, val):0} deg off the wall (limit {num:0})");
					return;
				}
			}
			Execute(left, right);
		}

		private void Deny(ClimbHold left, ClimbHold right)
		{
			_readyAt = Time.time + 0.25f;
			if (left.Valid)
			{
				Pulse((Handedness)1, 0.035f, 110f, 0.22f);
			}
			if (right.Valid)
			{
				Pulse((Handedness)2, 0.035f, 110f, 0.22f);
			}
		}

		private void Execute(ClimbHold left, ClimbHold right)
		{
			//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_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: 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_00c0: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: 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_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = FindOutward(left, right);
			Vector3 val2 = Compose(LookDirection(), val) * (3.5f * Mathf.Clamp(Prefs.JumpStrengthValue, 0.05f, 6f));
			if (val != Vector3.zero)
			{
				float num = Vector3.Dot(PlayerState.Velocity, -val);
				if (num > 0f)
				{
					val2 += val * Mathf.Min(num, 1.5f);
				}
			}
			float num2 = ((Vector3)(ref val2)).magnitude;
			if (!IsFinite(num2) || !IsFinite(val2.x) || !IsFinite(val2.y) || !IsFinite(val2.z))
			{
				Log.Debug("Wall jump aborted: computed a non-finite velocity");
				return;
			}
			if (num2 > 16f)
			{
				val2 = val2 / num2 * 16f;
				num2 = 16f;
			}
			if (num2 < 0.01f)
			{
				return;
			}
			int num3 = Release(left) + Release(right);
			if (num3 == 0)
			{
				_readyAt = Time.time + 0.25f;
				Log.Debug("Wall jump aborted: nothing could be released");
				return;
			}
			ClimbStaminaCompatibility.Spend();
			ClimbAnythingCompatibility.SuppressAfterJump(left, right, 0.3f);
			RegrabGuard.Suppress(0.3f);
			_pushDirection = val2 / num2;
			_pushBudget = num2;
			_pushApplied = 0f;
			_pushElapsed = 0f;
			_pushing = true;
			_readyAt = Time.time + 0.25f;
			if (left.Valid)
			{
				Pulse((Handedness)1, 0.09f, 150f, 0.55f);
			}
			if (right.Valid)
			{
				Pulse((Handedness)2, 0.09f, 150f, 0.55f);
			}
			Log.Debug($"Wall jump: {num3} hand(s) released, dV={num2:0.00} m/s, dir=({_pushDirection.x:0.00}, {_pushDirection.y:0.00}, {_pushDirection.z:0.00}), outward={((val == Vector3.zero) ? "none" : $"({val.x:0.00}, {val.y:0.00}, {val.z:0.00})")}");
		}

		private static int Release(ClimbHold hold)
		{
			if (!hold.Valid)
			{
				return 0;
			}
			try
			{
				switch (hold.Source)
				{
				case ClimbSource.Vanilla:
					if ((Object)(object)hold.Grip == (Object)null || (Object)(object)hold.Hand == (Object)null)
					{
						return 0;
					}
					hold.Grip.ForceDetach(hold.Hand);
					return 1;
				case ClimbSource.ClimbAnything:
					return ClimbAnythingCompatibility.Release(hold) ? 1 : 0;
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Release failed (" + ex.GetType().Name + ")");
			}
			return 0;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value))
			{
				return !float.IsInfinity(value);
			}
			return false;
		}

		private static Vector3 LookDirection()
		{
			//IL_0016: 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_000f: 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_002c: Unknown result type (might be due to invalid IL or missing references)
			Transform head = PlayerState.Head;
			if ((Object)(object)head == (Object)null)
			{
				return Vector3.zero;
			}
			Vector3 forward = head.forward;
			if (!(((Vector3)(ref forward)).sqrMagnitude < 1E-06f))
			{
				return ((Vector3)(ref forward)).normalized;
			}
			return Vector3.zero;
		}

		internal static Vector3 Compose(Vector3 look, Vector3 outward)
		{
			//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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: 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_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: 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_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: 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_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: 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_0138: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Clamp(Prefs.LookInfluenceValue, 0f, 5f);
			float num2 = Mathf.Clamp(Prefs.OutwardPushValue, 0f, 5f);
			float num3 = Mathf.Clamp(Prefs.UpwardBoostValue, 0f, 5f);
			Vector3 val = look * num + outward * num2 + Vector3.up * num3;
			if (((Vector3)(ref val)).sqrMagnitude < 1E-06f)
			{
				val = ((outward != Vector3.zero) ? (outward + Vector3.up * 0.5f) : Vector3.up);
			}
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			normalized = ClampVertical(normalized, outward);
			float num4 = Mathf.Clamp(Prefs.OutwardFloorValue, 0f, 0.95f);
			if (outward != Vector3.zero && num4 > 0f)
			{
				float num5 = Vector3.Dot(normalized, outward);
				if (num5 < num4)
				{
					Vector3 val2 = normalized - outward * num5;
					if (((Vector3)(ref val2)).sqrMagnitude < 1E-06f)
					{
						val2 = Vector3.up - outward * outward.y;
					}
					if (((Vector3)(ref val2)).sqrMagnitude < 1E-06f)
					{
						return outward;
					}
					Vector3 val3 = outward * num4 + ((Vector3)(ref val2)).normalized * Mathf.Sqrt(1f - num4 * num4);
					normalized = ((Vector3)(ref val3)).normalized;
					normalized = ClampVertical(normalized, outward);
				}
			}
			return normalized;
		}

		private static Vector3 ClampVertical(Vector3 direction, Vector3 outward)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if (direction.y >= -0.3f)
			{
				return direction;
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(direction.x, 0f, direction.z);
			if (((Vector3)(ref val)).sqrMagnitude < 1E-06f)
			{
				((Vector3)(ref val))..ctor(outward.x, 0f, outward.z);
			}
			if (((Vector3)(ref val)).sqrMagnitude < 1E-06f)
			{
				return Vector3.up;
			}
			float num = -0.3f;
			val = ((Vector3)(ref val)).normalized * Mathf.Sqrt(1f - num * num);
			return new Vector3(val.x, num, val.z);
		}

		private static Vector3 FindOutward(ClimbHold left, ClimbHold right)
		{
			//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_0013: 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_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_0054: 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_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_0060: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val;
			if (left.Valid && right.Valid)
			{
				val = (left.Anchor + right.Anchor) * 0.5f;
			}
			else if (left.Valid)
			{
				val = left.Anchor;
			}
			else
			{
				if (!right.Valid)
				{
					return Vector3.zero;
				}
				val = right.Anchor;
			}
			Vector3 bodyCentre = PlayerState.BodyCentre;
			if (TryCastToSurface(bodyCentre, val, out var normal) || TryClosestPoint(left, right, bodyCentre, out normal) || PlayerState.TryGetMarrowClimbNormal(out normal))
			{
				Vector3 val2 = LimitDownward(normal);
				if (val2 != Vector3.zero)
				{
					return val2;
				}
			}
			Vector3 val3 = bodyCentre - val;
			val3.y = 0f;
			if (((Vector3)(ref val3)).sqrMagnitude > 0.0025f)
			{
				return ((Vector3)(ref val3)).normalized;
			}
			return Vector3.zero;
		}

		private static bool TryCastToSurface(Vector3 body, Vector3 anchor, out Vector3 normal)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_0044: 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_0084: 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_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			normal = Vector3.zero;
			Vector3 val = anchor - body;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude < 0.05f)
			{
				return false;
			}
			try
			{
				Transform val2 = null;
				PhysicsRig physicsRig = Player.PhysicsRig;
				if ((Object)(object)physicsRig != (Object)null)
				{
					val2 = ((Component)physicsRig).transform.root;
				}
				Il2CppStructArray<RaycastHit> val3 = Physics.RaycastAll(body, val / magnitude, magnitude + 0.4f, -5, (QueryTriggerInteraction)1);
				if (val3 == null)
				{
					return false;
				}
				float num = float.MaxValue;
				float num2 = magnitude * 0.5f;
				for (int i = 0; i < ((Il2CppArrayBase<RaycastHit>)(object)val3).Length; i++)
				{
					RaycastHit val4 = ((Il2CppArrayBase<RaycastHit>)(object)val3)[i];
					if (((RaycastHit)(ref val4)).distance < num2 || ((RaycastHit)(ref val4)).distance >= num)
					{
						continue;
					}
					Collider collider = ((RaycastHit)(ref val4)).collider;
					if (!((Object)(object)collider == (Object)null) && (!((Object)(object)val2 != (Object)null) || !((Object)(object)((Component)collider).transform.root == (Object)(object)val2)))
					{
						Vector3 normal2 = ((RaycastHit)(ref val4)).normal;
						if (!(((Vector3)(ref normal2)).sqrMagnitude < 0.25f))
						{
							num = ((RaycastHit)(ref val4)).distance;
							normal2 = ((RaycastHit)(ref val4)).normal;
							normal = ((Vector3)(ref normal2)).normalized;
						}
					}
				}
				return num < float.MaxValue;
			}
			catch (Exception ex)
			{
				Log.Debug("Surface cast failed (" + ex.GetType().Name + ")");
				return false;
			}
		}

		private static bool TryClosestPoint(ClimbHold left, ClimbHold right, Vector3 body, out Vector3 normal)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0068: Unknown result type (might be due to invalid IL or missing references)
			normal = Vector3.zero;
			Vector3 val = Vector3.zero;
			int num = 0;
			for (int i = 0; i < 2; i++)
			{
				ClimbHold climbHold = ((i == 0) ? left : right);
				if (!climbHold.Valid || (Object)(object)climbHold.Surface == (Object)null)
				{
					continue;
				}
				try
				{
					Vector3 val2 = body - climbHold.Surface.ClosestPoint(body);
					if (!(((Vector3)(ref val2)).sqrMagnitude < 0.0004f))
					{
						val += ((Vector3)(ref val2)).normalized;
						num++;
					}
				}
				catch
				{
				}
			}
			if (num == 0 || ((Vector3)(ref val)).sqrMagnitude < 1E-06f)
			{
				return false;
			}
			normal = ((Vector3)(ref val)).normalized;
			return true;
		}

		private static Vector3 LimitDownward(Vector3 normal)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_0069: 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)
			if (normal.y >= -0.25f)
			{
				return normal;
			}
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(normal.x, 0f, normal.z);
			if (((Vector3)(ref val)).sqrMagnitude < 0.040000003f)
			{
				return Vector3.zero;
			}
			float num = -0.25f;
			val = ((Vector3)(ref val)).normalized * Mathf.Sqrt(1f - num * num);
			return new Vector3(val.x, num, val.z);
		}

		internal void FixedTick(float fixedDeltaTime)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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)
			if (!_pushing)
			{
				return;
			}
			if (fixedDeltaTime <= 0f || !PlayerState.IsValid)
			{
				CancelPush();
				return;
			}
			Rigidbody[] array = PlayerState.RigBodies();
			if (array == null || array.Length == 0)
			{
				CancelPush();
				return;
			}
			_pushElapsed += fixedDeltaTime;
			float num = _pushBudget * (fixedDeltaTime / 0.09f);
			float num2 = _pushBudget - _pushApplied;
			if (num > num2)
			{
				num = num2;
			}
			if (num > 1E-05f)
			{
				_pushApplied += num;
				Vector3 val = _pushDirection * num;
				foreach (Rigidbody val2 in array)
				{
					if (!((Object)(object)val2 == (Object)null) && !val2.isKinematic)
					{
						val2.AddForce(val, (ForceMode)2);
					}
				}
			}
			if (_pushApplied >= _pushBudget - 1E-05f || _pushElapsed >= 0.09f)
			{
				CancelPush();
			}
		}

		private static void Pulse(Handedness side, float seconds, float frequency, float amplitude)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			try
			{
				if (!Player.ControllersExist)
				{
					return;
				}
				BaseController val = (((int)side == 1) ? Player.LeftController : Player.RightController);
				if (!((Object)(object)val == (Object)null))
				{
					Haptor haptor = val.haptor;
					if (!((Object)(object)haptor == (Object)null) && haptor.hapticsAllowed)
					{
						haptor.SENDHAPTIC(0f, seconds, frequency, Mathf.Clamp01(amplitude));
					}
				}
			}
			catch (Exception ex)
			{
				Log.Debug("Haptic pulse failed (" + ex.GetType().Name + ")");
			}
		}
	}
}
namespace WallGrabJump.Compatibility
{
	internal static class ClimbAnythingCompatibility
	{
		private const float GripHeldThreshold = 0.01f;

		private const float UnreadableGripGrace = 1.5f;

		private static bool _looked;

		private static Harmony _harmony;

		private static object _melon;

		private static FieldInfo _leftStateField;

		private static FieldInfo _rightStateField;

		private static FieldInfo _handField;

		private static FieldInfo _climbPointField;

		private static FieldInfo _isClimbingField;

		private static MethodInfo _stopClimbing;

		private static object _leftState;

		private static object _rightState;

		private static bool _leftSuppressed;

		private static bool _rightSuppressed;

		private static float _leftUnreadableSince = -1f;

		private static float _rightUnreadableSince = -1f;

		private static float _blockAllUntil = float.NegativeInfinity;

		private static int _faults;

		private static float _lastFaultAt = float.NegativeInfinity;

		private const int MaxFaults = 5;

		private const float FaultWindow = 10f;

		internal static bool Active { get; private set; }

		internal static void Initialize(Harmony harmony)
		{
			_harmony = harmony;
			if (!_looked)
			{
				MelonBase val = MelonBase.FindMelon("ClimbAnything", "Waspothegreat");
				if (val != null)
				{
					_looked = true;
					TryBind(val);
				}
			}
		}

		internal static void Retry(Harmony harmony)
		{
			if (_harmony == null)
			{
				_harmony = harmony;
			}
			if (!_looked && _harmony != null)
			{
				_looked = true;
				MelonBase val = MelonBase.FindMelon("ClimbAnything", "Waspothegreat");
				if (val != null)
				{
					TryBind(val);
				}
			}
		}

		private static void TryBind(MelonBase melon)
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Expected O, but got Unknown
			try
			{
				Type type = ((object)melon).GetType();
				_leftStateField = type.GetField("_leftHandState", BindingFlags.Instance | BindingFlags.NonPublic);
				_rightStateField = type.GetField("_rightHandState", BindingFlags.Instance | BindingFlags.NonPublic);
				Type nestedType = type.GetNestedType("HandState", BindingFlags.Public | BindingFlags.NonPublic);
				_handField = nestedType?.GetField("Hand", BindingFlags.Instance | BindingFlags.Public);
				_climbPointField = nestedType?.GetField("ClimbPoint", BindingFlags.Instance | BindingFlags.Public);
				_isClimbingField = nestedType?.GetField("IsClimbing", BindingFlags.Instance | BindingFlags.Public);
				MethodInfo method = type.GetMethod("StartClimbing", BindingFlags.Instance | BindingFlags.NonPublic);
				_stopClimbing = type.GetMethod("StopClimbing", BindingFlags.Instance | BindingFlags.NonPublic);
				if (!ShapeIsSupported(method, nestedType))
				{
					Log.Warning("ClimbAnything is installed but its internals don't match the supported layout (verified against 1.0.0 / package 1.0.1). Wall jumping from ClimbAnything holds is disabled; vanilla climbing still works and ClimbAnything itself is untouched.");
					return;
				}
				_melon = melon;
				_harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(ClimbAnythingCompatibility), "StartClimbingPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Active = true;
				MelonInfoAttribute info = melon.Info;
				Log.Msg("ClimbAnything " + (((info != null) ? info.Version : null) ?? "?") + " detected — wall jumping works from its holds, and a hand that jumps needs a fresh grip before it can climb again.");
			}
			catch (Exception ex)
			{
				Active = false;
				Log.Warning($"Could not set up ClimbAnything compatibility ({ex.GetType().Name}: {ex.Message}). " + "Vanilla wall jumping still works; ClimbAnything itself is untouched.");
			}
		}

		private static bool ShapeIsSupported(MethodInfo startClimbing, Type handStateType)
		{
			if (handStateType == null || startClimbing == null || _stopClimbing == null)
			{
				return false;
			}
			if (_leftStateField == null || _rightStateField == null)
			{
				return false;
			}
			if (_handField == null || _climbPointField == null || _isClimbingField == null)
			{
				return false;
			}
			if (_handField.FieldType != typeof(Hand))
			{
				return false;
			}
			if (_isClimbingField.FieldType != typeof(bool))
			{
				return false;
			}
			if (!typeof(Object).IsAssignableFrom(_climbPointField.FieldType))
			{
				return false;
			}
			ParameterInfo[] parameters = startClimbing.GetParameters();
			if (parameters.Length != 2 || parameters[0].ParameterType != handStateType)
			{
				return false;
			}
			ParameterInfo[] parameters2 = _stopClimbing.GetParameters();
			if (parameters2.Length != 1 || parameters2[0].ParameterType != handStateType)
			{
				return false;
			}
			return true;
		}

		internal static ClimbHold Evaluate(Hand hand, Handedness side)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			if (!Active || (Object)(object)hand == (Object)null)
			{
				return default(ClimbHold);
			}
			try
			{
				object obj = StateFor(side);
				if (obj == null)
				{
					return default(ClimbHold);
				}
				if (!(bool)_isClimbingField.GetValue(obj))
				{
					return default(ClimbHold);
				}
				object? value = _climbPointField.GetValue(obj);
				Object val = (Object)((value is Object) ? value : null);
				if (val == (Object)null)
				{
					return default(ClimbHold);
				}
				object? value2 = _handField.GetValue(obj);
				Hand val2 = (Hand)((value2 is Hand) ? value2 : null);
				if ((Object)(object)val2 != (Object)null && (Object)(object)val2 != (Object)(object)hand)
				{
					return default(ClimbHold);
				}
				ClimbHold result = new ClimbHold
				{
					Source = ClimbSource.ClimbAnything,
					Side = side,
					Hand = hand,
					State = obj,
					Anchor = ((Component)hand).transform.position
				};
				GameObject val3 = ((Il2CppObjectBase)val).TryCast<GameObject>();
				if ((Object)(object)val3 != (Object)null)
				{
					result.Anchor = val3.transform.position;
					Transform parent = val3.transform.parent;
					if ((Object)(object)parent != (Object)null)
					{
						result.Surface = ((Component)parent).GetComponent<Collider>();
					}
				}
				return result;
			}
			catch (Exception e)
			{
				Fault(e);
				return default(ClimbHold);
			}
		}

		private static object StateFor(Handedness side)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			if (_melon == null)
			{
				return null;
			}
			if ((int)side == 1)
			{
				return _leftState ?? (_leftState = _leftStateField.GetValue(_melon));
			}
			if ((int)side == 2)
			{
				return _rightState ?? (_rightState = _rightStateField.GetValue(_melon));
			}
			return null;
		}

		internal static bool Release(ClimbHold hold)
		{
			if (!Active || hold.Source != ClimbSource.ClimbAnything || hold.State == null)
			{
				return false;
			}
			try
			{
				_stopClimbing.Invoke(_melon, new object[1] { hold.State });
				return true;
			}
			catch (Exception e)
			{
				Fault(e);
				return false;
			}
		}

		internal static void SuppressAfterJump(ClimbHold left, ClimbHold right, float blanketSeconds)
		{
			_blockAllUntil = Time.time + blanketSeconds;
			if (left.Valid)
			{
				_leftSuppressed = true;
				_leftUnreadableSince = -1f;
			}
			if (right.Valid)
			{
				_rightSuppressed = true;
				_rightUnreadableSince = -1f;
			}
		}

		internal static void Tick()
		{
			if (_leftSuppressed || _rightSuppressed)
			{
				if (_leftSuppressed && GripReleased((Handedness)1, ref _leftUnreadableSince))
				{
					_leftSuppressed = false;
					Log.Debug("Left hand may climb again (grip released)");
				}
				if (_rightSuppressed && GripReleased((Handedness)2, ref _rightUnreadableSince))
				{
					_rightSuppressed = false;
					Log.Debug("Right hand may climb again (grip released)");
				}
			}
		}

		private static bool GripReleased(Handedness side, ref float unreadableSince)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Invalid comparison between Unknown and I4
			float time = Time.time;
			try
			{
				BaseController val = null;
				if (Player.ControllersExist)
				{
					val = (((int)side == 1) ? Player.LeftController : Player.RightController);
				}
				if ((Object)(object)val == (Object)null)
				{
					if (unreadableSince < 0f)
					{
						unreadableSince = time;
					}
					return time - unreadableSince >= 1.5f;
				}
				unreadableSince = -1f;
				return val.GetGripForce() <= 0.01f;
			}
			catch
			{
				if (unreadableSince < 0f)
				{
					unreadableSince = time;
				}
				return time - unreadableSince >= 1.5f;
			}
		}

		internal static void ClearSuppression()
		{
			_leftSuppressed = false;
			_rightSuppressed = false;
			_leftUnreadableSince = -1f;
			_rightUnreadableSince = -1f;
			_blockAllUntil = float.NegativeInfinity;
		}

		private static bool StartClimbingPrefix(object __0)
		{
			//IL_007e: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Invalid comparison between Unknown and I4
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Invalid comparison between Unknown and I4
			if (!Active)
			{
				return true;
			}
			if (!_leftSuppressed && !_rightSuppressed && Time.time >= _blockAllUntil)
			{
				return true;
			}
			try
			{
				if (Time.time < _blockAllUntil)
				{
					return false;
				}
				if (__0 == _leftState)
				{
					return !_leftSuppressed;
				}
				if (__0 == _rightState)
				{
					return !_rightSuppressed;
				}
				object? value = _handField.GetValue(__0);
				Hand val = (Hand)((value is Hand) ? value : null);
				if ((Object)(object)val == (Object)null)
				{
					return true;
				}
				Handedness handedness = val.handedness;
				if ((int)handedness != 1)
				{
					if ((int)handedness == 2)
					{
						return !_rightSuppressed;
					}
					return true;
				}
				return !_leftSuppressed;
			}
			catch (Exception e)
			{
				Fault(e);
				return true;
			}
		}

		private static void Fault(Exception e)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup - _lastFaultAt > 10f)
			{
				_faults = 0;
			}
			_lastFaultAt = realtimeSinceStartup;
			_faults++;
			if (_faults == 1)
			{
				Log.Warning("Error talking to ClimbAnything: " + e.GetType().Name + ": " + e.Message);
			}
			if (_faults >= 5 && Active)
			{
				Active = false;
				ClearSuppression();
				Log.Warning("Disabling ClimbAnything compatibility after repeated errors. Vanilla wall jumping continues, and ClimbAnything runs exactly as it does unpatched.");
			}
		}
	}
	internal static class ClimbStaminaCompatibility
	{
		private static bool _looked;

		private static object _controller;

		private static FieldInfo _staminaField;

		private static FieldInfo _drainField;

		private static PropertyInfo _maxProp;

		private static FieldInfo _blockedField;

		private static FieldInfo _enabledField;

		private static MelonPreferences_Entry<bool> _enabledEntry;

		private static int _faults;

		private const int MaxFaults = 3;

		internal static bool Active { get; private set; }

		internal static bool ClimbingBlocked
		{
			get
			{
				if (!Active)
				{
					return false;
				}
				try
				{
					return (bool)_blockedField.GetValue(null);
				}
				catch (Exception e)
				{
					Fault(e);
					return false;
				}
			}
		}

		internal static void Initialize()
		{
			if (!_looked && MelonBase.FindMelon("ClimbStamina", "Cesar") != null && TryBind(final: false))
			{
				_looked = true;
			}
		}

		internal static void Retry()
		{
			if (!_looked)
			{
				_looked = true;
				if (MelonBase.FindMelon("ClimbStamina", "Cesar") != null)
				{
					TryBind(final: true);
				}
			}
		}

		private static bool TryBind(bool final)
		{
			try
			{
				MelonBase val = MelonBase.FindMelon("ClimbStamina", "Cesar");
				if (val == null)
				{
					return false;
				}
				Type type = ((object)val).GetType();
				_enabledField = type.Assembly.GetType("ClimbStamina.Prefs")?.GetField("Enabled", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (_enabledField != null && _enabledField.FieldType != typeof(MelonPreferences_Entry<bool>))
				{
					_enabledField = null;
				}
				_blockedField = type.GetField("ClimbBlocked", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				object obj = type.GetField("_stamina", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(val);
				if (obj == null)
				{
					if (final)
					{
						Log.Warning("ClimbStamina is installed but its stamina controller could not be reached; wall jumping will not cost stamina. ClimbStamina itself is untouched.");
					}
					return false;
				}
				Type type2 = obj.GetType();
				_staminaField = type2.GetField("_stamina", BindingFlags.Instance | BindingFlags.NonPublic);
				_drainField = type2.GetField("_timeSinceDrain", BindingFlags.Instance | BindingFlags.NonPublic);
				_maxProp = type2.GetProperty("MaxStamina", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (!ShapeIsSupported())
				{
					Log.Warning("ClimbStamina is installed but its internals don't match the supported layout (verified against 1.3.4). Wall jumping will not cost stamina; ClimbStamina itself is untouched.");
					return true;
				}
				_controller = obj;
				Active = true;
				MelonInfoAttribute info = val.Info;
				Log.Msg("ClimbStamina " + (((info != null) ? info.Version : null) ?? "?") + " detected — wall jumps draw from its stamina pool.");
				return true;
			}
			catch (Exception ex)
			{
				Active = false;
				Log.Warning($"Could not set up ClimbStamina integration ({ex.GetType().Name}: {ex.Message}). " + "Wall jumping works and costs nothing; ClimbStamina itself is untouched.");
				return true;
			}
		}

		private static bool ShapeIsSupported()
		{
			if (_staminaField == null || _staminaField.FieldType != typeof(float))
			{
				return false;
			}
			if (_drainField == null || _drainField.FieldType != typeof(float))
			{
				return false;
			}
			if (_maxProp == null || _maxProp.PropertyType != typeof(float) || !_maxProp.CanRead)
			{
				return false;
			}
			if (_blockedField == null || _blockedField.FieldType != typeof(bool))
			{
				return false;
			}
			return true;
		}

		private static bool PoolIsLive()
		{
			if (_enabledField == null)
			{
				return true;
			}
			try
			{
				if (_enabledEntry == null)
				{
					_enabledEntry = _enabledField.GetValue(null) as MelonPreferences_Entry<bool>;
				}
				return _enabledEntry == null || _enabledEntry.Value;
			}
			catch (Exception e)
			{
				Fault(e);
				return true;
			}
		}

		internal static float CostOfJump()
		{
			if (!Active || !PoolIsLive())
			{
				return 0f;
			}
			try
			{
				float num = (float)_maxProp.GetValue(_controller);
				float num2 = Mathf.Clamp(Prefs.StaminaCostValue, 0f, 100f);
				return num * (num2 / 100f);
			}
			catch (Exception e)
			{
				Fault(e);
				return 0f;
			}
		}

		internal static float Current()
		{
			if (!Active)
			{
				return float.MaxValue;
			}
			try
			{
				return (float)_staminaField.GetValue(_controller);
			}
			catch (Exception e)
			{
				Fault(e);
				return float.MaxValue;
			}
		}

		internal static bool CanAfford()
		{
			if (!Active)
			{
				return true;
			}
			float num = CostOfJump();
			if (!(num <= 0f))
			{
				return Current() >= num;
			}
			return true;
		}

		internal static void Spend()
		{
			if (!Active)
			{
				return;
			}
			try
			{
				float num = CostOfJump();
				if (!(num <= 0f))
				{
					float num2 = (float)_staminaField.GetValue(_controller);
					float num3 = Mathf.Max(0f, num2 - num);
					_staminaField.SetValue(_controller, num3);
					_drainField.SetValue(_controller, 0f);
					Log.Debug($"Wall jump cost {num:0.0} stamina ({num2:0.0} -> {num3:0.0})");
				}
			}
			catch (Exception e)
			{
				Fault(e);
			}
		}

		private static void Fault(Exception e)
		{
			_faults++;
			if (_faults == 1)
			{
				Log.Warning("ClimbStamina integration error: " + e.GetType().Name + ": " + e.Message);
			}
			if (_faults >= 3 && Active)
			{
				Active = false;
				_controller = null;
				Log.Warning("Disabling the ClimbStamina integration after repeated errors. Wall jumping continues without a stamina cost; ClimbStamina itself is untouched.");
			}
		}
	}
}