Decompiled source of ClimbStamina v1.3.0

Mods/ClimbStamina.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BoneLib;
using BoneLib.BoneMenu;
using BoneLib.Notifications;
using ClimbStamina;
using ClimbStamina.Compatibility;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSLZ.Bonelab;
using Il2CppSLZ.Marrow;
using Il2CppSLZ.Marrow.Interaction;
using Il2CppSLZ.VRMK;
using Il2CppTMPro;
using MelonLoader;
using MelonLoader.Preferences;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(ClimbStaminaMod), "ClimbStamina", "1.3.0", "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("ClimbStamina")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyInformationalVersion("1.3.0+c417cbe30ed54091dc052cf07de631265199e42b")]
[assembly: AssemblyProduct("ClimbStamina")]
[assembly: AssemblyTitle("ClimbStamina")]
[assembly: AssemblyVersion("1.3.0.0")]
namespace ClimbStamina
{
	internal sealed class ClimbDetector
	{
		private sealed class HandSlot
		{
			internal Hand Hand;

			internal Grip Grip;

			internal bool IsClimbing
			{
				get
				{
					if ((Object)(object)Hand != (Object)null)
					{
						return (Object)(object)Grip != (Object)null;
					}
					return false;
				}
			}

			internal void Clear()
			{
				Hand = null;
				Grip = null;
			}
		}

		private readonly HandSlot _left = new HandSlot();

		private readonly HandSlot _right = new HandSlot();

		internal Action<Hand, Grip> OnBlockedClimbAttach;

		internal Action<Hand> OnClimbAttached;

		internal Func<bool> CanStartClimb = () => true;

		internal int ClimbingHandCount => (_left.IsClimbing ? 1 : 0) + (_right.IsClimbing ? 1 : 0);

		internal bool LeftClimbing => _left.IsClimbing;

		internal bool RightClimbing => _right.IsClimbing;

		internal void Subscribe()
		{
			Hooking.OnGripAttached += OnGripAttached;
			Hooking.OnGripDetached += OnGripDetached;
		}

		internal void Unsubscribe()
		{
			Hooking.OnGripAttached -= OnGripAttached;
			Hooking.OnGripDetached -= OnGripDetached;
		}

		internal static bool IsClimbingGrip(Grip grip)
		{
			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;
			}
			return ((HandReciever)grip).IsStatic;
		}

		private void OnGripAttached(Grip grip, Hand hand)
		{
			if (!PlayerTracker.IsLocalHand(hand) || !IsClimbingGrip(grip))
			{
				return;
			}
			if (!CanStartClimb())
			{
				OnBlockedClimbAttach?.Invoke(hand, grip);
				return;
			}
			HandSlot handSlot = SlotFor(hand);
			if (handSlot != null)
			{
				handSlot.Hand = hand;
				handSlot.Grip = grip;
				ClimbStaminaMod.LogDebug("Climb attach: " + SideName(hand) + " -> " + ((Object)grip).name);
				OnClimbAttached?.Invoke(hand);
			}
		}

		private void OnGripDetached(Grip grip, Hand hand)
		{
			if (!((Object)(object)hand == (Object)null))
			{
				HandSlot handSlot = SlotFor(hand);
				if (handSlot != null && handSlot.IsClimbing && !((Object)(object)handSlot.Hand != (Object)(object)hand) && ((Object)(object)handSlot.Grip == (Object)(object)grip || (Object)(object)grip == (Object)null))
				{
					ClimbStaminaMod.LogDebug("Climb detach: " + SideName(hand));
					handSlot.Clear();
				}
			}
		}

		internal void Validate()
		{
			ValidateSlot(_left);
			ValidateSlot(_right);
		}

		private static void ValidateSlot(HandSlot slot)
		{
			if (!slot.IsClimbing)
			{
				if ((Object)(object)slot.Hand != (Object)null || (Object)(object)slot.Grip != (Object)null)
				{
					slot.Clear();
				}
			}
			else if ((Object)(object)slot.Hand == (Object)null || (Object)(object)slot.Grip == (Object)null || !PlayerTracker.IsLocalHand(slot.Hand) || (Object)(object)slot.Hand.AttachedReceiver != (Object)(object)slot.Grip)
			{
				slot.Clear();
			}
		}

		internal void Rescan()
		{
			RescanHand(Player.LeftHand, _left);
			RescanHand(Player.RightHand, _right);
		}

		private static void RescanHand(Hand hand, HandSlot slot)
		{
			if (slot.IsClimbing || (Object)(object)hand == (Object)null || !PlayerTracker.IsLocalHand(hand))
			{
				return;
			}
			HandReciever attachedReceiver = hand.AttachedReceiver;
			if (!((Object)(object)attachedReceiver == (Object)null))
			{
				Grip val = ((Il2CppObjectBase)attachedReceiver).TryCast<Grip>();
				if (!((Object)(object)val == (Object)null) && IsClimbingGrip(val))
				{
					slot.Hand = hand;
					slot.Grip = val;
				}
			}
		}

		internal void ForEachClimbingHand(Action<Hand> action)
		{
			if (_left.IsClimbing)
			{
				action(_left.Hand);
			}
			if (_right.IsClimbing)
			{
				action(_right.Hand);
			}
		}

		internal void Clear()
		{
			_left.Clear();
			_right.Clear();
		}

		private HandSlot SlotFor(Hand hand)
		{
			//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_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			Handedness handedness = hand.handedness;
			if ((int)handedness != 1)
			{
				if ((int)handedness == 2)
				{
					return _right;
				}
				return null;
			}
			return _left;
		}

		private static string SideName(Hand hand)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)hand.handedness != 1)
			{
				return "right";
			}
			return "left";
		}
	}
	public class ClimbStaminaMod : MelonMod
	{
		internal static bool ClimbBlocked;

		private static Instance _logger;

		private static float _lastBlockedNotifyStatic = -999f;

		private ClimbDetector _detector;

		private GripController _grips;

		private StaminaController _stamina;

		private StaminaHud _hud;

		private HapticsController _haptics;

		private PanicGrabController _panic;

		private bool _wasEnabled = true;

		private float _debugLogTimer;

		private Action<LevelInfo> _onLevelLoaded;

		private Action _onLevelUnloaded;

		private Action<Avatar> _onAvatarSwitched;

		private Action<RigManager> _onPlayerDeath;

		private Action<RigManager> _onPlayerResurrected;

		public override void OnInitializeMelon()
		{
			_logger = ((MelonBase)this).LoggerInstance;
			Prefs.Setup();
			SettingsMenu.Setup();
			_detector = new ClimbDetector();
			_grips = new GripController();
			_stamina = new StaminaController(_detector, _grips);
			_hud = new StaminaHud();
			_haptics = new HapticsController(_detector);
			_panic = new PanicGrabController(_detector, _stamina, _haptics, _hud);
			_detector.OnBlockedClimbAttach = OnBlockedClimbAttach;
			_detector.OnClimbAttached = _panic.OnClimbAttached;
			_stamina.OnExhausted = OnExhausted;
			_stamina.OnLowStamina = OnLowStamina;
			_detector.Subscribe();
			GripController.ApplyPatches(((MelonBase)this).HarmonyInstance);
			_onLevelLoaded = OnLevelLoaded;
			_onLevelUnloaded = OnLevelUnloaded;
			_onAvatarSwitched = delegate
			{
				PlayerTracker.RefreshAvatarMultiplier();
			};
			_onPlayerDeath = delegate(RigManager rig)
			{
				if (IsLocalRig(rig))
				{
					_detector.Clear();
					_grips.Clear();
					_panic.Reset();
				}
			};
			_onPlayerResurrected = delegate(RigManager rig)
			{
				if (IsLocalRig(rig))
				{
					_stamina.ResetFull();
				}
			};
			Hooking.OnLevelLoaded += _onLevelLoaded;
			Hooking.OnLevelUnloaded += _onLevelUnloaded;
			Hooking.OnSwitchAvatarPostfix += _onAvatarSwitched;
			Hooking.OnPlayerDeath += _onPlayerDeath;
			Hooking.OnPlayerResurrected += _onPlayerResurrected;
			Log("BoneLib detected.");
			FusionCompatibility.Detect();
			ClimbAnythingCompatibility.Detect();
			Log("Initialized.");
		}

		public override void OnDeinitializeMelon()
		{
			_hud?.Drop();
			_detector?.Unsubscribe();
			Hooking.OnLevelLoaded -= _onLevelLoaded;
			Hooking.OnLevelUnloaded -= _onLevelUnloaded;
			Hooking.OnSwitchAvatarPostfix -= _onAvatarSwitched;
			Hooking.OnPlayerDeath -= _onPlayerDeath;
			Hooking.OnPlayerResurrected -= _onPlayerResurrected;
		}

		public override void OnUpdate()
		{
			if (_stamina == null)
			{
				return;
			}
			bool value = Prefs.Enabled.Value;
			if (value != _wasEnabled)
			{
				_wasEnabled = value;
				if (!value)
				{
					_detector.Clear();
					_grips.Clear();
					_stamina.ResetFull();
					_hud.Drop();
					_haptics.Reset();
					_panic.Reset();
				}
				else if (PlayerTracker.IsValid)
				{
					_detector.Rescan();
				}
			}
			if (value)
			{
				float deltaTime = Time.deltaTime;
				_panic.Update(deltaTime);
				_stamina.Update(deltaTime);
				_hud.Update(_stamina, deltaTime);
				_haptics.Update(_stamina, deltaTime);
				EmitDebugLog(deltaTime);
			}
		}

		private void OnLevelLoaded(LevelInfo info)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			_detector.Clear();
			_grips.Clear();
			_stamina.ResetFull();
			_hud.Drop();
			_haptics.Reset();
			_panic.Reset();
			PlayerTracker.RefreshAvatarMultiplier();
			Log("BONELAB player rig acquired (" + info.title + ").");
		}

		private void OnLevelUnloaded()
		{
			_detector.Clear();
			_grips.Clear();
			_hud.Drop();
			_haptics.Reset();
			_panic.Reset();
		}

		private void OnBlockedClimbAttach(Hand hand, Grip grip)
		{
			_grips.QueueDetach(hand);
			NotifyBlockedRegrab();
		}

		internal static void NotifyBlockedRegrab()
		{
			//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_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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: 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_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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			if (!(Time.time - _lastBlockedNotifyStatic < 2.5f))
			{
				_lastBlockedNotifyStatic = Time.time;
				Notifier.Send(new Notification
				{
					Title = NotificationText.op_Implicit("Climb Stamina"),
					Message = NotificationText.op_Implicit("Too exhausted to climb."),
					Type = (NotificationType)1,
					PopupLength = 1.2f,
					ShowTitleOnPopup = false
				});
			}
		}

		private static bool IsLocalRig(RigManager rig)
		{
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rig != (Object)null && (Object)(object)rigManager != (Object)null)
			{
				return (Object)(object)rig == (Object)(object)rigManager;
			}
			return false;
		}

		private void OnExhausted()
		{
			_haptics.PulseRelease();
		}

		private void OnLowStamina()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: 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_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_0020: 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_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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			Notifier.Send(new Notification
			{
				Title = NotificationText.op_Implicit("Climb Stamina"),
				Message = NotificationText.op_Implicit("Grip weakening..."),
				Type = (NotificationType)0,
				PopupLength = 1.2f,
				ShowTitleOnPopup = false
			});
		}

		private void EmitDebugLog(float deltaTime)
		{
			if (!Prefs.DebugLogging.Value)
			{
				return;
			}
			_debugLogTimer += deltaTime;
			if (!(_debugLogTimer < 1f))
			{
				_debugLogTimer = 0f;
				if (PlayerTracker.IsValid)
				{
					LogDebug($"left climbing = {_detector.LeftClimbing}, right climbing = {_detector.RightClimbing}, stamina = {_stamina.Stamina:0.0}, state = {_stamina.State}, standing = {PlayerTracker.IsStanding} ({PlayerTracker.DescribeGroundState()}), avatarMult = {PlayerTracker.AvatarDrainMultiplier:0.###}");
				}
			}
		}

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

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

		internal static void LogDebug(string message)
		{
			if (Prefs.DebugLogging != null && Prefs.DebugLogging.Value)
			{
				Instance logger = _logger;
				if (logger != null)
				{
					logger.Msg("[debug] " + message);
				}
			}
		}
	}
	internal sealed class GripController
	{
		private readonly List<Hand> _pendingDetach = new List<Hand>(2);

		internal static void ReleaseClimbingHand(Hand hand)
		{
			if ((Object)(object)hand == (Object)null)
			{
				return;
			}
			HandReciever attachedReceiver = hand.AttachedReceiver;
			if (!((Object)(object)attachedReceiver == (Object)null))
			{
				Grip val = ((Il2CppObjectBase)attachedReceiver).TryCast<Grip>();
				if (!((Object)(object)val == (Object)null) && ClimbDetector.IsClimbingGrip(val))
				{
					val.ForceDetach(hand);
					ClimbStaminaMod.LogDebug("Force-detached climbing hand");
				}
			}
		}

		internal static void SweepClimbingHands()
		{
			Hand leftHand = Player.LeftHand;
			if ((Object)(object)leftHand != (Object)null && PlayerTracker.IsLocalHand(leftHand))
			{
				ReleaseClimbingHand(leftHand);
			}
			Hand rightHand = Player.RightHand;
			if ((Object)(object)rightHand != (Object)null && PlayerTracker.IsLocalHand(rightHand))
			{
				ReleaseClimbingHand(rightHand);
			}
		}

		internal void QueueDetach(Hand hand)
		{
			if ((Object)(object)hand != (Object)null && !_pendingDetach.Contains(hand))
			{
				_pendingDetach.Add(hand);
			}
		}

		internal void ProcessQueue()
		{
			if (_pendingDetach.Count == 0)
			{
				return;
			}
			for (int i = 0; i < _pendingDetach.Count; i++)
			{
				Hand val = _pendingDetach[i];
				if ((Object)(object)val != (Object)null && PlayerTracker.IsLocalHand(val))
				{
					ReleaseClimbingHand(val);
				}
			}
			_pendingDetach.Clear();
		}

		internal void Clear()
		{
			_pendingDetach.Clear();
		}

		internal static void ApplyPatches(Harmony harmony)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			MethodInfo method = typeof(Grip).GetMethod("OnGrabConfirm", AccessTools.all);
			MethodInfo method2 = typeof(GripController).GetMethod("OnGrabConfirmPrefix", AccessTools.all);
			if (method == null || method2 == null)
			{
				ClimbStaminaMod.LogWarning("Grip.OnGrabConfirm not found; exhausted grabs will be detached a frame late instead of blocked.");
			}
			else
			{
				harmony.Patch((MethodBase)method, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static bool OnGrabConfirmPrefix(Grip __instance, Hand hand)
		{
			try
			{
				if (!ClimbStaminaMod.ClimbBlocked)
				{
					return true;
				}
				if ((Object)(object)hand == (Object)null || !PlayerTracker.IsLocalHand(hand))
				{
					return true;
				}
				if ((Object)(object)__instance == (Object)null || !ClimbDetector.IsClimbingGrip(__instance))
				{
					return true;
				}
				ClimbStaminaMod.NotifyBlockedRegrab();
				ClimbStaminaMod.LogDebug("Blocked climbing grab while exhausted");
				return false;
			}
			catch (Exception ex)
			{
				ClimbStaminaMod.LogDebug("Grab prefix error, failing open: " + ex.GetType().Name);
				return true;
			}
		}
	}
	internal sealed class HapticsController
	{
		private const float StartFraction = 0.35f;

		private const float MinAmplitude = 0.05f;

		private const float MaxAmplitude = 0.55f;

		private const float IntensityCurve = 1.6f;

		private const float MaxInterval = 0.6f;

		private const float MinInterval = 0.1f;

		private const float IntervalCurve = 1.3f;

		private const float MinTickSeconds = 0.03f;

		private const float MaxTickSeconds = 0.08f;

		private const float TickFrequencyCalm = 170f;

		private const float TickFrequencyEmpty = 120f;

		private const float ReleaseAmplitude = 0.9f;

		private const float ReleaseSeconds = 0.15f;

		private const float ReleaseFrequency = 140f;

		private const float PanicAmplitude = 0.75f;

		private const float PanicSeconds = 0.12f;

		private const float PanicFrequency = 150f;

		private readonly ClimbDetector _detector;

		private float _sinceLastPulse;

		private bool _leftWasClimbing;

		private bool _rightWasClimbing;

		internal HapticsController(ClimbDetector detector)
		{
			_detector = detector;
		}

		internal void Reset()
		{
			_sinceLastPulse = 0f;
			_leftWasClimbing = false;
			_rightWasClimbing = false;
		}

		internal void Update(StaminaController stamina, float deltaTime)
		{
			bool leftClimbing = _detector.LeftClimbing;
			bool rightClimbing = _detector.RightClimbing;
			bool flag = stamina.State == StaminaState.CLIMBING_ONE_HAND || stamina.State == StaminaState.CLIMBING_TWO_HANDS;
			float fraction = stamina.Fraction;
			if (!Prefs.HapticsEnabled.Value || !flag || fraction >= 0.35f)
			{
				_sinceLastPulse = 0f;
			}
			else
			{
				float num = Mathf.Clamp01((0.35f - fraction) / 0.35f);
				float num2 = Mathf.Lerp(0.6f, 0.1f, Mathf.Pow(num, 1.3f));
				_sinceLastPulse += deltaTime;
				if (_sinceLastPulse >= num2)
				{
					_sinceLastPulse = 0f;
					float amplitude = Mathf.Lerp(0.05f, 0.55f, Mathf.Pow(num, 1.6f));
					float seconds = Mathf.Lerp(0.03f, 0.08f, num);
					float frequency = Mathf.Lerp(170f, 120f, num);
					if (leftClimbing)
					{
						Pulse(Player.LeftController, seconds, frequency, amplitude);
					}
					if (rightClimbing)
					{
						Pulse(Player.RightController, seconds, frequency, amplitude);
					}
				}
			}
			_leftWasClimbing = leftClimbing;
			_rightWasClimbing = rightClimbing;
		}

		internal void PulsePanic(Hand hand)
		{
			//IL_0017: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			if (!Prefs.HapticsEnabled.Value || (Object)(object)hand == (Object)null)
			{
				return;
			}
			Handedness handedness = hand.handedness;
			BaseController controller;
			if ((int)handedness != 1)
			{
				if ((int)handedness != 2)
				{
					return;
				}
				controller = Player.RightController;
			}
			else
			{
				controller = Player.LeftController;
			}
			Pulse(controller, 0.12f, 150f, 0.75f);
		}

		internal void PulseRelease()
		{
			if (Prefs.HapticsEnabled.Value)
			{
				if (_leftWasClimbing)
				{
					Pulse(Player.LeftController, 0.15f, 140f, 0.9f);
				}
				if (_rightWasClimbing)
				{
					Pulse(Player.RightController, 0.15f, 140f, 0.9f);
				}
			}
		}

		private static void Pulse(BaseController controller, float seconds, float frequency, float amplitude)
		{
			try
			{
				if ((Object)(object)controller == (Object)null)
				{
					return;
				}
				Haptor haptor = controller.haptor;
				if (!((Object)(object)haptor == (Object)null) && haptor.hapticsAllowed)
				{
					amplitude = Mathf.Clamp01(amplitude * Mathf.Max(0f, Prefs.HapticStrength.Value));
					if (!(amplitude <= 0f))
					{
						haptor.SENDHAPTIC(0f, seconds, frequency, amplitude);
					}
				}
			}
			catch (Exception ex)
			{
				ClimbStaminaMod.LogDebug("Haptic pulse failed: " + ex.GetType().Name);
			}
		}
	}
	internal sealed class PanicGrabController
	{
		private const float PeakHoldSeconds = 0.2f;

		private const float CooldownSeconds = 0.75f;

		private readonly ClimbDetector _detector;

		private readonly StaminaController _stamina;

		private readonly HapticsController _haptics;

		private readonly StaminaHud _hud;

		private float _peakFallSpeed;

		private float _peakAge;

		private float _cooldown;

		internal PanicGrabController(ClimbDetector detector, StaminaController stamina, HapticsController haptics, StaminaHud hud)
		{
			_detector = detector;
			_stamina = stamina;
			_haptics = haptics;
			_hud = hud;
		}

		internal void Reset()
		{
			_peakFallSpeed = 0f;
			_peakAge = 0f;
			_cooldown = 0f;
		}

		internal void Update(float deltaTime)
		{
			if (_cooldown > 0f)
			{
				_cooldown -= deltaTime;
			}
			float verticalVelocity = PlayerTracker.VerticalVelocity;
			if (PlayerTracker.IsStanding)
			{
				_peakFallSpeed = verticalVelocity;
				_peakAge = 0f;
				return;
			}
			if (verticalVelocity <= _peakFallSpeed)
			{
				_peakFallSpeed = verticalVelocity;
				_peakAge = 0f;
				return;
			}
			_peakAge += deltaTime;
			if (_peakAge >= 0.2f)
			{
				_peakFallSpeed = verticalVelocity;
				_peakAge = 0f;
			}
		}

		internal void OnClimbAttached(Hand hand)
		{
			if (Prefs.PanicGrabEnabled.Value && !(_cooldown > 0f) && _detector.ClimbingHandCount == 1 && !PlayerTracker.IsStanding)
			{
				float num = Mathf.Max(0.5f, Prefs.PanicFallSpeed.Value);
				if (!(_peakFallSpeed > 0f - num))
				{
					_cooldown = 0.75f;
					ClimbStaminaMod.LogDebug($"Panic grab at {_peakFallSpeed:0.0} m/s");
					_peakFallSpeed = 0f;
					_peakAge = 0f;
					_haptics.PulsePanic(hand);
					_hud.FlashPanic();
					_stamina.ApplyPanicGrab();
				}
			}
		}
	}
	internal static class PlayerTracker
	{
		private static float _avatarDrainMultiplier = 1f;

		private const float MinGroundNormalUp = 0.55f;

		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 bool IsStanding
		{
			get
			{
				//IL_0012: 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)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Invalid comparison between Unknown and I4
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Invalid comparison between Unknown and I4
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Invalid comparison between Unknown and I4
				//IL_0043: Unknown result type (might be due to invalid IL or missing references)
				PhysicsRig physicsRig = Player.PhysicsRig;
				if ((Object)(object)physicsRig == (Object)null)
				{
					return true;
				}
				StepState stepState = physicsRig._stepState;
				if ((int)stepState == 3 || (int)stepState == 4 || (int)stepState == 5)
				{
					return false;
				}
				PhysGrounder physG = physicsRig.physG;
				if ((Object)(object)physG == (Object)null)
				{
					return true;
				}
				if (!physG.isGrounded)
				{
					return false;
				}
				return physG.groundNormal.y >= 0.55f;
			}
		}

		internal static float VerticalVelocity
		{
			get
			{
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				PhysicsRig physicsRig = Player.PhysicsRig;
				if ((Object)(object)physicsRig == (Object)null)
				{
					return 0f;
				}
				PhysTorso torso = physicsRig.torso;
				if ((Object)(object)torso == (Object)null)
				{
					return 0f;
				}
				Rigidbody rbPelvis = torso.rbPelvis;
				if ((Object)(object)rbPelvis == (Object)null)
				{
					return 0f;
				}
				return rbPelvis.velocity.y;
			}
		}

		internal static float AvatarDrainMultiplier => _avatarDrainMultiplier;

		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 string DescribeGroundState()
		{
			//IL_0086: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				PhysicsRig physicsRig = Player.PhysicsRig;
				if ((Object)(object)physicsRig == (Object)null)
				{
					return "physicsRig=null";
				}
				PhysGrounder physG = physicsRig.physG;
				if (!((Object)(object)physG == (Object)null))
				{
					Collider groundedCollider = physG._groundedCollider;
					return $"step={physicsRig._stepState}, isGrounded={physG.isGrounded}, normalY={physG.groundNormal.y:0.00}, groundCollider={(((Object)(object)groundedCollider != (Object)null) ? ((Object)groundedCollider).name : "none")}, footSupported={physicsRig.footSupported:0.00}";
				}
				return $"step={physicsRig._stepState}, grounder=null";
			}
			catch (Exception ex)
			{
				return "unavailable (" + ex.GetType().Name + ")";
			}
		}

		internal static void RefreshAvatarMultiplier()
		{
			_avatarDrainMultiplier = 1f;
			if (!Prefs.AvatarStrengthScaling.Value)
			{
				return;
			}
			RigManager rigManager = Player.RigManager;
			if ((Object)(object)rigManager == (Object)null)
			{
				return;
			}
			Avatar avatar = rigManager.avatar;
			if (!((Object)(object)avatar == (Object)null))
			{
				float strengthUpper = avatar._strengthUpper;
				float num = Mathf.Max(0.01f, Prefs.AvatarReferenceStrength.Value);
				if (float.IsNaN(strengthUpper) || strengthUpper <= 0f)
				{
					ClimbStaminaMod.LogDebug("Avatar strengthUpper unavailable; multiplier stays 1.0");
					return;
				}
				_avatarDrainMultiplier = Mathf.Clamp(num / strengthUpper, 0.7f, 1.3f);
				ClimbStaminaMod.LogDebug($"Avatar strengthUpper={strengthUpper:0.###} reference={num:0.###} drainMult={_avatarDrainMultiplier:0.###}");
			}
		}
	}
	internal static class Prefs
	{
		internal static MelonPreferences_Category Category;

		internal static MelonPreferences_Entry<bool> Enabled;

		internal static MelonPreferences_Entry<float> MaxStamina;

		internal static MelonPreferences_Entry<float> OneHandDrainRate;

		internal static MelonPreferences_Entry<float> TwoHandDrainRate;

		internal static MelonPreferences_Entry<float> AscendingMultiplier;

		internal static MelonPreferences_Entry<float> GroundedRegenRate;

		internal static MelonPreferences_Entry<float> AirborneRegenRate;

		internal static MelonPreferences_Entry<float> RecoveryDelay;

		internal static MelonPreferences_Entry<float> MinRegrabStamina;

		internal static MelonPreferences_Entry<bool> AvatarStrengthScaling;

		internal static MelonPreferences_Entry<float> AvatarReferenceStrength;

		internal static MelonPreferences_Entry<bool> ShowHud;

		internal static MelonPreferences_Entry<bool> HapticsEnabled;

		internal static MelonPreferences_Entry<float> HapticStrength;

		internal static MelonPreferences_Entry<bool> PanicGrabEnabled;

		internal static MelonPreferences_Entry<float> PanicFallSpeed;

		internal static MelonPreferences_Entry<float> PanicStaminaCost;

		internal static MelonPreferences_Entry<bool> DebugLogging;

		internal static void Setup()
		{
			Category = MelonPreferences.CreateCategory("ClimbStamina", "Climb Stamina");
			Enabled = Category.CreateEntry<bool>("Enabled", true, "Enabled", "Master switch for the climbing stamina system.", false, false, (ValueValidator)null, (string)null);
			MaxStamina = Category.CreateEntry<float>("MaxStamina", 100f, "Max Stamina", "Total stamina pool.", false, false, (ValueValidator)null, (string)null);
			OneHandDrainRate = Category.CreateEntry<float>("OneHandDrainRate", 10f, "One-Hand Drain Rate", "Stamina drained per second while hanging from one hand.", false, false, (ValueValidator)null, (string)null);
			TwoHandDrainRate = Category.CreateEntry<float>("TwoHandDrainRate", 5f, "Two-Hand Drain Rate", "Stamina drained per second while hanging from two hands.", false, false, (ValueValidator)null, (string)null);
			AscendingMultiplier = Category.CreateEntry<float>("AscendingMultiplier", 1.3f, "Ascending Multiplier", "Extra drain multiplier while actively climbing upward.", false, false, (ValueValidator)null, (string)null);
			GroundedRegenRate = Category.CreateEntry<float>("GroundedRegenRate", 20f, "Grounded Regen Rate", "Stamina regenerated per second while standing on the ground.", false, false, (ValueValidator)null, (string)null);
			AirborneRegenRate = Category.CreateEntry<float>("AirborneRegenRate", 5f, "Airborne Regen Rate", "Stamina regenerated per second while airborne (not climbing).", false, false, (ValueValidator)null, (string)null);
			RecoveryDelay = Category.CreateEntry<float>("RecoveryDelay", 1f, "Recovery Delay", "Seconds after climbing stops before stamina starts regenerating.", false, false, (ValueValidator)null, (string)null);
			MinRegrabStamina = Category.CreateEntry<float>("MinRegrabStamina", 100f, "Regrab Stamina %", "After exhaustion, climbing stays blocked until the bar refills to this percent.", false, false, (ValueValidator)null, (string)null);
			AvatarStrengthScaling = Category.CreateEntry<bool>("AvatarStrengthScaling", false, "Avatar Strength Scaling", "Scale drain by the avatar's upper-body strength stat (clamped 0.70x-1.30x).", false, false, (ValueValidator)null, (string)null);
			AvatarReferenceStrength = Category.CreateEntry<float>("AvatarReferenceStrength", 1f, "Avatar Reference Strength", "strengthUpper value treated as the 1.0x baseline. Enable Debug Logging to see your avatars' values.", false, false, (ValueValidator)null, (string)null);
			ShowHud = Category.CreateEntry<bool>("ShowHud", true, "Show Stamina HUD", "World-space stamina bar shown while climbing/recovering.", false, false, (ValueValidator)null, (string)null);
			HapticsEnabled = Category.CreateEntry<bool>("HapticsEnabled", true, "Haptic Feedback", "Pulse the gripping hands' controllers as stamina runs low.", false, false, (ValueValidator)null, (string)null);
			HapticStrength = Category.CreateEntry<float>("HapticStrength", 1f, "Haptic Strength", "Multiplier on haptic pulse strength (0 = silent, 1 = default).", false, false, (ValueValidator)null, (string)null);
			PanicGrabEnabled = Category.CreateEntry<bool>("PanicGrabEnabled", true, "Panic Grab", "Catching a climbable surface mid-fall costs stamina and briefly drains faster.", false, false, (ValueValidator)null, (string)null);
			PanicFallSpeed = Category.CreateEntry<float>("PanicFallSpeed", 4f, "Panic Fall Speed", "Downward speed (m/s) a fall must reach for a grab to count as a panic grab.", false, false, (ValueValidator)null, (string)null);
			PanicStaminaCost = Category.CreateEntry<float>("PanicStaminaCost", 20f, "Panic Stamina Cost %", "Stamina lost instantly on a panic grab, as a percent of max.", false, false, (ValueValidator)null, (string)null);
			DebugLogging = Category.CreateEntry<bool>("DebugLogging", false, "Debug Logging", "Verbose state logging for troubleshooting.", false, false, (ValueValidator)null, (string)null);
		}

		internal static void Save()
		{
			MelonPreferences.Save();
		}
	}
	internal static class SettingsMenu
	{
		internal static void Setup()
		{
			//IL_0019: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0321: Unknown result type (might be due to invalid IL or missing references)
			//IL_035b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0427: Unknown result type (might be due to invalid IL or missing references)
			//IL_046f: Unknown result type (might be due to invalid IL or missing references)
			Page obj = Page.Root.CreatePage("Climb Stamina", new Color(0.35f, 0.75f, 1f), 0, true);
			obj.CreateBool("Enabled", Color.white, Prefs.Enabled.Value, (Action<bool>)delegate(bool v)
			{
				Prefs.Enabled.Value = v;
				Prefs.Save();
			});
			obj.CreateFloat("Max Stamina", Color.white, Prefs.MaxStamina.Value, 10f, 20f, 300f, (Action<float>)delegate(float v)
			{
				Prefs.MaxStamina.Value = v;
				Prefs.Save();
			});
			obj.CreateFloat("One-Hand Drain Rate", Color.white, Prefs.OneHandDrainRate.Value, 1f, 0f, 60f, (Action<float>)delegate(float v)
			{
				Prefs.OneHandDrainRate.Value = v;
				Prefs.Save();
			});
			obj.CreateFloat("Two-Hand Drain Rate", Color.white, Prefs.TwoHandDrainRate.Value, 1f, 0f, 60f, (Action<float>)delegate(float v)
			{
				Prefs.TwoHandDrainRate.Value = v;
				Prefs.Save();
			});
			obj.CreateFloat("Ascending Multiplier", Color.white, Prefs.AscendingMultiplier.Value, 0.05f, 1f, 3f, (Action<float>)delegate(float v)
			{
				Prefs.AscendingMultiplier.Value = v;
				Prefs.Save();
			});
			obj.CreateFloat("Grounded Regen Rate", Color.white, Prefs.GroundedRegenRate.Value, 1f, 0f, 100f, (Action<float>)delegate(float v)
			{
				Prefs.GroundedRegenRate.Value = v;
				Prefs.Save();
			});
			obj.CreateFloat("Airborne Regen Rate", Color.white, Prefs.AirborneRegenRate.Value, 1f, 0f, 100f, (Action<float>)delegate(float v)
			{
				Prefs.AirborneRegenRate.Value = v;
				Prefs.Save();
			});
			obj.CreateFloat("Recovery Delay", Color.white, Prefs.RecoveryDelay.Value, 0.25f, 0f, 10f, (Action<float>)delegate(float v)
			{
				Prefs.RecoveryDelay.Value = v;
				Prefs.Save();
			});
			obj.CreateFloat("Regrab Stamina %", Color.white, Prefs.MinRegrabStamina.Value, 5f, 0f, 100f, (Action<float>)delegate(float v)
			{
				Prefs.MinRegrabStamina.Value = v;
				Prefs.Save();
			});
			obj.CreateBool("Avatar Strength Scaling", Color.white, Prefs.AvatarStrengthScaling.Value, (Action<bool>)delegate(bool v)
			{
				Prefs.AvatarStrengthScaling.Value = v;
				Prefs.Save();
				PlayerTracker.RefreshAvatarMultiplier();
			});
			obj.CreateBool("Show Stamina HUD", Color.white, Prefs.ShowHud.Value, (Action<bool>)delegate(bool v)
			{
				Prefs.ShowHud.Value = v;
				Prefs.Save();
			});
			obj.CreateBool("Haptic Feedback", Color.white, Prefs.HapticsEnabled.Value, (Action<bool>)delegate(bool v)
			{
				Prefs.HapticsEnabled.Value = v;
				Prefs.Save();
			});
			obj.CreateFloat("Haptic Strength", Color.white, Prefs.HapticStrength.Value, 0.1f, 0f, 2f, (Action<float>)delegate(float v)
			{
				Prefs.HapticStrength.Value = v;
				Prefs.Save();
			});
			obj.CreateBool("Panic Grab", Color.white, Prefs.PanicGrabEnabled.Value, (Action<bool>)delegate(bool v)
			{
				Prefs.PanicGrabEnabled.Value = v;
				Prefs.Save();
			});
			obj.CreateFloat("Panic Fall Speed", Color.white, Prefs.PanicFallSpeed.Value, 0.5f, 1f, 12f, (Action<float>)delegate(float v)
			{
				Prefs.PanicFallSpeed.Value = v;
				Prefs.Save();
			});
			obj.CreateFloat("Panic Stamina Cost %", Color.white, Prefs.PanicStaminaCost.Value, 5f, 0f, 60f, (Action<float>)delegate(float v)
			{
				Prefs.PanicStaminaCost.Value = v;
				Prefs.Save();
			});
			obj.CreateBool("Debug Logging", Color.white, Prefs.DebugLogging.Value, (Action<bool>)delegate(bool v)
			{
				Prefs.DebugLogging.Value = v;
				Prefs.Save();
			});
		}
	}
	internal enum StaminaState
	{
		READY,
		CLIMBING_ONE_HAND,
		CLIMBING_TWO_HANDS,
		RECOVERY_DELAY,
		RECOVERING,
		EXHAUSTED
	}
	internal sealed class StaminaController
	{
		private const float AscendDeadZone = 0.3f;

		private const float AscendSmoothingRate = 8f;

		internal const float LowStaminaFraction = 0.25f;

		private const float LowStaminaRearmFraction = 0.3f;

		private const float PanicDrainWindowSeconds = 1f;

		private const float PanicDrainMultiplier = 1.75f;

		private readonly ClimbDetector _detector;

		private readonly GripController _grips;

		private float _stamina;

		private float _timeSinceDrain = float.MaxValue;

		private float _smoothedVerticalVelocity;

		private bool _wasSupporting;

		private float _panicDrainRemaining;

		internal Action OnExhausted;

		internal Action OnLowStamina;

		private bool _lowStaminaSignaled;

		internal StaminaState State { get; private set; }

		internal float Stamina => _stamina;

		internal float MaxStamina => Mathf.Max(1f, Prefs.MaxStamina.Value);

		internal float Fraction => Mathf.Clamp01(_stamina / MaxStamina);

		internal bool IsExhausted => State == StaminaState.EXHAUSTED;

		internal StaminaController(ClimbDetector detector, GripController grips)
		{
			_detector = detector;
			_grips = grips;
			_stamina = MaxStamina;
			_detector.CanStartClimb = () => !IsExhausted;
		}

		internal void ResetFull()
		{
			_stamina = MaxStamina;
			_timeSinceDrain = float.MaxValue;
			_smoothedVerticalVelocity = 0f;
			_wasSupporting = false;
			_panicDrainRemaining = 0f;
			_lowStaminaSignaled = false;
			State = StaminaState.READY;
			ClimbStaminaMod.ClimbBlocked = false;
		}

		internal void Update(float deltaTime)
		{
			if (!PlayerTracker.IsValid)
			{
				_detector.Clear();
				_grips.Clear();
				_smoothedVerticalVelocity = 0f;
				return;
			}
			_grips.ProcessQueue();
			_detector.Validate();
			float maxStamina = MaxStamina;
			_stamina = Mathf.Min(_stamina, maxStamina);
			int climbingHandCount = _detector.ClimbingHandCount;
			bool isStanding = PlayerTracker.IsStanding;
			bool flag = climbingHandCount > 0 && !isStanding;
			if (flag != _wasSupporting)
			{
				if (Prefs.DebugLogging.Value && climbingHandCount > 0)
				{
					ClimbStaminaMod.LogDebug($"support {(flag ? "gained" : "lost")}: hands={climbingHandCount}, {PlayerTracker.DescribeGroundState()}");
				}
				_wasSupporting = flag;
			}
			UpdateAscendSmoothing(deltaTime, flag);
			if (_timeSinceDrain < float.MaxValue)
			{
				_timeSinceDrain += deltaTime;
			}
			if (_panicDrainRemaining > 0f)
			{
				_panicDrainRemaining -= deltaTime;
			}
			if (State == StaminaState.EXHAUSTED)
			{
				GripController.SweepClimbingHands();
				_detector.Clear();
				Regenerate(deltaTime, isStanding);
				float num = maxStamina * Mathf.Clamp01(Prefs.MinRegrabStamina.Value / 100f);
				if (_timeSinceDrain >= Prefs.RecoveryDelay.Value && _stamina >= num)
				{
					State = StaminaState.READY;
					ClimbStaminaMod.ClimbBlocked = false;
					_lowStaminaSignaled = false;
					ClimbStaminaMod.LogDebug($"Recovered from exhaustion at stamina={_stamina:0.0}");
				}
			}
			else if (flag)
			{
				State = ((climbingHandCount < 2) ? StaminaState.CLIMBING_ONE_HAND : StaminaState.CLIMBING_TWO_HANDS);
				float num2 = ((climbingHandCount >= 2) ? Prefs.TwoHandDrainRate.Value : Prefs.OneHandDrainRate.Value);
				if (_smoothedVerticalVelocity > 0.3f)
				{
					num2 *= Mathf.Max(1f, Prefs.AscendingMultiplier.Value);
				}
				num2 *= PlayerTracker.AvatarDrainMultiplier;
				if (_panicDrainRemaining > 0f)
				{
					num2 *= 1.75f;
				}
				_stamina -= Mathf.Max(0f, num2) * deltaTime;
				_timeSinceDrain = 0f;
				if (!_lowStaminaSignaled && Fraction < 0.25f)
				{
					_lowStaminaSignaled = true;
					OnLowStamina?.Invoke();
				}
				if (_stamina <= 0f)
				{
					_stamina = 0f;
					Exhaust(deferRelease: false);
				}
			}
			else if (_stamina >= maxStamina)
			{
				State = StaminaState.READY;
				_lowStaminaSignaled = false;
			}
			else if (_timeSinceDrain < Prefs.RecoveryDelay.Value)
			{
				State = StaminaState.RECOVERY_DELAY;
			}
			else
			{
				State = StaminaState.RECOVERING;
				Regenerate(deltaTime, isStanding);
				if (_lowStaminaSignaled && Fraction >= 0.3f)
				{
					_lowStaminaSignaled = false;
				}
				if (_stamina >= maxStamina)
				{
					_stamina = maxStamina;
					State = StaminaState.READY;
					_lowStaminaSignaled = false;
				}
			}
		}

		internal void ApplyPanicGrab()
		{
			float num = MaxStamina * Mathf.Clamp01(Prefs.PanicStaminaCost.Value / 100f);
			_stamina -= num;
			_timeSinceDrain = 0f;
			_panicDrainRemaining = 1f;
			if (_stamina <= 0f)
			{
				_stamina = 0f;
				Exhaust(deferRelease: true);
			}
		}

		private void Regenerate(float deltaTime, bool standing)
		{
			if (!(_timeSinceDrain < Prefs.RecoveryDelay.Value))
			{
				float num = (standing ? Prefs.GroundedRegenRate.Value : Prefs.AirborneRegenRate.Value);
				_stamina = Mathf.Min(MaxStamina, _stamina + Mathf.Max(0f, num) * deltaTime);
			}
		}

		private void UpdateAscendSmoothing(float deltaTime, bool supporting)
		{
			if (!supporting)
			{
				_smoothedVerticalVelocity = 0f;
				return;
			}
			float verticalVelocity = PlayerTracker.VerticalVelocity;
			float num = 1f - Mathf.Exp(-8f * deltaTime);
			_smoothedVerticalVelocity = Mathf.Lerp(_smoothedVerticalVelocity, verticalVelocity, num);
		}

		private void Exhaust(bool deferRelease)
		{
			State = StaminaState.EXHAUSTED;
			ClimbStaminaMod.ClimbBlocked = true;
			_timeSinceDrain = 0f;
			if (deferRelease)
			{
				_detector.ForEachClimbingHand(_grips.QueueDetach);
			}
			else
			{
				_detector.ForEachClimbingHand(GripController.ReleaseClimbingHand);
			}
			_detector.Clear();
			ClimbStaminaMod.LogDebug("Stamina exhausted; climbing grips released");
			OnExhausted?.Invoke();
		}
	}
	internal sealed class StaminaHud
	{
		private const float FollowDistance = 0.72f;

		private const float FollowDrop = 0.16f;

		private const float FollowRate = 5f;

		private const float FadeRate = 7f;

		private const float FullLingerSeconds = 1.2f;

		private const float WallClearance = 0.06f;

		private const float MinFollowDistance = 0.1f;

		private const float AbsoluteMinDistance = 0.055f;

		private const float BaseScale = 0.00075f;

		private const float SnapDistance = 2.5f;

		private const float WarnFraction = 0.5f;

		private static readonly float NominalDistance = Mathf.Sqrt(0.544f);

		private static readonly Color BarNormal = new Color(0.55f, 0.85f, 0.55f, 1f);

		private static readonly Color BarWarn = new Color(0.93f, 0.78f, 0.35f, 1f);

		private static readonly Color BarLow = new Color(0.9f, 0.35f, 0.28f, 1f);

		private static readonly Color BarExhausted = new Color(0.82f, 0.16f, 0.12f, 1f);

		private static readonly Color LabelExhausted = new Color(1f, 0.55f, 0.5f, 1f);

		private readonly string[] _percentText;

		private const float MinFillFraction = 0.11f;

		private GameObject _root;

		private CanvasGroup _group;

		private RectTransform _fillRect;

		private Image _fillImage;

		private TextMeshProUGUI _label;

		private Texture2D _bgTexture;

		private Sprite _bgSprite;

		private Texture2D _fillTexture;

		private Sprite _fillSprite;

		private Texture2D _outlineTexture;

		private Sprite _outlineSprite;

		private Texture2D _glowTexture;

		private Sprite _glowSprite;

		private Image _glowImage;

		private float _glowAlpha;

		private float _alpha;

		private float _fullSince = -999f;

		private int _shownPercent = -1;

		private bool _shownExhausted;

		private float _shownFraction = -1f;

		private int _shownColorBand = -1;

		internal StaminaHud()
		{
			_percentText = new string[101];
			for (int i = 0; i <= 100; i++)
			{
				_percentText[i] = i + "%";
			}
		}

		internal void Drop()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
			DestroyVisualAssets();
			_root = null;
			_group = null;
			_fillRect = null;
			_fillImage = null;
			_label = null;
			_glowImage = null;
			_glowAlpha = 0f;
			_alpha = 0f;
			_shownPercent = -1;
			_shownFraction = -1f;
			_shownColorBand = -1;
		}

		private void DestroyVisualAssets()
		{
			if ((Object)(object)_bgSprite != (Object)null)
			{
				Object.Destroy((Object)(object)_bgSprite);
			}
			if ((Object)(object)_bgTexture != (Object)null)
			{
				Object.Destroy((Object)(object)_bgTexture);
			}
			if ((Object)(object)_fillSprite != (Object)null)
			{
				Object.Destroy((Object)(object)_fillSprite);
			}
			if ((Object)(object)_fillTexture != (Object)null)
			{
				Object.Destroy((Object)(object)_fillTexture);
			}
			if ((Object)(object)_outlineSprite != (Object)null)
			{
				Object.Destroy((Object)(object)_outlineSprite);
			}
			if ((Object)(object)_outlineTexture != (Object)null)
			{
				Object.Destroy((Object)(object)_outlineTexture);
			}
			if ((Object)(object)_glowSprite != (Object)null)
			{
				Object.Destroy((Object)(object)_glowSprite);
			}
			if ((Object)(object)_glowTexture != (Object)null)
			{
				Object.Destroy((Object)(object)_glowTexture);
			}
			_bgSprite = null;
			_bgTexture = null;
			_fillSprite = null;
			_fillTexture = null;
			_outlineSprite = null;
			_outlineTexture = null;
			_glowSprite = null;
			_glowTexture = null;
		}

		internal void Update(StaminaController stamina, float deltaTime)
		{
			bool flag = ComputeVisibility(stamina);
			if ((Object)(object)_root == (Object)null)
			{
				if (!flag || !PlayerTracker.IsValid || !Build())
				{
					return;
				}
				SnapToTarget();
			}
			float num = (flag ? 1f : 0f);
			_alpha = Mathf.Lerp(_alpha, num, 1f - Mathf.Exp(-7f * deltaTime));
			if (_alpha < 0.01f && !flag)
			{
				if (_root.activeSelf)
				{
					_root.SetActive(false);
				}
				return;
			}
			if (!_root.activeSelf)
			{
				_root.SetActive(true);
				SnapToTarget();
			}
			_group.alpha = _alpha;
			Follow(deltaTime);
			Refresh(stamina, deltaTime);
		}

		private bool ComputeVisibility(StaminaController stamina)
		{
			if (!Prefs.ShowHud.Value || !PlayerTracker.IsValid)
			{
				return false;
			}
			if (stamina.Fraction < 1f || stamina.IsExhausted)
			{
				_fullSince = Time.time;
				return true;
			}
			return Time.time - _fullSince < 1.2f;
		}

		private void Refresh(StaminaController stamina, float deltaTime)
		{
			//IL_0069: 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_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_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: 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_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			float fraction = stamina.Fraction;
			if (fraction <= 0.001f)
			{
				if (((Behaviour)_fillImage).enabled)
				{
					((Behaviour)_fillImage).enabled = false;
				}
			}
			else
			{
				if (!((Behaviour)_fillImage).enabled)
				{
					((Behaviour)_fillImage).enabled = true;
				}
				if (fraction != _shownFraction)
				{
					_shownFraction = fraction;
					_fillRect.anchorMax = new Vector2(Mathf.Max(fraction, 0.11f), 1f);
				}
			}
			bool isExhausted = stamina.IsExhausted;
			int num;
			Color color;
			if (isExhausted)
			{
				num = 3;
				color = BarExhausted;
			}
			else if (fraction < 0.25f)
			{
				num = 2;
				color = BarLow;
			}
			else if (fraction < 0.5f)
			{
				num = 1;
				color = BarWarn;
			}
			else
			{
				num = 0;
				color = BarNormal;
			}
			if (num != _shownColorBand)
			{
				_shownColorBand = num;
				((Graphic)_fillImage).color = color;
			}
			if ((Object)(object)_glowImage != (Object)null)
			{
				float num2 = ((isExhausted || fraction < 0.25f) ? (0.45f + 0.25f * Mathf.Sin(Time.time * 5f)) : 0f);
				_glowAlpha = Mathf.Lerp(_glowAlpha, num2, 1f - Mathf.Exp(-6f * deltaTime));
				if (_glowAlpha < 0.01f)
				{
					if (((Behaviour)_glowImage).enabled)
					{
						((Behaviour)_glowImage).enabled = false;
					}
				}
				else
				{
					if (!((Behaviour)_glowImage).enabled)
					{
						((Behaviour)_glowImage).enabled = true;
					}
					Color val = (isExhausted ? BarExhausted : BarLow);
					((Graphic)_glowImage).color = new Color(val.r, val.g, val.b, _glowAlpha);
				}
			}
			if ((Object)(object)_label == (Object)null)
			{
				return;
			}
			if (isExhausted)
			{
				if (!_shownExhausted)
				{
					_shownExhausted = true;
					_shownPercent = -1;
					((TMP_Text)_label).text = "EXHAUSTED";
					((Graphic)_label).color = LabelExhausted;
				}
				return;
			}
			_shownExhausted = false;
			int num3 = Mathf.Clamp(Mathf.RoundToInt(fraction * 100f), 0, 100);
			if (num3 != _shownPercent)
			{
				_shownPercent = num3;
				((TMP_Text)_label).text = _percentText[num3];
				((Graphic)_label).color = Color.white;
			}
		}

		internal void FlashPanic()
		{
			_glowAlpha = Mathf.Max(_glowAlpha, 0.85f);
		}

		private void Follow(float deltaTime)
		{
			//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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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_0096: 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_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_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_00b1: 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_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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: 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)
			//IL_00d6: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: 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_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: 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_00fa: 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)
			Transform head = Player.Head;
			if ((Object)(object)head == (Object)null)
			{
				return;
			}
			Vector3 val = TargetPosition(head);
			Transform transform = _root.transform;
			Vector3 val2 = transform.position - val;
			if (((Vector3)(ref val2)).sqrMagnitude > 6.25f)
			{
				transform.position = val;
				Vector3 val3 = val - head.position;
				if (((Vector3)(ref val3)).sqrMagnitude > 1E-06f)
				{
					transform.rotation = Quaternion.LookRotation(val3, Vector3.up);
				}
				ApplyDistanceScale(((Vector3)(ref val3)).magnitude);
				return;
			}
			float num = 1f - Mathf.Exp(-5f * deltaTime);
			Vector3 val4 = Vector3.Lerp(transform.position, val, num);
			Vector3 val5 = val4 - head.position;
			float num2 = ((Vector3)(ref val5)).magnitude;
			if (num2 > 0.001f)
			{
				Vector3 val6 = val5 / num2;
				float num3 = ClampToGeometry(head.position, val6, num2);
				if (num3 < num2)
				{
					num2 = num3;
					val4 = head.position + val6 * num2;
				}
			}
			transform.position = val4;
			Vector3 val7 = val4 - head.position;
			if (((Vector3)(ref val7)).sqrMagnitude > 1E-06f)
			{
				Quaternion val8 = Quaternion.LookRotation(val7, Vector3.up);
				transform.rotation = Quaternion.Slerp(transform.rotation, val8, num);
			}
			ApplyDistanceScale(num2);
		}

		private void SnapToTarget()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_0043: 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_005d: 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_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)
			Transform head = Player.Head;
			if (!((Object)(object)head == (Object)null) && !((Object)(object)_root == (Object)null))
			{
				Vector3 val = TargetPosition(head);
				_root.transform.position = val;
				_root.transform.rotation = Quaternion.LookRotation(val - head.position, Vector3.up);
				Vector3 val2 = val - head.position;
				ApplyDistanceScale(((Vector3)(ref val2)).magnitude);
			}
		}

		private static Vector3 TargetPosition(Transform head)
		{
			//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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: 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_006b: 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_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_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)
			Vector3 forward = head.forward;
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.001f)
			{
				forward = Vector3.forward;
			}
			((Vector3)(ref forward)).Normalize();
			Vector3 val = (forward * 0.72f + Vector3.down * 0.16f) / NominalDistance;
			float num = ClampToGeometry(head.position, val, NominalDistance);
			return head.position + val * num;
		}

		private static float ClampToGeometry(Vector3 origin, Vector3 dir, float desired)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(origin, dir, ref val, desired + 0.06f, -5, (QueryTriggerInteraction)1))
			{
				return desired;
			}
			Collider collider = ((RaycastHit)(ref val)).collider;
			if ((Object)(object)collider == (Object)null || (Object)(object)collider.attachedRigidbody != (Object)null)
			{
				return desired;
			}
			float num = ((RaycastHit)(ref val)).distance - 0.06f;
			if (num >= 0.1f)
			{
				return Mathf.Min(num, desired);
			}
			return Mathf.Max(((RaycastHit)(ref val)).distance * 0.75f, 0.055f);
		}

		private void ApplyDistanceScale(float distance)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			float num = 0.00075f * Mathf.Clamp(distance / NominalDistance, 0.05f, 1f);
			_root.transform.localScale = new Vector3(num, num, num);
		}

		private bool Build()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: 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_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Expected O, but got Unknown
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: 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_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Expected O, but got Unknown
			//IL_031a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Unknown result type (might be due to invalid IL or missing references)
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			//IL_038b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f7: Expected O, but got Unknown
			//IL_046c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0477: Unknown result type (might be due to invalid IL or missing references)
			//IL_0482: Unknown result type (might be due to invalid IL or missing references)
			//IL_048c: Unknown result type (might be due to invalid IL or missing references)
			DestroyVisualAssets();
			_root = new GameObject("ClimbStamina_HUD");
			Canvas obj = _root.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)2;
			obj.sortingOrder = 100;
			_group = _root.AddComponent<CanvasGroup>();
			_group.interactable = false;
			_group.blocksRaycasts = false;
			_group.alpha = 0f;
			RectTransform component = _root.GetComponent<RectTransform>();
			component.sizeDelta = new Vector2(276f, 36f);
			((Transform)component).localScale = new Vector3(0.00075f, 0.00075f, 0.00075f);
			_bgSprite = CreateRoundedSprite(10, out _bgTexture);
			_fillSprite = CreateRoundedSprite(8, out _fillTexture);
			_outlineSprite = CreateRoundedOutlineSprite(10, 1.5f, soft: false, out _outlineTexture);
			_glowSprite = CreateRoundedOutlineSprite(10, 8f, soft: true, out _glowTexture);
			GameObject val = new GameObject("Background");
			val.transform.SetParent(_root.transform, false);
			Image val2 = val.AddComponent<Image>();
			((Graphic)val2).color = new Color(0f, 0f, 0f, 0.55f);
			if ((Object)(object)_bgSprite != (Object)null)
			{
				val2.sprite = _bgSprite;
				val2.type = (Type)1;
			}
			RectTransform component2 = val.GetComponent<RectTransform>();
			component2.anchorMin = Vector2.zero;
			component2.anchorMax = Vector2.one;
			component2.offsetMin = Vector2.zero;
			component2.offsetMax = Vector2.zero;
			if ((Object)(object)_glowSprite != (Object)null)
			{
				GameObject val3 = new GameObject("Glow");
				val3.transform.SetParent(_root.transform, false);
				_glowImage = val3.AddComponent<Image>();
				_glowImage.sprite = _glowSprite;
				_glowImage.type = (Type)1;
				((Graphic)_glowImage).color = new Color(0f, 0f, 0f, 0f);
				((Behaviour)_glowImage).enabled = false;
				RectTransform component3 = val3.GetComponent<RectTransform>();
				component3.anchorMin = Vector2.zero;
				component3.anchorMax = Vector2.one;
				component3.offsetMin = Vector2.zero;
				component3.offsetMax = Vector2.zero;
			}
			if ((Object)(object)_outlineSprite != (Object)null)
			{
				GameObject val4 = new GameObject("Outline");
				val4.transform.SetParent(_root.transform, false);
				Image obj2 = val4.AddComponent<Image>();
				obj2.sprite = _outlineSprite;
				obj2.type = (Type)1;
				((Graphic)obj2).color = new Color(0.38f, 0.38f, 0.38f, 0.85f);
				RectTransform component4 = val4.GetComponent<RectTransform>();
				component4.anchorMin = Vector2.zero;
				component4.anchorMax = Vector2.one;
				component4.offsetMin = Vector2.zero;
				component4.offsetMax = Vector2.zero;
			}
			GameObject val5 = new GameObject("Fill");
			val5.transform.SetParent(_root.transform, false);
			_fillImage = val5.AddComponent<Image>();
			((Graphic)_fillImage).color = BarNormal;
			if ((Object)(object)_fillSprite != (Object)null)
			{
				_fillImage.sprite = _fillSprite;
				_fillImage.type = (Type)1;
			}
			_fillRect = val5.GetComponent<RectTransform>();
			_fillRect.anchorMin = Vector2.zero;
			_fillRect.anchorMax = Vector2.one;
			_fillRect.pivot = new Vector2(0f, 0.5f);
			_fillRect.offsetMin = new Vector2(4f, 4f);
			_fillRect.offsetMax = new Vector2(-4f, -4f);
			((Transform)_fillRect).localScale = Vector3.one;
			TMP_FontAsset val6 = ResolveFont();
			if ((Object)(object)val6 != (Object)null)
			{
				GameObject val7 = new GameObject("Label");
				val7.transform.SetParent(_root.transform, false);
				_label = val7.AddComponent<TextMeshProUGUI>();
				((TMP_Text)_label).font = val6;
				((TMP_Text)_label).fontSize = 20f;
				((TMP_Text)_label).alignment = (TextAlignmentOptions)514;
				((TMP_Text)_label).enableWordWrapping = false;
				((TMP_Text)_label).text = string.Empty;
				RectTransform component5 = val7.GetComponent<RectTransform>();
				component5.anchorMin = Vector2.zero;
				component5.anchorMax = Vector2.one;
				component5.offsetMin = Vector2.zero;
				component5.offsetMax = Vector2.zero;
			}
			else
			{
				ClimbStaminaMod.LogDebug("No TMP font asset found; HUD runs bar-only");
			}
			_shownPercent = -1;
			_shownExhausted = false;
			_shownFraction = -1f;
			_shownColorBand = -1;
			return true;
		}

		private static Sprite CreateRoundedSprite(int radius, out Texture2D texture)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: 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)
			texture = null;
			try
			{
				int num = radius * 2 + 6;
				texture = new Texture2D(num, num, (TextureFormat)4, false);
				((Texture)texture).wrapMode = (TextureWrapMode)1;
				((Texture)texture).filterMode = (FilterMode)1;
				Color32[] array = (Color32[])(object)new Color32[num * num];
				float num2 = (float)num * 0.5f;
				float num3 = num2 - (float)radius;
				for (int i = 0; i < num; i++)
				{
					for (int j = 0; j < num; j++)
					{
						float num4 = Mathf.Max(Mathf.Abs((float)j + 0.5f - num2) - num3, 0f);
						float num5 = Mathf.Max(Mathf.Abs((float)i + 0.5f - num2) - num3, 0f);
						float num6 = Mathf.Sqrt(num4 * num4 + num5 * num5) - ((float)radius - 0.5f);
						float num7 = Mathf.Clamp01(0.5f - num6);
						array[i * num + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num7 * 255f));
					}
				}
				texture.SetPixels32(Il2CppStructArray<Color32>.op_Implicit(array));
				texture.Apply(false, true);
				float num8 = (float)radius + 2f;
				return Sprite.Create(texture, new Rect(0f, 0f, (float)num, (float)num), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(num8, num8, num8, num8));
			}
			catch (Exception ex)
			{
				ClimbStaminaMod.LogDebug("Rounded sprite creation failed (" + ex.GetType().Name + "); HUD keeps square corners");
				if ((Object)(object)texture != (Object)null)
				{
					Object.Destroy((Object)(object)texture);
				}
				texture = null;
				return null;
			}
		}

		private static Sprite CreateRoundedOutlineSprite(int radius, float thickness, bool soft, out Texture2D texture)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: 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)
			texture = null;
			try
			{
				int num = Mathf.CeilToInt(thickness * 0.5f + 1.5f);
				int num2 = radius * 2 + num * 2 + 4;
				texture = new Texture2D(num2, num2, (TextureFormat)4, false);
				((Texture)texture).wrapMode = (TextureWrapMode)1;
				((Texture)texture).filterMode = (FilterMode)1;
				Color32[] array = (Color32[])(object)new Color32[num2 * num2];
				float num3 = (float)num2 * 0.5f;
				float num4 = num3 - (float)num - (float)radius;
				for (int i = 0; i < num2; i++)
				{
					for (int j = 0; j < num2; j++)
					{
						float num5 = Mathf.Max(Mathf.Abs((float)j + 0.5f - num3) - num4, 0f);
						float num6 = Mathf.Max(Mathf.Abs((float)i + 0.5f - num3) - num4, 0f);
						float num7 = Mathf.Abs(Mathf.Sqrt(num5 * num5 + num6 * num6) - (float)radius);
						float num8;
						if (soft)
						{
							num8 = Mathf.Clamp01(1f - num7 / (thickness * 0.5f));
							num8 *= num8;
						}
						else
						{
							num8 = Mathf.Clamp01(thickness * 0.5f + 0.5f - num7);
						}
						array[i * num2 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num8 * 255f));
					}
				}
				texture.SetPixels32(Il2CppStructArray<Color32>.op_Implicit(array));
				texture.Apply(false, true);
				float num9 = (float)(radius + num) + 1f;
				return Sprite.Create(texture, new Rect(0f, 0f, (float)num2, (float)num2), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4(num9, num9, num9, num9));
			}
			catch (Exception ex)
			{
				ClimbStaminaMod.LogDebug("Outline sprite creation failed (" + ex.GetType().Name + "); HUD runs without an outline");
				if ((Object)(object)texture != (Object)null)
				{
					Object.Destroy((Object)(object)texture);
				}
				texture = null;
				return null;
			}
		}

		private static TMP_FontAsset ResolveFont()
		{
			TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset;
			if ((Object)(object)defaultFontAsset != (Object)null)
			{
				return defaultFontAsset;
			}
			Il2CppReferenceArray<Object> val = Resources.FindObjectsOfTypeAll(Il2CppType.Of<TMP_FontAsset>());
			if (val != null && ((Il2CppArrayBase<Object>)(object)val).Length > 0)
			{
				return ((Il2CppObjectBase)((Il2CppArrayBase<Object>)(object)val)[0]).TryCast<TMP_FontAsset>();
			}
			return null;
		}
	}
}
namespace ClimbStamina.Compatibility
{
	internal static class ClimbAnythingCompatibility
	{
		internal static bool ClimbAnythingDetected { get; private set; }

		internal static void Detect()
		{
			foreach (MelonBase registeredMelon in MelonBase.RegisteredMelons)
			{
				MelonInfoAttribute info = registeredMelon.Info;
				string text = ((info != null) ? info.Name : null);
				if (text != null && text.Replace(" ", "").ToLowerInvariant().Contains("climbanything"))
				{
					ClimbAnythingDetected = true;
					ClimbStaminaMod.Log("'" + text + "' detected; its climbing grips are covered by stamina rules automatically.");
					break;
				}
			}
		}
	}
	internal static class FusionCompatibility
	{
		internal static bool FusionDetected { get; private set; }

		internal static void Detect()
		{
			FusionDetected = MelonBase.FindMelon("LabFusion", "Lakatrazz") != null;
			if (FusionDetected)
			{
				ClimbStaminaMod.Log("Fusion detected; local-only mode enabled.");
			}
		}
	}
}